diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index c420f8e6729..5b47bf0308d 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -49,6 +49,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/testconfig/ocr" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" @@ -609,29 +610,48 @@ func TrackForwarder( Msg("Forwarder tracked") } -// DeployOCRv2Contracts deploys a number of OCRv2 contracts and configures them with defaults -func DeployOCRv2Contracts( +// SetupOCRv2Contracts deploys a number of OCRv2 contracts and configures them with defaults +func SetupOCRv2Contracts( l zerolog.Logger, seth *seth.Client, - numberOfContracts int, + ocrContractsConfig ocr.OffChainAggregatorsConfig, linkTokenAddress common.Address, transmitters []string, ocrOptions contracts.OffchainOptions, ) ([]contracts.OffchainAggregatorV2, error) { var ocrInstances []contracts.OffchainAggregatorV2 - for contractCount := 0; contractCount < numberOfContracts; contractCount++ { - ocrInstance, err := contracts.DeployOffchainAggregatorV2( - l, - seth, - linkTokenAddress, - ocrOptions, - ) - if err != nil { - return nil, fmt.Errorf("OCRv2 instance deployment have failed: %w", err) + + if ocrContractsConfig == nil { + return nil, fmt.Errorf("you need to pass non-nil OffChainAggregatorsConfig to setup OCR contracts") + } + + if !ocrContractsConfig.UseExistingOffChainAggregatorsContracts() { + for contractCount := 0; contractCount < ocrContractsConfig.NumberOfContractsToDeploy(); contractCount++ { + ocrInstance, err := contracts.DeployOffchainAggregatorV2( + l, + seth, + linkTokenAddress, + ocrOptions, + ) + if err != nil { + return nil, fmt.Errorf("OCRv2 instance deployment have failed: %w", err) + } + ocrInstances = append(ocrInstances, &ocrInstance) + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } } - ocrInstances = append(ocrInstances, &ocrInstance) - if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some - time.Sleep(2 * time.Second) + } else { + for _, address := range ocrContractsConfig.OffChainAggregatorsContractsAddresses() { + ocrInstance, err := contracts.LoadOffchainAggregatorV2(l, seth, address) + if err != nil { + return nil, fmt.Errorf("OCRv2 instance loading have failed: %w", err) + } + ocrInstances = append(ocrInstances, &ocrInstance) + } + + if !ocrContractsConfig.ConfigureExistingOffChainAggregatorsContracts() { + return ocrInstances, nil } } @@ -780,7 +800,7 @@ func StartNewRound( func DeployOCRContractsForwarderFlow( logger zerolog.Logger, seth *seth.Client, - numberOfContracts int, + ocrContractsConfig ocr.OffChainAggregatorsConfig, linkTokenContractAddress common.Address, workerNodes []contracts.ChainlinkNodeWithKeysAndAddress, forwarderAddresses []common.Address, @@ -801,23 +821,23 @@ func DeployOCRContractsForwarderFlow( return forwarderAddresses, nil } - return deployAnyOCRv1Contracts(logger, seth, numberOfContracts, linkTokenContractAddress, workerNodes, transmitterPayeesFn, transmitterAddressesFn) + return setupAnyOCRv1Contracts(logger, seth, ocrContractsConfig, linkTokenContractAddress, workerNodes, transmitterPayeesFn, transmitterAddressesFn) } -// DeployOCRv1Contracts deploys and funds a certain number of offchain aggregator contracts -func DeployOCRv1Contracts( +// SetupOCRv1Contracts deploys and funds a certain number of offchain aggregator contracts or uses existing ones and returns a slice of contract wrappers. +func SetupOCRv1Contracts( logger zerolog.Logger, seth *seth.Client, - numberOfContracts int, + ocrContractsConfig ocr.OffChainAggregatorsConfig, linkTokenContractAddress common.Address, workerNodes []contracts.ChainlinkNodeWithKeysAndAddress, ) ([]contracts.OffchainAggregator, error) { transmitterPayeesFn := func() (transmitters []string, payees []string, err error) { transmitters = make([]string, 0) payees = make([]string, 0) - for _, node := range workerNodes { + for _, n := range workerNodes { var addr string - addr, err = node.PrimaryEthAddress() + addr, err = n.PrimaryEthAddress() if err != nil { err = fmt.Errorf("error getting node's primary ETH address: %w", err) return @@ -831,8 +851,8 @@ func DeployOCRv1Contracts( transmitterAddressesFn := func() ([]common.Address, error) { transmitterAddresses := make([]common.Address, 0) - for _, node := range workerNodes { - primaryAddress, err := node.PrimaryEthAddress() + for _, n := range workerNodes { + primaryAddress, err := n.PrimaryEthAddress() if err != nil { return nil, err } @@ -842,28 +862,48 @@ func DeployOCRv1Contracts( return transmitterAddresses, nil } - return deployAnyOCRv1Contracts(logger, seth, numberOfContracts, linkTokenContractAddress, workerNodes, transmitterPayeesFn, transmitterAddressesFn) + return setupAnyOCRv1Contracts(logger, seth, ocrContractsConfig, linkTokenContractAddress, workerNodes, transmitterPayeesFn, transmitterAddressesFn) } -func deployAnyOCRv1Contracts( +func setupAnyOCRv1Contracts( logger zerolog.Logger, seth *seth.Client, - numberOfContracts int, + ocrContractsConfig ocr.OffChainAggregatorsConfig, linkTokenContractAddress common.Address, workerNodes []contracts.ChainlinkNodeWithKeysAndAddress, getTransmitterAndPayeesFn func() ([]string, []string, error), getTransmitterAddressesFn func() ([]common.Address, error), ) ([]contracts.OffchainAggregator, error) { - // Deploy contracts var ocrInstances []contracts.OffchainAggregator - for contractCount := 0; contractCount < numberOfContracts; contractCount++ { - ocrInstance, err := contracts.DeployOffchainAggregator(logger, seth, linkTokenContractAddress, contracts.DefaultOffChainAggregatorOptions()) - if err != nil { - return nil, fmt.Errorf("OCR instance deployment have failed: %w", err) + + if ocrContractsConfig == nil { + return nil, fmt.Errorf("you need to pass non-nil OffChainAggregatorsConfig to setup OCR contracts") + } + + if !ocrContractsConfig.UseExistingOffChainAggregatorsContracts() { + // Deploy contracts + for contractCount := 0; contractCount < ocrContractsConfig.NumberOfContractsToDeploy(); contractCount++ { + ocrInstance, err := contracts.DeployOffchainAggregator(logger, seth, linkTokenContractAddress, contracts.DefaultOffChainAggregatorOptions()) + if err != nil { + return nil, fmt.Errorf("OCR instance deployment have failed: %w", err) + } + ocrInstances = append(ocrInstances, &ocrInstance) + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } } - ocrInstances = append(ocrInstances, &ocrInstance) - if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some - time.Sleep(2 * time.Second) + } else { + // Load contract wrappers + for _, address := range ocrContractsConfig.OffChainAggregatorsContractsAddresses() { + ocrInstance, err := contracts.LoadOffChainAggregator(logger, seth, address) + if err != nil { + return nil, fmt.Errorf("OCR instance loading have failed: %w", err) + } + ocrInstances = append(ocrInstances, &ocrInstance) + } + + if !ocrContractsConfig.ConfigureExistingOffChainAggregatorsContracts() { + return ocrInstances, nil } } diff --git a/integration-tests/actions/contracts.go b/integration-tests/actions/contracts.go new file mode 100644 index 00000000000..1a50c4d7ba9 --- /dev/null +++ b/integration-tests/actions/contracts.go @@ -0,0 +1,23 @@ +package actions + +import ( + "github.com/rs/zerolog" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" +) + +// LinkTokenContract returns a link token contract instance. Depending on test configuration, it either deploys a new one or uses an existing one. +func LinkTokenContract(l zerolog.Logger, sethClient *seth.Client, configWithLinkToken tc.LinkTokenContractConfig) (*contracts.EthereumLinkToken, error) { + if configWithLinkToken != nil && configWithLinkToken.UseExistingLinkTokenContract() { + linkAddress, err := configWithLinkToken.LinkTokenContractAddress() + if err != nil { + return nil, err + } + + return contracts.LoadLinkTokenContract(l, sethClient, linkAddress) + } + return contracts.DeployLinkTokenContract(l, sethClient) +} diff --git a/integration-tests/actions/ocr_helpers.go b/integration-tests/actions/ocr_helpers.go index 19cad817b75..0f6f65d1289 100644 --- a/integration-tests/actions/ocr_helpers.go +++ b/integration-tests/actions/ocr_helpers.go @@ -21,6 +21,8 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/testconfig/ocr" ) // This actions file often returns functions, rather than just values. These are used as common test helpers, and are @@ -229,13 +231,14 @@ func BuildNodeContractPairID(node contracts.ChainlinkNodeWithKeysAndAddress, ocr func SetupOCRv1Cluster( l zerolog.Logger, seth *seth.Client, + configWithLinkToken tc.LinkTokenContractConfig, workerNodes []*client.ChainlinkK8sClient, ) (common.Address, error) { err := FundChainlinkNodesFromRootAddress(l, seth, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes), big.NewFloat(3)) if err != nil { return common.Address{}, err } - linkContract, err := contracts.DeployLinkTokenContract(l, seth) + linkContract, err := LinkTokenContract(l, seth, configWithLinkToken) if err != nil { return common.Address{}, err } @@ -246,11 +249,12 @@ func SetupOCRv1Feed( l zerolog.Logger, seth *seth.Client, lta common.Address, + ocrContractsConfig ocr.OffChainAggregatorsConfig, msClient *ctfClient.MockserverClient, bootstrapNode *client.ChainlinkK8sClient, workerNodes []*client.ChainlinkK8sClient, ) ([]contracts.OffchainAggregator, error) { - ocrInstances, err := DeployOCRv1Contracts(l, seth, 1, lta, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes)) + ocrInstances, err := SetupOCRv1Contracts(l, seth, ocrContractsConfig, lta, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes)) if err != nil { return nil, err } diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 7ef03d98c8a..821d3cc48c0 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -179,13 +179,13 @@ func TestOCRChaos(t *testing.T) { }) ms := ctfClient.ConnectMockServer(testEnvironment) - linkContract, err := contracts.DeployLinkTokenContract(l, seth) + linkContract, err := actions.LinkTokenContract(l, seth, config.OCR) require.NoError(t, err, "Error deploying link token contract") err = actions.FundChainlinkNodesFromRootAddress(l, seth, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(chainlinkNodes), big.NewFloat(10)) require.NoError(t, err) - ocrInstances, err := actions.DeployOCRv1Contracts(l, seth, 1, common.HexToAddress(linkContract.Address()), contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes)) + ocrInstances, err := actions.SetupOCRv1Contracts(l, seth, config.OCR, common.HexToAddress(linkContract.Address()), contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes)) require.NoError(t, err) err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, ms, fmt.Sprint(seth.ChainID)) require.NoError(t, err) diff --git a/integration-tests/contracts/ethereum_contracts.go b/integration-tests/contracts/ethereum_contracts.go index c2f86286faf..288a36978a8 100644 --- a/integration-tests/contracts/ethereum_contracts.go +++ b/integration-tests/contracts/ethereum_contracts.go @@ -204,22 +204,17 @@ type EthereumOffchainAggregator struct { l zerolog.Logger } -func LoadOffchainAggregator(l zerolog.Logger, seth *seth.Client, contractAddress common.Address) (EthereumOffchainAggregator, error) { - abi, err := offchainaggregator.OffchainAggregatorMetaData.GetAbi() - if err != nil { - return EthereumOffchainAggregator{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) - } - seth.ContractStore.AddABI("OffChainAggregator", *abi) - seth.ContractStore.AddBIN("OffChainAggregator", common.FromHex(offchainaggregator.OffchainAggregatorMetaData.Bin)) +func LoadOffChainAggregator(l zerolog.Logger, sethClient *seth.Client, contractAddress common.Address) (EthereumOffchainAggregator, error) { + loader := seth.NewContractLoader[offchainaggregator.OffchainAggregator](sethClient) + instance, err := loader.LoadContract("LinkToken", contractAddress, offchainaggregator.OffchainAggregatorMetaData.GetAbi, offchainaggregator.NewOffchainAggregator) - ocr, err := offchainaggregator.NewOffchainAggregator(contractAddress, wrappers.MustNewWrappedContractBackend(nil, seth)) if err != nil { - return EthereumOffchainAggregator{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + return EthereumOffchainAggregator{}, fmt.Errorf("failed to instantiate OCR v2 instance: %w", err) } return EthereumOffchainAggregator{ - client: seth, - ocr: ocr, + client: sethClient, + ocr: instance, address: &contractAddress, l: l, }, nil @@ -357,10 +352,6 @@ func (o *EthereumOffchainAggregator) SetConfig( return err } - // fails with error setting OCR config for contract '0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82': both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified - // but we only have gasPrice set... It also fails with the same error when we enable EIP-1559 - // fails when we wait for it to be minted, inside the wrapper there's no error when we call it, so it must be something inside smart contract - // that's reverting it and maybe the error message is completely off _, err = o.client.Decode(o.ocr.SetConfig(o.client.NewTXOpts(), signers, transmitters, threshold, encodedConfigVersion, encodedConfig)) return err } @@ -584,36 +575,36 @@ type EthereumOffchainAggregatorV2 struct { l zerolog.Logger } -func LoadOffChainAggregatorV2(l zerolog.Logger, seth *seth.Client, contractAddress common.Address) (EthereumOffchainAggregatorV2, error) { - oAbi, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi() +func LoadOffchainAggregatorV2(l zerolog.Logger, seth *seth.Client, address common.Address) (EthereumOffchainAggregatorV2, error) { + contractAbi, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi() if err != nil { - return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to get OffChain Aggregator v2 ABI: %w", err) } - seth.ContractStore.AddABI("OffChainAggregatorV2", *oAbi) + seth.ContractStore.AddABI("OffChainAggregatorV2", *contractAbi) seth.ContractStore.AddBIN("OffChainAggregatorV2", common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin)) - ocr2, err := ocr2aggregator.NewOCR2Aggregator(contractAddress, seth.Client) + ocr2, err := ocr2aggregator.NewOCR2Aggregator(address, seth.Client) if err != nil { - return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to instantiate OCRv2 instance: %w", err) } return EthereumOffchainAggregatorV2{ client: seth, contract: ocr2, - address: &contractAddress, + address: &address, l: l, }, nil } func DeployOffchainAggregatorV2(l zerolog.Logger, seth *seth.Client, linkTokenAddress common.Address, offchainOptions OffchainOptions) (EthereumOffchainAggregatorV2, error) { - oAbi, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi() + contractAbi, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi() if err != nil { - return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to get OffChain Aggregator v2 ABI: %w", err) } - seth.ContractStore.AddABI("OffChainAggregatorV2", *oAbi) + seth.ContractStore.AddABI("OffChainAggregatorV2", *contractAbi) seth.ContractStore.AddBIN("OffChainAggregatorV2", common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin)) - ocrDeploymentData2, err := seth.DeployContract(seth.NewTXOpts(), "OffChainAggregatorV2", *oAbi, common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin), + ocrDeploymentData2, err := seth.DeployContract(seth.NewTXOpts(), "OffChainAggregatorV2", *contractAbi, common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin), linkTokenAddress, offchainOptions.MinimumAnswer, offchainOptions.MaximumAnswer, @@ -624,12 +615,12 @@ func DeployOffchainAggregatorV2(l zerolog.Logger, seth *seth.Client, linkTokenAd ) if err != nil { - return EthereumOffchainAggregatorV2{}, fmt.Errorf("OCR instance deployment have failed: %w", err) + return EthereumOffchainAggregatorV2{}, fmt.Errorf("OCRv2 instance deployment have failed: %w", err) } ocr2, err := ocr2aggregator.NewOCR2Aggregator(ocrDeploymentData2.Address, wrappers.MustNewWrappedContractBackend(nil, seth)) if err != nil { - return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to instantiate OCRv2 instance: %w", err) } return EthereumOffchainAggregatorV2{ @@ -772,22 +763,16 @@ func DeployLinkTokenContract(l zerolog.Logger, client *seth.Client) (*EthereumLi } func LoadLinkTokenContract(l zerolog.Logger, client *seth.Client, address common.Address) (*EthereumLinkToken, error) { - linkABI, err := link_token_interface.LinkTokenMetaData.GetAbi() - if err != nil { - return &EthereumLinkToken{}, fmt.Errorf("failed to get LinkToken ABI: %w", err) - } + loader := seth.NewContractLoader[link_token_interface.LinkToken](client) + instance, err := loader.LoadContract("LinkToken", address, link_token_interface.LinkTokenMetaData.GetAbi, link_token_interface.NewLinkToken) - client.ContractStore.AddABI("LinkToken", *linkABI) - client.ContractStore.AddBIN("LinkToken", common.FromHex(link_token_interface.LinkTokenMetaData.Bin)) - - linkToken, err := link_token_interface.NewLinkToken(address, wrappers.MustNewWrappedContractBackend(nil, client)) if err != nil { return &EthereumLinkToken{}, fmt.Errorf("failed to instantiate LinkToken instance: %w", err) } return &EthereumLinkToken{ client: client, - instance: linkToken, + instance: instance, address: address, l: l, }, nil diff --git a/integration-tests/crib/ocr_test.go b/integration-tests/crib/ocr_test.go index 69804e24d77..215734c318e 100644 --- a/integration-tests/crib/ocr_test.go +++ b/integration-tests/crib/ocr_test.go @@ -8,25 +8,30 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/havoc" "github.com/smartcontractkit/chainlink-testing-framework/lib/client" + "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" + "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + ocr_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/ocr" ) // TestCRIBChaos an example of how we can run chaos tests with havoc and core CRIB func TestCRIBChaos(t *testing.T) { l := logging.GetTestLogger(t) + config, err := tc.GetConfig([]string{"Crib"}, tc.OCR) + require.NoError(t, err) sethClient, msClient, bootstrapNode, workerNodes, _, err := ConnectRemote() require.NoError(t, err) - lta, err := actions.SetupOCRv1Cluster(l, sethClient, workerNodes) + lta, err := actions.SetupOCRv1Cluster(l, sethClient, config.OCR, workerNodes) require.NoError(t, err) - ocrInstances, err := actions.SetupOCRv1Feed(l, sethClient, lta, msClient, bootstrapNode, workerNodes) + ocrInstances, err := actions.SetupOCRv1Feed(l, sethClient, lta, config.OCR, msClient, bootstrapNode, workerNodes) require.NoError(t, err) err = actions.SetAllAdapterResponsesToTheSameValue(10, ocrInstances, workerNodes, msClient) @@ -41,11 +46,12 @@ func TestCRIBChaos(t *testing.T) { 1*time.Second, os.Getenv("CRIB_NAMESPACE"), ) + require.NoError(t, err, "Error rebooting CL namespace") ch.Create(context.Background()) ch.AddListener(havoc.NewChaosLogger(l)) t.Cleanup(func() { err := ch.Delete(context.Background()) - require.NoError(t, err) + require.NoError(t, err, "Error deleting chaos") }) require.Eventually(t, func() bool { err = actions.WatchNewOCRRound(l, sethClient, 3, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), 5*time.Minute) @@ -65,9 +71,15 @@ func TestCRIBRPCChaos(t *testing.T) { sethClient, msClient, bootstrapNode, workerNodes, vars, err := ConnectRemote() require.NoError(t, err) - lta, err := actions.SetupOCRv1Cluster(l, sethClient, workerNodes) + ocrConfig := &ocr_config.Config{ + Contracts: &ocr_config.Contracts{ + ShouldBeUsed: ptr.Ptr(false), + }, + } + + lta, err := actions.SetupOCRv1Cluster(l, sethClient, ocrConfig, workerNodes) require.NoError(t, err) - ocrInstances, err := actions.SetupOCRv1Feed(l, sethClient, lta, msClient, bootstrapNode, workerNodes) + ocrInstances, err := actions.SetupOCRv1Feed(l, sethClient, lta, ocrConfig, msClient, bootstrapNode, workerNodes) require.NoError(t, err) err = actions.SetAllAdapterResponsesToTheSameValue(10, ocrInstances, workerNodes, msClient) diff --git a/integration-tests/load/ocr/ocr_test.go b/integration-tests/load/ocr/ocr_test.go index 281d2da2bb1..55b683a0a74 100644 --- a/integration-tests/load/ocr/ocr_test.go +++ b/integration-tests/load/ocr/ocr_test.go @@ -31,9 +31,9 @@ func TestOCRLoad(t *testing.T) { sethClient, msClient, bootstrapNode, workerNodes, _, err := crib.ConnectRemote() require.NoError(t, err) - lta, err := actions.SetupOCRv1Cluster(l, sethClient, workerNodes) + lta, err := actions.SetupOCRv1Cluster(l, sethClient, config.OCR, workerNodes) require.NoError(t, err) - ocrInstances, err := actions.SetupOCRv1Feed(l, sethClient, lta, msClient, bootstrapNode, workerNodes) + ocrInstances, err := actions.SetupOCRv1Feed(l, sethClient, lta, config.OCR, msClient, bootstrapNode, workerNodes) require.NoError(t, err) cfg := config.OCR @@ -64,7 +64,7 @@ func TestOCRVolume(t *testing.T) { sethClient, msClient, bootstrapNode, workerNodes, _, err := crib.ConnectRemote() require.NoError(t, err) - lta, err := actions.SetupOCRv1Cluster(l, sethClient, workerNodes) + lta, err := actions.SetupOCRv1Cluster(l, sethClient, config.OCR, workerNodes) require.NoError(t, err) cfg := config.OCR @@ -77,7 +77,7 @@ func TestOCRVolume(t *testing.T) { LoadType: wasp.VU, CallTimeout: cfg.Volume.VerificationTimeout.Duration, Schedule: wasp.Plain(*cfg.Volume.Rate, cfg.Volume.TestDuration.Duration), - VU: NewVU(l, sethClient, *cfg.Volume.VURequestsPerUnit, cfg.Volume.RateLimitUnitDuration.Duration, lta, bootstrapNode, workerNodes, msClient), + VU: NewVU(l, sethClient, cfg, *cfg.Volume.VURequestsPerUnit, cfg.Volume.RateLimitUnitDuration.Duration, lta, bootstrapNode, workerNodes, msClient), Labels: CommonTestLabels, LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })) diff --git a/integration-tests/load/ocr/vu.go b/integration-tests/load/ocr/vu.go index aece9cb74b1..c337e338a82 100644 --- a/integration-tests/load/ocr/vu.go +++ b/integration-tests/load/ocr/vu.go @@ -8,18 +8,16 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" - - "github.com/smartcontractkit/chainlink-testing-framework/seth" - "go.uber.org/ratelimit" - "github.com/smartcontractkit/chainlink-testing-framework/wasp" - client2 "github.com/smartcontractkit/chainlink-testing-framework/lib/client" + "github.com/smartcontractkit/chainlink-testing-framework/seth" + "github.com/smartcontractkit/chainlink-testing-framework/wasp" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/testconfig/ocr" ) // VU is a virtual user for the OCR load test @@ -37,11 +35,13 @@ type VU struct { msClient *client2.MockserverClient l zerolog.Logger ocrInstances []contracts.OffchainAggregator + config ocr.OffChainAggregatorsConfig } func NewVU( l zerolog.Logger, seth *seth.Client, + config ocr.OffChainAggregatorsConfig, rate int, rateUnit time.Duration, lta common.Address, @@ -60,6 +60,7 @@ func NewVU( msClient: msClient, bootstrapNode: bootstrapNode, workerNodes: workerNodes, + config: config, } } @@ -75,11 +76,12 @@ func (m *VU) Clone(_ *wasp.Generator) wasp.VirtualUser { msClient: m.msClient, bootstrapNode: m.bootstrapNode, workerNodes: m.workerNodes, + config: m.config, } } func (m *VU) Setup(_ *wasp.Generator) error { - ocrInstances, err := actions.DeployOCRv1Contracts(m.l, m.seth, 1, m.lta, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(m.workerNodes)) + ocrInstances, err := actions.SetupOCRv1Contracts(m.l, m.seth, m.config, m.lta, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(m.workerNodes)) if err != nil { return err } diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index 4441a592886..5e5fb1d40b6 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -60,8 +60,8 @@ func TestForwarderOCRBasic(t *testing.T) { _ = actions.ReturnFundsFromNodes(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs())) }) - lt, err := contracts.DeployLinkTokenContract(l, sethClient) - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + linkContract, err := actions.LinkTokenContract(l, sethClient, config.OCR) + require.NoError(t, err, "Error loading/deploying link token contract") fundingAmount := big.NewFloat(.05) l.Info().Str("ETH amount per node", fundingAmount.String()).Msg("Funding Chainlink nodes") @@ -69,7 +69,7 @@ func TestForwarderOCRBasic(t *testing.T) { require.NoError(t, err, "Error funding Chainlink nodes") operators, authorizedForwarders, _ := actions.DeployForwarderContracts( - t, sethClient, common.HexToAddress(lt.Address()), len(workerNodes), + t, sethClient, common.HexToAddress(linkContract.Address()), len(workerNodes), ) require.Equal(t, len(workerNodes), len(operators), "Number of operators should match number of worker nodes") @@ -81,11 +81,12 @@ func TestForwarderOCRBasic(t *testing.T) { require.NoError(t, err, "Accepting Authorize Receivers on Operator shouldn't fail") actions.TrackForwarder(t, sethClient, authorizedForwarders[i], workerNodes[i]) } + ocrInstances, err := actions.DeployOCRContractsForwarderFlow( l, sethClient, - 1, - common.HexToAddress(lt.Address()), + config.OCR, + common.HexToAddress(linkContract.Address()), contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes), authorizedForwarders, ) diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index 219df9ce432..0cc7d9fafe4 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -61,8 +61,8 @@ func TestForwarderOCR2Basic(t *testing.T) { _ = actions.ReturnFundsFromNodes(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs())) }) - lt, err := contracts.DeployLinkTokenContract(l, sethClient) - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + linkContract, err := actions.LinkTokenContract(l, sethClient, config.OCR2) + require.NoError(t, err, "Error loading/deploying link token contract") fundingAmount := big.NewFloat(.05) l.Info().Str("ETH amount per node", fundingAmount.String()).Msg("Funding Chainlink nodes") @@ -70,7 +70,7 @@ func TestForwarderOCR2Basic(t *testing.T) { require.NoError(t, err, "Error funding Chainlink nodes") operators, authorizedForwarders, _ := actions.DeployForwarderContracts( - t, sethClient, common.HexToAddress(lt.Address()), len(workerNodes), + t, sethClient, common.HexToAddress(linkContract.Address()), len(workerNodes), ) require.Equal(t, len(workerNodes), len(operators), "Number of operators should match number of worker nodes") @@ -90,7 +90,7 @@ func TestForwarderOCR2Basic(t *testing.T) { } ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - ocrInstances, err := actions.DeployOCRv2Contracts(l, sethClient, 1, common.HexToAddress(lt.Address()), transmitters, ocrOffchainOptions) + ocrInstances, err := actions.SetupOCRv2Contracts(l, sethClient, config.OCR2, common.HexToAddress(linkContract.Address()), transmitters, ocrOffchainOptions) require.NoError(t, err, "Error deploying OCRv2 contracts with forwarders") ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 3203037ae55..325c88f979a 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -103,11 +103,11 @@ func TestOCRv2JobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - env, aggregatorContracts, sethClient := prepareORCv2SmokeTestEnv(t, defaultTestData(), l, 5) - nodeClients := env.ClCluster.NodeAPIs() + testEnv, aggregatorContracts, sethClient := prepareORCv2SmokeTestEnv(t, defaultTestData(), l, 5) + nodeClients := testEnv.ClCluster.NodeAPIs() bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - err := env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) + err := testEnv.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) require.NoError(t, err) err = actions.WatchNewOCRRound(l, sethClient, 2, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) require.NoError(t, err, "Error watching for new OCR2 round") @@ -125,7 +125,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.DeleteBridges(nodeClients) require.NoError(t, err) - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, uint64(sethClient.ChainID), false, false) + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, testEnv.MockAdapter, "ocr2", 15, uint64(sethClient.ChainID), false, false) require.NoError(t, err, "Error creating OCRv2 jobs") err = actions.WatchNewOCRRound(l, sethClient, 3, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*3) @@ -170,8 +170,8 @@ func prepareORCv2SmokeTestEnv(t *testing.T, testData ocr2test, l zerolog.Logger, nodeClients := testEnv.ClCluster.NodeAPIs() bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - linkContract, err := contracts.DeployLinkTokenContract(l, sethClient) - require.NoError(t, err, "Error deploying link token contract") + linkContract, err := actions.LinkTokenContract(l, sethClient, config.OCR2) + require.NoError(t, err, "Error loading/deploying link token contract") err = actions.FundChainlinkNodesFromRootAddress(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes), big.NewFloat(*config.Common.ChainlinkNodeFunding)) require.NoError(t, err, "Error funding Chainlink nodes") @@ -191,18 +191,20 @@ func prepareORCv2SmokeTestEnv(t *testing.T, testData ocr2test, l zerolog.Logger, transmitters = append(transmitters, addr) } - ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(l, sethClient, 1, common.HexToAddress(linkContract.Address()), transmitters, ocrOffchainOptions) + ocrOffChainOptions := contracts.DefaultOffChainAggregatorOptions() + aggregatorContracts, err := actions.SetupOCRv2Contracts(l, sethClient, config.OCR2, common.HexToAddress(linkContract.Address()), transmitters, ocrOffChainOptions) require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, testEnv.MockAdapter, "ocr2", 5, uint64(sethClient.ChainID), false, testData.chainReaderAndCodec) require.NoError(t, err, "Error creating OCRv2 jobs") - ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) - require.NoError(t, err, "Error building OCRv2 config") + if !config.OCR2.UseExistingOffChainAggregatorsContracts() || (config.OCR2.UseExistingOffChainAggregatorsContracts() && config.OCR2.ConfigureExistingOffChainAggregatorsContracts()) { + ocrV2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffChainOptions) + require.NoError(t, err, "Error building OCRv2 config") - err = actions.ConfigureOCRv2AggregatorContracts(ocrv2Config, aggregatorContracts) - require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") + err = actions.ConfigureOCRv2AggregatorContracts(ocrV2Config, aggregatorContracts) + require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") + } assertCorrectNodeConfiguration(t, l, clNodeCount, testData, testEnv) diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index cd9db0e9740..a19adb5c022 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -113,10 +113,10 @@ func prepareORCv1SmokeTestEnv(t *testing.T, l zerolog.Logger, firstRoundResult i _ = actions.ReturnFundsFromNodes(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs())) }) - linkContract, err := contracts.DeployLinkTokenContract(l, sethClient) - require.NoError(t, err, "Error deploying link token contract") + linkContract, err := actions.LinkTokenContract(l, sethClient, config.OCR) + require.NoError(t, err, "Error loading/deploying link token contract") - ocrInstances, err := actions.DeployOCRv1Contracts(l, sethClient, 1, common.HexToAddress(linkContract.Address()), contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes)) + ocrInstances, err := actions.SetupOCRv1Contracts(l, sethClient, config.OCR, common.HexToAddress(linkContract.Address()), contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes)) require.NoError(t, err, "Error deploying OCR contracts") err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, big.NewInt(sethClient.ChainID)) diff --git a/integration-tests/testconfig/README.md b/integration-tests/testconfig/README.md index c698281b76e..f6186580550 100644 --- a/integration-tests/testconfig/README.md +++ b/integration-tests/testconfig/README.md @@ -194,6 +194,96 @@ BumpMin = '100 gwei' For more examples see `example.toml` in product TOML configs like `testconfig/automation/example.toml`. If either ChainConfigTOMLByChainID or CommonChainConfigTOML is defined, it will override any defaults that Chainlink Node might have for the given network. Part of the configuration that defines blockchain node URLs is always dynamically generated based on the EVMNetwork configuration. Currently, all networks are treated as EVM networks. There's no way to provide Solana, Starknet, Cosmos or Aptos configuration yet. +### OCR tests contract config +In order to allow running OCR soak/load/smoke tests with already deployed contracts, we have provided an experimental feature for providing addresses of LINK token and OCR contracts in the TOML config. Additionally, user can choose, whether existing OCR contracts should be configured or not. +If no contract addresses are provided, the tests will deploy new contracts. + +The feature is highly configurable and it possible to use existing LINK token contract, but deploy new OCR contracts or vice versa. Both OCRv1 and OCRv2 contracts are supported. + +To use existing LINK and OCRv1 contracts, provide the following configuration in the TOML file: +```toml +[OCR.Contracts] +link_token = "0x88d1239894D9582f5849E5b5a964da9e5730f1E6" +offchain_aggregators = ["0xc1ce3815d6e7f3705265c2577F1342344752A5Eb"] +``` + +For OCRv2, provide the following configuration: +```toml +[OCR2.Contracts] +link_token = "0x88d1239894D9582f5849E5b5a964da9e5730f1E6" +offchain_aggregators = ["0xc1ce3815d6e7f3705265c2577F1342344752A5Eb"] +``` + +If you want to disable them, you can set `use = false` or remove the addresses from the configuration. + +If you want to use existing OCRv1 contract, without configuring it, you can set `configure = false` in the configuration: +```toml +[OCR.Contracts] +link_token = "0x88d1239894D9582f5849E5b5a964da9e5730f1E6" +offchain_aggregators = ["0xc1ce3815d6e7f3705265c2577F1342344752A5Eb"] + +# notice that this address needs to match the one in offchain_aggregators +[OCR.Contracts.Settings."0xc1ce3815d6e7f3705265c2577F1342344752A5Eb"] +configure = false +``` + +Be aware that using multiple existing OCR contracts, but configuring only some of them is not supported. This is not a valid configuration: +```toml +[OCR.Contracts] +link_token = "0x88d1239894D9582f5849E5b5a964da9e5730f1E6" +offchain_aggregators = ["0xc1ce3815d6e7f3705265c2577F1342344752A5Eb", "0x2f4FA21fCd917C448C160caafEC874032F404c08"] + +# notice that this address needs to match the one in offchain_aggregators +[OCR.Contracts.Settings."0xc1ce3815d6e7f3705265c2577F1342344752A5Eb"] +configure = false + +# if setting for a given address is not present, we assume it should be used and configured +# so in this case "0x2f4FA21fCd917C448C160caafEC874032F404c08" will be evaluated as configure = true, +# but "0xc1ce3815d6e7f3705265c2577F1342344752A5Eb" is set to configure = false. +# this will fail configuration validation +``` + +This, more explicit version is also invalid: +```toml +[OCR.Contracts] +link_token = "0x88d1239894D9582f5849E5b5a964da9e5730f1E6" +offchain_aggregators = ["0xc1ce3815d6e7f3705265c2577F1342344752A5Eb", "0x2f4FA21fCd917C448C160caafEC874032F404c08"] + +# notice that this address needs to match the one in offchain_aggregators +[OCR.Contracts.Settings."0xc1ce3815d6e7f3705265c2577F1342344752A5Eb"] +configure = false + +[OCR.Contracts.Settings."0x2f4FA21fCd917C448C160caafEC874032F404c08"] +configure = true +``` + +Similarly, this one is also invalid: +```toml +[OCR.Contracts] +link_token = "0x88d1239894D9582f5849E5b5a964da9e5730f1E6" +offchain_aggregators = ["0xc1ce3815d6e7f3705265c2577F1342344752A5Eb", "0x2f4FA21fCd917C448C160caafEC874032F404c08"] + +# notice that this address needs to match the one in offchain_aggregators +[OCR.Contracts.Settings."0xc1ce3815d6e7f3705265c2577F1342344752A5Eb"] +use = false + +[OCR.Contracts.Settings."0x2f4FA21fCd917C448C160caafEC874032F404c08"] +use = true +``` + +There are no settings available for LINK token contract. + +Last, but not least, when deploying new OCR contracts you need to provide their number. For example: +```toml +# for OCRv1 +[OCR.Common] +number_of_contracts=2 + +# for OCRv2 +[OCR2.Common] +number_of_contracts=2 +``` + ### Setting env vars for Chainlink Node To set env vars for Chainlink Node use `WithCLNodeOptions()` and `WithNodeEnvVars()` when building a test environment. Example: diff --git a/integration-tests/testconfig/forwarder_ocr/example.toml b/integration-tests/testconfig/forwarder_ocr/example.toml index 0b762299af1..75143d7b77f 100644 --- a/integration-tests/testconfig/forwarder_ocr/example.toml +++ b/integration-tests/testconfig/forwarder_ocr/example.toml @@ -31,7 +31,7 @@ bearer_token_secret="bearer_token" base_url="http://grafana.url" # url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard dashboard_url="/d/your-dashboard" -# Grafana dashboard uid to annotate. Find it in Dashboard Settings -> JSON Model +# Grafana dashboard uid to annotate. Find it in Dashboard Settings -> JSON Model dashboard_uid="dashboard-uid-to-annotate" bearer_token_secret="my-awesome-token" @@ -143,6 +143,9 @@ BumpPercent = 20 BumpMin = '100 gwei' """ +[OCR.Common] +number_of_contracts=1 + # load test specific configuration [Load.OCR] [Load.OCR.Common] @@ -162,9 +165,8 @@ chainlink_node_funding = 100 [Soak.OCR] [Soak.OCR.Common] +number_of_contracts=2 test_duration="15m" [Soak.OCR.Soak] -ocr_version="1" -number_of_contracts=2 -time_between_rounds="1m" \ No newline at end of file +time_between_rounds="1m" diff --git a/integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml b/integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml index 8fa0fa5db25..68ed21404f3 100644 --- a/integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml +++ b/integration-tests/testconfig/forwarder_ocr/forwarder_ocr.toml @@ -57,7 +57,10 @@ MinContractPayment = 0 [Transactions] ForwardersEnabled = true - """ +""" + +[OCR.Common] +number_of_contracts=1 # load test specific configuration [Load.OCR] @@ -92,8 +95,8 @@ chainlink_node_funding = 0.5 [Soak.OCR] [Soak.OCR.Common] +number_of_contracts=2 test_duration = "15m" [Soak.OCR.Soak] -number_of_contracts = 2 -time_between_rounds = "1m" +time_between_rounds="1m" diff --git a/integration-tests/testconfig/forwarder_ocr2/example.toml b/integration-tests/testconfig/forwarder_ocr2/example.toml index b3bc45d270c..4941c49a983 100644 --- a/integration-tests/testconfig/forwarder_ocr2/example.toml +++ b/integration-tests/testconfig/forwarder_ocr2/example.toml @@ -31,7 +31,7 @@ bearer_token_secret="bearer_token" base_url="http://grafana.url" # url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard dashboard_url="/d/your-dashboard" -# Grafana dashboard uid to annotate. Find it in Dashboard Settings -> JSON Model +# Grafana dashboard uid to annotate. Find it in Dashboard Settings -> JSON Model dashboard_uid="dashboard-uid-to-annotate" bearer_token_secret="my-awesome-token" @@ -143,12 +143,15 @@ BumpPercent = 20 BumpMin = '100 gwei' """ +[OCR2.Common] +number_of_contracts=1 + # load test specific configuration -[Load.OCR] -[Load.OCR.Common] +[Load.OCR2] +[Load.OCR2.Common] eth_funds = 3 -[Load.OCR.Load] +[Load.OCR2.Load] test_duration = "3m" rate_limit_unit_duration = "1m" rate = 3 @@ -160,11 +163,10 @@ ea_change_interval = "5s" [Soak.Common] chainlink_node_funding = 100 -[Soak.OCR] -[Soak.OCR.Common] +[Soak.OCR2] +[Soak.OCR2.Common] +number_of_contracts=2 test_duration="15m" -[Soak.OCR.Soak] -ocr_version="1" -number_of_contracts=2 -time_between_rounds="1m" \ No newline at end of file +[Soak.OCR2.Soak] +time_between_rounds="1m" diff --git a/integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml b/integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml index 3f2a8610a83..76d5695a8b6 100644 --- a/integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml +++ b/integration-tests/testconfig/forwarder_ocr2/forwarder_ocr2.toml @@ -53,7 +53,10 @@ MinContractPayment = 0 [Transactions] ForwardersEnabled = true - """ +""" + +[OCR2.Common] +number_of_contracts=1 # load test specific configuration [Load.OCR2.Common] @@ -85,10 +88,10 @@ ea_change_interval = "5s" chainlink_node_funding = 1 [Soak.OCR2.Common] +number_of_contracts=2 test_duration="15m" [Soak.OCR2.Soak] -number_of_contracts=2 time_between_rounds="1m" diff --git a/integration-tests/testconfig/ocr/example.toml b/integration-tests/testconfig/ocr/example.toml index 92262241dff..26f0dd5a84e 100644 --- a/integration-tests/testconfig/ocr/example.toml +++ b/integration-tests/testconfig/ocr/example.toml @@ -31,7 +31,7 @@ bearer_token_secret="bearer_token" base_url="http://grafana.url" # url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard dashboard_url="/d/your-dashboard" -# Grafana dashboard uid to annotate. Find it in Dashboard Settings -> JSON Model +# Grafana dashboard uid to annotate. Find it in Dashboard Settings -> JSON Model dashboard_uid="dashboard-uid-to-annotate" bearer_token_secret="my-awesome-token" @@ -142,6 +142,9 @@ BumpPercent = 20 BumpMin = '100 gwei' """ +[OCR.Common] +number_of_contracts=1 + # load test specific configuration [Load.OCR] [Load.OCR.Common] @@ -161,8 +164,8 @@ chainlink_node_funding = 100 [Soak.OCR] [Soak.OCR.Common] +number_of_contracts=2 test_duration="15m" [Soak.OCR.Soak] -number_of_contracts=2 time_between_rounds="1m" diff --git a/integration-tests/testconfig/ocr/ocr.go b/integration-tests/testconfig/ocr/ocr.go index d8250d407fa..240fd2afeaa 100644 --- a/integration-tests/testconfig/ocr/ocr.go +++ b/integration-tests/testconfig/ocr/ocr.go @@ -2,15 +2,19 @@ package ocr import ( "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" ) type Config struct { - Soak *SoakConfig `toml:"Soak"` - Load *Load `toml:"Load"` - Volume *Volume `toml:"Volume"` - Common *Common `toml:"Common"` + Soak *SoakConfig `toml:"Soak"` + Load *Load `toml:"Load"` + Volume *Volume `toml:"Volume"` + Common *Common `toml:"Common"` + Contracts *Contracts `toml:"Contracts"` } func (o *Config) Validate() error { @@ -29,15 +33,24 @@ func (o *Config) Validate() error { return err } } + if o.Contracts != nil { + if err := o.Contracts.Validate(); err != nil { + return err + } + } return nil } type Common struct { - ETHFunds *int `toml:"eth_funds"` - TestDuration *blockchain.StrDuration `toml:"test_duration"` + NumberOfContracts *int `toml:"number_of_contracts"` + ETHFunds *int `toml:"eth_funds"` + TestDuration *blockchain.StrDuration `toml:"test_duration"` } func (o *Common) Validate() error { + if o.NumberOfContracts != nil && *o.NumberOfContracts < 1 { + return errors.New("when number_of_contracts is set, it must be greater than 0") + } if o.ETHFunds != nil && *o.ETHFunds < 0 { return errors.New("eth_funds must be set and cannot be negative") } @@ -117,16 +130,169 @@ func (o *Volume) Validate() error { } type SoakConfig struct { - NumberOfContracts *int `toml:"number_of_contracts"` TimeBetweenRounds *blockchain.StrDuration `toml:"time_between_rounds"` } func (o *SoakConfig) Validate() error { - if o.NumberOfContracts == nil || *o.NumberOfContracts <= 1 { - return errors.New("number_of_contracts must be set and be greater than 1") - } if o.TimeBetweenRounds == nil || o.TimeBetweenRounds.Duration == 0 { return errors.New("time_between_rounds must be set and be a positive integer") } return nil } + +// For more information on the configuration of contracts, see https://smartcontract-it.atlassian.net/wiki/spaces/TT/pages/828407894/Contracts+addresses+in+TOML+convention +type Contracts struct { + ShouldBeUsed *bool `toml:"use"` + LinkTokenAddress *string `toml:"link_token"` + OffChainAggregatorAddresses []string `toml:"offchain_aggregators"` + Settings map[string]ContractSetting `toml:"Settings"` +} + +func (o *Contracts) Validate() error { + if o.LinkTokenAddress != nil && !common.IsHexAddress(*o.LinkTokenAddress) { + return errors.New("link_token must be a valid ethereum address") + } + if o.OffChainAggregatorAddresses != nil { + allEnabled := make(map[bool]int) + allConfigure := make(map[bool]int) + for _, address := range o.OffChainAggregatorAddresses { + if !common.IsHexAddress(address) { + return fmt.Errorf("offchain_aggregators must be valid ethereum addresses, but %s is not", address) + } + + if v, ok := o.Settings[address]; ok { + if v.ShouldBeUsed != nil { + allEnabled[*v.ShouldBeUsed]++ + } else { + allEnabled[true]++ + } + if v.Configure != nil { + allConfigure[*v.Configure]++ + } else { + allConfigure[true]++ + } + } + } + + if allEnabled[true] > 0 && allEnabled[false] > 0 { + return errors.New("either all or none offchain_aggregators must be used") + } + + if allConfigure[true] > 0 && allConfigure[false] > 0 { + return errors.New("either all or none offchain_aggregators must be configured") + } + } + + return nil +} + +func (o *Config) UseExistingContracts() bool { + if o.Contracts == nil { + return false + } + + if o.Contracts.ShouldBeUsed != nil { + return *o.Contracts.ShouldBeUsed + } + + return false +} + +func (o *Config) LinkTokenContractAddress() (common.Address, error) { + if o.Contracts != nil && o.Contracts.LinkTokenAddress != nil { + return common.HexToAddress(*o.Contracts.LinkTokenAddress), nil + } + + return common.Address{}, errors.New("link token address must be set") +} + +func (o *Config) UseExistingLinkTokenContract() bool { + if !o.UseExistingContracts() { + return false + } + + if o.Contracts.LinkTokenAddress == nil { + return false + } + + if len(o.Contracts.Settings) == 0 { + return true + } + + if v, ok := o.Contracts.Settings[*o.Contracts.LinkTokenAddress]; ok { + return v.ShouldBeUsed != nil && *v.ShouldBeUsed + } + + return true +} + +type ContractSetting struct { + ShouldBeUsed *bool `toml:"use"` + Configure *bool `toml:"configure"` +} + +type OffChainAggregatorsConfig interface { + OffChainAggregatorsContractsAddresses() []common.Address + UseExistingOffChainAggregatorsContracts() bool + ConfigureExistingOffChainAggregatorsContracts() bool + NumberOfContractsToDeploy() int +} + +func (o *Config) UseExistingOffChainAggregatorsContracts() bool { + if !o.UseExistingContracts() { + return false + } + + if len(o.Contracts.OffChainAggregatorAddresses) == 0 { + return false + } + + if len(o.Contracts.Settings) == 0 { + return true + } + + for _, address := range o.Contracts.OffChainAggregatorAddresses { + if v, ok := o.Contracts.Settings[address]; ok { + return v.ShouldBeUsed != nil && *v.ShouldBeUsed + } + } + + return true +} + +func (o *Config) OffChainAggregatorsContractsAddresses() []common.Address { + var ocrInstanceAddresses []common.Address + if !o.UseExistingOffChainAggregatorsContracts() { + return ocrInstanceAddresses + } + + for _, address := range o.Contracts.OffChainAggregatorAddresses { + ocrInstanceAddresses = append(ocrInstanceAddresses, common.HexToAddress(address)) + } + + return ocrInstanceAddresses +} + +func (o *Config) ConfigureExistingOffChainAggregatorsContracts() bool { + if !o.UseExistingOffChainAggregatorsContracts() { + return true + } + + for _, address := range o.Contracts.OffChainAggregatorAddresses { + for maybeOcrAddress, setting := range o.Contracts.Settings { + if maybeOcrAddress == address { + return setting.Configure != nil && *setting.Configure + } + } + } + + return true +} + +func (o *Config) NumberOfContractsToDeploy() int { + if o.Common != nil && o.Common.NumberOfContracts != nil { + return *o.Common.NumberOfContracts + } + + return 0 +} diff --git a/integration-tests/testconfig/ocr/ocr.toml b/integration-tests/testconfig/ocr/ocr.toml index 17ee4d7b687..36cded0b85c 100644 --- a/integration-tests/testconfig/ocr/ocr.toml +++ b/integration-tests/testconfig/ocr/ocr.toml @@ -43,6 +43,9 @@ Enabled = true ListenAddresses = ['0.0.0.0:6690'] """ +[OCR.Common] +number_of_contracts=1 + # load test specific configuration [Load.OCR] [Load.OCR.Common] @@ -77,9 +80,9 @@ chainlink_node_funding = 0.5 [Soak.OCR] [Soak.OCR.Common] test_duration="15m" +number_of_contracts=2 [Soak.OCR.Soak] -number_of_contracts=2 time_between_rounds="1m" # Soak test configuration with Geth reorg below finality with FinalityTagEnabled=false diff --git a/integration-tests/testconfig/ocr2/example.toml b/integration-tests/testconfig/ocr2/example.toml index 36e3105f219..624c3b77752 100644 --- a/integration-tests/testconfig/ocr2/example.toml +++ b/integration-tests/testconfig/ocr2/example.toml @@ -31,7 +31,7 @@ bearer_token_secret="bearer_token" base_url="http://grafana.url" # url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard dashboard_url="/d/your-dashboard" -# Grafana dashboard uid to annotate. Find it in Dashboard Settings -> JSON Model +# Grafana dashboard uid to annotate. Find it in Dashboard Settings -> JSON Model dashboard_uid="dashboard-uid-to-annotate" bearer_token_secret="my-awesome-token" @@ -142,6 +142,9 @@ BumpPercent = 20 BumpMin = '100 gwei' """ +[OCR2.Common] +number_of_contracts=1 + # load test specific configuration [Load.OCR2] [Load.OCR2.Common] @@ -161,8 +164,8 @@ chainlink_node_funding = 100 [Soak.OCR2] [Soak.OCR.Common] +number_of_contracts=2 test_duration="15m" [Soak.OCR2.Soak] -number_of_contracts=2 time_between_rounds="1m" diff --git a/integration-tests/testconfig/ocr2/ocr2.go b/integration-tests/testconfig/ocr2/ocr2.go index 1e7f034e043..60169e944fa 100644 --- a/integration-tests/testconfig/ocr2/ocr2.go +++ b/integration-tests/testconfig/ocr2/ocr2.go @@ -1,14 +1,13 @@ package ocr2 import ( - "errors" - - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" + "github.com/smartcontractkit/chainlink/integration-tests/testconfig/ocr" ) type Config struct { - Soak *SoakConfig `toml:"Soak"` - Common *Common `toml:"Common"` + Soak *ocr.SoakConfig `toml:"Soak"` + Common *ocr.Common `toml:"Common"` + Contracts *ocr.Contracts `toml:"Contracts"` } func (o *Config) Validate() error { @@ -22,32 +21,10 @@ func (o *Config) Validate() error { return err } } - return nil -} - -type Common struct { - ETHFunds *int `toml:"eth_funds"` - TestDuration *blockchain.StrDuration `toml:"test_duration"` -} - -func (o *Common) Validate() error { - if o.ETHFunds != nil && *o.ETHFunds < 0 { - return errors.New("eth_funds must be set and cannot be negative") - } - return nil -} - -type SoakConfig struct { - NumberOfContracts *int `toml:"number_of_contracts"` - TimeBetweenRounds *blockchain.StrDuration `toml:"time_between_rounds"` -} - -func (o *SoakConfig) Validate() error { - if o.NumberOfContracts == nil || *o.NumberOfContracts <= 1 { - return errors.New("number_of_contracts must be set and be greater than 1") - } - if o.TimeBetweenRounds == nil || o.TimeBetweenRounds.Duration == 0 { - return errors.New("time_between_rounds must be set and be a positive integer") + if o.Contracts != nil { + if err := o.Contracts.Validate(); err != nil { + return err + } } return nil } diff --git a/integration-tests/testconfig/ocr2/ocr2.toml b/integration-tests/testconfig/ocr2/ocr2.toml index ad195913bd0..62d92574ea8 100644 --- a/integration-tests/testconfig/ocr2/ocr2.toml +++ b/integration-tests/testconfig/ocr2/ocr2.toml @@ -43,6 +43,9 @@ Enabled = true ListenAddresses = ['0.0.0.0:6690'] """ +[OCR2.Common] +number_of_contracts=1 + # load test specific configuration [Load.OCR2] [Load.OCR2.Common] @@ -76,8 +79,8 @@ chainlink_node_funding = 0.5 [Soak.OCR2] [Soak.OCR2.Common] +number_of_contracts=2 test_duration="15m" [Soak.OCR2.Soak] -number_of_contracts=2 time_between_rounds="1m" diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index fb692c56a75..545818e3348 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/barkimedes/go-deepcopy" + "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/pelletier/go-toml/v2" "github.com/pkg/errors" @@ -77,6 +78,11 @@ type CCIPTestConfig interface { GetCCIPConfig() *ccip_config.Config } +type LinkTokenContractConfig interface { + LinkTokenContractAddress() (common.Address, error) + UseExistingLinkTokenContract() bool +} + const ( E2E_TEST_DATA_STREAMS_URL_ENV = "E2E_TEST_DATA_STREAMS_URL" E2E_TEST_DATA_STREAMS_USERNAME_ENV = "E2E_TEST_DATA_STREAMS_USERNAME" diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index 3653b9d5fb9..73b142b6297 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -281,8 +281,8 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { o.mockServer = ctf_client.ConnectMockServer(o.testEnvironment) require.NoError(o.t, err, "Creating mockserver clients shouldn't fail") - linkContract, err := contracts.DeployLinkTokenContract(o.log, sethClient) - require.NoError(o.t, err, "Error deploying LINK contract") + linkContract, err := actions.LinkTokenContract(o.log, sethClient, ocrTestConfig.GetActiveOCRConfig()) + require.NoError(o.t, err, "Error loading/deploying link token contract") // Fund Chainlink nodes, excluding the bootstrap node o.log.Info().Float64("ETH amount per node", *o.Config.Common.ChainlinkNodeFunding).Msg("Funding Chainlink nodes") @@ -290,7 +290,6 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { require.NoError(o.t, err, "Error funding Chainlink nodes") var forwarders []common.Address - if o.OperatorForwarderFlow { var operators []common.Address operators, forwarders, _ = actions.DeployForwarderContracts( @@ -311,17 +310,17 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { o.ocrV1Instances, err = actions.DeployOCRContractsForwarderFlow( o.log, o.seth, - *o.Config.GetActiveOCRConfig().Soak.NumberOfContracts, + o.Config.GetActiveOCRConfig(), common.HexToAddress(linkContract.Address()), contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(o.workerNodes), forwarders, ) require.NoError(o.t, err, "Error deploying OCR Forwarder contracts") } else { - o.ocrV1Instances, err = actions.DeployOCRv1Contracts( + o.ocrV1Instances, err = actions.SetupOCRv1Contracts( o.log, sethClient, - *o.Config.GetActiveOCRConfig().Soak.NumberOfContracts, + o.Config.GetActiveOCRConfig(), common.HexToAddress(linkContract.Address()), contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(o.workerNodes), ) @@ -343,19 +342,22 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { } ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - o.ocrV2Instances, err = actions.DeployOCRv2Contracts( + o.ocrV2Instances, err = actions.SetupOCRv2Contracts( o.log, o.seth, - *ocrTestConfig.GetActiveOCRConfig().Soak.NumberOfContracts, + ocrTestConfig.GetActiveOCRConfig(), common.HexToAddress(linkContract.Address()), transmitters, ocrOffchainOptions, ) require.NoError(o.t, err, "Error deploying OCRv2 contracts") - contractConfig, err := actions.BuildMedianOCR2Config(o.workerNodes, ocrOffchainOptions) - require.NoError(o.t, err, "Error building median config") - err = actions.ConfigureOCRv2AggregatorContracts(contractConfig, o.ocrV2Instances) - require.NoError(o.t, err, "Error configuring OCRv2 aggregator contracts") + + if !ocrTestConfig.GetActiveOCRConfig().UseExistingOffChainAggregatorsContracts() || (ocrTestConfig.GetActiveOCRConfig().UseExistingOffChainAggregatorsContracts() && ocrTestConfig.GetActiveOCRConfig().ConfigureExistingOffChainAggregatorsContracts()) { + contractConfig, err := actions.BuildMedianOCR2Config(o.workerNodes, ocrOffchainOptions) + require.NoError(o.t, err, "Error building median config") + err = actions.ConfigureOCRv2AggregatorContracts(contractConfig, o.ocrV2Instances) + require.NoError(o.t, err, "Error configuring OCRv2 aggregator contracts") + } } if o.OCRVersion == "1" { @@ -399,7 +401,7 @@ func (o *OCRSoakTest) Run() { o.log.Info(). Str("Test Duration", o.Config.GetActiveOCRConfig().Common.TestDuration.Duration.Truncate(time.Second).String()). - Int("Number of OCR Contracts", *config.GetActiveOCRConfig().Soak.NumberOfContracts). + Int("Number of OCR Contracts", *config.GetActiveOCRConfig().Common.NumberOfContracts). Str("OCR Version", o.OCRVersion). Msg("Starting OCR Soak Test") @@ -530,7 +532,7 @@ func (o *OCRSoakTest) LoadState() error { if testState.OCRVersion == "1" { o.ocrV1Instances = make([]contracts.OffchainAggregator, len(testState.OCRContractAddresses)) for i, addr := range testState.OCRContractAddresses { - instance, err := contracts.LoadOffchainAggregator(o.log, o.seth, common.HexToAddress(addr)) + instance, err := contracts.LoadOffChainAggregator(o.log, o.seth, common.HexToAddress(addr)) if err != nil { return fmt.Errorf("failed to instantiate OCR instance: %w", err) } @@ -539,7 +541,7 @@ func (o *OCRSoakTest) LoadState() error { } else if testState.OCRVersion == "2" { o.ocrV2Instances = make([]contracts.OffchainAggregatorV2, len(testState.OCRContractAddresses)) for i, addr := range testState.OCRContractAddresses { - instance, err := contracts.LoadOffChainAggregatorV2(o.log, o.seth, common.HexToAddress(addr)) + instance, err := contracts.LoadOffchainAggregatorV2(o.log, o.seth, common.HexToAddress(addr)) if err != nil { return err } @@ -561,7 +563,7 @@ func (o *OCRSoakTest) Resume() { Str("Time Left", o.timeLeft.String()). Msg("Resuming OCR Soak Test") - ocrAddresses := make([]common.Address, *o.Config.GetActiveOCRConfig().Soak.NumberOfContracts) + ocrAddresses := make([]common.Address, *o.Config.GetActiveOCRConfig().Common.NumberOfContracts) if o.OCRVersion == "1" { for i, ocrInstance := range o.ocrV1Instances { @@ -1021,12 +1023,12 @@ func (o *OCRSoakTest) collectEvents() error { // ensureValues ensures that all values needed to run the test are present func (o *OCRSoakTest) ensureInputValues() error { - ocrConfig := o.Config.GetActiveOCRConfig().Soak + ocrConfig := o.Config.GetActiveOCRConfig() if o.OCRVersion != "1" && o.OCRVersion != "2" { return fmt.Errorf("OCR version must be 1 or 2, found %s", o.OCRVersion) } - if ocrConfig.NumberOfContracts != nil && *ocrConfig.NumberOfContracts <= 0 { - return fmt.Errorf("number of OCR contracts must be set and greater than 0, found %d", ocrConfig.NumberOfContracts) + if ocrConfig.Common.NumberOfContracts != nil && *ocrConfig.Common.NumberOfContracts <= 0 { + return fmt.Errorf("number of OCR contracts must be set and greater than 0, found %d", ocrConfig.Common.NumberOfContracts) } if o.Config.Common.ChainlinkNodeFunding != nil && *o.Config.Common.ChainlinkNodeFunding <= 0 { return fmt.Errorf("chainlink node funding must be greater than 0, found %f", *o.Config.Common.ChainlinkNodeFunding) @@ -1034,11 +1036,12 @@ func (o *OCRSoakTest) ensureInputValues() error { if o.Config.GetActiveOCRConfig().Common.TestDuration != nil && o.Config.GetActiveOCRConfig().Common.TestDuration.Duration <= time.Minute { return fmt.Errorf("test duration must be greater than 1 minute, found %s", o.Config.GetActiveOCRConfig().Common.TestDuration) } - if ocrConfig.TimeBetweenRounds != nil && ocrConfig.TimeBetweenRounds.Duration >= time.Hour { - return fmt.Errorf("time between rounds must be less than 1 hour, found %s", ocrConfig.TimeBetweenRounds) + soakConfig := ocrConfig.Soak + if soakConfig.TimeBetweenRounds != nil && soakConfig.TimeBetweenRounds.Duration >= time.Hour { + return fmt.Errorf("time between rounds must be less than 1 hour, found %s", soakConfig.TimeBetweenRounds) } - if ocrConfig.TimeBetweenRounds != nil && ocrConfig.TimeBetweenRounds.Duration < time.Second*30 { - return fmt.Errorf("time between rounds must be greater or equal to 30 seconds, found %s", ocrConfig.TimeBetweenRounds) + if soakConfig.TimeBetweenRounds != nil && soakConfig.TimeBetweenRounds.Duration < time.Second*30 { + return fmt.Errorf("time between rounds must be greater or equal to 30 seconds, found %s", soakConfig.TimeBetweenRounds) } return nil