diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index 5972725ec1..edc6b5bf2d 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -36,7 +36,6 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Setup Other Tags If Not Workflow Dispatch id: tags - if: github.event_name != 'workflow_dispatch' run: | echo "other_tags=${ECR_TAG}" >> $GITHUB_OUTPUT echo 'release_tag="${{ format('{0}.dkr.ecr.{1}.amazonaws.com/chainlink-ccip-tests:{2}', secrets.QA_AWS_ACCOUNT_NUMBER, secrets.QA_AWS_REGION, github.ref_name) }}"' >> $GITHUB_OUTPUT diff --git a/integration-tests/.tool-versions b/integration-tests/.tool-versions index ac6300f979..7f84d58b91 100644 --- a/integration-tests/.tool-versions +++ b/integration-tests/.tool-versions @@ -1,4 +1,4 @@ -golang 1.21.5 +golang 1.21.7 k3d 5.4.6 kubectl 1.25.5 nodejs 18.13.0 diff --git a/integration-tests/Makefile b/integration-tests/Makefile index 8415c00f9c..2d887e97ba 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -130,7 +130,7 @@ test_node_migrations: install_gotestfmt ## Run all node migration tests. go test -timeout 1h -count=1 -json $(args) ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt .PHONY: test_node_migrations_simulated -test_node_migrations_simulated: install_gotestfmt +test_node_migrations_simulated: install_gotestfmt TEST_LOG_LEVEL="disabled" \ go test -timeout 1h -count=1 -json $(args) ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt @@ -145,30 +145,22 @@ test_node_migrations_simulated_verbose: # Soak .PHONY: test_soak_ocr test_soak_ocr: - go test -v -count=1 -run TestOCRSoak ./soak - -.PHONY: test_soak_ocr_simulated -test_soak_ocr_simulated: + . ./scripts/check_base64_env_var.sh go test -v -count=1 -run TestOCRSoak ./soak .PHONY: test_soak_forwarder_ocr test_soak_forwarder_ocr: - go test -v -count=1 -run TestForwarderOCRSoak ./soak - -.PHONY: test_soak_forwarder_ocr_simulated -test_soak_forwarder_ocr_simulated: + . ./scripts/check_base64_env_var.sh go test -v -count=1 -run TestForwarderOCRSoak ./soak .PHONY: test_soak_automation test_soak_automation: - go test -v -run ^TestAutomationBenchmark$$ ./benchmark -count=1 - -.PHONY: test_soak_automation_simulated -test_soak_automation_simulated: + . ./scripts/check_base64_env_var.sh go test -v -run ^TestAutomationBenchmark$$ ./benchmark -count=1 .PHONY: test_benchmark_automation test_benchmark_automation: ## Run the automation benchmark tests + . ./scripts/check_base64_env_var.sh go test -timeout 30m -v -run ^TestAutomationBenchmark$$ ./benchmark -count=1 .PHONY: test_reorg_automation diff --git a/integration-tests/README.md b/integration-tests/README.md index 37a8fa19ed..80154a6dd1 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -14,7 +14,18 @@ If you have previously run these smoke tests using GitHub Actions or some sort o ## Configure -See the [example.env](./example.env) file for environment variables you can set to configure things like network settings, Chainlink version, and log level. Remember to use `source .env` to activate your settings. +We have finished the first pass at moving all test configuration from env vars to TOML files. All product-related configuration is already in TOML files, but env vars are still used to control the log level, Slack notifications, and Kubernetes-related settings. See the [example.env](./example.env) file for how to set these environment variables. + +We have defined some sensible defaults for all products, you can find them in `./testconfig//.toml` files. Each product folder contains an `example.toml` file that describes all options. If you wish to override these values, you can do so by creating a `./testconfig/overrides.toml`. A detailed description of TOML configuration can be found in the [testconfig README](./testconfig/README.md), but if you want to run some tests using default values all you need to do is provide the Chainlink image and version you want to run tests on: +```toml +# ./testconfig/overrides.toml + +[ChainlinkImage] +image = "your image name" +version = "your tag" +``` + +The `./testconfig/overrides.toml` file **should never be committed** and has been added to the [.gitignore](../.gitignore) file as it can often contain secrets like private keys and RPC URLs. ## Build @@ -26,10 +37,10 @@ e.g. `make build_docker_image image=chainlink tag=test-tag` -You'll want to set the `CHAINLINK_IMAGE` and `CHAINLINK_VERSION` env values appropriately as well. See [example.env](./example.env) for more details. - ## Run +Ensure you have created a `./testconfig/overrides.toml` file with your desired Chainlink image and version. + `go test ./smoke/_test.go` Most test files have a couple of tests, it's recommended to look into the file and focus on a specific one if possible. 90% of the time this will probably be the `Basic` test. See [ocr_test.go](./smoke/ocr_test.go) for example, which contains the `TestOCRBasic` test. diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index 95b538129c..f5c91b6352 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -2,14 +2,18 @@ package actions import ( + "context" "crypto/ecdsa" "encoding/json" "errors" "fmt" "math/big" "strings" + "sync" "testing" + "time" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -26,7 +30,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/testreporters" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" - + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) @@ -35,7 +39,8 @@ import ( // Example: When deploying 1000 contracts, stop every ContractDeploymentInterval have been deployed to wait before continuing var ContractDeploymentInterval = 200 -// FundChainlinkNodes will fund all of the provided Chainlink nodes with a set amount of native currency +// FundChainlinkNodes will fund all of the provided Chainlink nodes with a set amountCreateOCRv2Jobs of native currency +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.FundChainlinkNodes func FundChainlinkNodes( nodes []*client.ChainlinkK8sClient, client blockchain.EVMClient, @@ -299,6 +304,7 @@ func TeardownSuite( // TeardownRemoteSuite is used when running a test within a remote-test-runner, like for long-running performance and // soak tests +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.TeardownRemoteSuite func TeardownRemoteSuite( t *testing.T, namespace string, @@ -484,3 +490,75 @@ func GetTxFromAddress(tx *types.Transaction) (string, error) { from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx) return from.String(), err } + +// todo - move to CTF +func GetTxByHash(ctx context.Context, client blockchain.EVMClient, hash common.Hash) (*types.Transaction, bool, error) { + return client.(*blockchain.EthereumMultinodeClient). + DefaultClient.(*blockchain.EthereumClient). + Client. + TransactionByHash(ctx, hash) +} + +// todo - move to CTF +func DecodeTxInputData(abiString string, data []byte) (map[string]interface{}, error) { + jsonABI, err := abi.JSON(strings.NewReader(abiString)) + if err != nil { + return nil, err + } + methodSigData := data[:4] + inputsSigData := data[4:] + method, err := jsonABI.MethodById(methodSigData) + if err != nil { + return nil, err + } + inputsMap := make(map[string]interface{}) + if err := method.Inputs.UnpackIntoMap(inputsMap, inputsSigData); err != nil { + return nil, err + } + return inputsMap, nil +} + +// todo - move to EVMClient +func WaitForBlockNumberToBe( + waitForBlockNumberToBe uint64, + client blockchain.EVMClient, + wg *sync.WaitGroup, + timeout time.Duration, + t testing.TB, +) (uint64, error) { + blockNumberChannel := make(chan uint64) + errorChannel := make(chan error) + testContext, testCancel := context.WithTimeout(context.Background(), timeout) + defer testCancel() + + ticker := time.NewTicker(time.Second * 1) + var blockNumber uint64 + for { + select { + case <-testContext.Done(): + ticker.Stop() + wg.Done() + return blockNumber, + fmt.Errorf("timeout waiting for Block Number to be: %d. Last recorded block number was: %d", + waitForBlockNumberToBe, blockNumber) + case <-ticker.C: + go func() { + currentBlockNumber, err := client.LatestBlockNumber(testcontext.Get(t)) + if err != nil { + errorChannel <- err + } + blockNumberChannel <- currentBlockNumber + }() + case blockNumber = <-blockNumberChannel: + if blockNumber == waitForBlockNumberToBe { + ticker.Stop() + wg.Done() + return blockNumber, nil + } + case err := <-errorChannel: + ticker.Stop() + wg.Done() + return 0, err + } + } +} diff --git a/integration-tests/actions/actions_local.go b/integration-tests/actions/actions_local.go index 8ac623d884..5bb0c202a5 100644 --- a/integration-tests/actions/actions_local.go +++ b/integration-tests/actions/actions_local.go @@ -1,9 +1,7 @@ // Package actions enables common chainlink interactions package actions -import ( - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" -) +import "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" // UpgradeChainlinkNodeVersions upgrades all Chainlink nodes to a new version, and then runs the test environment // to apply the upgrades diff --git a/integration-tests/actions/automation_ocr_helpers.go b/integration-tests/actions/automation_ocr_helpers.go index 38df2e00b5..8aba2b7dd0 100644 --- a/integration-tests/actions/automation_ocr_helpers.go +++ b/integration-tests/actions/automation_ocr_helpers.go @@ -20,17 +20,15 @@ import ( ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" - "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func BuildAutoOCR2ConfigVars( @@ -65,7 +63,7 @@ func BuildAutoOCR2ConfigVarsWithKeyIndex( var offchainConfigVersion uint64 var offchainConfig []byte - if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_1 { + if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_1 || registryConfig.RegistryVersion == ethereum.RegistryVersion_2_2 { offC, err = json.Marshal(ocr2keepers30config.OffchainConfig{ TargetProbability: "0.999", TargetInRounds: 1, @@ -183,12 +181,12 @@ func CreateOCRKeeperJobs( bootstrapP2PId := bootstrapP2PIds.Data[0].Attributes.PeerID var contractVersion string - if registryVersion == ethereum.RegistryVersion_2_1 { + if registryVersion == ethereum.RegistryVersion_2_2 { + contractVersion = "v2.1+" + } else if registryVersion == ethereum.RegistryVersion_2_1 { contractVersion = "v2.1" - } else if registryVersion == ethereum.RegistryVersion_2_0 { - contractVersion = "v2.0" } else { - require.FailNow(t, "v2.0 and v2.1 are the only supported versions") + require.FailNow(t, "v2.0, v2.1, and v2.2 are the only supported versions") } bootstrapSpec := &client.OCR2TaskJobSpec{ diff --git a/integration-tests/actions/ocr2_helpers.go b/integration-tests/actions/ocr2_helpers.go index 829d85a849..e5f9beff74 100644 --- a/integration-tests/actions/ocr2_helpers.go +++ b/integration-tests/actions/ocr2_helpers.go @@ -33,6 +33,7 @@ import ( ) // DeployOCRv2Contracts deploys a number of OCRv2 contracts and configures them with defaults +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.DeployOCRv2Contracts func DeployOCRv2Contracts( numberOfContracts int, linkTokenContract contracts.LinkToken, @@ -85,6 +86,7 @@ func DeployOCRv2Contracts( return ocrInstances, client.WaitForEvents() } +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.ConfigureOCRv2AggregatorContracts func ConfigureOCRv2AggregatorContracts( client blockchain.EVMClient, contractConfig *contracts.OCRv2Config, @@ -266,7 +268,7 @@ func CreateOCRv2Jobs( workerChainlinkNodes []*client.ChainlinkK8sClient, mockserver *ctfClient.MockserverClient, mockServerValue int, // Value to get from the mock server when querying the path - chainId uint64, // EVM chain ID + chainId int64, // EVM chain ID forwardingAllowed bool, ) error { // Collect P2P ID diff --git a/integration-tests/actions/ocr_helpers.go b/integration-tests/actions/ocr_helpers.go index dd0e6606e4..89a9d1574d 100644 --- a/integration-tests/actions/ocr_helpers.go +++ b/integration-tests/actions/ocr_helpers.go @@ -22,7 +22,7 @@ import ( // This actions file often returns functions, rather than just values. These are used as common test helpers, and are // handy to have returning as functions so that Ginkgo can use them in an aesthetically pleasing way. -// DeployOCRContracts deploys and funds a certain number of offchain aggregator contracts +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.DeployOCRContracts func DeployOCRContracts( numberOfContracts int, linkTokenContract contracts.LinkToken, @@ -90,7 +90,7 @@ func DeployOCRContracts( for contractCount, ocrInstance := range ocrInstances { // Exclude the first node, which will be used as a bootstrapper err = ocrInstance.SetConfig( - workerNodes, + contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes), contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), transmitterAddresses, ) @@ -113,6 +113,7 @@ func DeployOCRContracts( // DeployOCRContractsForwarderFlow deploys and funds a certain number of offchain // aggregator contracts with forwarders as effectiveTransmitters +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.DeployOCRContractsForwarderFlow func DeployOCRContractsForwarderFlow( t *testing.T, numberOfContracts int, @@ -163,7 +164,7 @@ func DeployOCRContractsForwarderFlow( for contractCount, ocrInstance := range ocrInstances { // Exclude the first node, which will be used as a bootstrapper err = ocrInstance.SetConfig( - workerNodes, + contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes), contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), forwarderAddresses, ) @@ -267,7 +268,7 @@ func CreateOCRJobsWithForwarder( workerNodes []*client.ChainlinkK8sClient, mockValue int, mockserver *ctfClient.MockserverClient, - evmChainID string, + evmChainID int64, ) { for _, ocrInstance := range ocrInstances { bootstrapP2PIds, err := bootstrapNode.MustReadP2PKeys() @@ -276,7 +277,7 @@ func CreateOCRJobsWithForwarder( bootstrapSpec := &client.OCRBootstrapJobSpec{ Name: fmt.Sprintf("bootstrap-%s", uuid.New().String()), ContractAddress: ocrInstance.Address(), - EVMChainID: evmChainID, + EVMChainID: fmt.Sprint(evmChainID), P2PPeerID: bootstrapP2PId, IsBootstrapPeer: true, } @@ -307,7 +308,7 @@ func CreateOCRJobsWithForwarder( bootstrapPeers := []*client.ChainlinkClient{bootstrapNode.ChainlinkClient} ocrSpec := &client.OCRTaskJobSpec{ ContractAddress: ocrInstance.Address(), - EVMChainID: evmChainID, + EVMChainID: fmt.Sprint(evmChainID), P2PPeerID: nodeP2PId, P2PBootstrapPeers: bootstrapPeers, KeyBundleID: nodeOCRKeyId, @@ -322,6 +323,7 @@ func CreateOCRJobsWithForwarder( } // StartNewRound requests a new round from the ocr contracts and waits for confirmation +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.StartNewRound func StartNewRound( roundNumber int64, ocrInstances []contracts.OffchainAggregator, @@ -345,6 +347,7 @@ func StartNewRound( // WatchNewRound watches for a new OCR round, similarly to StartNewRound, but it does not explicitly request a new // round from the contract, as this can cause some odd behavior in some cases +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.WatchNewRound func WatchNewRound( roundNumber int64, ocrInstances []contracts.OffchainAggregator, @@ -430,7 +433,7 @@ func SetAllAdapterResponsesToDifferentValues( } // BuildNodeContractPairID builds a UUID based on a related pair of a Chainlink node and OCR contract -func BuildNodeContractPairID(node *client.ChainlinkK8sClient, ocrInstance contracts.OffchainAggregator) (string, error) { +func BuildNodeContractPairID(node contracts.ChainlinkNodeWithKeysAndAddress, ocrInstance contracts.OffchainAggregator) (string, error) { if node == nil { return "", fmt.Errorf("chainlink node is nil") } diff --git a/integration-tests/actions/ocr_helpers_local.go b/integration-tests/actions/ocr_helpers_local.go index e9cad3f67e..1a7371517a 100644 --- a/integration-tests/actions/ocr_helpers_local.go +++ b/integration-tests/actions/ocr_helpers_local.go @@ -118,10 +118,11 @@ func DeployOCRContractsLocal( if err != nil { return nil, fmt.Errorf("getting node common addresses should not fail: %w", err) } + for _, ocrInstance := range ocrInstances { // Exclude the first node, which will be used as a bootstrapper - err = ocrInstance.SetConfigLocal( - workerNodes, + err = ocrInstance.SetConfig( + contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes), contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), transmitterAddresses, ) @@ -182,7 +183,7 @@ func CreateOCRJobsLocal( } nodeOCRKeyId := nodeOCRKeys.Data[0].ID - nodeContractPairID, err := BuildNodeContractPairIDLocal(node, ocrInstance) + nodeContractPairID, err := BuildNodeContractPairID(node, ocrInstance) if err != nil { return err } @@ -218,29 +219,13 @@ func CreateOCRJobsLocal( return nil } -func BuildNodeContractPairIDLocal(node *client.ChainlinkClient, ocrInstance contracts.OffchainAggregator) (string, error) { - if node == nil { - return "", fmt.Errorf("chainlink node is nil") - } - if ocrInstance == nil { - return "", fmt.Errorf("OCR Instance is nil") - } - nodeAddress, err := node.PrimaryEthAddress() - if err != nil { - return "", fmt.Errorf("getting chainlink node's primary ETH address failed: %w", err) - } - shortNodeAddr := nodeAddress[2:12] - shortOCRAddr := ocrInstance.Address()[2:12] - return strings.ToLower(fmt.Sprintf("node_%s_contract_%s", shortNodeAddr, shortOCRAddr)), nil -} - func SetAdapterResponseLocal( response int, ocrInstance contracts.OffchainAggregator, chainlinkNode *client.ChainlinkClient, mockAdapter *test_env.Killgrave, ) error { - nodeContractPairID, err := BuildNodeContractPairIDLocal(chainlinkNode, ocrInstance) + nodeContractPairID, err := BuildNodeContractPairID(chainlinkNode, ocrInstance) if err != nil { return err } @@ -342,8 +327,8 @@ func DeployOCRContractsForwarderFlowLocal( // Set Config for _, ocrInstance := range ocrInstances { // Exclude the first node, which will be used as a bootstrapper - err := ocrInstance.SetConfigLocal( - workerNodes, + err := ocrInstance.SetConfig( + contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes), contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), forwarderAddresses, ) @@ -399,7 +384,7 @@ func CreateOCRJobsWithForwarderLocal( } nodeOCRKeyId := nodeOCRKeys.Data[0].ID - nodeContractPairID, err := BuildNodeContractPairIDLocal(node, ocrInstance) + nodeContractPairID, err := BuildNodeContractPairID(node, ocrInstance) if err != nil { return err } diff --git a/integration-tests/actions/operator_forwarder_helpers.go b/integration-tests/actions/operator_forwarder_helpers.go index e308cd8fd5..31eed7b7aa 100644 --- a/integration-tests/actions/operator_forwarder_helpers.go +++ b/integration-tests/actions/operator_forwarder_helpers.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.DeployForwarderContracts func DeployForwarderContracts( t *testing.T, contractDeployer contracts.ContractDeployer, @@ -49,6 +50,7 @@ func DeployForwarderContracts( return operators, authorizedForwarders, operatorFactoryInstance } +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.AcceptAuthorizedReceiversOperator func AcceptAuthorizedReceiversOperator( t *testing.T, operator common.Address, @@ -178,6 +180,7 @@ func SubscribeOperatorFactoryEvents( }() } +// Deprecated: we are moving away from blockchain.EVMClient, use actions_seth.TrackForwarder func TrackForwarder( t *testing.T, chainClient blockchain.EVMClient, diff --git a/integration-tests/actions/private_network.go b/integration-tests/actions/private_network.go index 7f8bfe8bb2..01a084b66d 100644 --- a/integration-tests/actions/private_network.go +++ b/integration-tests/actions/private_network.go @@ -12,7 +12,7 @@ func EthereumNetworkConfigFromConfig(l zerolog.Logger, config tc.GlobalTestConfi l.Warn().Msg("No TOML private ethereum network config found, will use old geth") ethBuilder := ctf_test_env.NewEthereumNetworkBuilder() network, err = ethBuilder. - WithConsensusType(ctf_test_env.ConsensusType_PoW). + WithEthereumVersion(ctf_test_env.EthereumVersion_Eth1). WithExecutionLayer(ctf_test_env.ExecutionLayer_Geth). Build() diff --git a/integration-tests/actions/seth/actions.go b/integration-tests/actions/seth/actions.go new file mode 100644 index 0000000000..ae5016852f --- /dev/null +++ b/integration-tests/actions/seth/actions.go @@ -0,0 +1,568 @@ +package actions_seth + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" + "github.com/test-go/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" + + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +var ContractDeploymentInterval = 200 + +// FundChainlinkNodesFromRootAddress sends native token amount (expressed in human-scale) to each Chainlink Node +// from root private key. It returns an error if any of the transactions failed. +func FundChainlinkNodesFromRootAddress( + logger zerolog.Logger, + client *seth.Client, + nodes []contracts.ChainlinkNodeWithKeysAndAddress, + amount *big.Float, +) error { + if len(client.PrivateKeys) == 0 { + return errors.Wrap(errors.New(seth.ErrNoKeyLoaded), fmt.Sprintf("requested key: %d", 0)) + } + + return FundChainlinkNodes(logger, client, nodes, client.PrivateKeys[0], amount) +} + +// FundChainlinkNodes sends native token amount (expressed in human-scale) to each Chainlink Node +// from private key's address. It returns an error if any of the transactions failed. +func FundChainlinkNodes( + logger zerolog.Logger, + client *seth.Client, + nodes []contracts.ChainlinkNodeWithKeysAndAddress, + privateKey *ecdsa.PrivateKey, + amount *big.Float, +) error { + fundingErrors := []error{} + for _, cl := range nodes { + toAddress, err := cl.PrimaryEthAddress() + if err != nil { + return err + } + + fromAddress, err := privateKeyToAddress(privateKey) + if err != nil { + return err + } + + receipt, err := SendFunds(logger, client, FundsToSendPayload{ + ToAddress: common.HexToAddress(toAddress), + Amount: conversions.EtherToWei(amount), + PrivateKey: privateKey, + }) + if err != nil { + fundingErrors = append(fundingErrors, err) + + txHash := "(none)" + if receipt != nil { + txHash = receipt.TxHash.String() + } + + logger.Err(err). + Str("From", fromAddress.Hex()). + Str("To", toAddress). + Str("TxHash", txHash). + Msg("Failed to fund Chainlink node") + } + + logger.Info(). + Str("From", fromAddress.Hex()). + Str("To", toAddress). + Str("TxHash", receipt.TxHash.String()). + Str("Amount", amount.String()). + Msg("Funded Chainlink node") + } + + if len(fundingErrors) > 0 { + var wrapped error + for _, e := range fundingErrors { + wrapped = errors.Wrapf(e, ",") + } + return fmt.Errorf("failed to fund chainlink nodes due to following errors: %w", wrapped) + } + + return nil +} + +type FundsToSendPayload struct { + ToAddress common.Address + Amount *big.Int + PrivateKey *ecdsa.PrivateKey + GasLimit *uint64 +} + +// TODO: move to CTF? +// SendFunds sends native token amount (expressed in human-scale) from address controlled by private key +// to given address. If no gas limit is set, then network's default will be used. +func SendFunds(logger zerolog.Logger, client *seth.Client, payload FundsToSendPayload) (*types.Receipt, error) { + fromAddress, err := privateKeyToAddress(payload.PrivateKey) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), client.Cfg.Network.TxnTimeout.Duration()) + nonce, err := client.Client.PendingNonceAt(ctx, fromAddress) + defer cancel() + if err != nil { + return nil, err + } + + gasLimit := uint64(client.Cfg.Network.GasLimit) + if payload.GasLimit != nil { + gasLimit = *payload.GasLimit + } + + rawTx := &types.LegacyTx{ + Nonce: nonce, + To: &payload.ToAddress, + Value: payload.Amount, + Gas: gasLimit, + GasPrice: big.NewInt(client.Cfg.Network.GasPrice), + } + signedTx, err := types.SignNewTx(payload.PrivateKey, types.NewEIP155Signer(big.NewInt(client.ChainID)), rawTx) + if err != nil { + return nil, errors.Wrap(err, "failed to sign tx") + } + + ctx, cancel = context.WithTimeout(ctx, client.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + err = client.Client.SendTransaction(ctx, signedTx) + if err != nil { + return nil, errors.Wrap(err, "failed to send transaction") + } + + logger.Debug(). + Str("From", fromAddress.Hex()). + Str("To", payload.ToAddress.Hex()). + Str("TxHash", signedTx.Hash().String()). + Str("Amount", conversions.WeiToEther(payload.Amount).String()). + Uint64("Nonce", nonce). + Uint64("Gas Limit", gasLimit). + Int64("Gas Price", client.Cfg.Network.GasPrice). + Msg("Sent funds") + + return client.WaitMined(ctx, logger, client.Client, signedTx) +} + +// DeployForwarderContracts first deploys Operator Factory and then uses it to deploy given number of +// operator and forwarder pairs. It waits for each transaction to be mined and then extracts operator and +// forwarder addresses from emitted events. +func DeployForwarderContracts( + t *testing.T, + seth *seth.Client, + linkTokenData seth.DeploymentData, + numberOfOperatorForwarderPairs int, +) (operators []common.Address, authorizedForwarders []common.Address, operatorFactoryInstance contracts.OperatorFactory) { + instance, err := contracts.DeployEthereumOperatorFactory(seth, linkTokenData.Address) + require.NoError(t, err, "failed to create new instance of operator factory") + operatorFactoryInstance = &instance + + for i := 0; i < numberOfOperatorForwarderPairs; i++ { + decodedTx, err := seth.Decode(operatorFactoryInstance.DeployNewOperatorAndForwarder()) + require.NoError(t, err, "Deploying new operator with proposed ownership with forwarder shouldn't fail") + + for i, event := range decodedTx.Events { + require.True(t, len(event.Topics) > 0, fmt.Sprintf("Event %d should have topics", i)) + switch event.Topics[0] { + case operator_factory.OperatorFactoryOperatorCreated{}.Topic().String(): + if address, ok := event.EventData["operator"]; ok { + operators = append(operators, address.(common.Address)) + } else { + require.Fail(t, "Operator address not found in event", event) + } + case operator_factory.OperatorFactoryAuthorizedForwarderCreated{}.Topic().String(): + if address, ok := event.EventData["forwarder"]; ok { + authorizedForwarders = append(authorizedForwarders, address.(common.Address)) + } else { + require.Fail(t, "Forwarder address not found in event", event) + } + } + } + } + return operators, authorizedForwarders, operatorFactoryInstance +} + +// WatchNewRound watches for a new OCR round, similarly to StartNewRound, but it does not explicitly request a new +// round from the contract, as this can cause some odd behavior in some cases. It announces success if latest round +// is >= roundNumber. +func WatchNewRound( + l zerolog.Logger, + seth *seth.Client, + roundNumber int64, + ocrInstances []contracts.OffChainAggregatorWithRounds, + timeout time.Duration, +) error { + confirmed := make(map[string]bool) + timeoutC := time.After(timeout) + ticker := time.NewTicker(time.Millisecond * 200) + defer ticker.Stop() + + l.Info().Msgf("Waiting for round %d to be confirmed by all nodes", roundNumber) + + for { + select { + case <-timeoutC: + return fmt.Errorf("timeout waiting for round %d to be confirmed. %d/%d nodes confirmed it", roundNumber, len(confirmed), len(ocrInstances)) + case <-ticker.C: + for i := 0; i < len(ocrInstances); i++ { + if confirmed[ocrInstances[i].Address()] { + continue + } + ctx, cancel := context.WithTimeout(context.Background(), seth.Cfg.Network.TxnTimeout.Duration()) + roundData, err := ocrInstances[i].GetLatestRound(ctx) + if err != nil { + cancel() + return fmt.Errorf("getting latest round from OCR instance %d have failed: %w", i+1, err) + } + cancel() + if roundData.RoundId.Cmp(big.NewInt(roundNumber)) >= 0 { + l.Debug().Msgf("OCR instance %d/%d confirmed round %d", i+1, len(ocrInstances), roundNumber) + confirmed[ocrInstances[i].Address()] = true + } + } + if len(confirmed) == len(ocrInstances) { + return nil + } + } + } +} + +// AcceptAuthorizedReceiversOperator sets authorized receivers for each operator contract to +// authorizedForwarder and authorized EA to nodeAddresses. Once done, it confirms that authorizations +// were set correctly. +func AcceptAuthorizedReceiversOperator( + t *testing.T, + logger zerolog.Logger, + seth *seth.Client, + operator common.Address, + authorizedForwarder common.Address, + nodeAddresses []common.Address, +) { + operatorInstance, err := contracts.LoadEthereumOperator(logger, seth, operator) + require.NoError(t, err, "Loading operator contract shouldn't fail") + forwarderInstance, err := contracts.LoadEthereumAuthorizedForwarder(seth, authorizedForwarder) + require.NoError(t, err, "Loading authorized forwarder contract shouldn't fail") + + err = operatorInstance.AcceptAuthorizedReceivers([]common.Address{authorizedForwarder}, nodeAddresses) + require.NoError(t, err, "Accepting authorized forwarder shouldn't fail") + + senders, err := forwarderInstance.GetAuthorizedSenders(testcontext.Get(t)) + require.NoError(t, err, "Getting authorized senders shouldn't fail") + var nodesAddrs []string + for _, o := range nodeAddresses { + nodesAddrs = append(nodesAddrs, o.Hex()) + } + require.Equal(t, nodesAddrs, senders, "Senders addresses should match node addresses") + + owner, err := forwarderInstance.Owner(testcontext.Get(t)) + require.NoError(t, err, "Getting authorized forwarder owner shouldn't fail") + require.Equal(t, operator.Hex(), owner, "Forwarder owner should match operator") +} + +// TrackForwarder creates forwarder track for a given Chainlink node +func TrackForwarder( + t *testing.T, + seth *seth.Client, + authorizedForwarder common.Address, + node *client.ChainlinkK8sClient, +) { + l := logging.GetTestLogger(t) + chainID := big.NewInt(seth.ChainID) + _, _, err := node.TrackForwarder(chainID, authorizedForwarder) + require.NoError(t, err, "Forwarder track should be created") + l.Info().Str("NodeURL", node.Config.URL). + Str("ForwarderAddress", authorizedForwarder.Hex()). + Str("ChaindID", chainID.String()). + Msg("Forwarder tracked") +} + +// DeployOCRv2Contracts deploys a number of OCRv2 contracts and configures them with defaults +func DeployOCRv2Contracts( + l zerolog.Logger, + seth *seth.Client, + numberOfContracts int, + 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) + } + ocrInstances = append(ocrInstances, &ocrInstance) + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + + // Gather address payees + var payees []string + for range transmitters { + payees = append(payees, seth.Addresses[0].Hex()) + } + + // Set Payees + for contractCount, ocrInstance := range ocrInstances { + err := ocrInstance.SetPayees(transmitters, payees) + if err != nil { + return nil, fmt.Errorf("error settings OCR payees: %w", err) + } + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + return ocrInstances, nil +} + +// ConfigureOCRv2AggregatorContracts sets configuration for a number of OCRv2 contracts +func ConfigureOCRv2AggregatorContracts( + contractConfig *contracts.OCRv2Config, + ocrv2Contracts []contracts.OffchainAggregatorV2, +) error { + for contractCount, ocrInstance := range ocrv2Contracts { + // Exclude the first node, which will be used as a bootstrapper + err := ocrInstance.SetConfig(contractConfig) + if err != nil { + return fmt.Errorf("error setting OCR config for contract '%s': %w", ocrInstance.Address(), err) + } + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + return nil +} + +// TeardownRemoteSuite sends a report and returns funds from chainlink nodes to network's default wallet +func TeardownRemoteSuite( + t *testing.T, + client *seth.Client, + namespace string, + chainlinkNodes []*client.ChainlinkK8sClient, + optionalTestReporter testreporters.TestReporter, // Optionally pass in a test reporter to log further metrics + grafnaUrlProvider testreporters.GrafanaURLProvider, +) error { + l := logging.GetTestLogger(t) + if err := testreporters.SendReport(t, namespace, "./", optionalTestReporter, grafnaUrlProvider); err != nil { + l.Warn().Err(err).Msg("Error writing test report") + } + // Delete all jobs to stop depleting the funds + err := DeleteAllJobs(chainlinkNodes) + if err != nil { + l.Warn().Msgf("Error deleting jobs %+v", err) + } + + if err = ReturnFunds(l, client, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(chainlinkNodes)); err != nil { + l.Error().Err(err).Str("Namespace", namespace). + Msg("Error attempting to return funds from chainlink nodes to network's default wallet. " + + "Environment is left running so you can try manually!") + } + return err +} + +// DeleteAllJobs deletes all jobs from all chainlink nodes +// added here temporarily to avoid circular import +func DeleteAllJobs(chainlinkNodes []*client.ChainlinkK8sClient) error { + for _, node := range chainlinkNodes { + if node == nil { + return fmt.Errorf("found a nil chainlink node in the list of chainlink nodes while tearing down: %v", chainlinkNodes) + } + jobs, _, err := node.ReadJobs() + if err != nil { + return fmt.Errorf("error reading jobs from chainlink node, err: %w", err) + } + for _, maps := range jobs.Data { + if _, ok := maps["id"]; !ok { + return fmt.Errorf("error reading job id from chainlink node's jobs %+v", jobs.Data) + } + id := maps["id"].(string) + _, err := node.DeleteJob(id) + if err != nil { + return fmt.Errorf("error deleting job from chainlink node, err: %w", err) + } + } + } + return nil +} + +// StartNewRound requests a new round from the ocr contracts and returns once transaction was mined +func StartNewRound( + ocrInstances []contracts.OffChainAggregatorWithRounds, +) error { + for i := 0; i < len(ocrInstances); i++ { + err := ocrInstances[i].RequestNewRound() + if err != nil { + return fmt.Errorf("requesting new OCR round %d have failed: %w", i+1, err) + } + } + return nil +} + +// DeployOCRContractsForwarderFlow deploys and funds a certain number of offchain +// aggregator contracts with forwarders as effectiveTransmitters +func DeployOCRContractsForwarderFlow( + logger zerolog.Logger, + seth *seth.Client, + numberOfContracts int, + linkTokenContractAddress common.Address, + workerNodes []contracts.ChainlinkNodeWithKeysAndAddress, + forwarderAddresses []common.Address, +) ([]contracts.OffchainAggregator, error) { + transmitterPayeesFn := func() (transmitters []string, payees []string, err error) { + transmitters = make([]string, 0) + payees = make([]string, 0) + for _, forwarderCommonAddress := range forwarderAddresses { + forwarderAddress := forwarderCommonAddress.Hex() + transmitters = append(transmitters, forwarderAddress) + payees = append(payees, seth.Addresses[0].Hex()) + } + + return + } + + transmitterAddressesFn := func() ([]common.Address, error) { + return forwarderAddresses, nil + } + + return deployAnyOCRv1Contracts(logger, seth, numberOfContracts, linkTokenContractAddress, workerNodes, transmitterPayeesFn, transmitterAddressesFn) +} + +// DeployOCRv1Contracts deploys and funds a certain number of offchain aggregator contracts +func DeployOCRv1Contracts( + logger zerolog.Logger, + seth *seth.Client, + numberOfContracts int, + 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 { + var addr string + addr, err = node.PrimaryEthAddress() + if err != nil { + err = fmt.Errorf("error getting node's primary ETH address: %w", err) + return + } + transmitters = append(transmitters, addr) + payees = append(payees, seth.Addresses[0].Hex()) + } + + return + } + + transmitterAddressesFn := func() ([]common.Address, error) { + transmitterAddresses := make([]common.Address, 0) + for _, node := range workerNodes { + primaryAddress, err := node.PrimaryEthAddress() + if err != nil { + return nil, err + } + transmitterAddresses = append(transmitterAddresses, common.HexToAddress(primaryAddress)) + } + + return transmitterAddresses, nil + } + + return deployAnyOCRv1Contracts(logger, seth, numberOfContracts, linkTokenContractAddress, workerNodes, transmitterPayeesFn, transmitterAddressesFn) +} + +func deployAnyOCRv1Contracts( + logger zerolog.Logger, + seth *seth.Client, + numberOfContracts int, + 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) + } + ocrInstances = append(ocrInstances, &ocrInstance) + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + + // Gather transmitter and address payees + var transmitters, payees []string + var err error + transmitters, payees, err = getTransmitterAndPayeesFn() + if err != nil { + return nil, fmt.Errorf("error getting transmitter and payees: %w", err) + } + + // Set Payees + for contractCount, ocrInstance := range ocrInstances { + err := ocrInstance.SetPayees(transmitters, payees) + if err != nil { + return nil, fmt.Errorf("error settings OCR payees: %w", err) + } + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + + // Set Config + transmitterAddresses, err := getTransmitterAddressesFn() + if err != nil { + return nil, fmt.Errorf("getting transmitter addresses should not fail: %w", err) + } + + for contractCount, ocrInstance := range ocrInstances { + // Exclude the first node, which will be used as a bootstrapper + err = ocrInstance.SetConfig( + workerNodes, + contracts.DefaultOffChainAggregatorConfig(len(workerNodes)), + transmitterAddresses, + ) + if err != nil { + return nil, fmt.Errorf("error setting OCR config for contract '%s': %w", ocrInstance.Address(), err) + } + if (contractCount+1)%ContractDeploymentInterval == 0 { // For large amounts of contract deployments, space things out some + time.Sleep(2 * time.Second) + } + } + + return ocrInstances, nil +} + +func privateKeyToAddress(privateKey *ecdsa.PrivateKey) (common.Address, error) { + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return common.Address{}, errors.New("error casting public key to ECDSA") + } + return crypto.PubkeyToAddress(*publicKeyECDSA), nil +} diff --git a/integration-tests/actions/seth/refund.go b/integration-tests/actions/seth/refund.go new file mode 100644 index 0000000000..79fc60e675 --- /dev/null +++ b/integration-tests/actions/seth/refund.go @@ -0,0 +1,287 @@ +package actions_seth + +import ( + "context" + "crypto/ecdsa" + "encoding/json" + "fmt" + "math/big" + "regexp" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +const ( + InsufficientFundsErr = "insufficient funds" + GasTooLowErr = "gas too low" + OvershotErr = "overshot" +) + +var ( + RetrySuccessfulMsg = "Retry successful" + NotSupportedMsg = "Error not supported. Passing to next retrier" +) + +// TransactionRetrier is an interface that every retrier of failed funds transfer transaction needs to implement +type TransactionRetrier interface { + Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error +} + +// InsufficientFundTransferRetrier will retry a failed funds transfer transaction if the error is due to insufficient funds +// by subtracting 1 Gwei from amount to send and retrying it up to maxRetries times +type InsufficientFundTransferRetrier struct { + nextRetrier TransactionRetrier + maxRetries int +} + +func (r *InsufficientFundTransferRetrier) Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error { + if currentAttempt >= r.maxRetries { + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "InsufficientFundTransferRetrier"). + Msg("Max gas limit reached. Passing to next retrier") + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + return txErr + } + + for txErr != nil && (strings.Contains(txErr.Error(), InsufficientFundsErr)) { + logger.Info(). + Msg("Insufficient funds error detected, retrying with less funds") + + newAmount := big.NewInt(0).Sub(payload.Amount, big.NewInt(blockchain.GWei)) + + logger.Debug(). + Str("retier", "InsufficientFundTransferRetrier"). + Str("old amount", payload.Amount.String()). + Str("new amount", newAmount.String()). + Str("diff", big.NewInt(0).Sub(payload.Amount, newAmount).String()). + Msg("New amount to send") + + payload.Amount = newAmount + + _, retryErr := SendFunds(logger, client, payload) + if retryErr == nil { + logger.Info(). + Str("retier", "InsufficientFundTransferRetrier"). + Msg(RetrySuccessfulMsg) + return nil + } + + if strings.Contains(retryErr.Error(), InsufficientFundsErr) { + return r.Retry(ctx, logger, client, retryErr, payload, currentAttempt+1) + } + } + + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "InsufficientFundTransferRetrier"). + Msg(NotSupportedMsg) + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + + logger.Warn(). + Str("retier", "InsufficientFundTransferRetrier"). + Msg("No more retriers available. Unable to retry transaction. Returning error.") + + return txErr +} + +// GasTooLowTransferRetrier will retry a failed funds transfer transaction if the error is due to gas too low +// by doubling the gas limit and retrying until reaching maxGasLimit +type GasTooLowTransferRetrier struct { + nextRetrier TransactionRetrier + maxGasLimit uint64 +} + +func (r *GasTooLowTransferRetrier) Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error { + if payload.GasLimit != nil && *payload.GasLimit >= r.maxGasLimit { + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "GasTooLowTransferRetrier"). + Msg("Max gas limit reached. Passing to next retrier") + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + return txErr + } + + for txErr != nil && strings.Contains(txErr.Error(), GasTooLowErr) { + logger.Info(). + Msg("Too low gas error detected, retrying with more gas") + var newGasLimit uint64 + if payload.GasLimit != nil { + newGasLimit = *payload.GasLimit * 2 + } else { + newGasLimit = uint64(client.Cfg.Network.TransferGasFee) * 2 + } + + logger.Debug(). + Str("retier", "GasTooLowTransferRetrier"). + Uint64("old gas limit", newGasLimit/2). + Uint64("new gas limit", newGasLimit). + Uint64("diff", newGasLimit). + Msg("New gas limit to use") + + payload.GasLimit = &newGasLimit + + _, retryErr := SendFunds(logger, client, payload) + if retryErr == nil { + logger.Info(). + Str("retier", "GasTooLowTransferRetrier"). + Msg(RetrySuccessfulMsg) + return nil + } + + if strings.Contains(retryErr.Error(), GasTooLowErr) { + return r.Retry(ctx, logger, client, retryErr, payload, currentAttempt+1) + } + } + + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "OvershotTransferRetrier"). + Msg(NotSupportedMsg) + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + + logger.Warn(). + Str("retier", "OvershotTransferRetrier"). + Msg("No more retriers available. Unable to retry transaction. Returning error.") + + return txErr +} + +// OvershotTransferRetrier will retry a failed funds transfer transaction if the error is due to overshot +// by subtracting the overshot amount from the amount to send and retrying it up to maxRetries times +type OvershotTransferRetrier struct { + nextRetrier TransactionRetrier + maxRetries int +} + +func (r *OvershotTransferRetrier) Retry(ctx context.Context, logger zerolog.Logger, client *seth.Client, txErr error, payload FundsToSendPayload, currentAttempt int) error { + if currentAttempt >= r.maxRetries { + logger.Debug(). + Str("retier", "OvershotTransferRetrier"). + Msg("Max retries reached. Passing to next retrier") + if r.nextRetrier != nil { + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + return txErr + } + + overshotRe := regexp.MustCompile(`overshot (\d+)`) + if txErr != nil && strings.Contains(txErr.Error(), OvershotErr) { + logger.Info(). + Msg("Overshot error detected, retrying with less funds") + submatches := overshotRe.FindStringSubmatch(txErr.Error()) + if len(submatches) < 1 { + return fmt.Errorf("error parsing overshot amount in error: %w", txErr) + } + numberString := submatches[1] + overshotAmount, err := strconv.Atoi(numberString) + if err != nil { + return err + } + + newAmount := big.NewInt(0).Sub(payload.Amount, big.NewInt(int64(overshotAmount))) + logger.Debug(). + Str("retier", "OvershotTransferRetrier"). + Str("old amount", payload.Amount.String()). + Str("new amount", newAmount.String()). + Str("diff", big.NewInt(0).Sub(payload.Amount, newAmount).String()). + Msg("New amount to send") + + payload.Amount = newAmount + + _, retryErr := SendFunds(logger, client, payload) + if retryErr == nil { + logger.Info(). + Str("retier", "OvershotTransferRetrier"). + Msg(RetrySuccessfulMsg) + return nil + } + + if strings.Contains(retryErr.Error(), OvershotErr) { + return r.Retry(ctx, logger, client, retryErr, payload, currentAttempt+1) + } + } + + if r.nextRetrier != nil { + logger.Debug(). + Str("retier", "OvershotTransferRetrier"). + Msg(NotSupportedMsg) + return r.nextRetrier.Retry(ctx, logger, client, txErr, payload, 0) + } + + return txErr +} + +// ReturnFunds returns funds from the given chainlink nodes to the default network wallet. It will use a variety +// of strategies to attempt to return funds, including retrying with less funds if the transaction fails due to +// insufficient funds, and retrying with a higher gas limit if the transaction fails due to gas too low. +func ReturnFunds(log zerolog.Logger, seth *seth.Client, chainlinkNodes []contracts.ChainlinkNodeWithKeysAndAddress) error { + if seth == nil { + return fmt.Errorf("Seth client is nil, unable to return funds from chainlink nodes") + } + log.Info().Msg("Attempting to return Chainlink node funds to default network wallets") + if seth.Cfg.IsSimulatedNetwork() { + log.Info().Str("Network Name", seth.Cfg.Network.Name). + Msg("Network is a simulated network. Skipping fund return.") + return nil + } + + for _, chainlinkNode := range chainlinkNodes { + fundedKeys, err := chainlinkNode.ExportEVMKeysForChain(fmt.Sprint(seth.ChainID)) + if err != nil { + return err + } + for _, key := range fundedKeys { + keyToDecrypt, err := json.Marshal(key) + if err != nil { + return err + } + // This can take up a good bit of RAM and time. When running on the remote-test-runner, this can lead to OOM + // issues. So we avoid running in parallel; slower, but safer. + decryptedKey, err := keystore.DecryptKey(keyToDecrypt, client.ChainlinkKeyPassword) + if err != nil { + return err + } + + publicKey := decryptedKey.PrivateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return errors.New("error casting public key to ECDSA") + } + fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + + balance, err := seth.Client.BalanceAt(context.Background(), fromAddress, nil) + if err != nil { + return err + } + + totalGasCost := new(big.Int).Mul(big.NewInt(0).SetUint64(seth.Cfg.Network.GasLimit), big.NewInt(0).SetInt64(seth.Cfg.Network.GasPrice)) + toSend := new(big.Int).Sub(balance, totalGasCost) + + payload := FundsToSendPayload{ToAddress: seth.Addresses[0], Amount: toSend, PrivateKey: decryptedKey.PrivateKey} + + _, err = SendFunds(log, seth, payload) + if err != nil { + handler := OvershotTransferRetrier{maxRetries: 3, nextRetrier: &InsufficientFundTransferRetrier{maxRetries: 3, nextRetrier: &GasTooLowTransferRetrier{maxGasLimit: seth.Cfg.Network.GasLimit * 3}}} + return handler.Retry(context.Background(), log, seth, err, payload, 0) + } + } + } + + return nil +} diff --git a/integration-tests/actions/vrf/common/actions.go b/integration-tests/actions/vrf/common/actions.go new file mode 100644 index 0000000000..7121374571 --- /dev/null +++ b/integration-tests/actions/vrf/common/actions.go @@ -0,0 +1,223 @@ +package common + +import ( + "context" + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" + "github.com/rs/zerolog" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "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/docker/test_env" + vrf_common_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/common/vrf" +) + +func CreateFundAndGetSendingKeys( + client blockchain.EVMClient, + node *VRFNode, + chainlinkNodeFunding float64, + numberOfTxKeysToCreate int, + chainID *big.Int, +) ([]string, []common.Address, error) { + newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(client, node, chainlinkNodeFunding, numberOfTxKeysToCreate, chainID) + if err != nil { + return nil, nil, err + } + nativeTokenPrimaryKeyAddress, err := node.CLNode.API.PrimaryEthAddress() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) + } + allNativeTokenKeyAddressStrings := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) + allNativeTokenKeyAddresses := make([]common.Address, len(allNativeTokenKeyAddressStrings)) + for _, addressString := range allNativeTokenKeyAddressStrings { + allNativeTokenKeyAddresses = append(allNativeTokenKeyAddresses, common.HexToAddress(addressString)) + } + return allNativeTokenKeyAddressStrings, allNativeTokenKeyAddresses, nil +} + +func CreateAndFundSendingKeys( + client blockchain.EVMClient, + node *VRFNode, + chainlinkNodeFunding float64, + numberOfNativeTokenAddressesToCreate int, + chainID *big.Int, +) ([]string, error) { + var newNativeTokenKeyAddresses []string + for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ { + newTxKey, response, err := node.CLNode.API.CreateTxKey("evm", chainID.String()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrNodeNewTxKey, err) + } + if response.StatusCode != 200 { + return nil, fmt.Errorf("error creating transaction key - response code, err %d", response.StatusCode) + } + newNativeTokenKeyAddresses = append(newNativeTokenKeyAddresses, newTxKey.Data.Attributes.Address) + err = actions.FundAddress(client, newTxKey.Data.Attributes.Address, big.NewFloat(chainlinkNodeFunding)) + if err != nil { + return nil, err + } + } + return newNativeTokenKeyAddresses, nil +} + +func SetupBHSNode( + env *test_env.CLClusterTestEnv, + config *vrf_common_config.General, + numberOfTxKeysToCreate int, + chainID *big.Int, + coordinatorAddress string, + BHSAddress string, + txKeyFunding float64, + l zerolog.Logger, + bhsNode *VRFNode, +) error { + evmClient, err := env.GetEVMClient(chainID.Int64()) + if err != nil { + return err + } + + bhsTXKeyAddressStrings, _, err := CreateFundAndGetSendingKeys( + evmClient, + bhsNode, + txKeyFunding, + numberOfTxKeysToCreate, + chainID, + ) + if err != nil { + return err + } + bhsNode.TXKeyAddressStrings = bhsTXKeyAddressStrings + bhsSpec := client.BlockhashStoreJobSpec{ + ForwardingAllowed: false, + CoordinatorV2Address: coordinatorAddress, + CoordinatorV2PlusAddress: coordinatorAddress, + BlockhashStoreAddress: BHSAddress, + FromAddresses: bhsTXKeyAddressStrings, + EVMChainID: chainID.String(), + WaitBlocks: *config.BHSJobWaitBlocks, + LookbackBlocks: *config.BHSJobLookBackBlocks, + PollPeriod: config.BHSJobPollPeriod.Duration, + RunTimeout: config.BHSJobRunTimeout.Duration, + } + l.Info().Msg("Creating BHS Job") + bhsJob, err := CreateBHSJob( + bhsNode.CLNode.API, + bhsSpec, + ) + if err != nil { + return fmt.Errorf("%s, err %w", "", err) + } + bhsNode.Job = bhsJob + return nil +} + +func CreateBHSJob( + chainlinkNode *client.ChainlinkClient, + bhsJobSpecConfig client.BlockhashStoreJobSpec, +) (*client.Job, error) { + jobUUID := uuid.New() + spec := &client.BlockhashStoreJobSpec{ + Name: fmt.Sprintf("bhs-%s", jobUUID), + ForwardingAllowed: bhsJobSpecConfig.ForwardingAllowed, + CoordinatorV2Address: bhsJobSpecConfig.CoordinatorV2Address, + CoordinatorV2PlusAddress: bhsJobSpecConfig.CoordinatorV2PlusAddress, + BlockhashStoreAddress: bhsJobSpecConfig.BlockhashStoreAddress, + FromAddresses: bhsJobSpecConfig.FromAddresses, + EVMChainID: bhsJobSpecConfig.EVMChainID, + ExternalJobID: jobUUID.String(), + WaitBlocks: bhsJobSpecConfig.WaitBlocks, + LookbackBlocks: bhsJobSpecConfig.LookbackBlocks, + PollPeriod: bhsJobSpecConfig.PollPeriod, + RunTimeout: bhsJobSpecConfig.RunTimeout, + } + + job, err := chainlinkNode.MustCreateJob(spec) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrCreatingBHSJob, err) + } + return job, nil +} + +func WaitForRequestCountEqualToFulfilmentCount( + ctx context.Context, + consumer VRFLoadTestConsumer, + timeout time.Duration, + wg *sync.WaitGroup, +) (*big.Int, *big.Int, error) { + metricsChannel := make(chan *contracts.VRFLoadTestMetrics) + metricsErrorChannel := make(chan error) + + testContext, testCancel := context.WithTimeout(ctx, timeout) + defer testCancel() + + ticker := time.NewTicker(time.Second * 1) + var metrics *contracts.VRFLoadTestMetrics + for { + select { + case <-testContext.Done(): + ticker.Stop() + wg.Done() + return metrics.RequestCount, metrics.FulfilmentCount, + fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", + metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) + case <-ticker.C: + go retrieveLoadTestMetrics(ctx, consumer, metricsChannel, metricsErrorChannel) + case metrics = <-metricsChannel: + if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { + ticker.Stop() + wg.Done() + return metrics.RequestCount, metrics.FulfilmentCount, nil + } + case err := <-metricsErrorChannel: + ticker.Stop() + wg.Done() + return nil, nil, err + } + } +} + +func retrieveLoadTestMetrics( + ctx context.Context, + consumer VRFLoadTestConsumer, + metricsChannel chan *contracts.VRFLoadTestMetrics, + metricsErrorChannel chan error, +) { + metrics, err := consumer.GetLoadTestMetrics(ctx) + if err != nil { + metricsErrorChannel <- err + } + metricsChannel <- metrics +} + +func CreateNodeTypeToNodeMap(cluster *test_env.ClCluster, nodesToCreate []VRFNodeType) map[VRFNodeType]*VRFNode { + var nodesMap = make(map[VRFNodeType]*VRFNode) + for i, nodeType := range nodesToCreate { + nodesMap[nodeType] = &VRFNode{ + CLNode: cluster.Nodes[i], + } + } + return nodesMap +} + +func CreateVRFKeyOnVRFNode(vrfNode *VRFNode, l zerolog.Logger) (*client.VRFKey, string, error) { + l.Info().Str("Node URL", vrfNode.CLNode.API.URL()).Msg("Creating VRF Key on the Node") + vrfKey, err := vrfNode.CLNode.API.MustCreateVRFKey() + if err != nil { + return nil, "", fmt.Errorf("%s, err %w", ErrCreatingVRFKey, err) + } + pubKeyCompressed := vrfKey.Data.ID + l.Info(). + Str("Node URL", vrfNode.CLNode.API.URL()). + Str("Keyhash", vrfKey.Data.Attributes.Hash). + Str("VRF Compressed Key", vrfKey.Data.Attributes.Compressed). + Str("VRF Uncompressed Key", vrfKey.Data.Attributes.Uncompressed). + Msg("VRF Key created on the Node") + return vrfKey, pubKeyCompressed, nil +} diff --git a/integration-tests/actions/vrf/common/errors.go b/integration-tests/actions/vrf/common/errors.go new file mode 100644 index 0000000000..ba852a4c55 --- /dev/null +++ b/integration-tests/actions/vrf/common/errors.go @@ -0,0 +1,28 @@ +package common + +const ( + ErrNodePrimaryKey = "error getting node's primary ETH key" + ErrNodeNewTxKey = "error creating node's EVM transaction key" + ErrCreatingProvingKeyHash = "error creating a keyHash from the proving key" + ErrRegisteringProvingKey = "error registering a proving key on Coordinator contract" + ErrRegisterProvingKey = "error registering proving keys" + ErrEncodingProvingKey = "error encoding proving key" + ErrDeployBlockHashStore = "error deploying blockhash store" + ErrDeployCoordinator = "error deploying VRF CoordinatorV2" + ErrABIEncodingFunding = "error Abi encoding subscriptionID" + ErrSendingLinkToken = "error sending Link token" + ErrCreatingBHSJob = "error creating BHS job" + ErrParseJob = "error parsing job definition" + ErrSetVRFCoordinatorConfig = "error setting config for VRF Coordinator contract" + ErrCreateVRFSubscription = "error creating VRF Subscription" + ErrAddConsumerToSub = "error adding consumer to VRF Subscription" + ErrFundSubWithLinkToken = "error funding subscription with Link tokens" + ErrRestartCLNode = "error restarting CL node" + ErrWaitTXsComplete = "error waiting for TXs to complete" + ErrRequestRandomness = "error requesting randomness" + ErrLoadingCoordinator = "error loading coordinator contract" + ErrCreatingVRFKey = "error creating VRF key" + + ErrWaitRandomWordsRequestedEvent = "error waiting for RandomWordsRequested event" + ErrWaitRandomWordsFulfilledEvent = "error waiting for RandomWordsFulfilled event" +) diff --git a/integration-tests/actions/vrf/common/models.go b/integration-tests/actions/vrf/common/models.go new file mode 100644 index 0000000000..08a004da48 --- /dev/null +++ b/integration-tests/actions/vrf/common/models.go @@ -0,0 +1,75 @@ +package common + +import ( + "context" + "math/big" + "time" + + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" +) + +type VRFEncodedProvingKey [2]*big.Int + +// VRFV2PlusKeyData defines a jobs into and proving key info +type VRFKeyData struct { + VRFKey *client.VRFKey + EncodedProvingKey VRFEncodedProvingKey + KeyHash [32]byte +} + +type VRFNodeType int + +const ( + VRF VRFNodeType = iota + 1 + BHS +) + +func (n VRFNodeType) String() string { + return [...]string{"VRF", "BHS"}[n-1] +} + +func (n VRFNodeType) Index() int { + return int(n) +} + +type VRFNode struct { + CLNode *test_env.ClNode + Job *client.Job + TXKeyAddressStrings []string +} + +type VRFContracts struct { + CoordinatorV2 contracts.VRFCoordinatorV2 + CoordinatorV2Plus contracts.VRFCoordinatorV2_5 + VRFOwner contracts.VRFOwner + BHS contracts.BlockHashStore + VRFV2Consumer []contracts.VRFv2LoadTestConsumer + VRFV2PlusConsumer []contracts.VRFv2PlusLoadTestConsumer +} + +type VRFOwnerConfig struct { + OwnerAddress string + UseVRFOwner bool +} + +type VRFJobSpecConfig struct { + ForwardingAllowed bool + CoordinatorAddress string + FromAddresses []string + EVMChainID string + MinIncomingConfirmations int + PublicKey string + BatchFulfillmentEnabled bool + BatchFulfillmentGasMultiplier float64 + EstimateGasMultiplier float64 + PollPeriod time.Duration + RequestTimeout time.Duration + VRFOwnerConfig *VRFOwnerConfig + SimulationBlock *string +} + +type VRFLoadTestConsumer interface { + GetLoadTestMetrics(ctx context.Context) (*contracts.VRFLoadTestMetrics, error) +} diff --git a/integration-tests/actions/vrf/vrfv2/errors.go b/integration-tests/actions/vrf/vrfv2/errors.go new file mode 100644 index 0000000000..3ca94dd630 --- /dev/null +++ b/integration-tests/actions/vrf/vrfv2/errors.go @@ -0,0 +1,9 @@ +package vrfv2 + +const ( + ErrDeployVRFV2Wrapper = "error deploying VRFV2Wrapper" + ErrCreateVRFV2Jobs = "error creating VRF V2 Jobs" + ErrDeployVRFV2Contracts = "error deploying VRFV2 contracts" + ErrCreatingVRFv2Job = "error creating VRFv2 job" + ErrAdvancedConsumer = "error deploying VRFv2 Advanced Consumer" +) diff --git a/integration-tests/actions/vrf/vrfv2/vrfv2_models.go b/integration-tests/actions/vrf/vrfv2/vrfv2_models.go index be627b43e4..3216af4990 100644 --- a/integration-tests/actions/vrf/vrfv2/vrfv2_models.go +++ b/integration-tests/actions/vrf/vrfv2/vrfv2_models.go @@ -1,44 +1,10 @@ package vrfv2 import ( - "math/big" - - "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) -type VRFV2EncodedProvingKey [2]*big.Int - -// VRFV2JobInfo defines a jobs into and proving key info -type VRFV2JobInfo struct { - Job *client.Job - VRFKey *client.VRFKey - EncodedProvingKey VRFV2EncodedProvingKey - KeyHash [32]byte -} - -type VRFV2Contracts struct { - Coordinator contracts.VRFCoordinatorV2 - VRFOwner contracts.VRFOwner - BHS contracts.BlockHashStore - LoadTestConsumers []contracts.VRFv2LoadTestConsumer -} - type VRFV2WrapperContracts struct { VRFV2Wrapper contracts.VRFV2Wrapper LoadTestConsumers []contracts.VRFv2WrapperLoadTestConsumer } - -// VRFV2PlusKeyData defines a jobs into and proving key info -type VRFV2KeyData struct { - VRFKey *client.VRFKey - EncodedProvingKey VRFV2EncodedProvingKey - KeyHash [32]byte -} - -type VRFV2Data struct { - VRFV2KeyData - VRFJob *client.Job - PrimaryEthAddress string - ChainID *big.Int -} diff --git a/integration-tests/actions/vrf/vrfv2/vrfv2_steps.go b/integration-tests/actions/vrf/vrfv2/vrfv2_steps.go index 276105d20e..0175ba52cf 100644 --- a/integration-tests/actions/vrf/vrfv2/vrfv2_steps.go +++ b/integration-tests/actions/vrf/vrfv2/vrfv2_steps.go @@ -4,15 +4,16 @@ import ( "context" "fmt" "math/big" - "sync" "time" "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" + "golang.org/x/sync/errgroup" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + testconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner" @@ -23,6 +24,7 @@ import ( chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -30,93 +32,49 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/types" ) -var ( - ErrNodePrimaryKey = "error getting node's primary ETH key" - ErrNodeNewTxKey = "error creating node's EVM transaction key" - ErrCreatingProvingKeyHash = "error creating a keyHash from the proving key" - ErrRegisteringProvingKey = "error registering a proving key on Coordinator contract" - ErrRegisterProvingKey = "error registering proving keys" - ErrEncodingProvingKey = "error encoding proving key" - ErrCreatingVRFv2Key = "error creating VRFv2 key" - ErrDeployBlockHashStore = "error deploying blockhash store" - ErrDeployCoordinator = "error deploying VRF CoordinatorV2" - ErrAdvancedConsumer = "error deploying VRFv2 Advanced Consumer" - ErrABIEncodingFunding = "error Abi encoding subscriptionID" - ErrSendingLinkToken = "error sending Link token" - ErrCreatingVRFv2Job = "error creating VRFv2 job" - ErrParseJob = "error parsing job definition" - ErrDeployVRFV2Contracts = "error deploying VRFV2 contracts" - ErrSetVRFCoordinatorConfig = "error setting config for VRF Coordinator contract" - ErrCreateVRFSubscription = "error creating VRF Subscription" - ErrAddConsumerToSub = "error adding consumer to VRF Subscription" - ErrFundSubWithLinkToken = "error funding subscription with Link tokens" - ErrCreateVRFV2Jobs = "error creating VRF V2 Jobs" - ErrRestartCLNode = "error restarting CL node" - ErrWaitTXsComplete = "error waiting for TXs to complete" - ErrRequestRandomness = "error requesting randomness" - ErrLoadingCoordinator = "error loading coordinator contract" - - ErrWaitRandomWordsRequestedEvent = "error waiting for RandomWordsRequested event" - ErrWaitRandomWordsFulfilledEvent = "error waiting for RandomWordsFulfilled event" - ErrDeployWrapper = "error deploying VRFV2PlusWrapper" -) - -type VRFOwnerConfig struct { - OwnerAddress string - useVRFOwner bool -} - -type VRFJobSpecConfig struct { - ForwardingAllowed bool - CoordinatorAddress string - FromAddresses []string - EVMChainID string - MinIncomingConfirmations int - PublicKey string - BatchFulfillmentEnabled bool - BatchFulfillmentGasMultiplier float64 - EstimateGasMultiplier float64 - PollPeriod time.Duration - RequestTimeout time.Duration - VRFOwnerConfig VRFOwnerConfig -} - func DeployVRFV2Contracts( env *test_env.CLClusterTestEnv, + chainID int64, linkTokenContract contracts.LinkToken, linkEthFeedContract contracts.MockETHLINKFeed, consumerContractsAmount int, useVRFOwner bool, useTestCoordinator bool, -) (*VRFV2Contracts, error) { +) (*vrfcommon.VRFContracts, error) { bhs, err := env.ContractDeployer.DeployBlockhashStore() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployBlockHashStore, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrDeployBlockHashStore, err) } - err = env.EVMClient.WaitForEvents() + + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return nil, err + } + + err = evmClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } var coordinatorAddress string if useTestCoordinator { testCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorTestV2(linkTokenContract.Address(), bhs.Address(), linkEthFeedContract.Address()) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrDeployCoordinator, err) } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } coordinatorAddress = testCoordinator.Address() } else { coordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2(linkTokenContract.Address(), bhs.Address(), linkEthFeedContract.Address()) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrDeployCoordinator, err) } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } coordinatorAddress = coordinator.Address() } @@ -124,27 +82,37 @@ func DeployVRFV2Contracts( if err != nil { return nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(coordinatorAddress) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrLoadingCoordinator, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrLoadingCoordinator, err) } if useVRFOwner { vrfOwner, err := env.ContractDeployer.DeployVRFOwner(coordinatorAddress) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrDeployCoordinator, err) } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } - return &VRFV2Contracts{coordinator, vrfOwner, bhs, consumers}, nil - } - return &VRFV2Contracts{coordinator, nil, bhs, consumers}, nil + return &vrfcommon.VRFContracts{ + CoordinatorV2: coordinator, + VRFOwner: vrfOwner, + BHS: bhs, + VRFV2Consumer: consumers, + }, nil + } + return &vrfcommon.VRFContracts{ + CoordinatorV2: coordinator, + VRFOwner: nil, + BHS: bhs, + VRFV2Consumer: consumers, + }, nil } func DeployVRFV2Consumers(contractDeployer contracts.ContractDeployer, coordinatorAddress string, consumerContractsAmount int) ([]contracts.VRFv2LoadTestConsumer, error) { @@ -181,11 +149,11 @@ func DeployVRFV2DirectFundingContracts( ) (*VRFV2WrapperContracts, error) { vrfv2Wrapper, err := contractDeployer.DeployVRFV2Wrapper(linkTokenAddress, linkEthFeedAddress, coordinator.Address()) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployWrapper, err) + return nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2Wrapper, err) } err = chainClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } consumers, err := DeployVRFV2WrapperConsumers(contractDeployer, linkTokenAddress, vrfv2Wrapper, consumerContractsAmount) @@ -194,24 +162,25 @@ func DeployVRFV2DirectFundingContracts( } err = chainClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return &VRFV2WrapperContracts{vrfv2Wrapper, consumers}, nil } func CreateVRFV2Job( chainlinkNode *client.ChainlinkClient, - vrfJobSpecConfig VRFJobSpecConfig, + vrfJobSpecConfig vrfcommon.VRFJobSpecConfig, ) (*client.Job, error) { jobUUID := uuid.New() os := &client.VRFV2TxPipelineSpec{ Address: vrfJobSpecConfig.CoordinatorAddress, EstimateGasMultiplier: vrfJobSpecConfig.EstimateGasMultiplier, FromAddress: vrfJobSpecConfig.FromAddresses[0], + SimulationBlock: vrfJobSpecConfig.SimulationBlock, } ost, err := os.String() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrParseJob, err) } spec := &client.VRFV2JobSpec{ @@ -229,13 +198,13 @@ func CreateVRFV2Job( PollPeriod: vrfJobSpecConfig.PollPeriod, RequestTimeout: vrfJobSpecConfig.RequestTimeout, } - if vrfJobSpecConfig.VRFOwnerConfig.useVRFOwner { + if vrfJobSpecConfig.VRFOwnerConfig.UseVRFOwner { spec.VRFOwner = vrfJobSpecConfig.VRFOwnerConfig.OwnerAddress spec.UseVRFOwner = true } if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrParseJob, err) } job, err := chainlinkNode.MustCreateJob(spec) @@ -249,17 +218,17 @@ func VRFV2RegisterProvingKey( vrfKey *client.VRFKey, oracleAddress string, coordinator contracts.VRFCoordinatorV2, -) (VRFV2EncodedProvingKey, error) { +) (vrfcommon.VRFEncodedProvingKey, error) { provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) if err != nil { - return VRFV2EncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrEncodingProvingKey, err) + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrEncodingProvingKey, err) } err = coordinator.RegisterProvingKey( oracleAddress, provingKey, ) if err != nil { - return VRFV2EncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrRegisterProvingKey, err) + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisterProvingKey, err) } return provingKey, nil } @@ -273,11 +242,11 @@ func FundVRFCoordinatorV2Subscription( ) error { encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint64"}]`, subscriptionID) if err != nil { - return fmt.Errorf("%s, err %w", ErrABIEncodingFunding, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrABIEncodingFunding, err) } _, err = linkToken.TransferAndCall(coordinator.Address(), linkFundingAmountJuels, encodedSubId) if err != nil { - return fmt.Errorf("%s, err %w", ErrSendingLinkToken, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrSendingLinkToken, err) } return chainClient.WaitForEvents() } @@ -285,6 +254,8 @@ func FundVRFCoordinatorV2Subscription( // SetupVRFV2Environment will create specified number of subscriptions and add the same conumer/s to each of them func SetupVRFV2Environment( env *test_env.CLClusterTestEnv, + chainID int64, + nodesToCreate []vrfcommon.VRFNodeType, vrfv2TestConfig types.VRFv2TestConfig, useVRFOwner bool, useTestCoordinator bool, @@ -295,205 +266,290 @@ func SetupVRFV2Environment( numberOfConsumers int, numberOfSubToCreate int, l zerolog.Logger, -) (*VRFV2Contracts, []uint64, *VRFV2Data, error) { +) (*vrfcommon.VRFContracts, []uint64, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { l.Info().Msg("Starting VRFV2 environment setup") - l.Info().Msg("Deploying VRFV2 contracts") - vrfv2Contracts, err := DeployVRFV2Contracts( + configGeneral := vrfv2TestConfig.GetVRFv2Config().General + vrfContracts, subIDs, err := SetupVRFV2Contracts( env, + chainID, linkToken, mockNativeLINKFeed, numberOfConsumers, useVRFOwner, useTestCoordinator, + configGeneral, + numberOfSubToCreate, + l, ) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2Contracts, err) + return nil, nil, nil, nil, err } - vrfv2Config := vrfv2TestConfig.GetVRFv2Config().General - vrfCoordinatorV2FeeConfig := vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ - FulfillmentFlatFeeLinkPPMTier1: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier1, - FulfillmentFlatFeeLinkPPMTier2: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier2, - FulfillmentFlatFeeLinkPPMTier3: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier3, - FulfillmentFlatFeeLinkPPMTier4: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier4, - FulfillmentFlatFeeLinkPPMTier5: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier5, - ReqsForTier2: big.NewInt(*vrfv2Config.ReqsForTier2), - ReqsForTier3: big.NewInt(*vrfv2Config.ReqsForTier3), - ReqsForTier4: big.NewInt(*vrfv2Config.ReqsForTier4), - ReqsForTier5: big.NewInt(*vrfv2Config.ReqsForTier5)} - l.Info().Str("Coordinator", vrfv2Contracts.Coordinator.Address()).Msg("Setting Coordinator Config") - err = vrfv2Contracts.Coordinator.SetConfig( - *vrfv2Config.MinimumConfirmations, - *vrfv2Config.MaxGasLimitCoordinatorConfig, - *vrfv2Config.StalenessSeconds, - *vrfv2Config.GasAfterPaymentCalculation, - big.NewInt(*vrfv2Config.FallbackWeiPerUnitLink), - vrfCoordinatorV2FeeConfig, - ) - if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrSetVRFCoordinatorConfig, err) - } - err = env.EVMClient.WaitForEvents() + nodeTypeToNodeMap := vrfcommon.CreateNodeTypeToNodeMap(env.ClCluster, nodesToCreate) + vrfKey, pubKeyCompressed, err := vrfcommon.CreateVRFKeyOnVRFNode(nodeTypeToNodeMap[vrfcommon.VRF], l) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, nil, nil, err } - l.Info(). - Str("Coordinator", vrfv2Contracts.Coordinator.Address()). - Int("Number of Subs to create", numberOfSubToCreate). - Msg("Creating and funding subscriptions, adding consumers") - subIDs, err := CreateFundSubsAndAddConsumers( - env, - big.NewFloat(*vrfv2Config.SubscriptionFundingAmountLink), - linkToken, - vrfv2Contracts.Coordinator, vrfv2Contracts.LoadTestConsumers, numberOfSubToCreate) + + l.Info().Str("Coordinator", vrfContracts.CoordinatorV2.Address()).Msg("Registering Proving Key") + provingKey, err := VRFV2RegisterProvingKey(vrfKey, registerProvingKeyAgainstAddress, vrfContracts.CoordinatorV2) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisteringProvingKey, err) } - l.Info().Str("Node URL", env.ClCluster.NodeAPIs()[0].URL()).Msg("Creating VRF Key on the Node") - vrfKey, err := env.ClCluster.NodeAPIs()[0].MustCreateVRFKey() + keyHash, err := vrfContracts.CoordinatorV2.HashOfKey(context.Background(), provingKey) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Key, err) + return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreatingProvingKeyHash, err) } - pubKeyCompressed := vrfKey.Data.ID - l.Info().Str("Coordinator", vrfv2Contracts.Coordinator.Address()).Msg("Registering Proving Key") - provingKey, err := VRFV2RegisterProvingKey(vrfKey, registerProvingKeyAgainstAddress, vrfv2Contracts.Coordinator) + evmClient, err := env.GetEVMClient(chainID) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRegisteringProvingKey, err) - } - keyHash, err := vrfv2Contracts.Coordinator.HashOfKey(context.Background(), provingKey) - if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreatingProvingKeyHash, err) + return nil, nil, nil, nil, err } - chainID := env.EVMClient.GetChainID() - newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2TestConfig, numberOfTxKeysToCreate, chainID) + vrfTXKeyAddressStrings, vrfTXKeyAddresses, err := vrfcommon.CreateFundAndGetSendingKeys( + evmClient, + nodeTypeToNodeMap[vrfcommon.VRF], + *vrfv2TestConfig.GetCommonConfig().ChainlinkNodeFunding, + numberOfTxKeysToCreate, + big.NewInt(chainID), + ) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } - nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) + return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } - allNativeTokenKeyAddressStrings := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) - allNativeTokenKeyAddresses := make([]common.Address, len(allNativeTokenKeyAddressStrings)) - for _, addressString := range allNativeTokenKeyAddressStrings { - allNativeTokenKeyAddresses = append(allNativeTokenKeyAddresses, common.HexToAddress(addressString)) - } + nodeTypeToNodeMap[vrfcommon.VRF].TXKeyAddressStrings = vrfTXKeyAddressStrings - var vrfOwnerConfig VRFOwnerConfig + var vrfOwnerConfig *vrfcommon.VRFOwnerConfig if useVRFOwner { - err := setupVRFOwnerContract(env, vrfv2Contracts, allNativeTokenKeyAddressStrings, allNativeTokenKeyAddresses, l) + err := setupVRFOwnerContract(env, chainID, vrfContracts, vrfTXKeyAddressStrings, vrfTXKeyAddresses, l) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } - vrfOwnerConfig = VRFOwnerConfig{ - OwnerAddress: vrfv2Contracts.VRFOwner.Address(), - useVRFOwner: useVRFOwner, + vrfOwnerConfig = &vrfcommon.VRFOwnerConfig{ + OwnerAddress: vrfContracts.VRFOwner.Address(), + UseVRFOwner: useVRFOwner, } } else { - vrfOwnerConfig = VRFOwnerConfig{ + vrfOwnerConfig = &vrfcommon.VRFOwnerConfig{ OwnerAddress: "", - useVRFOwner: useVRFOwner, + UseVRFOwner: useVRFOwner, } } - vrfJobSpecConfig := VRFJobSpecConfig{ - ForwardingAllowed: false, - CoordinatorAddress: vrfv2Contracts.Coordinator.Address(), - FromAddresses: allNativeTokenKeyAddressStrings, + g := errgroup.Group{} + if vrfNode, exists := nodeTypeToNodeMap[vrfcommon.VRF]; exists { + g.Go(func() error { + err := setupVRFNode(vrfContracts, big.NewInt(chainID), configGeneral, pubKeyCompressed, vrfOwnerConfig, l, vrfNode) + if err != nil { + return err + } + return nil + }) + } + + if bhsNode, exists := nodeTypeToNodeMap[vrfcommon.BHS]; exists { + g.Go(func() error { + err := vrfcommon.SetupBHSNode( + env, + configGeneral.General, + numberOfTxKeysToCreate, + big.NewInt(chainID), + vrfContracts.CoordinatorV2.Address(), + vrfContracts.BHS.Address(), + *vrfv2TestConfig.GetCommonConfig().ChainlinkNodeFunding, + l, + bhsNode, + ) + if err != nil { + return err + } + return nil + }) + } + + if err := g.Wait(); err != nil { + return nil, nil, nil, nil, fmt.Errorf("VRF node setup ended up with an error: %w", err) + } + + vrfKeyData := vrfcommon.VRFKeyData{ + VRFKey: vrfKey, + EncodedProvingKey: provingKey, + KeyHash: keyHash, + } + + l.Info().Msg("VRFV2 environment setup is finished") + return vrfContracts, subIDs, &vrfKeyData, nodeTypeToNodeMap, nil +} + +func setupVRFNode(contracts *vrfcommon.VRFContracts, chainID *big.Int, vrfv2Config *testconfig.General, pubKeyCompressed string, vrfOwnerConfig *vrfcommon.VRFOwnerConfig, l zerolog.Logger, vrfNode *vrfcommon.VRFNode) error { + vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ + ForwardingAllowed: *vrfv2Config.VRFJobForwardingAllowed, + CoordinatorAddress: contracts.CoordinatorV2.Address(), + FromAddresses: vrfNode.TXKeyAddressStrings, EVMChainID: chainID.String(), MinIncomingConfirmations: int(*vrfv2Config.MinimumConfirmations), PublicKey: pubKeyCompressed, - EstimateGasMultiplier: 1, - BatchFulfillmentEnabled: false, - BatchFulfillmentGasMultiplier: 1.15, - PollPeriod: time.Second * 1, - RequestTimeout: time.Hour * 24, + EstimateGasMultiplier: *vrfv2Config.VRFJobEstimateGasMultiplier, + BatchFulfillmentEnabled: *vrfv2Config.VRFJobBatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *vrfv2Config.VRFJobBatchFulfillmentGasMultiplier, + PollPeriod: vrfv2Config.VRFJobPollPeriod.Duration, + RequestTimeout: vrfv2Config.VRFJobRequestTimeout.Duration, + SimulationBlock: vrfv2Config.VRFJobSimulationBlock, VRFOwnerConfig: vrfOwnerConfig, } l.Info().Msg("Creating VRFV2 Job") vrfV2job, err := CreateVRFV2Job( - env.ClCluster.NodeAPIs()[0], + vrfNode.CLNode.API, vrfJobSpecConfig, ) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreateVRFV2Jobs, err) + return fmt.Errorf("%s, err %w", ErrCreateVRFV2Jobs, err) } + vrfNode.Job = vrfV2job // this part is here because VRFv2 can work with only a specific key // [[EVM.KeySpecific]] // Key = '...' - nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(allNativeTokenKeyAddressStrings, *vrfv2Config.CLNodeMaxGasPriceGWei), + nodeConfig := node.NewConfig(vrfNode.CLNode.NodeConfig, + node.WithLogPollInterval(1*time.Second), + node.WithVRFv2EVMEstimator(vrfNode.TXKeyAddressStrings, *vrfv2Config.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration") - err = env.ClCluster.Nodes[0].Restart(nodeConfig) + err = vrfNode.CLNode.Restart(nodeConfig) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRestartCLNode, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrRestartCLNode, err) } + return nil +} - vrfv2KeyData := VRFV2KeyData{ - VRFKey: vrfKey, - EncodedProvingKey: provingKey, - KeyHash: keyHash, +func SetupVRFV2Contracts( + env *test_env.CLClusterTestEnv, + chainID int64, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.MockETHLINKFeed, + numberOfConsumers int, + useVRFOwner bool, + useTestCoordinator bool, + vrfv2Config *testconfig.General, + numberOfSubToCreate int, + l zerolog.Logger, +) (*vrfcommon.VRFContracts, []uint64, error) { + l.Info().Msg("Deploying VRFV2 contracts") + vrfContracts, err := DeployVRFV2Contracts( + env, + chainID, + linkToken, + mockNativeLINKFeed, + numberOfConsumers, + useVRFOwner, + useTestCoordinator, + ) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2Contracts, err) } - data := VRFV2Data{ - vrfv2KeyData, - vrfV2job, - nativeTokenPrimaryKeyAddress, - chainID, + vrfCoordinatorV2FeeConfig := vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ + FulfillmentFlatFeeLinkPPMTier1: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier1, + FulfillmentFlatFeeLinkPPMTier2: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier2, + FulfillmentFlatFeeLinkPPMTier3: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier3, + FulfillmentFlatFeeLinkPPMTier4: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier4, + FulfillmentFlatFeeLinkPPMTier5: *vrfv2Config.FulfillmentFlatFeeLinkPPMTier5, + ReqsForTier2: big.NewInt(*vrfv2Config.ReqsForTier2), + ReqsForTier3: big.NewInt(*vrfv2Config.ReqsForTier3), + ReqsForTier4: big.NewInt(*vrfv2Config.ReqsForTier4), + ReqsForTier5: big.NewInt(*vrfv2Config.ReqsForTier5)} + + l.Info().Str("Coordinator", vrfContracts.CoordinatorV2.Address()).Msg("Setting Coordinator Config") + err = vrfContracts.CoordinatorV2.SetConfig( + *vrfv2Config.MinimumConfirmations, + *vrfv2Config.MaxGasLimitCoordinatorConfig, + *vrfv2Config.StalenessSeconds, + *vrfv2Config.GasAfterPaymentCalculation, + big.NewInt(*vrfv2Config.FallbackWeiPerUnitLink), + vrfCoordinatorV2FeeConfig, + ) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrSetVRFCoordinatorConfig, err) } - l.Info().Msg("VRFV2 environment setup is finished") - return vrfv2Contracts, subIDs, &data, nil + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return nil, nil, err + } + + err = evmClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + l.Info(). + Str("Coordinator", vrfContracts.CoordinatorV2.Address()). + Int("Number of Subs to create", numberOfSubToCreate). + Msg("Creating and funding subscriptions, adding consumers") + subIDs, err := CreateFundSubsAndAddConsumers( + env, + chainID, + big.NewFloat(*vrfv2Config.SubscriptionFundingAmountLink), + linkToken, + vrfContracts.CoordinatorV2, vrfContracts.VRFV2Consumer, numberOfSubToCreate) + if err != nil { + return nil, nil, err + } + return vrfContracts, subIDs, nil } -func setupVRFOwnerContract(env *test_env.CLClusterTestEnv, vrfv2Contracts *VRFV2Contracts, allNativeTokenKeyAddressStrings []string, allNativeTokenKeyAddresses []common.Address, l zerolog.Logger) error { +func setupVRFOwnerContract(env *test_env.CLClusterTestEnv, chainID int64, contracts *vrfcommon.VRFContracts, allNativeTokenKeyAddressStrings []string, allNativeTokenKeyAddresses []common.Address, l zerolog.Logger) error { l.Info().Msg("Setting up VRFOwner contract") l.Info(). - Str("Coordinator", vrfv2Contracts.Coordinator.Address()). - Str("VRFOwner", vrfv2Contracts.VRFOwner.Address()). + Str("Coordinator", contracts.CoordinatorV2.Address()). + Str("VRFOwner", contracts.VRFOwner.Address()). Msg("Transferring ownership of Coordinator to VRFOwner") - err := vrfv2Contracts.Coordinator.TransferOwnership(common.HexToAddress(vrfv2Contracts.VRFOwner.Address())) + err := contracts.CoordinatorV2.TransferOwnership(common.HexToAddress(contracts.VRFOwner.Address())) if err != nil { return nil } - err = env.EVMClient.WaitForEvents() + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return err + } + + err = evmClient.WaitForEvents() if err != nil { return nil } l.Info(). - Str("VRFOwner", vrfv2Contracts.VRFOwner.Address()). + Str("VRFOwner", contracts.VRFOwner.Address()). Msg("Accepting VRF Ownership") - err = vrfv2Contracts.VRFOwner.AcceptVRFOwnership() + err = contracts.VRFOwner.AcceptVRFOwnership() if err != nil { return nil } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { return nil } l.Info(). Strs("Authorized Senders", allNativeTokenKeyAddressStrings). - Str("VRFOwner", vrfv2Contracts.VRFOwner.Address()). + Str("VRFOwner", contracts.VRFOwner.Address()). Msg("Setting authorized senders for VRFOwner contract") - err = vrfv2Contracts.VRFOwner.SetAuthorizedSenders(allNativeTokenKeyAddresses) + err = contracts.VRFOwner.SetAuthorizedSenders(allNativeTokenKeyAddresses) if err != nil { return nil } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return err } func SetupVRFV2WrapperEnvironment( env *test_env.CLClusterTestEnv, + chainID int64, vrfv2TestConfig tc.VRFv2TestConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, @@ -501,10 +557,15 @@ func SetupVRFV2WrapperEnvironment( keyHash [32]byte, wrapperConsumerContractsAmount int, ) (*VRFV2WrapperContracts, *uint64, error) { + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return nil, nil, err + } + // Deploy VRF v2 direct funding contracts wrapperContracts, err := DeployVRFV2DirectFundingContracts( env.ContractDeployer, - env.EVMClient, + evmClient, linkToken.Address(), mockNativeLINKFeed.Address(), coordinator, @@ -513,9 +574,9 @@ func SetupVRFV2WrapperEnvironment( if err != nil { return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } vrfv2Config := vrfv2TestConfig.GetVRFv2Config() @@ -531,9 +592,9 @@ func SetupVRFV2WrapperEnvironment( if err != nil { return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } // Fetch wrapper subscription ID @@ -541,13 +602,13 @@ func SetupVRFV2WrapperEnvironment( if err != nil { return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } // Fund wrapper subscription - err = FundSubscriptions(env, big.NewFloat(*vrfv2Config.General.SubscriptionFundingAmountLink), linkToken, coordinator, []uint64{wrapperSubID}) + err = FundSubscriptions(env, chainID, big.NewFloat(*vrfv2Config.General.SubscriptionFundingAmountLink), linkToken, coordinator, []uint64{wrapperSubID}) if err != nil { return nil, nil, err } @@ -560,42 +621,24 @@ func SetupVRFV2WrapperEnvironment( if err != nil { return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return wrapperContracts, &wrapperSubID, nil } -func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, testConfig tc.CommonTestConfig, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { - var newNativeTokenKeyAddresses []string - for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ { - newTxKey, response, err := env.ClCluster.NodeAPIs()[0].CreateTxKey("evm", chainID.String()) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrNodeNewTxKey, err) - } - if response.StatusCode != 200 { - return nil, fmt.Errorf("error creating transaction key - response code, err %d", response.StatusCode) - } - newNativeTokenKeyAddresses = append(newNativeTokenKeyAddresses, newTxKey.Data.ID) - err = actions.FundAddress(env.EVMClient, newTxKey.Data.ID, big.NewFloat(*testConfig.GetCommonConfig().ChainlinkNodeFunding)) - if err != nil { - return nil, err - } - } - return newNativeTokenKeyAddresses, nil -} - func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, + chainID int64, subscriptionFundingAmountLink *big.Float, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, consumers []contracts.VRFv2LoadTestConsumer, numberOfSubToCreate int, ) ([]uint64, error) { - subIDs, err := CreateSubsAndFund(env, subscriptionFundingAmountLink, linkToken, coordinator, numberOfSubToCreate) + subIDs, err := CreateSubsAndFund(env, chainID, subscriptionFundingAmountLink, linkToken, coordinator, numberOfSubToCreate) if err != nil { return nil, err } @@ -614,29 +657,40 @@ func CreateFundSubsAndAddConsumers( return nil, err } - err = env.EVMClient.WaitForEvents() + evmClient, err := env.GetEVMClient(chainID) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, err + } + + err = evmClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return subIDs, nil } func CreateSubsAndFund( env *test_env.CLClusterTestEnv, + chainID int64, subscriptionFundingAmountLink *big.Float, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, subAmountToCreate int, ) ([]uint64, error) { - subs, err := CreateSubs(env, coordinator, subAmountToCreate) + subs, err := CreateSubs(env, chainID, coordinator, subAmountToCreate) + if err != nil { + return nil, err + } + evmClient, err := env.GetEVMClient(chainID) if err != nil { return nil, err } - err = env.EVMClient.WaitForEvents() + + err = evmClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } - err = FundSubscriptions(env, subscriptionFundingAmountLink, linkToken, coordinator, subs) + err = FundSubscriptions(env, chainID, subscriptionFundingAmountLink, linkToken, coordinator, subs) if err != nil { return nil, err } @@ -645,13 +699,14 @@ func CreateSubsAndFund( func CreateSubs( env *test_env.CLClusterTestEnv, + chainID int64, coordinator contracts.VRFCoordinatorV2, subAmountToCreate int, ) ([]uint64, error) { var subIDArr []uint64 for i := 0; i < subAmountToCreate; i++ { - subID, err := CreateSubAndFindSubID(env, coordinator) + subID, err := CreateSubAndFindSubID(env, chainID, coordinator) if err != nil { return nil, err } @@ -668,26 +723,31 @@ func AddConsumersToSubs( for _, consumer := range consumers { err := coordinator.AddConsumer(subID, consumer.Address()) if err != nil { - return fmt.Errorf("%s, err %w", ErrAddConsumerToSub, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrAddConsumerToSub, err) } } } return nil } -func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2) (uint64, error) { +func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, chainID int64, coordinator contracts.VRFCoordinatorV2) (uint64, error) { tx, err := coordinator.CreateSubscription() if err != nil { - return 0, fmt.Errorf("%s, err %w", ErrCreateVRFSubscription, err) + return 0, fmt.Errorf("%s, err %w", vrfcommon.ErrCreateVRFSubscription, err) } - err = env.EVMClient.WaitForEvents() + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return 0, err + } + + err = evmClient.WaitForEvents() if err != nil { - return 0, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return 0, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } - receipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + receipt, err := evmClient.GetTxReceipt(tx.Hash()) if err != nil { - return 0, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return 0, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } //SubscriptionsCreated Log should be emitted with the subscription ID @@ -698,22 +758,28 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts func FundSubscriptions( env *test_env.CLClusterTestEnv, + chainID int64, subscriptionFundingAmountLink *big.Float, linkAddress contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, subIDs []uint64, ) error { + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return err + } + for _, subID := range subIDs { //Link Billing amountJuels := conversions.EtherToWei(subscriptionFundingAmountLink) - err := FundVRFCoordinatorV2Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) + err := FundVRFCoordinatorV2Subscription(linkAddress, coordinator, evmClient, subID, amountJuels) if err != nil { - return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrFundSubWithLinkToken, err) } } - err := env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return nil } @@ -723,7 +789,7 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( consumer contracts.VRFv2WrapperLoadTestConsumer, coordinator contracts.VRFCoordinatorV2, subID uint64, - vrfv2Data *VRFV2Data, + vrfv2KeyData *vrfcommon.VRFKeyData, minimumConfirmations uint16, callbackGasLimit uint32, numberOfWords uint32, @@ -739,7 +805,7 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( randomnessRequestCountPerRequest, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) } wrapperAddress, err := consumer.GetWrapper(context.Background()) if err != nil { @@ -748,7 +814,7 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents( wrapperAddress.String(), coordinator, - vrfv2Data, + vrfv2KeyData, subID, randomWordsFulfilledEventTimeout, l, @@ -761,7 +827,7 @@ func RequestRandomnessAndWaitForFulfillment( consumer contracts.VRFv2LoadTestConsumer, coordinator contracts.VRFCoordinatorV2, subID uint64, - vrfv2Data *VRFV2Data, + vrfKeyData *vrfcommon.VRFKeyData, minimumConfirmations uint16, callbackGasLimit uint32, numberOfWords uint32, @@ -771,7 +837,7 @@ func RequestRandomnessAndWaitForFulfillment( ) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { logRandRequest(l, consumer.Address(), coordinator.Address(), subID, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) _, err := consumer.RequestRandomness( - vrfv2Data.KeyHash, + vrfKeyData.KeyHash, subID, minimumConfirmations, callbackGasLimit, @@ -779,13 +845,13 @@ func RequestRandomnessAndWaitForFulfillment( randomnessRequestCountPerRequest, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) } fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents( consumer.Address(), coordinator, - vrfv2Data, + vrfKeyData, subID, randomWordsFulfilledEventTimeout, l, @@ -798,7 +864,7 @@ func RequestRandomnessWithForceFulfillAndWaitForFulfillment( consumer contracts.VRFv2LoadTestConsumer, coordinator contracts.VRFCoordinatorV2, vrfOwner contracts.VRFOwner, - vrfv2Data *VRFV2Data, + vrfv2KeyData *vrfcommon.VRFKeyData, minimumConfirmations uint16, callbackGasLimit uint32, numberOfWords uint32, @@ -810,7 +876,7 @@ func RequestRandomnessWithForceFulfillAndWaitForFulfillment( ) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSet, *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, *vrf_owner.VRFOwnerRandomWordsForced, error) { logRandRequest(l, consumer.Address(), coordinator.Address(), 0, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) _, err := consumer.RequestRandomWordsWithForceFulfill( - vrfv2Data.KeyHash, + vrfv2KeyData.KeyHash, minimumConfirmations, callbackGasLimit, numberOfWords, @@ -819,17 +885,17 @@ func RequestRandomnessWithForceFulfillAndWaitForFulfillment( linkAddress, ) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) + return nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) } randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( - [][32]byte{vrfv2Data.KeyHash}, + [][32]byte{vrfv2KeyData.KeyHash}, nil, []common.Address{common.HexToAddress(consumer.Address())}, time.Minute*1, ) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsRequestedEvent, err) + return nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitRandomWordsRequestedEvent, err) } LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent) @@ -888,7 +954,7 @@ func RequestRandomnessWithForceFulfillAndWaitForFulfillment( case randomWordsForcedEvent = <-randWordsForcedEventChannel: LogRandomWordsForcedEvent(l, vrfOwner, randomWordsForcedEvent) case <-time.After(randomWordsFulfilledEventTimeout): - return nil, nil, nil, fmt.Errorf("timeout waiting for ConfigSet, RandomWordsFulfilled and RandomWordsForced events") + err = fmt.Errorf("timeout waiting for ConfigSet, RandomWordsFulfilled and RandomWordsForced events") } } return configSetEvent, randomWordsFulfilledEvent, randomWordsForcedEvent, err @@ -897,79 +963,33 @@ func RequestRandomnessWithForceFulfillAndWaitForFulfillment( func WaitForRequestAndFulfillmentEvents( consumerAddress string, coordinator contracts.VRFCoordinatorV2, - vrfv2Data *VRFV2Data, + vrfv2KeyData *vrfcommon.VRFKeyData, subID uint64, randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( - [][32]byte{vrfv2Data.KeyHash}, + [][32]byte{vrfv2KeyData.KeyHash}, []uint64{subID}, []common.Address{common.HexToAddress(consumerAddress)}, time.Minute*1, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsRequestedEvent, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitRandomWordsRequestedEvent, err) } - LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent) + randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent( []*big.Int{randomWordsRequestedEvent.RequestId}, randomWordsFulfilledEventTimeout, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsFulfilledEvent, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitRandomWordsFulfilledEvent, err) } - LogRandomWordsFulfilledEvent(l, coordinator, randomWordsFulfilledEvent) return randomWordsFulfilledEvent, err } -func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2LoadTestConsumer, timeout time.Duration, wg *sync.WaitGroup) (*big.Int, *big.Int, error) { - metricsChannel := make(chan *contracts.VRFLoadTestMetrics) - metricsErrorChannel := make(chan error) - - testContext, testCancel := context.WithTimeout(context.Background(), timeout) - defer testCancel() - - ticker := time.NewTicker(time.Second * 1) - var metrics *contracts.VRFLoadTestMetrics - for { - select { - case <-testContext.Done(): - ticker.Stop() - wg.Done() - return metrics.RequestCount, metrics.FulfilmentCount, - fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", - metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) - case <-ticker.C: - go retrieveLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) - case metrics = <-metricsChannel: - if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { - ticker.Stop() - wg.Done() - return metrics.RequestCount, metrics.FulfilmentCount, nil - } - case err := <-metricsErrorChannel: - ticker.Stop() - wg.Done() - return nil, nil, err - } - } -} - -func retrieveLoadTestMetrics( - consumer contracts.VRFv2LoadTestConsumer, - metricsChannel chan *contracts.VRFLoadTestMetrics, - metricsErrorChannel chan error, -) { - metrics, err := consumer.GetLoadTestMetrics(context.Background()) - if err != nil { - metricsErrorChannel <- err - } - metricsChannel <- metrics -} - func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2.GetSubscription, subID uint64, coordinator contracts.VRFCoordinatorV2) { l.Debug(). Str("Coordinator", coordinator.Address()). @@ -985,15 +1005,18 @@ func LogRandomnessRequestedEvent( coordinator contracts.VRFCoordinatorV2, randomWordsRequestedEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, ) { - l.Debug(). + l.Info(). Str("Coordinator", coordinator.Address()). Str("Request ID", randomWordsRequestedEvent.RequestId.String()). Uint64("Subscription ID", randomWordsRequestedEvent.SubId). Str("Sender Address", randomWordsRequestedEvent.Sender.String()). - Interface("Keyhash", randomWordsRequestedEvent.KeyHash). + Str("Keyhash", fmt.Sprintf("0x%x", randomWordsRequestedEvent.KeyHash)). Uint32("Callback Gas Limit", randomWordsRequestedEvent.CallbackGasLimit). Uint32("Number of Words", randomWordsRequestedEvent.NumWords). Uint16("Minimum Request Confirmations", randomWordsRequestedEvent.MinimumRequestConfirmations). + Str("TX Hash", randomWordsRequestedEvent.Raw.TxHash.String()). + Uint64("BlockNumber", randomWordsRequestedEvent.Raw.BlockNumber). + Str("BlockHash", randomWordsRequestedEvent.Raw.BlockHash.String()). Msg("RandomnessRequested Event") } @@ -1002,12 +1025,14 @@ func LogRandomWordsFulfilledEvent( coordinator contracts.VRFCoordinatorV2, randomWordsFulfilledEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, ) { - l.Debug(). + l.Info(). Str("Coordinator", coordinator.Address()). Str("Total Payment", randomWordsFulfilledEvent.Payment.String()). Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). Str("Request ID", randomWordsFulfilledEvent.RequestId.String()). Bool("Success", randomWordsFulfilledEvent.Success). + Uint64("BlockNumber", randomWordsFulfilledEvent.Raw.BlockNumber). + Str("BlockHash", randomWordsFulfilledEvent.Raw.BlockHash.String()). Msg("RandomWordsFulfilled Event (TX metadata)") } @@ -1036,7 +1061,7 @@ func logRandRequest( randomnessRequestCountPerRequest uint16, randomnessRequestCountPerRequestDeviation uint16, ) { - l.Debug(). + l.Info(). Str("Consumer", consumer). Str("Coordinator", coordinator). Uint64("SubID", subID). diff --git a/integration-tests/actions/vrf/vrfv2plus/errors.go b/integration-tests/actions/vrf/vrfv2plus/errors.go new file mode 100644 index 0000000000..d39e2002c1 --- /dev/null +++ b/integration-tests/actions/vrf/vrfv2plus/errors.go @@ -0,0 +1,17 @@ +package vrfv2plus + +const ( + ErrCreatingVRFv2PlusKey = "error creating VRFv2Plus key" + ErrAdvancedConsumer = "error deploying VRFv2Plus Advanced Consumer" + ErrCreatingVRFv2PlusJob = "error creating VRFv2Plus job" + ErrDeployVRFV2_5Contracts = "error deploying VRFV2_5 contracts" + ErrAddConsumerToSub = "error adding consumer to VRF Subscription" + ErrFundSubWithNativeToken = "error funding subscription with native token" + ErrSetLinkNativeLinkFeed = "error setting Link and ETH/LINK feed for VRF Coordinator contract" + ErrCreateVRFV2PlusJobs = "error creating VRF V2 Plus Jobs" + ErrRequestRandomnessDirectFundingLinkPayment = "error requesting randomness with direct funding and link payment" + ErrRequestRandomnessDirectFundingNativePayment = "error requesting randomness with direct funding and native payment" + ErrLinkTotalBalance = "error waiting for RandomWordsFulfilled event" + ErrNativeTokenBalance = "error waiting for RandomWordsFulfilled event" + ErrDeployWrapper = "error deploying VRFV2PlusWrapper" +) diff --git a/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_models.go b/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_models.go index c227d490eb..a2ca8ec582 100644 --- a/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_models.go +++ b/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_models.go @@ -1,34 +1,9 @@ package vrfv2plus import ( - "math/big" - - "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) -type VRFV2PlusEncodedProvingKey [2]*big.Int - -// VRFV2PlusKeyData defines a jobs into and proving key info -type VRFV2PlusKeyData struct { - VRFKey *client.VRFKey - EncodedProvingKey VRFV2PlusEncodedProvingKey - KeyHash [32]byte -} - -type VRFV2PlusData struct { - VRFV2PlusKeyData - VRFJob *client.Job - PrimaryEthAddress string - ChainID *big.Int -} - -type VRFV2_5Contracts struct { - Coordinator contracts.VRFCoordinatorV2_5 - BHS contracts.BlockHashStore - LoadTestConsumers []contracts.VRFv2PlusLoadTestConsumer -} - type VRFV2PlusWrapperContracts struct { VRFV2PlusWrapper contracts.VRFV2PlusWrapper LoadTestConsumers []contracts.VRFv2PlusWrapperLoadTestConsumer diff --git a/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go index fc2a47f53e..b58fddefb3 100644 --- a/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go @@ -4,11 +4,15 @@ import ( "context" "fmt" "math/big" - "sync" "time" + "golang.org/x/sync/errgroup" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" + testconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" @@ -21,86 +25,33 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/types" - "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" ) -var ( - ErrNodePrimaryKey = "error getting node's primary ETH key" - ErrNodeNewTxKey = "error creating node's EVM transaction key" - ErrCreatingProvingKeyHash = "error creating a keyHash from the proving key" - ErrRegisteringProvingKey = "error registering a proving key on Coordinator contract" - ErrRegisterProvingKey = "error registering proving keys" - ErrEncodingProvingKey = "error encoding proving key" - ErrCreatingVRFv2PlusKey = "error creating VRFv2Plus key" - ErrDeployBlockHashStore = "error deploying blockhash store" - ErrDeployCoordinator = "error deploying VRF CoordinatorV2Plus" - ErrAdvancedConsumer = "error deploying VRFv2Plus Advanced Consumer" - ErrABIEncodingFunding = "error Abi encoding subscriptionID" - ErrSendingLinkToken = "error sending Link token" - ErrCreatingVRFv2PlusJob = "error creating VRFv2Plus job" - ErrParseJob = "error parsing job definition" - ErrDeployVRFV2_5Contracts = "error deploying VRFV2_5 contracts" - ErrSetVRFCoordinatorConfig = "error setting config for VRF Coordinator contract" - ErrCreateVRFSubscription = "error creating VRF Subscription" - ErrAddConsumerToSub = "error adding consumer to VRF Subscription" - ErrFundSubWithNativeToken = "error funding subscription with native token" - ErrSetLinkNativeLinkFeed = "error setting Link and ETH/LINK feed for VRF Coordinator contract" - ErrFundSubWithLinkToken = "error funding subscription with Link tokens" - ErrCreateVRFV2PlusJobs = "error creating VRF V2 Plus Jobs" - ErrGetPrimaryKey = "error getting primary ETH key address" - ErrRestartCLNode = "error restarting CL node" - ErrWaitTXsComplete = "error waiting for TXs to complete" - ErrRequestRandomness = "error requesting randomness" - ErrRequestRandomnessDirectFundingLinkPayment = "error requesting randomness with direct funding and link payment" - ErrRequestRandomnessDirectFundingNativePayment = "error requesting randomness with direct funding and native payment" - - ErrWaitRandomWordsRequestedEvent = "error waiting for RandomWordsRequested event" - ErrWaitRandomWordsFulfilledEvent = "error waiting for RandomWordsFulfilled event" - ErrLinkTotalBalance = "error waiting for RandomWordsFulfilled event" - ErrNativeTokenBalance = "error waiting for RandomWordsFulfilled event" - ErrDeployWrapper = "error deploying VRFV2PlusWrapper" -) - -type VRFJobSpecConfig struct { - ForwardingAllowed bool - CoordinatorAddress string - FromAddresses []string - EVMChainID string - MinIncomingConfirmations int - PublicKey string - BatchFulfillmentEnabled bool - BatchFulfillmentGasMultiplier float64 - EstimateGasMultiplier float64 - PollPeriod time.Duration - RequestTimeout time.Duration -} - func DeployVRFV2_5Contracts( contractDeployer contracts.ContractDeployer, chainClient blockchain.EVMClient, consumerContractsAmount int, -) (*VRFV2_5Contracts, error) { +) (*vrfcommon.VRFContracts, error) { bhs, err := contractDeployer.DeployBlockhashStore() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployBlockHashStore, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrDeployBlockHashStore, err) } err = chainClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } coordinator, err := contractDeployer.DeployVRFCoordinatorV2_5(bhs.Address()) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrDeployCoordinator, err) } err = chainClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } consumers, err := DeployVRFV2PlusConsumers(contractDeployer, coordinator, consumerContractsAmount) if err != nil { @@ -108,9 +59,13 @@ func DeployVRFV2_5Contracts( } err = chainClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } - return &VRFV2_5Contracts{coordinator, bhs, consumers}, nil + return &vrfcommon.VRFContracts{ + CoordinatorV2Plus: coordinator, + BHS: bhs, + VRFV2PlusConsumer: consumers, + }, nil } func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coordinator contracts.VRFCoordinatorV2_5, consumerContractsAmount int) ([]contracts.VRFv2PlusLoadTestConsumer, error) { @@ -127,17 +82,18 @@ func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coord func CreateVRFV2PlusJob( chainlinkNode *client.ChainlinkClient, - vrfJobSpecConfig VRFJobSpecConfig, + vrfJobSpecConfig vrfcommon.VRFJobSpecConfig, ) (*client.Job, error) { jobUUID := uuid.New() os := &client.VRFV2PlusTxPipelineSpec{ Address: vrfJobSpecConfig.CoordinatorAddress, EstimateGasMultiplier: vrfJobSpecConfig.EstimateGasMultiplier, FromAddress: vrfJobSpecConfig.FromAddresses[0], + SimulationBlock: vrfJobSpecConfig.SimulationBlock, } ost, err := os.String() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrParseJob, err) } job, err := chainlinkNode.MustCreateJob(&client.VRFV2PlusJobSpec{ @@ -157,23 +113,24 @@ func CreateVRFV2PlusJob( if err != nil { return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2PlusJob, err) } - return job, nil } func VRFV2_5RegisterProvingKey( vrfKey *client.VRFKey, coordinator contracts.VRFCoordinatorV2_5, -) (VRFV2PlusEncodedProvingKey, error) { + gasLaneMaxGas uint64, +) (vrfcommon.VRFEncodedProvingKey, error) { provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) if err != nil { - return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrEncodingProvingKey, err) + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrEncodingProvingKey, err) } err = coordinator.RegisterProvingKey( provingKey, + gasLaneMaxGas, ) if err != nil { - return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrRegisterProvingKey, err) + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisterProvingKey, err) } return provingKey, nil } @@ -181,16 +138,16 @@ func VRFV2_5RegisterProvingKey( func VRFV2PlusUpgradedVersionRegisterProvingKey( vrfKey *client.VRFKey, coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, -) (VRFV2PlusEncodedProvingKey, error) { +) (vrfcommon.VRFEncodedProvingKey, error) { provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) if err != nil { - return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrEncodingProvingKey, err) + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrEncodingProvingKey, err) } err = coordinator.RegisterProvingKey( provingKey, ) if err != nil { - return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrRegisterProvingKey, err) + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisterProvingKey, err) } return provingKey, nil } @@ -204,11 +161,11 @@ func FundVRFCoordinatorV2_5Subscription( ) error { encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint256"}]`, subscriptionID) if err != nil { - return fmt.Errorf("%s, err %w", ErrABIEncodingFunding, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrABIEncodingFunding, err) } _, err = linkToken.TransferAndCall(coordinator.Address(), linkFundingAmountJuels, encodedSubId) if err != nil { - return fmt.Errorf("%s, err %w", ErrSendingLinkToken, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrSendingLinkToken, err) } return chainClient.WaitForEvents() } @@ -216,6 +173,8 @@ func FundVRFCoordinatorV2_5Subscription( // SetupVRFV2_5Environment will create specified number of subscriptions and add the same conumer/s to each of them func SetupVRFV2_5Environment( env *test_env.CLClusterTestEnv, + chainID int64, + nodesToCreate []vrfcommon.VRFNodeType, vrfv2PlusTestConfig types.VRFv2PlusTestConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, @@ -223,173 +182,230 @@ func SetupVRFV2_5Environment( numberOfConsumers int, numberOfSubToCreate int, l zerolog.Logger, -) (*VRFV2_5Contracts, []*big.Int, *VRFV2PlusData, error) { +) (*vrfcommon.VRFContracts, []*big.Int, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { l.Info().Msg("Starting VRFV2 Plus environment setup") - l.Info().Msg("Deploying VRFV2 Plus contracts") - vrfv2_5Contracts, err := DeployVRFV2_5Contracts(env.ContractDeployer, env.EVMClient, numberOfConsumers) + configGeneral := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General + vrfContracts, subIDs, err := SetupVRFV2PlusContracts( + env, + chainID, + linkToken, + mockNativeLINKFeed, + configGeneral, + numberOfSubToCreate, + numberOfConsumers, + l, + ) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2_5Contracts, err) + return nil, nil, nil, nil, err } - l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Setting Coordinator Config") - vrfv2PlusConfig := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General - err = vrfv2_5Contracts.Coordinator.SetConfig( - *vrfv2PlusConfig.MinimumConfirmations, - *vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, - *vrfv2PlusConfig.StalenessSeconds, - *vrfv2PlusConfig.GasAfterPaymentCalculation, - big.NewInt(*vrfv2PlusConfig.FallbackWeiPerUnitLink), - vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig{ - FulfillmentFlatFeeLinkPPM: *vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, - FulfillmentFlatFeeNativePPM: *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, - }, - ) + nodeTypeToNodeMap := vrfcommon.CreateNodeTypeToNodeMap(env.ClCluster, nodesToCreate) + vrfKey, pubKeyCompressed, err := vrfcommon.CreateVRFKeyOnVRFNode(nodeTypeToNodeMap[vrfcommon.VRF], l) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrSetVRFCoordinatorConfig, err) + return nil, nil, nil, nil, err } - l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Setting Link and ETH/LINK feed") - err = vrfv2_5Contracts.Coordinator.SetLINKAndLINKNativeFeed(linkToken.Address(), mockNativeLINKFeed.Address()) + l.Info().Str("Coordinator", vrfContracts.CoordinatorV2Plus.Address()).Msg("Registering Proving Key") + provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, vrfContracts.CoordinatorV2Plus, uint64(assets.GWei(*configGeneral.CLNodeMaxGasPriceGWei).Int64())) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrSetLinkNativeLinkFeed, err) + return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisteringProvingKey, err) } - err = env.EVMClient.WaitForEvents() + keyHash, err := vrfContracts.CoordinatorV2Plus.HashOfKey(context.Background(), provingKey) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreatingProvingKeyHash, err) } - l.Info(). - Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()). - Int("Number of Subs to create", numberOfSubToCreate). - Msg("Creating and funding subscriptions, adding consumers") - subIDs, err := CreateFundSubsAndAddConsumers( - env, - big.NewFloat(*vrfv2PlusConfig.SubscriptionFundingAmountNative), - big.NewFloat(*vrfv2PlusConfig.SubscriptionFundingAmountLink), - linkToken, - vrfv2_5Contracts.Coordinator, vrfv2_5Contracts.LoadTestConsumers, - numberOfSubToCreate, - vrfv2plus_config.BillingType(*vrfv2PlusConfig.SubscriptionBillingType)) + + evmClient, err := env.GetEVMClient(chainID) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } - l.Info().Str("Node URL", env.ClCluster.NodeAPIs()[0].URL()).Msg("Creating VRF Key on the Node") - vrfKey, err := env.ClCluster.NodeAPIs()[0].MustCreateVRFKey() + + vrfTXKeyAddressStrings, _, err := vrfcommon.CreateFundAndGetSendingKeys( + evmClient, + nodeTypeToNodeMap[vrfcommon.VRF], + *vrfv2PlusTestConfig.GetCommonConfig().ChainlinkNodeFunding, + numberOfTxKeysToCreate, + big.NewInt(chainID), + ) + if err != nil { + return nil, nil, nil, nil, err + } + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2PlusKey, err) + return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + nodeTypeToNodeMap[vrfcommon.VRF].TXKeyAddressStrings = vrfTXKeyAddressStrings + + g := errgroup.Group{} + if vrfNode, exists := nodeTypeToNodeMap[vrfcommon.VRF]; exists { + g.Go(func() error { + err := setupVRFNode(vrfContracts, big.NewInt(chainID), configGeneral, pubKeyCompressed, l, vrfNode) + if err != nil { + return err + } + return nil + }) + } + + if bhsNode, exists := nodeTypeToNodeMap[vrfcommon.BHS]; exists { + g.Go(func() error { + err := vrfcommon.SetupBHSNode( + env, + configGeneral.General, + numberOfTxKeysToCreate, + big.NewInt(chainID), + vrfContracts.CoordinatorV2Plus.Address(), + vrfContracts.BHS.Address(), + *vrfv2PlusTestConfig.GetCommonConfig().ChainlinkNodeFunding, + l, + bhsNode, + ) + if err != nil { + return err + } + return nil + }) + } + + if err := g.Wait(); err != nil { + return nil, nil, nil, nil, fmt.Errorf("VRF node setup ended up with an error: %w", err) + } + + vrfKeyData := vrfcommon.VRFKeyData{ + VRFKey: vrfKey, + EncodedProvingKey: provingKey, + KeyHash: keyHash, } - pubKeyCompressed := vrfKey.Data.ID - l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Registering Proving Key") - provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, vrfv2_5Contracts.Coordinator) + l.Info().Msg("VRFV2 Plus environment setup is finished") + return vrfContracts, subIDs, &vrfKeyData, nodeTypeToNodeMap, nil +} + +func SetupVRFV2PlusContracts( + env *test_env.CLClusterTestEnv, + chainID int64, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.MockETHLINKFeed, + configGeneral *testconfig.General, + numberOfSubToCreate int, + numberOfConsumers int, + l zerolog.Logger, +) (*vrfcommon.VRFContracts, []*big.Int, error) { + l.Info().Msg("Deploying VRFV2 Plus contracts") + evmClient, err := env.GetEVMClient(chainID) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRegisteringProvingKey, err) + return nil, nil, err } - keyHash, err := vrfv2_5Contracts.Coordinator.HashOfKey(context.Background(), provingKey) + vrfContracts, err := DeployVRFV2_5Contracts(env.ContractDeployer, evmClient, numberOfConsumers) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreatingProvingKeyHash, err) + return nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2_5Contracts, err) } - chainID := env.EVMClient.GetChainID() - newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2PlusTestConfig, numberOfTxKeysToCreate, chainID) + l.Info().Str("Coordinator", vrfContracts.CoordinatorV2Plus.Address()).Msg("Setting Coordinator Config") + err = vrfContracts.CoordinatorV2Plus.SetConfig( + *configGeneral.MinimumConfirmations, + *configGeneral.MaxGasLimitCoordinatorConfig, + *configGeneral.StalenessSeconds, + *configGeneral.GasAfterPaymentCalculation, + big.NewInt(*configGeneral.FallbackWeiPerUnitLink), + *configGeneral.FulfillmentFlatFeeNativePPM, + *configGeneral.FulfillmentFlatFeeLinkDiscountPPM, + *configGeneral.NativePremiumPercentage, + *configGeneral.LinkPremiumPercentage, + ) if err != nil { - return nil, nil, nil, err + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrSetVRFCoordinatorConfig, err) } - nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() + + l.Info().Str("Coordinator", vrfContracts.CoordinatorV2Plus.Address()).Msg("Setting Link and ETH/LINK feed") + err = vrfContracts.CoordinatorV2Plus.SetLINKAndLINKNativeFeed(linkToken.Address(), mockNativeLINKFeed.Address()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrSetLinkNativeLinkFeed, err) + } + err = evmClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + l.Info(). + Str("Coordinator", vrfContracts.CoordinatorV2Plus.Address()). + Int("Number of Subs to create", numberOfSubToCreate). + Msg("Creating and funding subscriptions, adding consumers") + subIDs, err := CreateFundSubsAndAddConsumers( + env, + chainID, + big.NewFloat(*configGeneral.SubscriptionFundingAmountNative), + big.NewFloat(*configGeneral.SubscriptionFundingAmountLink), + linkToken, + vrfContracts.CoordinatorV2Plus, vrfContracts.VRFV2PlusConsumer, + numberOfSubToCreate, + ) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) + return nil, nil, err } - allNativeTokenKeyAddresses := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) + return vrfContracts, subIDs, nil +} - vrfJobSpecConfig := VRFJobSpecConfig{ - ForwardingAllowed: false, - CoordinatorAddress: vrfv2_5Contracts.Coordinator.Address(), - FromAddresses: allNativeTokenKeyAddresses, +func setupVRFNode(contracts *vrfcommon.VRFContracts, chainID *big.Int, config *vrfv2plus_config.General, pubKeyCompressed string, l zerolog.Logger, vrfNode *vrfcommon.VRFNode) error { + vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ + ForwardingAllowed: *config.VRFJobForwardingAllowed, + CoordinatorAddress: contracts.CoordinatorV2Plus.Address(), + FromAddresses: vrfNode.TXKeyAddressStrings, EVMChainID: chainID.String(), - MinIncomingConfirmations: int(*vrfv2PlusConfig.MinimumConfirmations), + MinIncomingConfirmations: int(*config.MinimumConfirmations), PublicKey: pubKeyCompressed, - EstimateGasMultiplier: 1, - BatchFulfillmentEnabled: false, - BatchFulfillmentGasMultiplier: 1.15, - PollPeriod: time.Second * 1, - RequestTimeout: time.Hour * 24, + EstimateGasMultiplier: *config.VRFJobEstimateGasMultiplier, + BatchFulfillmentEnabled: *config.VRFJobBatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *config.VRFJobBatchFulfillmentGasMultiplier, + PollPeriod: config.VRFJobPollPeriod.Duration, + RequestTimeout: config.VRFJobRequestTimeout.Duration, + SimulationBlock: config.VRFJobSimulationBlock, + VRFOwnerConfig: nil, } l.Info().Msg("Creating VRFV2 Plus Job") job, err := CreateVRFV2PlusJob( - env.ClCluster.NodeAPIs()[0], + vrfNode.CLNode.API, vrfJobSpecConfig, ) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreateVRFV2PlusJobs, err) + return fmt.Errorf("%s, err %w", ErrCreateVRFV2PlusJobs, err) } + vrfNode.Job = job // this part is here because VRFv2 can work with only a specific key // [[EVM.KeySpecific]] // Key = '...' - nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, + nodeConfig := node.NewConfig(vrfNode.CLNode.NodeConfig, node.WithLogPollInterval(1*time.Second), - node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, *vrfv2PlusConfig.CLNodeMaxGasPriceGWei), + node.WithVRFv2EVMEstimator(vrfNode.TXKeyAddressStrings, *config.CLNodeMaxGasPriceGWei), ) - l.Info().Msg("Restarting Node with new sending key PriceMax configuration and log poll period configuration") - err = env.ClCluster.Nodes[0].Restart(nodeConfig) + l.Info().Msg("Restarting Node with new sending key PriceMax configuration") + err = vrfNode.CLNode.Restart(nodeConfig) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRestartCLNode, err) - } - - vrfv2PlusKeyData := VRFV2PlusKeyData{ - VRFKey: vrfKey, - EncodedProvingKey: provingKey, - KeyHash: keyHash, + return fmt.Errorf("%s, err %w", vrfcommon.ErrRestartCLNode, err) } - - data := VRFV2PlusData{ - vrfv2PlusKeyData, - job, - nativeTokenPrimaryKeyAddress, - chainID, - } - - l.Info().Msg("VRFV2 Plus environment setup is finished") - return vrfv2_5Contracts, subIDs, &data, nil -} - -func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, commonTestConfig tc.CommonTestConfig, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { - var newNativeTokenKeyAddresses []string - for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ { - newTxKey, response, err := env.ClCluster.NodeAPIs()[0].CreateTxKey("evm", chainID.String()) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrNodeNewTxKey, err) - } - if response.StatusCode != 200 { - return nil, fmt.Errorf("error creating transaction key - response code, err %d", response.StatusCode) - } - newNativeTokenKeyAddresses = append(newNativeTokenKeyAddresses, newTxKey.Data.ID) - err = actions.FundAddress(env.EVMClient, newTxKey.Data.ID, big.NewFloat(*commonTestConfig.GetCommonConfig().ChainlinkNodeFunding)) - if err != nil { - return nil, err - } - } - return newNativeTokenKeyAddresses, nil + return nil } func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, + chainID int64, subscriptionFundingAmountNative *big.Float, subscriptionFundingAmountLink *big.Float, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, consumers []contracts.VRFv2PlusLoadTestConsumer, numberOfSubToCreate int, - subscriptionBillingType vrfv2plus_config.BillingType, ) ([]*big.Int, error) { subIDs, err := CreateSubsAndFund( env, + chainID, subscriptionFundingAmountNative, subscriptionFundingAmountLink, linkToken, coordinator, numberOfSubToCreate, - subscriptionBillingType, ) if err != nil { return nil, err @@ -409,38 +425,48 @@ func CreateFundSubsAndAddConsumers( return nil, err } - err = env.EVMClient.WaitForEvents() + evmClient, err := env.GetEVMClient(chainID) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, err + } + + err = evmClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return subIDs, nil } func CreateSubsAndFund( env *test_env.CLClusterTestEnv, + chainID int64, subscriptionFundingAmountNative *big.Float, subscriptionFundingAmountLink *big.Float, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, subAmountToCreate int, - subscriptionBillingType vrfv2plus_config.BillingType, ) ([]*big.Int, error) { - subs, err := CreateSubs(env, coordinator, subAmountToCreate) + subs, err := CreateSubs(env, chainID, coordinator, subAmountToCreate) + if err != nil { + return nil, err + } + evmClient, err := env.GetEVMClient(chainID) if err != nil { return nil, err } - err = env.EVMClient.WaitForEvents() + + err = evmClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } err = FundSubscriptions( env, + chainID, subscriptionFundingAmountNative, subscriptionFundingAmountLink, linkToken, coordinator, subs, - subscriptionBillingType, ) if err != nil { return nil, err @@ -450,13 +476,14 @@ func CreateSubsAndFund( func CreateSubs( env *test_env.CLClusterTestEnv, + chainID int64, coordinator contracts.VRFCoordinatorV2_5, subAmountToCreate int, ) ([]*big.Int, error) { var subIDArr []*big.Int for i := 0; i < subAmountToCreate; i++ { - subID, err := CreateSubAndFindSubID(env, coordinator) + subID, err := CreateSubAndFindSubID(env, chainID, coordinator) if err != nil { return nil, err } @@ -480,19 +507,24 @@ func AddConsumersToSubs( return nil } -func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2_5) (*big.Int, error) { +func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, chainID int64, coordinator contracts.VRFCoordinatorV2_5) (*big.Int, error) { tx, err := coordinator.CreateSubscription() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrCreateVRFSubscription, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreateVRFSubscription, err) + } + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return nil, err } - err = env.EVMClient.WaitForEvents() + + err = evmClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } - receipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + receipt, err := evmClient.GetTxReceipt(tx.Hash()) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } //SubscriptionsCreated Log should be emitted with the subscription ID @@ -503,55 +535,39 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts func FundSubscriptions( env *test_env.CLClusterTestEnv, + chainID int64, subscriptionFundingAmountNative *big.Float, subscriptionFundingAmountLink *big.Float, linkAddress contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, subIDs []*big.Int, - subscriptionBillingType vrfv2plus_config.BillingType, ) error { + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return err + } + for _, subID := range subIDs { - switch subscriptionBillingType { - case vrfv2plus_config.BillingType_Native: - //Native Billing - amountWei := conversions.EtherToWei(subscriptionFundingAmountNative) - err := coordinator.FundSubscriptionWithNative( - subID, - amountWei, - ) - if err != nil { - return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) - } - case vrfv2plus_config.BillingType_Link: - //Link Billing - amountJuels := conversions.EtherToWei(subscriptionFundingAmountLink) - err := FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) - if err != nil { - return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) - } - case vrfv2plus_config.BillingType_Link_and_Native: - //Native Billing - amountWei := conversions.EtherToWei(subscriptionFundingAmountNative) - err := coordinator.FundSubscriptionWithNative( - subID, - amountWei, - ) - if err != nil { - return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) - } - //Link Billing - amountJuels := conversions.EtherToWei(subscriptionFundingAmountLink) - err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) - if err != nil { - return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) - } - default: - return fmt.Errorf("invalid billing type: %s", subscriptionBillingType) + //Native Billing + amountWei := conversions.EtherToWei(subscriptionFundingAmountNative) + err := coordinator.FundSubscriptionWithNative( + subID, + amountWei, + ) + if err != nil { + return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) + } + + //Link Billing + amountJuels := conversions.EtherToWei(subscriptionFundingAmountLink) + err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, evmClient, subID, amountJuels) + if err != nil { + return fmt.Errorf("%s, err %w", vrfcommon.ErrFundSubWithLinkToken, err) } } - err := env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return nil } @@ -583,7 +599,7 @@ func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkT func RequestRandomnessAndWaitForFulfillment( consumer contracts.VRFv2PlusLoadTestConsumer, coordinator contracts.VRFCoordinatorV2_5, - vrfv2PlusData *VRFV2PlusData, + vrfKeyData *vrfcommon.VRFKeyData, subID *big.Int, isNativeBilling bool, minimumConfirmations uint16, @@ -594,9 +610,21 @@ func RequestRandomnessAndWaitForFulfillment( randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { - logRandRequest(l, consumer.Address(), coordinator.Address(), subID, isNativeBilling, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) + logRandRequest( + l, + consumer.Address(), + coordinator.Address(), + subID, + isNativeBilling, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + vrfKeyData.KeyHash, + randomnessRequestCountPerRequest, + randomnessRequestCountPerRequestDeviation, + ) _, err := consumer.RequestRandomness( - vrfv2PlusData.KeyHash, + vrfKeyData.KeyHash, subID, minimumConfirmations, callbackGasLimit, @@ -605,13 +633,13 @@ func RequestRandomnessAndWaitForFulfillment( randomnessRequestCountPerRequest, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) } return WaitForRequestAndFulfillmentEvents( consumer.Address(), coordinator, - vrfv2PlusData, + vrfKeyData, subID, isNativeBilling, randomWordsFulfilledEventTimeout, @@ -622,7 +650,7 @@ func RequestRandomnessAndWaitForFulfillment( func RequestRandomnessAndWaitForFulfillmentUpgraded( consumer contracts.VRFv2PlusLoadTestConsumer, coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, - vrfv2PlusData *VRFV2PlusData, + vrfKeyData *vrfcommon.VRFKeyData, subID *big.Int, isNativeBilling bool, minimumConfirmations uint16, @@ -630,11 +658,24 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( numberOfWords uint32, randomnessRequestCountPerRequest uint16, randomnessRequestCountPerRequestDeviation uint16, + randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { - logRandRequest(l, consumer.Address(), coordinator.Address(), subID, isNativeBilling, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) + logRandRequest( + l, + consumer.Address(), + coordinator.Address(), + subID, + isNativeBilling, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + vrfKeyData.KeyHash, + randomnessRequestCountPerRequest, + randomnessRequestCountPerRequestDeviation, + ) _, err := consumer.RequestRandomness( - vrfv2PlusData.KeyHash, + vrfKeyData.KeyHash, subID, minimumConfirmations, callbackGasLimit, @@ -643,36 +684,23 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( randomnessRequestCountPerRequest, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) - } - - randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( - [][32]byte{vrfv2PlusData.KeyHash}, - []*big.Int{subID}, - []common.Address{common.HexToAddress(consumer.Address())}, - time.Minute*1, - ) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsRequestedEvent, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) } - LogRandomnessRequestedEventUpgraded(l, coordinator, randomWordsRequestedEvent) - - randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent( - []*big.Int{subID}, - []*big.Int{randomWordsRequestedEvent.RequestId}, - time.Minute*2, + return WaitForRequestAndFulfillmentEventsUpgraded( + consumer.Address(), + coordinator, + vrfKeyData, + subID, + isNativeBilling, + randomWordsFulfilledEventTimeout, + l, ) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsFulfilledEvent, err) - } - LogRandomWordsFulfilledEventUpgraded(l, coordinator, randomWordsFulfilledEvent) - - return randomWordsFulfilledEvent, err } func SetupVRFV2PlusWrapperEnvironment( env *test_env.CLClusterTestEnv, + chainID int64, vrfv2PlusTestConfig types.VRFv2PlusTestConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, @@ -680,10 +708,15 @@ func SetupVRFV2PlusWrapperEnvironment( keyHash [32]byte, wrapperConsumerContractsAmount int, ) (*VRFV2PlusWrapperContracts, *big.Int, error) { + evmClient, err := env.GetEVMClient(chainID) + if err != nil { + return nil, nil, err + } + vrfv2PlusConfig := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General wrapperContracts, err := DeployVRFV2PlusDirectFundingContracts( env.ContractDeployer, - env.EVMClient, + evmClient, linkToken.Address(), mockNativeLINKFeed.Address(), coordinator, @@ -693,10 +726,10 @@ func SetupVRFV2PlusWrapperEnvironment( return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } err = wrapperContracts.VRFV2PlusWrapper.SetConfig( *vrfv2PlusConfig.WrapperGasOverhead, @@ -713,9 +746,9 @@ func SetupVRFV2PlusWrapperEnvironment( return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } //fund sub @@ -724,12 +757,20 @@ func SetupVRFV2PlusWrapperEnvironment( return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } - err = FundSubscriptions(env, big.NewFloat(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), big.NewFloat(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, coordinator, []*big.Int{wrapperSubID}, vrfv2plus_config.BillingType(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionBillingType)) + err = FundSubscriptions( + env, + chainID, + big.NewFloat(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), + big.NewFloat(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), + linkToken, + coordinator, + []*big.Int{wrapperSubID}, + ) if err != nil { return nil, nil, err } @@ -742,9 +783,9 @@ func SetupVRFV2PlusWrapperEnvironment( if err != nil { return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } //fund consumer with Eth @@ -752,9 +793,9 @@ func SetupVRFV2PlusWrapperEnvironment( if err != nil { return nil, nil, err } - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return wrapperContracts, wrapperSubID, nil } @@ -786,7 +827,7 @@ func DeployVRFV2PlusDirectFundingContracts( } err = chainClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } consumers, err := DeployVRFV2PlusWrapperConsumers(contractDeployer, linkTokenAddress, vrfv2PlusWrapper, consumerContractsAmount) @@ -795,15 +836,15 @@ func DeployVRFV2PlusDirectFundingContracts( } err = chainClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return &VRFV2PlusWrapperContracts{vrfv2PlusWrapper, consumers}, nil } -func DirectFundingRequestRandomnessAndWaitForFulfillment( +func WrapperRequestRandomness( consumer contracts.VRFv2PlusWrapperLoadTestConsumer, - coordinator contracts.VRFCoordinatorV2_5, - vrfv2PlusData *VRFV2PlusData, + coordinatorAddress string, + vrfKeyData *vrfcommon.VRFKeyData, subID *big.Int, isNativeBilling bool, minimumConfirmations uint16, @@ -811,10 +852,20 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( numberOfWords uint32, randomnessRequestCountPerRequest uint16, randomnessRequestCountPerRequestDeviation uint16, - randomWordsFulfilledEventTimeout time.Duration, - l zerolog.Logger, -) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { - logRandRequest(l, consumer.Address(), coordinator.Address(), subID, isNativeBilling, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) + l zerolog.Logger) (string, error) { + logRandRequest( + l, + consumer.Address(), + coordinatorAddress, + subID, + isNativeBilling, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + vrfKeyData.KeyHash, + randomnessRequestCountPerRequest, + randomnessRequestCountPerRequestDeviation, + ) if isNativeBilling { _, err := consumer.RequestRandomnessNative( minimumConfirmations, @@ -823,7 +874,7 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( randomnessRequestCountPerRequest, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingNativePayment, err) + return "", fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingNativePayment, err) } } else { _, err := consumer.RequestRandomness( @@ -833,17 +884,73 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( randomnessRequestCountPerRequest, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingLinkPayment, err) + return "", fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingLinkPayment, err) } } wrapperAddress, err := consumer.GetWrapper(context.Background()) + if err != nil { + return "", fmt.Errorf("error getting wrapper address, err: %w", err) + } + return wrapperAddress.Hex(), nil +} + +func DirectFundingRequestRandomnessAndWaitForFulfillment( + consumer contracts.VRFv2PlusWrapperLoadTestConsumer, + coordinator contracts.VRFCoordinatorV2_5, + vrfKeyData *vrfcommon.VRFKeyData, + subID *big.Int, + isNativeBilling bool, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, + randomnessRequestCountPerRequest uint16, + randomnessRequestCountPerRequestDeviation uint16, + randomWordsFulfilledEventTimeout time.Duration, + l zerolog.Logger, +) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { + wrapperAddress, err := WrapperRequestRandomness(consumer, coordinator.Address(), vrfKeyData, subID, + isNativeBilling, minimumConfirmations, callbackGasLimit, numberOfWords, + randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation, + l) if err != nil { return nil, fmt.Errorf("error getting wrapper address, err: %w", err) } return WaitForRequestAndFulfillmentEvents( - wrapperAddress.String(), + wrapperAddress, + coordinator, + vrfKeyData, + subID, + isNativeBilling, + randomWordsFulfilledEventTimeout, + l, + ) +} + +func DirectFundingRequestRandomnessAndWaitForFulfillmentUpgraded( + consumer contracts.VRFv2PlusWrapperLoadTestConsumer, + coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, + vrfKeyData *vrfcommon.VRFKeyData, + subID *big.Int, + isNativeBilling bool, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, + randomnessRequestCountPerRequest uint16, + randomnessRequestCountPerRequestDeviation uint16, + randomWordsFulfilledEventTimeout time.Duration, + l zerolog.Logger, +) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { + wrapperAddress, err := WrapperRequestRandomness(consumer, coordinator.Address(), vrfKeyData, subID, + isNativeBilling, minimumConfirmations, callbackGasLimit, numberOfWords, + randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation, + l) + if err != nil { + return nil, fmt.Errorf("error getting wrapper address, err: %w", err) + } + return WaitForRequestAndFulfillmentEventsUpgraded( + wrapperAddress, coordinator, - vrfv2PlusData, + vrfKeyData, subID, isNativeBilling, randomWordsFulfilledEventTimeout, @@ -854,20 +961,20 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( func WaitForRequestAndFulfillmentEvents( consumerAddress string, coordinator contracts.VRFCoordinatorV2_5, - vrfv2PlusData *VRFV2PlusData, + vrfKeyData *vrfcommon.VRFKeyData, subID *big.Int, isNativeBilling bool, randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( - [][32]byte{vrfv2PlusData.KeyHash}, + [][32]byte{vrfKeyData.KeyHash}, []*big.Int{subID}, []common.Address{common.HexToAddress(consumerAddress)}, time.Minute*1, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsRequestedEvent, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitRandomWordsRequestedEvent, err) } LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent, isNativeBilling) @@ -878,44 +985,44 @@ func WaitForRequestAndFulfillmentEvents( randomWordsFulfilledEventTimeout, ) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsFulfilledEvent, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitRandomWordsFulfilledEvent, err) } LogRandomWordsFulfilledEvent(l, coordinator, randomWordsFulfilledEvent, isNativeBilling) return randomWordsFulfilledEvent, err } -func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2PlusLoadTestConsumer, timeout time.Duration, wg *sync.WaitGroup) (*big.Int, *big.Int, error) { - metricsChannel := make(chan *contracts.VRFLoadTestMetrics) - metricsErrorChannel := make(chan error) - - testContext, testCancel := context.WithTimeout(context.Background(), timeout) - defer testCancel() - - ticker := time.NewTicker(time.Second * 1) - var metrics *contracts.VRFLoadTestMetrics - for { - select { - case <-testContext.Done(): - ticker.Stop() - wg.Done() - return metrics.RequestCount, metrics.FulfilmentCount, - fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", - metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) - case <-ticker.C: - go retrieveLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) - case metrics = <-metricsChannel: - if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { - ticker.Stop() - wg.Done() - return metrics.RequestCount, metrics.FulfilmentCount, nil - } - case err := <-metricsErrorChannel: - ticker.Stop() - wg.Done() - return nil, nil, err - } +func WaitForRequestAndFulfillmentEventsUpgraded( + consumerAddress string, + coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, + vrfKeyData *vrfcommon.VRFKeyData, + subID *big.Int, + isNativeBilling bool, + randomWordsFulfilledEventTimeout time.Duration, + l zerolog.Logger, +) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { + randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfKeyData.KeyHash}, + []*big.Int{subID}, + []common.Address{common.HexToAddress(consumerAddress)}, + time.Minute*1, + ) + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitRandomWordsRequestedEvent, err) } + + LogRandomnessRequestedEventUpgraded(l, coordinator, randomWordsRequestedEvent, isNativeBilling) + + randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent( + []*big.Int{subID}, + []*big.Int{randomWordsRequestedEvent.RequestId}, + randomWordsFulfilledEventTimeout, + ) + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitRandomWordsFulfilledEvent, err) + } + LogRandomWordsFulfilledEventUpgraded(l, coordinator, randomWordsFulfilledEvent, isNativeBilling) + return randomWordsFulfilledEvent, err } func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator contracts.VRFCoordinatorV2_5, l zerolog.Logger) error { @@ -951,18 +1058,6 @@ func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator co return nil } -func retrieveLoadTestMetrics( - consumer contracts.VRFv2PlusLoadTestConsumer, - metricsChannel chan *contracts.VRFLoadTestMetrics, - metricsErrorChannel chan error, -) { - metrics, err := consumer.GetLoadTestMetrics(context.Background()) - if err != nil { - metricsErrorChannel <- err - } - metricsChannel <- metrics -} - func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2_5.GetSubscription, subID *big.Int, coordinator contracts.VRFCoordinatorV2_5) { l.Debug(). Str("Coordinator", coordinator.Address()). @@ -978,13 +1073,15 @@ func LogRandomnessRequestedEventUpgraded( l zerolog.Logger, coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, randomWordsRequestedEvent *vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsRequested, + isNativeBilling bool, ) { l.Debug(). Str("Coordinator", coordinator.Address()). + Bool("Native Billing", isNativeBilling). Str("Request ID", randomWordsRequestedEvent.RequestId.String()). Str("Subscription ID", randomWordsRequestedEvent.SubId.String()). Str("Sender Address", randomWordsRequestedEvent.Sender.String()). - Interface("Keyhash", randomWordsRequestedEvent.KeyHash). + Str("Keyhash", fmt.Sprintf("0x%x", randomWordsRequestedEvent.KeyHash)). Uint32("Callback Gas Limit", randomWordsRequestedEvent.CallbackGasLimit). Uint32("Number of Words", randomWordsRequestedEvent.NumWords). Uint16("Minimum Request Confirmations", randomWordsRequestedEvent.MinimumRequestConfirmations). @@ -995,9 +1092,11 @@ func LogRandomWordsFulfilledEventUpgraded( l zerolog.Logger, coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, randomWordsFulfilledEvent *vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, + isNativeBilling bool, ) { l.Debug(). Str("Coordinator", coordinator.Address()). + Bool("Native Billing", isNativeBilling). Str("Total Payment in Juels", randomWordsFulfilledEvent.Payment.String()). Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). Str("Subscription ID", randomWordsFulfilledEvent.SubID.String()). @@ -1012,16 +1111,19 @@ func LogRandomnessRequestedEvent( randomWordsRequestedEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested, isNativeBilling bool, ) { - l.Debug(). + l.Info(). Str("Coordinator", coordinator.Address()). Bool("Native Billing", isNativeBilling). Str("Request ID", randomWordsRequestedEvent.RequestId.String()). Str("Subscription ID", randomWordsRequestedEvent.SubId.String()). Str("Sender Address", randomWordsRequestedEvent.Sender.String()). - Interface("Keyhash", randomWordsRequestedEvent.KeyHash). + Str("Keyhash", fmt.Sprintf("0x%x", randomWordsRequestedEvent.KeyHash)). Uint32("Callback Gas Limit", randomWordsRequestedEvent.CallbackGasLimit). Uint32("Number of Words", randomWordsRequestedEvent.NumWords). Uint16("Minimum Request Confirmations", randomWordsRequestedEvent.MinimumRequestConfirmations). + Str("TX Hash", randomWordsRequestedEvent.Raw.TxHash.String()). + Uint64("BlockNumber", randomWordsRequestedEvent.Raw.BlockNumber). + Str("BlockHash", randomWordsRequestedEvent.Raw.BlockHash.String()). Msg("RandomnessRequested Event") } @@ -1031,7 +1133,7 @@ func LogRandomWordsFulfilledEvent( randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, isNativeBilling bool, ) { - l.Debug(). + l.Info(). Bool("Native Billing", isNativeBilling). Str("Coordinator", coordinator.Address()). Str("Total Payment", randomWordsFulfilledEvent.Payment.String()). @@ -1039,19 +1141,21 @@ func LogRandomWordsFulfilledEvent( Str("Subscription ID", randomWordsFulfilledEvent.SubId.String()). Str("Request ID", randomWordsFulfilledEvent.RequestId.String()). Bool("Success", randomWordsFulfilledEvent.Success). + Uint64("BlockNumber", randomWordsFulfilledEvent.Raw.BlockNumber). + Str("BlockHash", randomWordsFulfilledEvent.Raw.BlockHash.String()). Msg("RandomWordsFulfilled Event (TX metadata)") } -func LogMigrationCompletedEvent(l zerolog.Logger, migrationCompletedEvent *vrf_coordinator_v2_5.VRFCoordinatorV25MigrationCompleted, vrfv2PlusContracts *VRFV2_5Contracts) { - l.Debug(). +func LogMigrationCompletedEvent(l zerolog.Logger, migrationCompletedEvent *vrf_coordinator_v2_5.VRFCoordinatorV25MigrationCompleted, vrfv2PlusContracts *vrfcommon.VRFContracts) { + l.Info(). Str("Subscription ID", migrationCompletedEvent.SubId.String()). - Str("Migrated From Coordinator", vrfv2PlusContracts.Coordinator.Address()). + Str("Migrated From Coordinator", vrfv2PlusContracts.CoordinatorV2Plus.Address()). Str("Migrated To Coordinator", migrationCompletedEvent.NewCoordinator.String()). Msg("MigrationCompleted Event") } func LogSubDetailsAfterMigration(l zerolog.Logger, newCoordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, subID *big.Int, migratedSubscription vrf_v2plus_upgraded_version.GetSubscription) { - l.Debug(). + l.Info(). Str("New Coordinator", newCoordinator.Address()). Str("Subscription ID", subID.String()). Str("Juels Balance", migratedSubscription.Balance.String()). @@ -1068,7 +1172,7 @@ func LogFulfillmentDetailsLinkBilling( consumerStatus vrfv2plus_wrapper_load_test_consumer.GetRequestStatus, randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, ) { - l.Debug(). + l.Info(). Str("Consumer Balance Before Request (Link)", (*commonassets.Link)(wrapperConsumerJuelsBalanceBeforeRequest).Link()). Str("Consumer Balance After Request (Link)", (*commonassets.Link)(wrapperConsumerJuelsBalanceAfterRequest).Link()). Bool("Fulfilment Status", consumerStatus.Fulfilled). @@ -1089,7 +1193,7 @@ func LogFulfillmentDetailsNativeBilling( consumerStatus vrfv2plus_wrapper_load_test_consumer.GetRequestStatus, randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, ) { - l.Debug(). + l.Info(). Str("Consumer Balance Before Request", assets.FormatWei(wrapperConsumerBalanceBeforeRequestWei)). Str("Consumer Balance After Request", assets.FormatWei(wrapperConsumerBalanceAfterRequestWei)). Bool("Fulfilment Status", consumerStatus.Fulfilled). @@ -1112,9 +1216,10 @@ func logRandRequest( minimumConfirmations uint16, callbackGasLimit uint32, numberOfWords uint32, + keyHash [32]byte, randomnessRequestCountPerRequest uint16, randomnessRequestCountPerRequestDeviation uint16) { - l.Debug(). + l.Info(). Str("Consumer", consumer). Str("Coordinator", coordinator). Str("SubID", subID.String()). @@ -1122,6 +1227,7 @@ func logRandRequest( Uint16("MinimumConfirmations", minimumConfirmations). Uint32("CallbackGasLimit", callbackGasLimit). Uint32("NumberOfWords", numberOfWords). + Str("KeyHash", fmt.Sprintf("0x%x", keyHash)). Uint16("RandomnessRequestCountPerRequest", randomnessRequestCountPerRequest). Uint16("RandomnessRequestCountPerRequestDeviation", randomnessRequestCountPerRequestDeviation). Msg("Requesting randomness") diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 8997289a7e..68cff5ccd7 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -119,6 +119,12 @@ type NetworkConfig struct { funding *big.Float } +var defaultNetworkConfig = NetworkConfig{ + upkeepSLA: int64(120), + blockTime: time.Second, + deltaStage: time.Duration(0), +} + func TestAutomationBenchmark(t *testing.T) { l := logging.GetTestLogger(t) testType, err := tc.GetConfigurationNameFromEnv() @@ -204,15 +210,21 @@ func addRegistry(config *tc.TestConfig) []eth_contracts.KeeperRegistryVersion { return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} case "2_1": return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_1} + case "2_2": + return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_2} case "2_0-1_3": return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0, eth_contracts.RegistryVersion_1_3} case "2_1-2_0-1_3": return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_1, eth_contracts.RegistryVersion_2_0, eth_contracts.RegistryVersion_1_3} + case "2_2-2_1": + return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_2, eth_contracts.RegistryVersion_2_1} case "2_0-Multiple": return repeatRegistries(eth_contracts.RegistryVersion_2_0, *config.Keeper.Common.NumberOfRegistries) case "2_1-Multiple": return repeatRegistries(eth_contracts.RegistryVersion_2_1, *config.Keeper.Common.NumberOfRegistries) + case "2_2-Multiple": + return repeatRegistries(eth_contracts.RegistryVersion_2_2, *config.Keeper.Common.NumberOfRegistries) default: return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} } @@ -230,7 +242,7 @@ func getNetworkConfig(networkName string, config *tc.TestConfig) NetworkConfig { var nc NetworkConfig var ok bool if nc, ok = networkConfig[networkName]; !ok { - return NetworkConfig{} + return defaultNetworkConfig } if networkName == "SimulatedGeth" || networkName == "geth" { @@ -295,6 +307,11 @@ var networkConfig = map[string]NetworkConfig{ blockTime: time.Second, deltaStage: 20 * time.Second, }, + "GnosisChiado": { + upkeepSLA: int64(120), + blockTime: 6 * time.Second, + deltaStage: 20 * time.Second, + }, } func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenchmarkTestConfig) (*environment.Environment, blockchain.EVMNetwork) { diff --git a/integration-tests/ccip-tests/Makefile b/integration-tests/ccip-tests/Makefile index 3548eb63e2..8ce2edfcc1 100644 --- a/integration-tests/ccip-tests/Makefile +++ b/integration-tests/ccip-tests/Makefile @@ -1,5 +1,5 @@ ## To Override the default config, and secret config: -# example usage: make set_config override_toml=../config/config.toml secret_toml=../config/secret.toml +# example usage: make set_config override_toml=../config/config.toml secret_toml=../config/secret.toml network_config_toml=../config/network.toml .PHONY: set_config set_config: if [ -s "$(override_toml)" ]; then \ @@ -10,13 +10,17 @@ set_config: echo "No override config found, using default config"; \ echo > ./testconfig/override/.env; \ fi + if [ -s "$(network_config_toml)" ]; then \ + echo "Overriding network config with $(network_config_toml)"; \ + echo "export BASE64_NETWORK_CONFIG=$$(base64 -i $(network_config_toml))" >> ./testconfig/override/.env; \ + fi @echo "setting secret config with $(secret_toml)" @echo "export BASE64_CCIP_SECRETS_CONFIG=$$(base64 -i $(secret_toml))" >> ./testconfig/override/.env @echo "export TEST_BASE64_CCIP_SECRETS_CONFIG=$$(base64 -i $(secret_toml))" >> ./testconfig/override/.env -# example usage: make test_load_ccip testimage=chainlink-ccip-tests:latest testname=TestLoadCCIPStableRPS override_toml=./testconfig/override/config.toml secret_toml=./testconfig/tomls/secrets.toml +# example usage: make test_load_ccip testimage=chainlink-ccip-tests:latest testname=TestLoadCCIPStableRPS override_toml=./testconfig/override/config.toml secret_toml=./testconfig/tomls/secrets.toml network_config_toml=../config/network.toml .PHONY: test_load_ccip test_load_ccip: set_config source ./testconfig/override/.env && \ @@ -30,7 +34,7 @@ test_load_ccip: set_config go test -timeout 24h -count=1 -v -run ^$(testname)$$ ./load -# example usage: make test_smoke_ccip testimage=chainlink-ccip-tests:latest testname=TestSmokeCCIPForBidirectionalLane override_toml=../testconfig/override/config.toml secret_toml=./testconfig/tomls/secrets.toml +# example usage: make test_smoke_ccip testimage=chainlink-ccip-tests:latest testname=TestSmokeCCIPForBidirectionalLane override_toml=../testconfig/override/config.toml secret_toml=./testconfig/tomls/secrets.toml network_config_toml=../config/network.toml .PHONY: test_smoke_ccip test_smoke_ccip: set_config source ./testconfig/override/.env && \ diff --git a/integration-tests/ccip-tests/actions/ccip_helpers.go b/integration-tests/ccip-tests/actions/ccip_helpers.go index a66c8cefd6..47b7c18eee 100644 --- a/integration-tests/ccip-tests/actions/ccip_helpers.go +++ b/integration-tests/ccip-tests/actions/ccip_helpers.go @@ -5,14 +5,15 @@ import ( "encoding/json" "fmt" "math/big" - "math/rand" "net/http" + "runtime" "strings" "sync" "testing" "time" "github.com/AlekSi/pointer" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -23,6 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/atomic" + "golang.org/x/exp/rand" "golang.org/x/sync/errgroup" ctftestenv "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" @@ -37,11 +39,11 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/contracts/laneconfig" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testreporters" + testutils "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/utils" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" @@ -49,6 +51,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_onramp" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/mock_arm_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" @@ -71,7 +74,6 @@ const ( ChaosGroupNetworkACCIPGeth = "CCIPNetworkAGeth" ChaosGroupNetworkBCCIPGeth = "CCIPNetworkBGeth" RootSnoozeTimeSimulated = 3 * time.Minute - InflightExpirySimulated = 3 * time.Minute // The higher the load/throughput, the higher value we might need here to guarantee that nonces are not blocked // 1 day should be enough for most of the cases PermissionlessExecThreshold = 60 * 60 * 24 // 1 day @@ -90,8 +92,9 @@ var ( NetworkName = func(name string) string { return strings.ReplaceAll(strings.ToLower(name), " ", "-") } - - GethLabel = func(name string) string { + InflightExpiryExec = 3 * time.Minute + InflightExpiryCommit = 3 * time.Minute + GethLabel = func(name string) string { return fmt.Sprintf("%s-ethereum-geth", name) } // ApprovedAmountToRouter is the default amount which gets approved for router so that it can transfer token and use the fee token for fee payment @@ -121,118 +124,130 @@ func GetUSDCDomain(networkName string, simulated bool) (uint32, error) { } type CCIPCommon struct { - ChainClient blockchain.EVMClient - Deployer *contracts.CCIPContractsDeployer - FeeToken *contracts.LinkToken - BridgeTokens []*contracts.ERC20Token - PriceAggregators map[string]*contracts.MockAggregator - BridgeTokenPools []*contracts.TokenPool - RateLimiterConfig contracts.RateLimiterConfig - ARMContract *common.Address - ARM *contracts.ARM // populate only if the ARM contracts is not a mock and can be used to verify various ARM events; keep this nil for mock ARM - Router *contracts.Router - PriceRegistry *contracts.PriceRegistry - WrappedNative common.Address - MulticallEnabled bool - MulticallContract common.Address - ExistingDeployment bool - USDCDeployment bool - TokenMessenger *common.Address - TokenTransmitter *contracts.TokenTransmitter - poolFunds *big.Int - gasUpdateWatcherMu *sync.Mutex - gasUpdateWatcher map[uint64]*big.Int // key - destchain id; value - timestamp of update - priceUpdateSubs []event.Subscription -} - -func (ccipModule *CCIPCommon) StopWatchingPriceUpdates() { - for _, sub := range ccipModule.priceUpdateSubs { - sub.Unsubscribe() - } -} - -func (ccipModule *CCIPCommon) Copy(logger zerolog.Logger, chainClient blockchain.EVMClient) (*CCIPCommon, error) { - newCD, err := contracts.NewCCIPContractsDeployer(logger, chainClient) + ChainClient blockchain.EVMClient + Deployer *contracts.CCIPContractsDeployer + FeeToken *contracts.LinkToken + BridgeTokens []*contracts.ERC20Token + PriceAggregators map[common.Address]*contracts.MockAggregator + RemoteChains []uint64 + BridgeTokenPools []*contracts.TokenPool + RateLimiterConfig contracts.RateLimiterConfig + ARMContract *common.Address + ARM *contracts.ARM // populate only if the ARM contracts is not a mock and can be used to verify various ARM events; keep this nil for mock ARM + Router *contracts.Router + PriceRegistry *contracts.PriceRegistry + WrappedNative common.Address + MulticallEnabled bool + MulticallContract common.Address + ExistingDeployment bool + NoOfUSDCTokens *int + TokenMessenger *common.Address + TokenTransmitter *contracts.TokenTransmitter + poolFunds *big.Int + gasUpdateWatcherMu *sync.Mutex + gasUpdateWatcher map[uint64]*big.Int // key - destchain id; value - timestamp of update + IsConnectionRestoredRecently *atomic.Bool +} + +// FreeUpUnusedSpace sets nil to various elements of ccipModule which are only used +// during lane set up and not used for rest of the test duration +// this is called mainly by load test to keep the memory usage minimum for high number of lanes +func (ccipModule *CCIPCommon) FreeUpUnusedSpace() { + ccipModule.PriceAggregators = nil + ccipModule.BridgeTokenPools = []*contracts.TokenPool{} + ccipModule.RemoteChains = nil + ccipModule.TokenMessenger = nil + ccipModule.TokenTransmitter = nil + runtime.GC() +} + +func (ccipModule *CCIPCommon) UnvoteToCurseARM() error { + if ccipModule.ARM != nil { + return fmt.Errorf("real ARM deployed. cannot curse through test") + } + if ccipModule.ARMContract == nil { + return fmt.Errorf("no ARM contract is set") + } + arm, err := mock_arm_contract.NewMockARMContract(*ccipModule.ARMContract, ccipModule.ChainClient.Backend()) if err != nil { - return nil, err + return fmt.Errorf("error instantiating arm %w", err) } - var arm *contracts.ARM + opts, err := ccipModule.ChainClient.TransactionOpts(ccipModule.ChainClient.GetDefaultWallet()) + if err != nil { + return fmt.Errorf("error getting owners for ARM OwnerUnvoteToCurse %w", err) + } + tx, err := arm.OwnerUnvoteToCurse(opts, []mock_arm_contract.ARMUnvoteToCurseRecord{}) + if err != nil { + return fmt.Errorf("error in calling OwnerUnvoteToCurse %w", err) + } + err = ccipModule.ChainClient.ProcessTransaction(tx) + if err != nil { + return err + } + log.Info(). + Str("ARM", arm.Address().Hex()). + Msg("ARM is uncursed") + return ccipModule.ChainClient.WaitForEvents() +} + +func (ccipModule *CCIPCommon) IsCursed() (bool, error) { if ccipModule.ARM != nil { - arm, err = newCD.NewARMContract(*ccipModule.ARMContract) - if err != nil { - return nil, err - } + return false, fmt.Errorf("real ARM deployed. cannot validate cursing") } - var pools []*contracts.TokenPool - for i := range ccipModule.BridgeTokenPools { - if ccipModule.USDCDeployment { - pool, err := newCD.NewUSDCTokenPoolContract(common.HexToAddress(ccipModule.BridgeTokenPools[i].Address())) - if err != nil { - return nil, err - } - pools = append(pools, pool) - } else { - pool, err := newCD.NewLockReleaseTokenPoolContract(common.HexToAddress(ccipModule.BridgeTokenPools[i].Address())) - if err != nil { - return nil, err - } - pools = append(pools, pool) - } + if ccipModule.ARMContract == nil { + return false, fmt.Errorf("no ARM contract is set") } - var tokens []*contracts.ERC20Token - for i := range ccipModule.BridgeTokens { - token, err := newCD.NewERC20TokenContract(common.HexToAddress(ccipModule.BridgeTokens[i].Address())) - if err != nil { - return nil, err - } - tokens = append(tokens, token) + arm, err := mock_arm_contract.NewMockARMContract(*ccipModule.ARMContract, ccipModule.ChainClient.Backend()) + if err != nil { + return false, fmt.Errorf("error instantiating arm %w", err) } - priceAggregators := make(map[string]*contracts.MockAggregator) - for k, v := range ccipModule.PriceAggregators { - aggregator, err := newCD.NewMockAggregator(v.ContractAddress) + return arm.IsCursed(nil) +} + +func (ccipModule *CCIPCommon) SetRemoteChainsOnPools() error { + if ccipModule.ExistingDeployment { + return nil + } + for _, pool := range ccipModule.BridgeTokenPools { + err := pool.SetRemoteChainOnPool(ccipModule.RemoteChains) if err != nil { - return nil, err + return fmt.Errorf("error updating remote chain selectors %w", err) } - priceAggregators[k] = aggregator } - newCommon := &CCIPCommon{ - ChainClient: chainClient, - Deployer: newCD, - BridgeTokens: tokens, - PriceAggregators: priceAggregators, - BridgeTokenPools: pools, - RateLimiterConfig: ccipModule.RateLimiterConfig, - ARMContract: ccipModule.ARMContract, - ARM: arm, - WrappedNative: ccipModule.WrappedNative, - MulticallContract: ccipModule.MulticallContract, - ExistingDeployment: ccipModule.ExistingDeployment, - MulticallEnabled: ccipModule.MulticallEnabled, - USDCDeployment: ccipModule.USDCDeployment, - TokenMessenger: ccipModule.TokenMessenger, - poolFunds: ccipModule.poolFunds, - gasUpdateWatcherMu: &sync.Mutex{}, - gasUpdateWatcher: make(map[uint64]*big.Int), + if err := ccipModule.ChainClient.WaitForEvents(); err != nil { + return fmt.Errorf("error waiting for updating remote chain selectors %w", err) } - newCommon.FeeToken, err = newCommon.Deployer.NewLinkTokenContract(common.HexToAddress(ccipModule.FeeToken.Address())) + return nil +} + +func (ccipModule *CCIPCommon) CurseARM() (*types.Transaction, error) { + if ccipModule.ARM != nil { + return nil, fmt.Errorf("real ARM deployed. cannot curse through test") + } + if ccipModule.ARMContract == nil { + return nil, fmt.Errorf("no ARM contract is set") + } + arm, err := mock_arm_contract.NewMockARMContract(*ccipModule.ARMContract, ccipModule.ChainClient.Backend()) if err != nil { - return nil, err + return nil, fmt.Errorf("error instantiating arm %w", err) } - newCommon.PriceRegistry, err = newCommon.Deployer.NewPriceRegistry(common.HexToAddress(ccipModule.PriceRegistry.Address())) + opts, err := ccipModule.ChainClient.TransactionOpts(ccipModule.ChainClient.GetDefaultWallet()) if err != nil { - return nil, err + return nil, fmt.Errorf("error getting owners for ARM VoteToCurse %w", err) } - newCommon.Router, err = newCommon.Deployer.NewRouter(common.HexToAddress(ccipModule.Router.Address())) + tx, err := arm.VoteToCurse(opts, [32]byte{}) if err != nil { - return nil, err + return nil, fmt.Errorf("error in calling VoteToCurse %w", err) } - if ccipModule.TokenTransmitter != nil { - newCommon.TokenTransmitter, err = newCommon.Deployer.NewTokenTransmitter(ccipModule.TokenTransmitter.ContractAddress) - if err != nil { - return nil, err - } + err = ccipModule.ChainClient.ProcessTransaction(tx) + if err != nil { + return tx, err } - return newCommon, nil + log.Info(). + Str("ARM", arm.Address().Hex()). + Str("Network", ccipModule.ChainClient.GetNetworkName()). + Msg("ARM is cursed") + return tx, ccipModule.ChainClient.WaitForEvents() } func (ccipModule *CCIPCommon) LoadContractAddresses(conf *laneconfig.LaneConfig) { @@ -304,6 +319,17 @@ func (ccipModule *CCIPCommon) LoadContractAddresses(conf *laneconfig.LaneConfig) } ccipModule.BridgeTokenPools = pools } + if len(conf.PriceAggregators) > 0 { + priceAggrs := make(map[common.Address]*contracts.MockAggregator) + for token, aggr := range conf.PriceAggregators { + if common.IsHexAddress(aggr) { + priceAggrs[common.HexToAddress(token)] = &contracts.MockAggregator{ + ContractAddress: common.HexToAddress(aggr), + } + } + } + ccipModule.PriceAggregators = priceAggrs + } } } @@ -373,6 +399,7 @@ func (ccipModule *CCIPCommon) CleanUp() error { } func (ccipModule *CCIPCommon) WaitForPriceUpdates( + ctx context.Context, lggr zerolog.Logger, timeout time.Duration, destChainId uint64, @@ -398,7 +425,7 @@ func (ccipModule *CCIPCommon) WaitForPriceUpdates( lggr.Info().Msgf("Waiting for UsdPerUnitGas for dest chain %d Price Registry %s", destChainId, ccipModule.PriceRegistry.Address()) ticker := time.NewTicker(time.Second) defer ticker.Stop() - ctx, cancel := context.WithTimeout(context.Background(), timeout) + localCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() for { select { @@ -414,20 +441,24 @@ func (ccipModule *CCIPCommon) WaitForPriceUpdates( Msg("Price updated") return nil } - case <-ctx.Done(): + case <-localCtx.Done(): return fmt.Errorf("UsdPerUnitGasUpdated is not found for chain %d", destChainId) } } } -func (ccipModule *CCIPCommon) WatchForPriceUpdates() error { +func (ccipModule *CCIPCommon) WatchForPriceUpdates(ctx context.Context) error { gasUpdateEvent := make(chan *price_registry.PriceRegistryUsdPerUnitGasUpdated) - sub, err := ccipModule.PriceRegistry.Instance.WatchUsdPerUnitGasUpdated(nil, gasUpdateEvent, nil) - if err != nil { - return err - } + sub := event.Resubscribe(2*time.Hour, func(ctx context.Context) (event.Subscription, error) { + return ccipModule.PriceRegistry.Instance.WatchUsdPerUnitGasUpdated(nil, gasUpdateEvent, nil) + }) go func() { + defer func() { + sub.Unsubscribe() + ccipModule.gasUpdateWatcher = nil + ccipModule.gasUpdateWatcherMu = nil + }() for { select { case e := <-gasUpdateEvent: @@ -439,28 +470,62 @@ func (ccipModule *CCIPCommon) WatchForPriceUpdates() error { ccipModule.gasUpdateWatcher[destChain] = e.Timestamp ccipModule.gasUpdateWatcherMu.Unlock() log.Info(). + Uint64("chainSelector", e.DestChain). Str("source_chain", ccipModule.ChainClient.GetNetworkName()). Uint64("dest_chain", destChain). Str("price_registry", ccipModule.PriceRegistry.Address()). Msgf("UsdPerUnitGasUpdated event received for dest chain %d source chain %s", destChain, ccipModule.ChainClient.GetNetworkName()) - case <-sub.Err(): + case <-ctx.Done(): return } } }() - ccipModule.priceUpdateSubs = append(ccipModule.priceUpdateSubs, sub) return nil } +// UpdateTokenPricesAtRegularInterval updates aggregator contract with updated answer at regular interval. +// At each iteration of ticker it chooses one of the aggregator contracts and updates its round answer. +func (ccipModule *CCIPCommon) UpdateTokenPricesAtRegularInterval(ctx context.Context, interval time.Duration, conf *laneconfig.LaneConfig) error { + if ccipModule.ExistingDeployment { + return nil + } + var aggregators []*contracts.MockAggregator + for _, aggregatorContract := range conf.PriceAggregators { + contract, err := ccipModule.Deployer.NewMockAggregator(common.HexToAddress(aggregatorContract)) + if err != nil { + return err + } + aggregators = append(aggregators, contract) + } + go func() { + rand.NewSource(uint64(time.Now().UnixNano())) + ticker := time.NewTicker(interval) + for { + select { + case <-ticker.C: + // randomly choose an aggregator contract from slice of aggregators + randomIndex := rand.Intn(len(aggregators)) + err := aggregators[randomIndex].UpdateRoundData(new(big.Int).Add(big.NewInt(1e18), big.NewInt(rand.Int63n(1000)))) + if err != nil { + continue + } + case <-ctx.Done(): + return + } + } + }() + return nil +} + // SyncUSDCDomain makes domain updates to Source usdc pool domain with - // 1. USDC domain from destination chain's token transmitter contract // 2. Destination pool address as allowed caller func (ccipModule *CCIPCommon) SyncUSDCDomain(destTransmitter *contracts.TokenTransmitter, destPoolAddr []common.Address, destChainID uint64) error { // if not USDC new deployment, return // if existing deployment, consider that no syncing is required and return - if ccipModule.ExistingDeployment || !ccipModule.USDCDeployment { + if ccipModule.ExistingDeployment || !ccipModule.IsUSDCDeployment() { return nil } if destTransmitter == nil || len(destPoolAddr) == 0 { @@ -470,15 +535,13 @@ func (ccipModule *CCIPCommon) SyncUSDCDomain(destTransmitter *contracts.TokenTra if err != nil { return fmt.Errorf("invalid chain id %w", err) } - if len(destPoolAddr) != len(ccipModule.BridgeTokenPools) { - return fmt.Errorf("invalid pool address") - } + // sync USDC domain for i, pool := range ccipModule.BridgeTokenPools { if pool.USDCPool == nil { continue } - err := pool.SyncUSDCDomain(destTransmitter, destPoolAddr[i], destChainSelector) + err = pool.SyncUSDCDomain(destTransmitter, destPoolAddr[i], destChainSelector) if err != nil { return err } @@ -486,6 +549,81 @@ func (ccipModule *CCIPCommon) SyncUSDCDomain(destTransmitter *contracts.TokenTra return ccipModule.ChainClient.WaitForEvents() } +func (ccipModule *CCIPCommon) PollRPCConnection(ctx context.Context, lggr zerolog.Logger) { + for { + select { + case reconnectTime := <-ccipModule.ChainClient.ConnectionRestored(): + if ccipModule.IsConnectionRestoredRecently == nil { + ccipModule.IsConnectionRestoredRecently = atomic.NewBool(true) + } else { + ccipModule.IsConnectionRestoredRecently.Store(true) + } + lggr.Info().Time("Restored At", reconnectTime).Str("Network", ccipModule.ChainClient.GetNetworkName()).Msg("Connection Restored") + case issueTime := <-ccipModule.ChainClient.ConnectionIssue(): + if ccipModule.IsConnectionRestoredRecently == nil { + ccipModule.IsConnectionRestoredRecently = atomic.NewBool(false) + } else { + ccipModule.IsConnectionRestoredRecently.Store(false) + } + lggr.Info().Time("Started At", issueTime).Str("Network", ccipModule.ChainClient.GetNetworkName()).Msg("RPC Disconnected") + case <-ctx.Done(): + return + } + } +} + +func (ccipModule *CCIPCommon) IsUSDCDeployment() bool { + return pointer.GetInt(ccipModule.NoOfUSDCTokens) > 0 +} + +func (ccipModule *CCIPCommon) WriteLaneConfig(conf *laneconfig.LaneConfig) { + var btAddresses, btpAddresses []string + priceAggrs := make(map[string]string) + for i, bt := range ccipModule.BridgeTokens { + btAddresses = append(btAddresses, bt.Address()) + btpAddresses = append(btpAddresses, ccipModule.BridgeTokenPools[i].Address()) + } + for k, v := range ccipModule.PriceAggregators { + priceAggrs[k.Hex()] = v.ContractAddress.Hex() + } + conf.CommonContracts = laneconfig.CommonContracts{ + FeeToken: ccipModule.FeeToken.Address(), + BridgeTokens: btAddresses, + BridgeTokenPools: btpAddresses, + ARM: ccipModule.ARMContract.Hex(), + Router: ccipModule.Router.Address(), + PriceRegistry: ccipModule.PriceRegistry.Address(), + PriceAggregators: priceAggrs, + WrappedNative: ccipModule.WrappedNative.Hex(), + Multicall: ccipModule.MulticallContract.Hex(), + } + if ccipModule.TokenTransmitter != nil { + conf.CommonContracts.TokenTransmitter = ccipModule.TokenTransmitter.ContractAddress.Hex() + } + if ccipModule.TokenMessenger != nil { + conf.CommonContracts.TokenMessenger = ccipModule.TokenMessenger.Hex() + } + if ccipModule.ARM == nil { + conf.CommonContracts.IsMockARM = true + } +} + +func (ccipModule *CCIPCommon) AddPriceAggregatorToken(token common.Address, initialAns *big.Int) error { + var err error + if aggregator, ok := ccipModule.PriceAggregators[token]; !ok { + ccipModule.PriceAggregators[token], err = ccipModule.Deployer.DeployMockAggregator(18, initialAns) + if err != nil { + return fmt.Errorf("deploying mock aggregator contract shouldn't fail %w", err) + } + } else { + ccipModule.PriceAggregators[token], err = ccipModule.Deployer.NewMockAggregator(aggregator.ContractAddress) + if err != nil { + return fmt.Errorf("error instantiating price aggregator for token %s", token.Hex()) + } + } + return nil +} + // DeployContracts deploys the contracts which are necessary in both source and dest chain // This reuses common contracts for bidirectional lanes func (ccipModule *CCIPCommon) DeployContracts(noOfTokens int, @@ -525,11 +663,10 @@ func (ccipModule *CCIPCommon) DeployContracts(noOfTokens int, if err != nil { return fmt.Errorf("deploying wrapped native shouldn't fail %w", err) } - aggregator, err := cd.DeployMockAggregator(18, WrappedNativeToUSD) + err = ccipModule.AddPriceAggregatorToken(*weth9addr, WrappedNativeToUSD) if err != nil { return fmt.Errorf("deploying mock aggregator contract shouldn't fail %w", err) } - ccipModule.PriceAggregators[weth9addr.Hex()] = aggregator err = ccipModule.ChainClient.WaitForEvents() if err != nil { return fmt.Errorf("waiting for deploying wrapped native shouldn't fail %w", err) @@ -557,7 +694,7 @@ func (ccipModule *CCIPCommon) DeployContracts(noOfTokens int, ccipModule.Router = r } // if usdc deployment ,look for token transmitter and token messenger - if ccipModule.USDCDeployment { + if ccipModule.IsUSDCDeployment() { // if existing deployment, no need to deploy new USDC contracts, it should be considered as a generic erc20 token if ccipModule.ExistingDeployment { return fmt.Errorf("existing deployment and new USDC deployment cannot be done together") @@ -597,11 +734,10 @@ func (ccipModule *CCIPCommon) DeployContracts(noOfTokens int, } ccipModule.FeeToken = token - aggregator, err := cd.DeployMockAggregator(18, LinkToUSD) + err = ccipModule.AddPriceAggregatorToken(ccipModule.FeeToken.EthAddress, LinkToUSD) if err != nil { return fmt.Errorf("deploying mock aggregator contract shouldn't fail %w", err) } - ccipModule.PriceAggregators[ccipModule.FeeToken.Address()] = aggregator err = ccipModule.ChainClient.WaitForEvents() if err != nil { return fmt.Errorf("error in waiting for feetoken deployment %w", err) @@ -623,7 +759,7 @@ func (ccipModule *CCIPCommon) DeployContracts(noOfTokens int, var token *contracts.ERC20Token var err error if len(tokenDeployerFns) != noOfTokens { - if ccipModule.USDCDeployment { + if ccipModule.IsUSDCDeployment() && i == 0 { // if it's USDC deployment, we deploy the burn mint token 677 with decimal 6 and cast it to ERC20Token erc677Token, err := cd.DeployBurnMintERC677(new(big.Int).Mul(big.NewInt(1e6), big.NewInt(1e18))) if err != nil { @@ -651,22 +787,20 @@ func (ccipModule *CCIPCommon) DeployContracts(noOfTokens int, if err != nil { return fmt.Errorf("getting new bridge token contract shouldn't fail %w", err) } - aggregator, err := cd.DeployMockAggregator(18, LinkToUSD) + err = ccipModule.AddPriceAggregatorToken(linkToken.EthAddress, LinkToUSD) if err != nil { return fmt.Errorf("deploying mock aggregator contract shouldn't fail %w", err) } - ccipModule.PriceAggregators[linkToken.Address()] = aggregator } } else { token, err = cd.DeployERC20TokenContract(tokenDeployerFns[i]) if err != nil { return fmt.Errorf("deploying bridge token contract shouldn't fail %w", err) } - aggregator, err := cd.DeployMockAggregator(18, LinkToUSD) + err = ccipModule.AddPriceAggregatorToken(token.ContractAddress, LinkToUSD) if err != nil { return fmt.Errorf("deploying mock aggregator contract shouldn't fail %w", err) } - ccipModule.PriceAggregators[token.Address()] = aggregator } ccipModule.BridgeTokens = append(ccipModule.BridgeTokens, token) } @@ -693,7 +827,8 @@ func (ccipModule *CCIPCommon) DeployContracts(noOfTokens int, // deploy native token pool for i := len(ccipModule.BridgeTokenPools); i < len(ccipModule.BridgeTokens); i++ { token := ccipModule.BridgeTokens[i] - if ccipModule.USDCDeployment { + // usdc pool need to be the first one in the slice + if ccipModule.IsUSDCDeployment() && i == 0 { // deploy usdc token pool in case of usdc deployment if ccipModule.TokenMessenger == nil { return fmt.Errorf("TokenMessenger contract address is not provided") @@ -763,6 +898,10 @@ func (ccipModule *CCIPCommon) DeployContracts(noOfTokens int, } log.Info().Msg("finished deploying common contracts") + err = ccipModule.SetRemoteChainsOnPools() + if err != nil { + return fmt.Errorf("error setting remote chains %w", err) + } // approve router to spend fee token return ccipModule.ApproveTokens() } @@ -774,8 +913,8 @@ type DynamicPriceGetterConfig struct { StaticPrices map[common.Address]StaticPriceConfig `json:"staticPrices"` } -func (d *DynamicPriceGetterConfig) AddAggregatorPriceConfig(tokenAddr string, aggregatorMap map[string]*contracts.MockAggregator, price *big.Int) error { - aggregatorContract, ok := aggregatorMap[tokenAddr] +func (d *DynamicPriceGetterConfig) AddAggregatorPriceConfig(tokenAddr string, aggregatorMap map[common.Address]*contracts.MockAggregator, price *big.Int) error { + aggregatorContract, ok := aggregatorMap[common.HexToAddress(tokenAddr)] if !ok || aggregatorContract == nil { return fmt.Errorf("aggregator contract not found for token %s", tokenAddr) } @@ -784,11 +923,6 @@ func (d *DynamicPriceGetterConfig) AddAggregatorPriceConfig(tokenAddr string, ag if err != nil { return fmt.Errorf("error in updating round data %w", err) } - - err = aggregatorContract.WaitForTxConfirmations() - if err != nil { - return fmt.Errorf("error in waiting for tx confirmations %w", err) - } // check if latest round data is populated latestRoundData, err := aggregatorContract.Instance.LatestRoundData(nil) if err != nil { @@ -840,7 +974,79 @@ type StaticPriceConfig struct { Price *big.Int `json:"price"` } -func DefaultCCIPModule(logger zerolog.Logger, chainClient blockchain.EVMClient, existingDeployment, multiCall, usdc bool) (*CCIPCommon, error) { +func NewCCIPCommonFromConfig(logger zerolog.Logger, chainClient blockchain.EVMClient, existingDeployment, multiCall bool, NoOfUSDCToken *int, laneConfig *laneconfig.LaneConfig) (*CCIPCommon, error) { + newCCIPModule, err := DefaultCCIPModule(logger, chainClient, existingDeployment, multiCall, NoOfUSDCToken) + if err != nil { + return nil, err + } + newCD := newCCIPModule.Deployer + newCCIPModule.LoadContractAddresses(laneConfig) + var arm *contracts.ARM + if newCCIPModule.ARM != nil { + arm, err = newCD.NewARMContract(*newCCIPModule.ARMContract) + if err != nil { + return nil, err + } + newCCIPModule.ARM = arm + } + var pools []*contracts.TokenPool + for i := range newCCIPModule.BridgeTokenPools { + // if there is usdc token, the corresponding pool will always be added as first one in the slice + if newCCIPModule.IsUSDCDeployment() && i == 0 { + pool, err := newCD.NewUSDCTokenPoolContract(common.HexToAddress(newCCIPModule.BridgeTokenPools[i].Address())) + if err != nil { + return nil, err + } + pools = append(pools, pool) + } else { + pool, err := newCD.NewLockReleaseTokenPoolContract(common.HexToAddress(newCCIPModule.BridgeTokenPools[i].Address())) + if err != nil { + return nil, err + } + pools = append(pools, pool) + } + } + newCCIPModule.BridgeTokenPools = pools + var tokens []*contracts.ERC20Token + for i := range newCCIPModule.BridgeTokens { + token, err := newCD.NewERC20TokenContract(common.HexToAddress(newCCIPModule.BridgeTokens[i].Address())) + if err != nil { + return nil, err + } + tokens = append(tokens, token) + } + newCCIPModule.BridgeTokens = tokens + priceAggregators := make(map[common.Address]*contracts.MockAggregator) + for k, v := range newCCIPModule.PriceAggregators { + aggregator, err := newCD.NewMockAggregator(v.ContractAddress) + if err != nil { + return nil, err + } + priceAggregators[k] = aggregator + } + newCCIPModule.PriceAggregators = priceAggregators + newCCIPModule.FeeToken, err = newCCIPModule.Deployer.NewLinkTokenContract(common.HexToAddress(newCCIPModule.FeeToken.Address())) + if err != nil { + return nil, err + } + newCCIPModule.PriceRegistry, err = newCCIPModule.Deployer.NewPriceRegistry(common.HexToAddress(newCCIPModule.PriceRegistry.Address())) + if err != nil { + return nil, err + } + newCCIPModule.Router, err = newCCIPModule.Deployer.NewRouter(common.HexToAddress(newCCIPModule.Router.Address())) + if err != nil { + return nil, err + } + if newCCIPModule.TokenTransmitter != nil { + newCCIPModule.TokenTransmitter, err = newCCIPModule.Deployer.NewTokenTransmitter(newCCIPModule.TokenTransmitter.ContractAddress) + if err != nil { + return nil, err + } + } + return newCCIPModule, nil +} + +func DefaultCCIPModule(logger zerolog.Logger, chainClient blockchain.EVMClient, existingDeployment, multiCall bool, NoOfUSDCToken *int) (*CCIPCommon, error) { cd, err := contracts.NewCCIPContractsDeployer(logger, chainClient) if err != nil { return nil, err @@ -854,11 +1060,11 @@ func DefaultCCIPModule(logger zerolog.Logger, chainClient blockchain.EVMClient, }, ExistingDeployment: existingDeployment, MulticallEnabled: multiCall, - USDCDeployment: usdc, + NoOfUSDCTokens: NoOfUSDCToken, poolFunds: testhelpers.Link(5), gasUpdateWatcherMu: &sync.Mutex{}, gasUpdateWatcher: make(map[uint64]*big.Int), - PriceAggregators: make(map[string]*contracts.MockAggregator), + PriceAggregators: make(map[common.Address]*contracts.MockAggregator), }, nil } @@ -1023,19 +1229,6 @@ func (sourceCCIP *SourceCCIPModule) DeployContracts(lane *laneconfig.LaneConfig) return fmt.Errorf("getting new onramp contractshouldn't fail %w", err) } } - if !sourceCCIP.Common.ExistingDeployment { - // set remote chain on the pools - for _, pool := range sourceCCIP.Common.BridgeTokenPools { - err = pool.SetRemoteChainOnPool(sourceCCIP.DestChainSelector) - if err != nil { - return fmt.Errorf("setting remote chain on the bridge token pool shouldn't fail %w", err) - } - } - err = sourceCCIP.Common.ChainClient.WaitForEvents() - if err != nil { - return fmt.Errorf("waiting for events shouldn't fail %w", err) - } - } return nil } @@ -1088,7 +1281,11 @@ func (sourceCCIP *SourceCCIPModule) UpdateBalance( ) { if len(sourceCCIP.TransferAmount) > 0 { for i := range sourceCCIP.TransferAmount { - token := sourceCCIP.Common.BridgeTokens[i] + // if length of sourceCCIP.TransferAmount is more than available bridge token use first bridge token + token := sourceCCIP.Common.BridgeTokens[0] + if i < len(sourceCCIP.Common.BridgeTokens) { + token = sourceCCIP.Common.BridgeTokens[i] + } name := fmt.Sprintf("BridgeToken-%s-Address-%s", token.Address(), sourceCCIP.Sender.Hex()) balances.Update(name, BalanceItem{ Address: sourceCCIP.Sender, @@ -1097,11 +1294,18 @@ func (sourceCCIP *SourceCCIPModule) UpdateBalance( }) } for i := range sourceCCIP.TransferAmount { - pool := sourceCCIP.Common.BridgeTokenPools[i] - name := fmt.Sprintf("BridgeToken-%s-TokenPool-%s", sourceCCIP.Common.BridgeTokens[i].Address(), pool.Address()) + // if length of sourceCCIP.TransferAmount is more than available bridge token use first bridge token + pool := sourceCCIP.Common.BridgeTokenPools[0] + index := 0 + if i < len(sourceCCIP.Common.BridgeTokenPools) { + pool = sourceCCIP.Common.BridgeTokenPools[i] + index = i + } + + name := fmt.Sprintf("BridgeToken-%s-TokenPool-%s", sourceCCIP.Common.BridgeTokens[index].Address(), pool.Address()) balances.Update(name, BalanceItem{ Address: pool.EthAddress, - Getter: GetterForLinkToken(sourceCCIP.Common.BridgeTokens[i].BalanceOf, pool.Address()), + Getter: GetterForLinkToken(sourceCCIP.Common.BridgeTokens[index].BalanceOf, pool.Address()), AmtToAdd: bigmath.Mul(big.NewInt(noOfReq), sourceCCIP.TransferAmount[i]), }) } @@ -1138,12 +1342,9 @@ func (sourceCCIP *SourceCCIPModule) AssertSendRequestedLogFinalized( prevEventAt time.Time, reqStats []*testreporters.RequestStat, ) (time.Time, uint64, error) { - if sourceCCIP.Common.ChainClient.NetworkSimulated() { - return prevEventAt, 0, nil - } lggr.Info().Msg("Waiting for CCIPSendRequested event log to be finalized") finalizedBlockNum, finalizedAt, err := sourceCCIP.Common.ChainClient.WaitForFinalizedTx(txHash) - if err != nil { + if err != nil || finalizedBlockNum == nil { for _, stat := range reqStats { stat.UpdateState(lggr, stat.SeqNum, testreporters.SourceLogFinalized, time.Since(prevEventAt), testreporters.Failure) } @@ -1160,6 +1361,30 @@ func (sourceCCIP *SourceCCIPModule) AssertSendRequestedLogFinalized( return finalizedAt, finalizedBlockNum.Uint64(), nil } +func (sourceCCIP *SourceCCIPModule) IsRequestTriggeredWithinTimeframe(timeframe *config2.Duration) *time.Time { + if timeframe == nil { + return nil + } + var foundAt *time.Time + lastSeenTimestamp := time.Now().UTC().Add(-timeframe.Duration()) + sourceCCIP.CCIPSendRequestedWatcher.Range(func(key, value any) bool { + if sendRequestedEvents, exists := value.([]*evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested); exists { + for _, sendRequestedEvent := range sendRequestedEvents { + raw := sendRequestedEvent.Raw + hdr, err := sourceCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(raw.BlockNumber))) + if err == nil { + if hdr.Timestamp.After(lastSeenTimestamp) { + foundAt = pointer.ToTime(hdr.Timestamp) + return false + } + } + } + } + return true + }) + return foundAt +} + func (sourceCCIP *SourceCCIPModule) AssertEventCCIPSendRequested( lggr zerolog.Logger, txHash string, @@ -1170,9 +1395,9 @@ func (sourceCCIP *SourceCCIPModule) AssertEventCCIPSendRequested( lggr.Info().Msg("Waiting for CCIPSendRequested event") ticker := time.NewTicker(time.Second) defer ticker.Stop() - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - + timer := time.NewTimer(timeout) + defer timer.Stop() + resetTimer := 0 for { select { case <-ticker.C: @@ -1191,7 +1416,20 @@ func (sourceCCIP *SourceCCIPModule) AssertEventCCIPSendRequested( return sendRequestedEvents, prevEventAt, nil } } - case <-ctx.Done(): + case <-timer.C: + // if there is connection issue reset the timer : + if sourceCCIP.Common.IsConnectionRestoredRecently != nil && !sourceCCIP.Common.IsConnectionRestoredRecently.Load() { + if resetTimer > 2 { + for _, stat := range reqStat { + stat.UpdateState(lggr, 0, testreporters.CCIPSendRe, time.Since(prevEventAt), testreporters.Failure) + } + return nil, time.Now(), fmt.Errorf("possible RPC issue - CCIPSendRequested event is not found for tx %s", txHash) + } + resetTimer++ + timer.Reset(timeout) + lggr.Info().Int("count of reset", resetTimer).Msg("Resetting timer to validate CCIPSendRequested event") + continue + } for _, stat := range reqStat { stat.UpdateState(lggr, 0, testreporters.CCIPSendRe, time.Since(prevEventAt), testreporters.Failure) } @@ -1209,7 +1447,11 @@ func (sourceCCIP *SourceCCIPModule) CCIPMsg( tokenAndAmounts := []router.ClientEVMTokenAmount{} if msgType == TokenTransfer { for i, amount := range sourceCCIP.TransferAmount { - token := sourceCCIP.Common.BridgeTokens[i] + // if length of sourceCCIP.TransferAmount is more than available bridge token use first bridge token + token := sourceCCIP.Common.BridgeTokens[0] + if i < len(sourceCCIP.Common.BridgeTokens) { + token = sourceCCIP.Common.BridgeTokens[i] + } tokenAndAmounts = append(tokenAndAmounts, router.ClientEVMTokenAmount{ Token: common.HexToAddress(token.Address()), Amount: amount, }) @@ -1264,16 +1506,24 @@ func (sourceCCIP *SourceCCIPModule) SendRequest( timeNow := time.Now() feeToken := common.HexToAddress(sourceCCIP.Common.FeeToken.Address()) // initiate the transfer - // if the token address is 0x0 it will use Native as fee token and the fee amount should be mentioned in bind.TransactOpts's value + // if the fee token address is 0x0 it will use Native as fee token and the fee amount should be mentioned in bind.TransactOpts's value if feeToken != (common.Address{}) { sendTx, err = sourceCCIP.Common.Router.CCIPSendAndProcessTx(destChainSelector, msg, nil) if err != nil { - return common.Hash{}, time.Since(timeNow), nil, fmt.Errorf("failed initiating the transfer ccip-send: %w", err) + txHash := common.Hash{} + if sendTx != nil { + txHash = sendTx.Hash() + } + return txHash, time.Since(timeNow), nil, fmt.Errorf("failed initiating the transfer ccip-send: %w", err) } } else { sendTx, err = sourceCCIP.Common.Router.CCIPSendAndProcessTx(destChainSelector, msg, fee) if err != nil { - return common.Hash{}, time.Since(timeNow), nil, fmt.Errorf("failed initiating the transfer ccip-send: %w", err) + txHash := common.Hash{} + if sendTx != nil { + txHash = sendTx.Hash() + } + return txHash, time.Since(timeNow), nil, fmt.Errorf("failed initiating the transfer ccip-send: %w", err) } } @@ -1285,16 +1535,21 @@ func (sourceCCIP *SourceCCIPModule) SendRequest( return sendTx.Hash(), time.Since(timeNow), fee, nil } -func DefaultSourceCCIPModule(logger zerolog.Logger, chainClient blockchain.EVMClient, destChainId uint64, destChain string, transferAmount []*big.Int, ccipCommon *CCIPCommon) (*SourceCCIPModule, error) { - cmn, err := ccipCommon.Copy(logger, chainClient) +func DefaultSourceCCIPModule( + logger zerolog.Logger, + chainClient blockchain.EVMClient, + destChainId uint64, + destChain string, + transferAmount []*big.Int, + existingDeployment bool, + multiCall bool, + NoOfUSDCToken *int, + laneConf *laneconfig.LaneConfig, +) (*SourceCCIPModule, error) { + cmn, err := NewCCIPCommonFromConfig(logger, chainClient, existingDeployment, multiCall, NoOfUSDCToken, laneConf) if err != nil { return nil, err } - // if transfer amounts are provided for number of tokens greater than the number of supported bridge tokens, then update the transfer amount to be - // equivalent to the number of tokens supported by the bridge - if len(transferAmount) > 0 && len(transferAmount) > len(cmn.BridgeTokens) { - transferAmount = transferAmount[:len(cmn.BridgeTokens)] - } destChainSelector, err := chainselectors.SelectorFromChainId(destChainId) if err != nil { @@ -1321,12 +1576,12 @@ type DestCCIPModule struct { CommitStore *contracts.CommitStore ReceiverDapp *contracts.ReceiverDapp OffRamp *contracts.OffRamp - WrappedNative common.Address ReportAcceptedWatcher *sync.Map ExecStateChangedWatcher *sync.Map ReportBlessedWatcher *sync.Map ReportBlessedBySeqNum *sync.Map NextSeqNumToCommit *atomic.Uint64 + DestStartBlock uint64 } func (destCCIP *DestCCIPModule) LoadContracts(conf *laneconfig.LaneConfig) { @@ -1379,7 +1634,10 @@ func (destCCIP *DestCCIPModule) DeployContracts( if err != nil { return fmt.Errorf("failed to get chain selector for destination chain id %d: %w", destCCIP.Common.ChainClient.GetChainID().Uint64(), err) } - + destCCIP.DestStartBlock, err = destCCIP.Common.ChainClient.LatestBlockNumber(context.Background()) + if err != nil { + return fmt.Errorf("getting latest block number shouldn't fail %w", err) + } if destCCIP.CommitStore == nil { if destCCIP.Common.ExistingDeployment { return fmt.Errorf("commit store address not provided in lane config") @@ -1458,15 +1716,6 @@ func (destCCIP *DestCCIPModule) DeployContracts( return fmt.Errorf("getting new offramp shouldn't fail %w", err) } } - if !destCCIP.Common.ExistingDeployment { - // update pools with remote chain - for _, pool := range destCCIP.Common.BridgeTokenPools { - err = pool.SetRemoteChainOnPool(destCCIP.SourceChainSelector) - if err != nil { - return fmt.Errorf("setting remote chain on the bridge token pool shouldn't fail %w", err) - } - } - } if destCCIP.ReceiverDapp == nil { // ReceiverDapp destCCIP.ReceiverDapp, err = contractDeployer.DeployReceiverDapp(false) @@ -1524,7 +1773,10 @@ func (destCCIP *DestCCIPModule) UpdateBalance( ) { if len(transferAmount) > 0 { for i := range transferAmount { - token := destCCIP.Common.BridgeTokens[i] + token := destCCIP.Common.BridgeTokens[0] + if i < len(destCCIP.Common.BridgeTokens) { + token = destCCIP.Common.BridgeTokens[i] + } name := fmt.Sprintf("BridgeToken-%s-Address-%s", token.Address(), destCCIP.ReceiverDapp.Address()) balance.Update(name, BalanceItem{ Address: destCCIP.ReceiverDapp.EthAddress, @@ -1533,11 +1785,16 @@ func (destCCIP *DestCCIPModule) UpdateBalance( }) } for i := range transferAmount { - pool := destCCIP.Common.BridgeTokenPools[i] - name := fmt.Sprintf("BridgeToken-%s-TokenPool-%s", destCCIP.Common.BridgeTokens[i].Address(), pool.Address()) + pool := destCCIP.Common.BridgeTokenPools[0] + index := 0 + if i < len(destCCIP.Common.BridgeTokenPools) { + pool = destCCIP.Common.BridgeTokenPools[i] + index = i + } + name := fmt.Sprintf("BridgeToken-%s-TokenPool-%s", destCCIP.Common.BridgeTokens[index].Address(), pool.Address()) balance.Update(name, BalanceItem{ Address: pool.EthAddress, - Getter: GetterForLinkToken(destCCIP.Common.BridgeTokens[i].BalanceOf, pool.Address()), + Getter: GetterForLinkToken(destCCIP.Common.BridgeTokens[index].BalanceOf, pool.Address()), AmtToSub: bigmath.Mul(big.NewInt(noOfReq), transferAmount[i]), }) } @@ -1557,6 +1814,78 @@ func (destCCIP *DestCCIPModule) UpdateBalance( } } +// AssertNoReportAcceptedEventReceived validates that no ExecutionStateChangedEvent is emitted for mentioned timeRange after lastSeenTimestamp +func (destCCIP *DestCCIPModule) AssertNoReportAcceptedEventReceived(lggr zerolog.Logger, timeRange time.Duration, lastSeenTimestamp time.Time) error { + ctx, cancel := context.WithTimeout(context.Background(), timeRange) + defer cancel() + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + var eventFoundAfterCursing *time.Time + // verify if CommitReportAccepted is received, it's not generated after provided lastSeenTimestamp + destCCIP.ReportAcceptedWatcher.Range(func(key, value any) bool { + e, exists := value.(*evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged) + if exists { + vLogs := e.Raw + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, big.NewInt(int64(vLogs.BlockNumber))) + if err != nil { + return true + } + if hdr.Timestamp.After(lastSeenTimestamp) { + eventFoundAfterCursing = pointer.ToTime(hdr.Timestamp) + return false + } + } + return true + }) + if eventFoundAfterCursing != nil { + return fmt.Errorf("CommitReportAccepted Event detected at %s after %s", lastSeenTimestamp, eventFoundAfterCursing.String()) + } + case <-ctx.Done(): + lggr.Info().Msgf("successfully validated that no CommitReportAccepted detected after %s for %s", lastSeenTimestamp, timeRange) + return nil + } + } +} + +// AssertNoExecutionStateChangedEventReceived validates that no ExecutionStateChangedEvent is emitted for mentioned timeRange after lastSeenTimestamp +func (destCCIP *DestCCIPModule) AssertNoExecutionStateChangedEventReceived(lggr zerolog.Logger, timeRange time.Duration, lastSeenTimestamp time.Time) error { + ctx, cancel := context.WithTimeout(context.Background(), timeRange) + defer cancel() + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for { + select { + case <-ticker.C: + var eventFoundAfterCursing *time.Time + // verify if executionstate changed is received, it's not generated after provided lastSeenTimestamp + destCCIP.ExecStateChangedWatcher.Range(func(key, value any) bool { + e, exists := value.(*evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged) + if exists { + vLogs := e.Raw + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, big.NewInt(int64(vLogs.BlockNumber))) + if err != nil { + return true + } + if hdr.Timestamp.After(lastSeenTimestamp) { + eventFoundAfterCursing = pointer.ToTime(hdr.Timestamp) + return false + } + } + return true + }) + if eventFoundAfterCursing != nil { + return fmt.Errorf("ExecutionStateChanged Event detected at %s after %s", lastSeenTimestamp, eventFoundAfterCursing.String()) + } + case <-ctx.Done(): + lggr.Info().Msgf("successfully validated that no ExecutionStateChanged detected after %s for %s", lastSeenTimestamp, timeRange) + return nil + } + } +} + func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( lggr zerolog.Logger, seqNum uint64, @@ -1566,9 +1895,11 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( execState testhelpers.MessageExecutionState, ) (uint8, error) { lggr.Info().Int64("seqNum", int64(seqNum)).Msg("Waiting for ExecutionStateChanged event") - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() + timer := time.NewTimer(timeout) + defer timer.Stop() ticker := time.NewTicker(time.Second) + defer ticker.Stop() + resetTimer := 0 for { select { case <-ticker.C: @@ -1580,7 +1911,7 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( destCCIP.ExecStateChangedWatcher.Delete(seqNum) vLogs := e.Raw receivedAt := time.Now().UTC() - hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, big.NewInt(int64(vLogs.BlockNumber))) + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(vLogs.BlockNumber))) if err == nil { receivedAt = hdr.Timestamp } @@ -1607,7 +1938,20 @@ func (destCCIP *DestCCIPModule) AssertEventExecutionStateChanged( execState, testhelpers.MessageExecutionState(e.State), e.ReturnData, seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) } } - case <-ctx.Done(): + case <-timer.C: + // if there is connection issue reset the context : + if destCCIP.Common.IsConnectionRestoredRecently != nil && !destCCIP.Common.IsConnectionRestoredRecently.Load() { + // if timer already has been reset 2 times we fail with warning + if resetTimer > 2 { + reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, time.Since(timeNow), testreporters.Failure) + return 0, fmt.Errorf("possible RPC issues - ExecutionStateChanged event not found for seq num %d for lane %d-->%d", + seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) + } + timer.Reset(timeout) + resetTimer++ + lggr.Info().Int("count of reset", resetTimer).Msg("Resetting timer to validate ExecutionStateChanged event") + continue + } reqStat.UpdateState(lggr, seqNum, testreporters.ExecStateChanged, time.Since(timeNow), testreporters.Failure) return 0, fmt.Errorf("ExecutionStateChanged event not found for seq num %d for lane %d-->%d", seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) @@ -1623,9 +1967,11 @@ func (destCCIP *DestCCIPModule) AssertEventReportAccepted( reqStat *testreporters.RequestStat, ) (*commit_store.CommitStoreCommitReport, time.Time, error) { lggr.Info().Int64("seqNum", int64(seqNum)).Msg("Waiting for ReportAccepted event") - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() + timer := time.NewTimer(timeout) + defer timer.Stop() + resetTimerCount := 0 ticker := time.NewTicker(time.Second) + defer ticker.Stop() for { select { case <-ticker.C: @@ -1636,7 +1982,7 @@ func (destCCIP *DestCCIPModule) AssertEventReportAccepted( // if the value is processed, delete it from the map destCCIP.ReportAcceptedWatcher.Delete(seqNum) receivedAt := time.Now().UTC() - hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, big.NewInt(int64(reportAccepted.Raw.BlockNumber))) + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(reportAccepted.Raw.BlockNumber))) if err == nil { receivedAt = hdr.Timestamp } @@ -1672,7 +2018,19 @@ func (destCCIP *DestCCIPModule) AssertEventReportAccepted( return &reportAccepted.Report, receivedAt, nil } } - case <-ctx.Done(): + case <-timer.C: + // if there is connection issue reset the context : + if destCCIP.Common.IsConnectionRestoredRecently != nil && !destCCIP.Common.IsConnectionRestoredRecently.Load() { + if resetTimerCount > 2 { + reqStat.UpdateState(lggr, seqNum, testreporters.Commit, time.Since(prevEventAt), testreporters.Failure) + return nil, time.Now().UTC(), fmt.Errorf("possible RPC issue - ReportAccepted is not found for seq num %d lane %d-->%d", + seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) + } + timer.Reset(timeout) + resetTimerCount++ + lggr.Info().Int("count of reset", resetTimerCount).Msg("Resetting timer to validate ReportAccepted event") + continue + } reqStat.UpdateState(lggr, seqNum, testreporters.Commit, time.Since(prevEventAt), testreporters.Failure) return nil, time.Now().UTC(), fmt.Errorf("ReportAccepted is not found for seq num %d lane %d-->%d", seqNum, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) @@ -1693,9 +2051,11 @@ func (destCCIP *DestCCIPModule) AssertReportBlessed( return prevEventAt, nil } lggr.Info().Interface("commit store interval", CommitReport.Interval).Msg("Waiting for Report To be blessed") - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() + timer := time.NewTimer(timeout) + defer timer.Stop() + resetTimerCount := 0 ticker := time.NewTicker(time.Second) + defer ticker.Stop() for { select { case <-ticker.C: @@ -1723,7 +2083,7 @@ func (destCCIP *DestCCIPModule) AssertReportBlessed( // if the value is processed, delete it from the map destCCIP.ReportBlessedBySeqNum.Delete(seqNum) } - hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(ctx, big.NewInt(int64(vLogs.BlockNumber))) + hdr, err := destCCIP.Common.ChainClient.HeaderByNumber(context.Background(), big.NewInt(int64(vLogs.BlockNumber))) if err == nil { receivedAt = hdr.Timestamp } @@ -1744,7 +2104,19 @@ func (destCCIP *DestCCIPModule) AssertReportBlessed( return receivedAt, nil } } - case <-ctx.Done(): + case <-timer.C: + // if there is connection issue reset the context : + if destCCIP.Common.IsConnectionRestoredRecently != nil && !destCCIP.Common.IsConnectionRestoredRecently.Load() { + if resetTimerCount > 2 { + reqStat.UpdateState(lggr, seqNum, testreporters.ReportBlessed, time.Since(prevEventAt), testreporters.Failure) + return time.Now().UTC(), fmt.Errorf("possible RPC issue - ReportBlessed is not found for interval %+v lane %d-->%d", + CommitReport.Interval, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) + } + timer.Reset(timeout) + resetTimerCount++ + lggr.Info().Int("count of reset", resetTimerCount).Msg("Resetting timer to validate ReportBlessed event") + continue + } reqStat.UpdateState(lggr, seqNum, testreporters.ReportBlessed, time.Since(prevEventAt), testreporters.Failure) return time.Now().UTC(), fmt.Errorf("ReportBlessed is not found for interval %+v lane %d-->%d", CommitReport.Interval, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) @@ -1759,10 +2131,12 @@ func (destCCIP *DestCCIPModule) AssertSeqNumberExecuted( timeNow time.Time, reqStat *testreporters.RequestStat, ) error { - lggr.Info().Int64("seqNum", int64(seqNumberBefore)).Msg("Waiting to be executed") - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() + lggr.Info().Int64("seqNum", int64(seqNumberBefore)).Msg("Waiting to be processed by commit store") + timer := time.NewTimer(timeout) + defer timer.Stop() + resetTimerCount := 0 ticker := time.NewTicker(time.Second) + defer ticker.Stop() for { select { case <-ticker.C: @@ -1771,15 +2145,26 @@ func (destCCIP *DestCCIPModule) AssertSeqNumberExecuted( } seqNumberAfter, err := destCCIP.CommitStore.Instance.GetExpectedNextSequenceNumber(nil) if err != nil { - reqStat.UpdateState(lggr, seqNumberBefore, testreporters.Commit, time.Since(timeNow), testreporters.Failure) - return fmt.Errorf("error %w in GetNextExpectedSeqNumber by commitStore for seqNum %d lane %d-->%d", - err, seqNumberBefore+1, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) + // if we get error instead of returning error we continue, in case it's a temporary RPC failure . + continue } if seqNumberAfter > seqNumberBefore { destCCIP.NextSeqNumToCommit.Store(seqNumberAfter) return nil } - case <-ctx.Done(): + case <-timer.C: + // if there is connection issue reset the context : + if destCCIP.Common.IsConnectionRestoredRecently != nil && !destCCIP.Common.IsConnectionRestoredRecently.Load() { + if resetTimerCount > 2 { + reqStat.UpdateState(lggr, seqNumberBefore, testreporters.Commit, time.Since(timeNow), testreporters.Failure) + return fmt.Errorf("possible RPC issue - sequence number is not increased for seq num %d lane %d-->%d", + seqNumberBefore, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) + } + timer.Reset(timeout) + resetTimerCount++ + lggr.Info().Int("count of reset", resetTimerCount).Msg("Resetting timer to validate seqnumber increase in commit store") + continue + } reqStat.UpdateState(lggr, seqNumberBefore, testreporters.Commit, time.Since(timeNow), testreporters.Failure) return fmt.Errorf("sequence number is not increased for seq num %d lane %d-->%d", seqNumberBefore, destCCIP.SourceChainId, destCCIP.Common.ChainClient.GetChainID()) @@ -1787,8 +2172,17 @@ func (destCCIP *DestCCIPModule) AssertSeqNumberExecuted( } } -func DefaultDestinationCCIPModule(logger zerolog.Logger, chainClient blockchain.EVMClient, sourceChainId uint64, sourceChain string, ccipCommon *CCIPCommon) (*DestCCIPModule, error) { - cmn, err := ccipCommon.Copy(logger, chainClient) +func DefaultDestinationCCIPModule( + logger zerolog.Logger, + chainClient blockchain.EVMClient, + sourceChainId uint64, + sourceChain string, + existingDeployment bool, + multiCall bool, + NoOfUSDCToken *int, + laneConf *laneconfig.LaneConfig, +) (*DestCCIPModule, error) { + cmn, err := NewCCIPCommonFromConfig(logger, chainClient, existingDeployment, multiCall, NoOfUSDCToken, laneConf) if err != nil { return nil, err } @@ -1836,38 +2230,55 @@ func CCIPRequestFromTxHash(txHash common.Hash, chainClient blockchain.EVMClient) } type CCIPLane struct { - Test *testing.T - Logger zerolog.Logger - SourceNetworkName string - DestNetworkName string - SourceChain blockchain.EVMClient - DestChain blockchain.EVMClient - Source *SourceCCIPModule - Dest *DestCCIPModule - TestEnv *CCIPTestEnv - NumberOfReq int - Reports *testreporters.CCIPLaneStats - Balance *BalanceSheet - StartBlockOnSource uint64 - StartBlockOnDestination uint64 - SentReqs map[common.Hash][]CCIPRequest - TotalFee *big.Int // total fee for all the requests. Used for balance validation. - ValidationTimeout time.Duration - Context context.Context - SrcNetworkLaneCfg *laneconfig.LaneConfig - DstNetworkLaneCfg *laneconfig.LaneConfig - Subscriptions []event.Subscription -} - -func (lane *CCIPLane) TokenPricesConfig() (string, error) { + Test *testing.T + Logger zerolog.Logger + SourceNetworkName string + DestNetworkName string + SourceChain blockchain.EVMClient + DestChain blockchain.EVMClient + Source *SourceCCIPModule + Dest *DestCCIPModule + NumberOfReq int + Reports *testreporters.CCIPLaneStats + Balance *BalanceSheet + SentReqs map[common.Hash][]CCIPRequest + TotalFee *big.Int // total fee for all the requests. Used for balance validation. + ValidationTimeout time.Duration + Context context.Context + SrcNetworkLaneCfg *laneconfig.LaneConfig + DstNetworkLaneCfg *laneconfig.LaneConfig +} + +func (lane *CCIPLane) TokenPricesConfig(static bool) (string, error) { d := DynamicPriceGetterConfig{ AggregatorPrices: make(map[common.Address]AggregatorPriceConfig), StaticPrices: make(map[common.Address]StaticPriceConfig), } + if static { + for _, token := range lane.Dest.Common.BridgeTokens { + err := d.AddStaticPriceConfig(token.Address(), lane.DestChain.GetChainID().Uint64(), LinkToUSD) + if err != nil { + return "", fmt.Errorf("error in AddStaticPriceConfig for bridge token %s: %w", token.Address(), err) + } + } + err := d.AddStaticPriceConfig(lane.Dest.Common.FeeToken.Address(), lane.DestChain.GetChainID().Uint64(), LinkToUSD) + if err != nil { + return "", fmt.Errorf("error in AddStaticPriceConfig for Fee token %s: %w", lane.Dest.Common.FeeToken.Address(), err) + } + err = d.AddStaticPriceConfig(lane.Dest.Common.WrappedNative.Hex(), lane.DestChain.GetChainID().Uint64(), WrappedNativeToUSD) + if err != nil { + return "", fmt.Errorf("error in AddStaticPriceConfig for WrappedNative token %s: %w", lane.Dest.Common.WrappedNative.Hex(), err) + } + err = d.AddStaticPriceConfig(lane.Source.Common.WrappedNative.Hex(), lane.SourceChain.GetChainID().Uint64(), WrappedNativeToUSD) + if err != nil { + return "", fmt.Errorf("error in AddStaticPriceConfig for WrappedNative token %s: %w", lane.Source.Common.WrappedNative.Hex(), err) + } + return d.String() + } for _, token := range lane.Dest.Common.BridgeTokens { - err := d.AddStaticPriceConfig(token.Address(), lane.DestChain.GetChainID().Uint64(), LinkToUSD) + err := d.AddAggregatorPriceConfig(token.Address(), lane.Dest.Common.PriceAggregators, LinkToUSD) if err != nil { - return "", fmt.Errorf("error in AddStaticPriceConfig for bridge token %s: %w", token.Address(), err) + return "", fmt.Errorf("error in AddAggregatorPriceConfig for bridge token %s: %w", token.Address(), err) } } if err := d.AddAggregatorPriceConfig(lane.Dest.Common.FeeToken.Address(), lane.Dest.Common.PriceAggregators, LinkToUSD); err != nil { @@ -1882,63 +2293,38 @@ func (lane *CCIPLane) TokenPricesConfig() (string, error) { return d.String() } -func (lane *CCIPLane) UpdateLaneConfig() { - var btAddresses, btpAddresses []string - - for i, bt := range lane.Source.Common.BridgeTokens { - btAddresses = append(btAddresses, bt.Address()) - btpAddresses = append(btpAddresses, lane.Source.Common.BridgeTokenPools[i].Address()) - } - lane.SrcNetworkLaneCfg.CommonContracts = laneconfig.CommonContracts{ - FeeToken: lane.Source.Common.FeeToken.Address(), - BridgeTokens: btAddresses, - BridgeTokenPools: btpAddresses, - ARM: lane.Source.Common.ARMContract.Hex(), - Router: lane.Source.Common.Router.Address(), - PriceRegistry: lane.Source.Common.PriceRegistry.Address(), - WrappedNative: lane.Source.Common.WrappedNative.Hex(), - Multicall: lane.Source.Common.MulticallContract.Hex(), - } - if lane.Source.Common.TokenTransmitter != nil { - lane.SrcNetworkLaneCfg.CommonContracts.TokenTransmitter = lane.Source.Common.TokenTransmitter.ContractAddress.Hex() - } - if lane.Source.Common.TokenMessenger != nil { - lane.SrcNetworkLaneCfg.CommonContracts.TokenMessenger = lane.Source.Common.TokenMessenger.Hex() - } - if lane.Source.Common.ARM == nil { - lane.SrcNetworkLaneCfg.CommonContracts.IsMockARM = true +// OptimizeStorage sets nil to various elements of CCIPLane which are only used +// during lane set up and not used for rest of the test duration +// this is called mainly by load test to keep the memory usage minimum for high number of lanes +func (lane *CCIPLane) OptimizeStorage() { + lane.Source.Common.FreeUpUnusedSpace() + lane.Dest.Common.FreeUpUnusedSpace() + lane.DstNetworkLaneCfg = nil + lane.SrcNetworkLaneCfg = nil + // close all header subscriptions for dest chains + queuedEvents := lane.Dest.Common.ChainClient.GetHeaderSubscriptions() + for subName := range queuedEvents { + lane.Dest.Common.ChainClient.DeleteHeaderEventSubscription(subName) + } + // close all header subscriptions for source chains except for finalized header + queuedEvents = lane.Source.Common.ChainClient.GetHeaderSubscriptions() + for subName := range queuedEvents { + if subName == blockchain.FinalizedHeaderKey { + continue + } + lane.Source.Common.ChainClient.DeleteHeaderEventSubscription(subName) } +} + +func (lane *CCIPLane) UpdateLaneConfig() { + lane.Source.Common.WriteLaneConfig(lane.SrcNetworkLaneCfg) lane.SrcNetworkLaneCfg.SrcContractsMu.Lock() lane.SrcNetworkLaneCfg.SrcContracts[lane.Source.DestNetworkName] = laneconfig.SourceContracts{ OnRamp: lane.Source.OnRamp.Address(), DepolyedAt: lane.Source.SrcStartBlock, } lane.SrcNetworkLaneCfg.SrcContractsMu.Unlock() - btAddresses, btpAddresses = []string{}, []string{} - - for i, bt := range lane.Dest.Common.BridgeTokens { - btAddresses = append(btAddresses, bt.Address()) - btpAddresses = append(btpAddresses, lane.Dest.Common.BridgeTokenPools[i].Address()) - } - lane.DstNetworkLaneCfg.CommonContracts = laneconfig.CommonContracts{ - FeeToken: lane.Dest.Common.FeeToken.Address(), - BridgeTokens: btAddresses, - BridgeTokenPools: btpAddresses, - ARM: lane.Dest.Common.ARMContract.Hex(), - Router: lane.Dest.Common.Router.Address(), - PriceRegistry: lane.Dest.Common.PriceRegistry.Address(), - WrappedNative: lane.Dest.Common.WrappedNative.Hex(), - Multicall: lane.Dest.Common.MulticallContract.Hex(), - } - if lane.Dest.Common.TokenTransmitter != nil { - lane.DstNetworkLaneCfg.CommonContracts.TokenTransmitter = lane.Dest.Common.TokenTransmitter.ContractAddress.Hex() - } - if lane.Dest.Common.TokenMessenger != nil { - lane.DstNetworkLaneCfg.CommonContracts.TokenMessenger = lane.Dest.Common.TokenMessenger.Hex() - } - if lane.Dest.Common.ARM == nil { - lane.DstNetworkLaneCfg.CommonContracts.IsMockARM = true - } + lane.Dest.Common.WriteLaneConfig(lane.DstNetworkLaneCfg) lane.DstNetworkLaneCfg.DestContractsMu.Lock() lane.DstNetworkLaneCfg.DestContracts[lane.Dest.SourceNetworkName] = laneconfig.DestContracts{ OffRamp: lane.Dest.OffRamp.Address(), @@ -1959,10 +2345,6 @@ func (lane *CCIPLane) RecordStateBeforeTransfer() { lane.Balance.RecordBalance(bal) // save the current block numbers to use in various filter log requests - lane.StartBlockOnSource, err = lane.Source.Common.ChainClient.LatestBlockNumber(context.Background()) - require.NoError(lane.Test, err, "Getting current block should be successful in source chain") - lane.StartBlockOnDestination, err = lane.Dest.Common.ChainClient.LatestBlockNumber(context.Background()) - require.NoError(lane.Test, err, "Getting current block should be successful in dest chain") lane.TotalFee = big.NewInt(0) lane.NumberOfReq = 0 lane.SentReqs = make(map[common.Hash][]CCIPRequest) @@ -1980,13 +2362,13 @@ func (lane *CCIPLane) AddToSentReqs(txHash common.Hash, reqStats []*testreporter for _, stat := range reqStats { allRequests = append(allRequests, CCIPRequest{ ReqNo: stat.ReqNo, - txHash: request.txHash, + txHash: rcpt.TxHash.Hex(), txConfirmationTimestamp: request.txConfirmationTimestamp, RequestStat: stat, }) lane.NumberOfReq++ } - lane.SentReqs[txHash] = allRequests + lane.SentReqs[rcpt.TxHash] = allRequests return rcpt, nil } @@ -2030,7 +2412,11 @@ func (lane *CCIPLane) Multicall(noOfRequests int, msgType string, multiSendAddr // if token transfer is required, transfer the token amount to multisend if msgType == TokenTransfer { for i, amount := range lane.Source.TransferAmount { - token := lane.Source.Common.BridgeTokens[i] + // if length of sourceCCIP.TransferAmount is more than available bridge token use first bridge token + token := lane.Source.Common.BridgeTokens[0] + if i < len(lane.Source.Common.BridgeTokens) { + token = lane.Source.Common.BridgeTokens[i] + } err := token.Transfer(multiSendAddr.Hex(), amount) if err != nil { return err @@ -2122,7 +2508,7 @@ func (lane *CCIPLane) SendRequests(noOfRequests int, msgType string, gasLimit *b testreporters.TX, txConfirmationDur, testreporters.Success, testreporters.TransactionStats{ Fee: fee.String(), GasUsed: gasUsed, - TxHash: txHash.Hex(), + TxHash: rcpt.TxHash.Hex(), NoOfTokensSent: noOfTokens, MessageBytesLength: len([]byte(msg)), }) @@ -2305,48 +2691,107 @@ func (lane *CCIPLane) ValidateRequestByTxHash(txHash common.Hash, execState test } func (lane *CCIPLane) StartEventWatchers() error { - if !lane.Source.Common.ChainClient.NetworkSimulated() && - lane.Source.Common.ChainClient.GetNetworkConfig().FinalityDepth == 0 { + if lane.Source.Common.ChainClient.GetNetworkConfig().FinalityDepth == 0 { err := lane.Source.Common.ChainClient.PollFinality() if err != nil { return err } } + go lane.Source.Common.PollRPCConnection(lane.Context, lane.Logger) + go lane.Dest.Common.PollRPCConnection(lane.Context, lane.Logger) + sendReqEvent := make(chan *evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested) sub, err := lane.Source.OnRamp.Instance.WatchCCIPSendRequested(nil, sendReqEvent) if err != nil { return err } - lane.Subscriptions = append(lane.Subscriptions, sub) - go func() { + go func(sub event.Subscription) { + defer sub.Unsubscribe() + resubscribed := false for { - e := <-sendReqEvent - lane.Logger.Info().Msgf("CCIPSendRequested event received for seq number %d", e.Message.SequenceNumber) - eventsForTx, ok := lane.Source.CCIPSendRequestedWatcher.Load(e.Raw.TxHash.Hex()) - if ok { - lane.Source.CCIPSendRequestedWatcher.Store(e.Raw.TxHash.Hex(), append(eventsForTx.([]*evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested), e)) - } else { - lane.Source.CCIPSendRequestedWatcher.Store(e.Raw.TxHash.Hex(), []*evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{e}) + select { + case e := <-sendReqEvent: + lane.Logger.Info().Msgf("CCIPSendRequested event received for seq number %d", e.Message.SequenceNumber) + eventsForTx, ok := lane.Source.CCIPSendRequestedWatcher.Load(e.Raw.TxHash.Hex()) + if ok { + lane.Source.CCIPSendRequestedWatcher.Store(e.Raw.TxHash.Hex(), append(eventsForTx.([]*evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested), e)) + } else { + lane.Source.CCIPSendRequestedWatcher.Store(e.Raw.TxHash.Hex(), []*evm_2_evm_onramp.EVM2EVMOnRampCCIPSendRequested{e}) + } + + lane.Source.CCIPSendRequestedWatcher = testutils.DeleteNilEntriesFromMap(lane.Source.CCIPSendRequestedWatcher) + // check every second if connection is restored + case <-time.After(1 * time.Second): + // if there is a connection issue, set resubscribed to false + if lane.Source.Common.IsConnectionRestoredRecently != nil && !lane.Source.Common.IsConnectionRestoredRecently.Load() { + resubscribed = false + } + // if connection is restored re-subscribe, if not already resubscribed + if lane.Source.Common.IsConnectionRestoredRecently != nil && lane.Source.Common.IsConnectionRestoredRecently.Load() && !resubscribed { + lane.Logger.Info().Msg("source connection restored restarting subscription") + if sub != nil { + sub.Unsubscribe() + } + sub, err = lane.Source.OnRamp.Instance.WatchCCIPSendRequested(&bind.WatchOpts{ + Start: pointer.ToUint64(lane.Source.SrcStartBlock), + }, sendReqEvent) + if err != nil { + resubscribed = false + lane.Logger.Error().Err(err).Msg("error in resubscribing to CCIPSendRequested after restoring connection") + } else { + resubscribed = true + } + } + case <-lane.Context.Done(): + return } } - }() + }(sub) + reportAcceptedEvent := make(chan *commit_store.CommitStoreReportAccepted) sub, err = lane.Dest.CommitStore.Instance.WatchReportAccepted(nil, reportAcceptedEvent) if err != nil { return err } - lane.Subscriptions = append(lane.Subscriptions, sub) - - go func() { + go func(sub event.Subscription) { + defer sub.Unsubscribe() + resubscribed := false for { - e := <-reportAcceptedEvent - for i := e.Report.Interval.Min; i <= e.Report.Interval.Max; i++ { - lane.Dest.ReportAcceptedWatcher.Store(i, e) + select { + case e := <-reportAcceptedEvent: + for i := e.Report.Interval.Min; i <= e.Report.Interval.Max; i++ { + lane.Dest.ReportAcceptedWatcher.Store(i, e) + } + lane.Dest.ReportAcceptedWatcher = testutils.DeleteNilEntriesFromMap(lane.Dest.ReportAcceptedWatcher) + // check every second if connection is restored + case <-time.After(1 * time.Second): + // if there is a connection issue, set resubscribed to false + if lane.Dest.Common.IsConnectionRestoredRecently != nil && !lane.Dest.Common.IsConnectionRestoredRecently.Load() { + resubscribed = false + } + // if connection is restored re-subscribe, if not already resubscribed + if lane.Dest.Common.IsConnectionRestoredRecently != nil && lane.Dest.Common.IsConnectionRestoredRecently.Load() && !resubscribed { + lane.Logger.Info().Msg("dest connection restored restarting ReportAccepted subscription") + if sub != nil { + sub.Unsubscribe() + } + sub, err = lane.Dest.CommitStore.Instance.WatchReportAccepted(&bind.WatchOpts{ + Start: pointer.ToUint64(lane.Dest.DestStartBlock), + }, reportAcceptedEvent) + if err != nil { + resubscribed = false + lane.Logger.Error().Err(err).Msg("error in resubscribing to ReportAccepted after restoring connection") + } else { + resubscribed = true + } + } + case <-lane.Context.Done(): + return } } - }() + }(sub) if lane.Dest.Common.ARM != nil { reportBlessedEvent := make(chan *arm_contract.ARMContractTaggedRootBlessed) @@ -2355,45 +2800,96 @@ func (lane *CCIPLane) StartEventWatchers() error { return err } - lane.Subscriptions = append(lane.Subscriptions, sub) - - go func() { + go func(sub event.Subscription) { + defer sub.Unsubscribe() + resubscribed := false for { - e := <-reportBlessedEvent - lane.Logger.Info().Msgf("TaggedRootBlessed event received for root %x", e.TaggedRoot.Root) - if e.TaggedRoot.CommitStore == lane.Dest.CommitStore.EthAddress { - lane.Dest.ReportBlessedWatcher.Store(e.TaggedRoot.Root, &e.Raw) + select { + case e := <-reportBlessedEvent: + lane.Logger.Info().Msgf("TaggedRootBlessed event received for root %x", e.TaggedRoot.Root) + if e.TaggedRoot.CommitStore == lane.Dest.CommitStore.EthAddress { + lane.Dest.ReportBlessedWatcher.Store(e.TaggedRoot.Root, &e.Raw) + } + lane.Dest.ReportBlessedWatcher = testutils.DeleteNilEntriesFromMap(lane.Dest.ReportBlessedWatcher) + // check every second if connection is restored + case <-time.After(1 * time.Second): + // if there is a connection issue, set resubscribed to false + if lane.Dest.Common.IsConnectionRestoredRecently != nil && !lane.Dest.Common.IsConnectionRestoredRecently.Load() { + resubscribed = false + } + // if connection is restored re-subscribe, if not already resubscribed + if lane.Dest.Common.IsConnectionRestoredRecently != nil && lane.Dest.Common.IsConnectionRestoredRecently.Load() && !resubscribed { + lane.Logger.Info().Msg("dest connection restored restarting TaggedRootBlessed subscription") + if sub != nil { + sub.Unsubscribe() + } + sub, err = lane.Dest.Common.ARM.Instance.WatchTaggedRootBlessed(&bind.WatchOpts{ + Start: pointer.ToUint64(lane.Dest.DestStartBlock), + }, reportBlessedEvent, nil) + if err != nil { + resubscribed = false + lane.Logger.Error().Err(err).Msg("error in resubscribing to TaggedRootBlessed after restoring connection") + } else { + resubscribed = true + } + } + case <-lane.Context.Done(): + return } } - }() + }(sub) } + execStateChangedEvent := make(chan *evm_2_evm_offramp.EVM2EVMOffRampExecutionStateChanged) sub, err = lane.Dest.OffRamp.Instance.WatchExecutionStateChanged(nil, execStateChangedEvent, nil, nil) if err != nil { return err } - lane.Subscriptions = append(lane.Subscriptions, sub) - - go func() { + go func(sub event.Subscription) { + defer sub.Unsubscribe() + resubscribed := false for { - e := <-execStateChangedEvent - lane.Logger.Info().Msgf("Execution state changed event received for seq number %d", e.SequenceNumber) - lane.Dest.ExecStateChangedWatcher.Store(e.SequenceNumber, e) + select { + case e := <-execStateChangedEvent: + lane.Logger.Info().Msgf("Execution state changed event received for seq number %d", e.SequenceNumber) + lane.Dest.ExecStateChangedWatcher.Store(e.SequenceNumber, e) + lane.Dest.ExecStateChangedWatcher = testutils.DeleteNilEntriesFromMap(lane.Dest.ExecStateChangedWatcher) + // check every second if connection is restored + case <-time.After(1 * time.Second): + // if there is a connection issue, set resubscribed to false + if lane.Dest.Common.IsConnectionRestoredRecently != nil && !lane.Dest.Common.IsConnectionRestoredRecently.Load() { + resubscribed = false + } + // if connection is restored re-subscribe, if not already resubscribed + if lane.Dest.Common.IsConnectionRestoredRecently != nil && lane.Dest.Common.IsConnectionRestoredRecently.Load() && !resubscribed { + lane.Logger.Info().Msg("dest connection restored restarting ExecutionStateChanged subscription") + if sub != nil { + sub.Unsubscribe() + } + sub, err = lane.Dest.OffRamp.Instance.WatchExecutionStateChanged(&bind.WatchOpts{ + Start: pointer.ToUint64(lane.Dest.DestStartBlock), + }, execStateChangedEvent, nil, nil) + if err != nil { + resubscribed = false + lane.Logger.Error().Err(err).Msg("error in resubscribing to ExecutionStateChanged after restoring connection") + } else { + resubscribed = true + } + } + case <-lane.Context.Done(): + return + } } - }() + }(sub) return nil } func (lane *CCIPLane) CleanUp(clearFees bool) error { lane.Logger.Info().Msg("Cleaning up lane") - if !lane.Source.Common.ChainClient.NetworkSimulated() && - lane.Source.Common.ChainClient.GetNetworkConfig().FinalityDepth == 0 { + if lane.Source.Common.ChainClient.GetNetworkConfig().FinalityDepth == 0 { lane.Source.Common.ChainClient.CancelFinalityPolling() } - for _, sub := range lane.Subscriptions { - sub.Unsubscribe() - } // recover fees from onRamp contract if clearFees && !lane.Source.Common.ChainClient.NetworkSimulated() { err := lane.Source.PayCCIPFeeToOwnerAddress() @@ -2411,67 +2907,65 @@ func (lane *CCIPLane) CleanUp(clearFees bool) error { // DeployNewCCIPLane sets up a lane and initiates lane.Source and lane.Destination // If configureCLNodes is true it sets up jobs and contract config for the lane func (lane *CCIPLane) DeployNewCCIPLane( - numOfCommitNodes int, + setUpCtx context.Context, + env *CCIPTestEnv, commitAndExecOnSameDON bool, - sourceCommon *CCIPCommon, - destCommon *CCIPCommon, transferAmounts []*big.Int, bootstrapAdded *atomic.Bool, configureCLNodes bool, jobErrGroup *errgroup.Group, withPipeline bool, -) (*laneconfig.LaneConfig, *laneconfig.LaneConfig, error) { + staticPrice bool, + existingDeployment bool, + multiCall bool, + NoOfUSDCToken *int, +) error { var err error - env := lane.TestEnv sourceChainClient := lane.SourceChain destChainClient := lane.DestChain - - if sourceCommon == nil { - return nil, nil, fmt.Errorf("common contracts for source chain %s not found", sourceChainClient.GetChainID().String()) - } - - if destCommon == nil { - return nil, nil, fmt.Errorf("common contracts for destination chain %s not found", destChainClient.GetChainID().String()) - } - + srcConf := lane.SrcNetworkLaneCfg + destConf := lane.DstNetworkLaneCfg lane.Source, err = DefaultSourceCCIPModule( lane.Logger, sourceChainClient, destChainClient.GetChainID().Uint64(), - destChainClient.GetNetworkName(), transferAmounts, sourceCommon) + destChainClient.GetNetworkName(), transferAmounts, + existingDeployment, multiCall, NoOfUSDCToken, srcConf, + ) if err != nil { - return nil, nil, fmt.Errorf("failed to create source module: %w", err) + return fmt.Errorf("failed to create source module: %w", err) } lane.Dest, err = DefaultDestinationCCIPModule( lane.Logger, destChainClient, sourceChainClient.GetChainID().Uint64(), - sourceChainClient.GetNetworkName(), destCommon) + sourceChainClient.GetNetworkName(), + existingDeployment, multiCall, NoOfUSDCToken, destConf, + ) if err != nil { - return nil, nil, fmt.Errorf("failed to create destination module: %w", err) + return fmt.Errorf("failed to create destination module: %w", err) } - srcConf := lane.SrcNetworkLaneCfg - - destConf := lane.DstNetworkLaneCfg - // deploy all source contracts err = lane.Source.DeployContracts(srcConf) if err != nil { - return nil, nil, fmt.Errorf("failed to deploy source contracts: %w", err) + return fmt.Errorf("failed to deploy source contracts: %w", err) } // deploy all destination contracts err = lane.Dest.DeployContracts(*lane.Source, destConf) if err != nil { - return nil, nil, fmt.Errorf("failed to deploy destination contracts: %w", err) + return fmt.Errorf("failed to deploy destination contracts: %w", err) } // if it's a new USDC deployment, sync the USDC domain var destPools []common.Address for _, pool := range lane.Dest.Common.BridgeTokenPools { + if pool.USDCPool == nil { + continue + } destPools = append(destPools, pool.EthAddress) } err = lane.Source.Common.SyncUSDCDomain(lane.Dest.Common.TokenTransmitter, destPools, lane.Source.DestinationChainId) if err != nil { - return nil, nil, fmt.Errorf("failed to sync USDC domain: %w", err) + return fmt.Errorf("failed to sync USDC domain: %w", err) } lane.UpdateLaneConfig() @@ -2479,49 +2973,36 @@ func (lane *CCIPLane) DeployNewCCIPLane( // if lane is being set up for already configured CL nodes and contracts // no further action is necessary if !configureCLNodes { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, nil + return nil + } + err = lane.Source.Common.WatchForPriceUpdates(setUpCtx) + if err != nil { + return fmt.Errorf("error in starting price update watch") } - if env == nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("test environment not set") + return fmt.Errorf("test environment not set") } // wait for the CL nodes to be ready before moving ahead with job creation err = env.CLNodeWithKeyReady.Wait() if err != nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("failed to wait for CL nodes to be ready: %w", err) + return fmt.Errorf("failed to wait for CL nodes to be ready: %w", err) } clNodesWithKeys := env.CLNodesWithKeys // set up ocr2 jobs clNodes, exists := clNodesWithKeys[lane.Dest.Common.ChainClient.GetChainID().String()] if !exists { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("could not find CL nodes for %s", lane.Dest.Common.ChainClient.GetChainID().String()) + return fmt.Errorf("could not find CL nodes for %s", lane.Dest.Common.ChainClient.GetChainID().String()) } - - // first node is the bootstrapper bootstrapCommit := clNodes[0] var bootstrapExec *client.CLNodesWithKeys - var execNodes []*client.CLNodesWithKeys - commitNodes := clNodes[1:] - env.commitNodeStartIndex = 2 - env.execNodeStartIndex = 2 - env.numOfCommitNodes = numOfCommitNodes - env.numOfExecNodes = numOfCommitNodes + commitNodes := clNodes[env.CommitNodeStartIndex : env.CommitNodeStartIndex+env.NumOfCommitNodes] + execNodes := clNodes[env.ExecNodeStartIndex : env.ExecNodeStartIndex+env.NumOfExecNodes] if !commitAndExecOnSameDON { if len(clNodes) < 11 { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("not enough CL nodes for separate commit and execution nodes") + return fmt.Errorf("not enough CL nodes for separate commit and execution nodes") } bootstrapExec = clNodes[1] // for a set-up of different commit and execution nodes second node is the bootstrapper for execution nodes - commitNodes = clNodes[2 : 2+numOfCommitNodes] - execNodes = clNodes[2+numOfCommitNodes:] - env.commitNodeStartIndex = 3 - env.execNodeStartIndex = 3 + numOfCommitNodes - env.numOfCommitNodes = len(commitNodes) - env.numOfExecNodes = len(execNodes) - } else { - execNodes = commitNodes } - env.numOfAllowedFaultyExec = (len(execNodes) - 1) / 3 - env.numOfAllowedFaultyCommit = (len(commitNodes) - 1) / 3 // save the current block numbers. If there is a delay between job start up and ocr config set up, the jobs will // replay the log polling from these mentioned block number. The dest block number should ideally be the block number on which @@ -2529,7 +3010,7 @@ func (lane *CCIPLane) DeployNewCCIPLane( // Here for simplicity we are just taking the current block number just before the job is created. currentBlockOnDest, err := destChainClient.LatestBlockNumber(context.Background()) if err != nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("getting current block should be successful in destination chain %w", err) + return fmt.Errorf("getting current block should be successful in destination chain %w", err) } var killgrave *ctftestenv.Killgrave @@ -2549,9 +3030,9 @@ func (lane *CCIPLane) DeployNewCCIPLane( tokensUSDUrl := TokenPricePipelineURLs(tokenAddresses, killgrave, env.MockServer) tokenPricesUSDPipeline = TokenFeeForMultipleTokenAddr(tokensUSDUrl) } else { - tokenPricesConfigJson, err = lane.TokenPricesConfig() + tokenPricesConfigJson, err = lane.TokenPricesConfig(staticPrice) if err != nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("error getting token prices config %w", err) + return fmt.Errorf("error getting token prices config %w", err) } lane.Logger.Info().Str("tokenPricesConfigJson", tokenPricesConfigJson).Msg("Price getter config") } @@ -2567,7 +3048,7 @@ func (lane *CCIPLane) DeployNewCCIPLane( PriceGetterConfig: tokenPricesConfigJson, DestStartBlock: currentBlockOnDest, } - if !lane.Source.Common.ExistingDeployment && lane.Source.Common.USDCDeployment { + if !lane.Source.Common.ExistingDeployment && lane.Source.Common.IsUSDCDeployment() { api := "" if killgrave != nil { api = killgrave.InternalEndpoint @@ -2576,8 +3057,10 @@ func (lane *CCIPLane) DeployNewCCIPLane( api = env.MockServer.Config.ClusterURL } if lane.Source.Common.TokenTransmitter == nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("token transmitter address not set") + return fmt.Errorf("token transmitter address not set") } + // TODO: Need to know if there can be more than one USDC token per chain + // currently the jobspec supports only one. Need to update this if more than two is supported jobParams.USDCConfig = &config.USDCConfig{ SourceTokenAddress: common.HexToAddress(lane.Source.Common.BridgeTokens[0].Address()), SourceMessageTransmitterAddress: lane.Source.Common.TokenTransmitter.ContractAddress, @@ -2589,7 +3072,7 @@ func (lane *CCIPLane) DeployNewCCIPLane( bootstrapAdded.Store(true) err := CreateBootstrapJob(jobParams, bootstrapCommit, bootstrapExec) if err != nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("failed to create bootstrap job: %w", err) + return fmt.Errorf("failed to create bootstrap job: %w", err) } } @@ -2612,12 +3095,12 @@ func (lane *CCIPLane) DeployNewCCIPLane( // set up ocr2 config err = SetOCR2Configs(commitNodes, execNodes, *lane.Dest) if err != nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("failed to set ocr2 config: %w", err) + return fmt.Errorf("failed to set ocr2 config: %w", err) } err = CreateOCR2CCIPCommitJobs(lane.Logger, jobParams, commitNodes, env.nodeMutexes, jobErrGroup) if err != nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("failed to create ocr2 commit jobs: %w", err) + return fmt.Errorf("failed to create ocr2 commit jobs: %w", err) } if p2pBootstrappersExec != nil { jobParams.P2PV2Bootstrappers = []string{p2pBootstrappersExec.P2PV2Bootstrapper()} @@ -2625,34 +3108,33 @@ func (lane *CCIPLane) DeployNewCCIPLane( err = CreateOCR2CCIPExecutionJobs(lane.Logger, jobParams, execNodes, env.nodeMutexes, jobErrGroup) if err != nil { - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, fmt.Errorf("failed to create ocr2 execution jobs: %w", err) + return fmt.Errorf("failed to create ocr2 execution jobs: %w", err) } lane.Dest.Common.ChainClient.ParallelTransactions(false) lane.Source.Common.ChainClient.ParallelTransactions(false) - return lane.SrcNetworkLaneCfg, lane.DstNetworkLaneCfg, nil + return nil } // SetOCR2Configs sets the oracle config in ocr2 contracts // nil value in execNodes denotes commit and execution jobs are to be set up in same DON func SetOCR2Configs(commitNodes, execNodes []*client.CLNodesWithKeys, destCCIP DestCCIPModule) error { rootSnooze := config2.MustNewDuration(7 * time.Minute) - inflightExpiry := config2.MustNewDuration(3 * time.Minute) + inflightExpiryExec := config2.MustNewDuration(InflightExpiryExec) + inflightExpiryCommit := config2.MustNewDuration(InflightExpiryCommit) if destCCIP.Common.ChainClient.NetworkSimulated() { rootSnooze = config2.MustNewDuration(RootSnoozeTimeSimulated) - inflightExpiry = config2.MustNewDuration(InflightExpirySimulated) } signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig, err := contracts.NewOffChainAggregatorV2ConfigForCCIPPlugin( commitNodes, testhelpers.NewCommitOffchainConfig( - *config2.MustNewDuration(10 * time.Second), // reduce the heartbeat to 10 sec for faster fee updates + *config2.MustNewDuration(5 * time.Second), 1e6, 1e6, - *config2.MustNewDuration(10 * time.Second), + *config2.MustNewDuration(5 * time.Second), 1e6, - 200e9, - *inflightExpiry, + *inflightExpiryCommit, ), testhelpers.NewCommitOnchainConfig( destCCIP.Common.PriceRegistry.EthAddress, ), contracts.OCR2ParamsForCommit, 3*time.Minute) @@ -2674,10 +3156,9 @@ func SetOCR2Configs(commitNodes, execNodes []*client.CLNodesWithKeys, destCCIP D signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig, err = contracts.NewOffChainAggregatorV2ConfigForCCIPPlugin( nodes, testhelpers.NewExecOffchainConfig( 1, - 5_000_000, + 7_000_000, 0.7, - 200e9, - *inflightExpiry, + *inflightExpiryExec, *rootSnooze, ), testhelpers.NewExecOnchainConfig( PermissionlessExecThreshold, @@ -2823,12 +3304,12 @@ type CCIPTestEnv struct { CLNodesWithKeys map[string][]*client.CLNodesWithKeys // key - network chain-id CLNodes []*client.ChainlinkK8sClient nodeMutexes []*sync.Mutex - execNodeStartIndex int - commitNodeStartIndex int - numOfAllowedFaultyCommit int - numOfAllowedFaultyExec int - numOfCommitNodes int - numOfExecNodes int + ExecNodeStartIndex int + CommitNodeStartIndex int + NumOfAllowedFaultyCommit int + NumOfAllowedFaultyExec int + NumOfCommitNodes int + NumOfExecNodes int K8Env *environment.Environment CLNodeWithKeyReady *errgroup.Group // denotes if keys are created in chainlink node and ready to be used for job creation } @@ -2858,43 +3339,45 @@ func (c *CCIPTestEnv) ChaosLabelForAllGeth(t *testing.T, gethNetworksLabels []st } func (c *CCIPTestEnv) ChaosLabelForCLNodes(t *testing.T) { - allowedFaulty := c.numOfAllowedFaultyCommit - for i := c.commitNodeStartIndex; i < len(c.CLNodes); i++ { + allowedFaulty := c.NumOfAllowedFaultyCommit + commitStartInstance := c.CommitNodeStartIndex + 1 + execStartInstance := c.ExecNodeStartIndex + 1 + for i := commitStartInstance; i < len(c.CLNodes); i++ { labelSelector := map[string]string{ "app": "chainlink-0", "instance": fmt.Sprintf("node-%d", i), } - if i >= c.commitNodeStartIndex && i < c.commitNodeStartIndex+allowedFaulty+1 { + if i >= commitStartInstance && i < commitStartInstance+allowedFaulty+1 { err := c.K8Env.Client.LabelChaosGroupByLabels(c.K8Env.Cfg.Namespace, labelSelector, ChaosGroupCommitAndExecFaultyPlus) require.NoError(t, err) } - if i >= c.commitNodeStartIndex && i < c.commitNodeStartIndex+allowedFaulty { + if i >= commitStartInstance && i < commitStartInstance+allowedFaulty { err := c.K8Env.Client.LabelChaosGroupByLabels(c.K8Env.Cfg.Namespace, labelSelector, ChaosGroupCommitAndExecFaulty) require.NoError(t, err) } // commit node starts from index 2 - if i >= c.commitNodeStartIndex && i < c.commitNodeStartIndex+c.numOfCommitNodes { + if i >= commitStartInstance && i < commitStartInstance+c.NumOfCommitNodes { err := c.K8Env.Client.LabelChaosGroupByLabels(c.K8Env.Cfg.Namespace, labelSelector, ChaosGroupCommit) require.NoError(t, err) } - if i >= c.commitNodeStartIndex && i < c.commitNodeStartIndex+c.numOfAllowedFaultyCommit+1 { + if i >= commitStartInstance && i < commitStartInstance+c.NumOfAllowedFaultyCommit+1 { err := c.K8Env.Client.LabelChaosGroupByLabels(c.K8Env.Cfg.Namespace, labelSelector, ChaosGroupCommitFaultyPlus) require.NoError(t, err) } - if i >= c.commitNodeStartIndex && i < c.commitNodeStartIndex+c.numOfAllowedFaultyCommit { + if i >= commitStartInstance && i < commitStartInstance+c.NumOfAllowedFaultyCommit { err := c.K8Env.Client.LabelChaosGroupByLabels(c.K8Env.Cfg.Namespace, labelSelector, ChaosGroupCommitFaulty) require.NoError(t, err) } - if i >= c.execNodeStartIndex && i < c.execNodeStartIndex+c.numOfExecNodes { + if i >= execStartInstance && i < execStartInstance+c.NumOfExecNodes { err := c.K8Env.Client.LabelChaosGroupByLabels(c.K8Env.Cfg.Namespace, labelSelector, ChaosGroupExecution) require.NoError(t, err) } - if i >= c.execNodeStartIndex && i < c.execNodeStartIndex+c.numOfAllowedFaultyExec+1 { + if i >= execStartInstance && i < execStartInstance+c.NumOfAllowedFaultyExec+1 { err := c.K8Env.Client.LabelChaosGroupByLabels(c.K8Env.Cfg.Namespace, labelSelector, ChaosGroupExecutionFaultyPlus) require.NoError(t, err) } - if i >= c.execNodeStartIndex && i < c.execNodeStartIndex+c.numOfAllowedFaultyExec { + if i >= execStartInstance && i < execStartInstance+c.NumOfAllowedFaultyExec { err := c.K8Env.Client.LabelChaosGroupByLabels(c.K8Env.Cfg.Namespace, labelSelector, ChaosGroupExecutionFaulty) require.NoError(t, err) } @@ -3003,25 +3486,50 @@ func (c *CCIPTestEnv) SetUpNodeKeysAndFund( } }() log.Info().Str("chain id", c1.GetChainID().String()).Msg("Funding Chainlink nodes for chain") - err = actions.FundChainlinkNodesAddresses(chainlinkNodes[1:], c1, nodeFund) - if err != nil { - return fmt.Errorf("funding nodes for chain %s %w", c1.GetNetworkName(), err) + for i := 1; i < len(chainlinkNodes); i++ { + cl := chainlinkNodes[i] + m := c.nodeMutexes[i] + toAddress, err := cl.EthAddressesForChain(c1.GetChainID().String()) + if err != nil { + return err + } + for _, addr := range toAddress { + toAddr := common.HexToAddress(addr) + gasEstimates, err := c1.EstimateGas(ethereum.CallMsg{ + To: &toAddr, + }) + if err != nil { + return err + } + m.Lock() + err = c1.Fund(addr, nodeFund, gasEstimates) + m.Unlock() + if err != nil { + return err + } + } } - return nil + return c1.WaitForEvents() } - + grp, _ := errgroup.WithContext(context.Background()) for _, chain := range chains { err := populateKeys(chain) if err != nil { return err } - err = fund(chain) - if err != nil { - return err - } } - + for _, chain := range chains { + chain := chain + grp.Go(func() error { + return fund(chain) + }) + } + err := grp.Wait() + if err != nil { + return fmt.Errorf("error funding nodes %w", err) + } c.CLNodesWithKeys = nodesWithKeys + return nil } diff --git a/integration-tests/ccip-tests/chaos/ccip_test.go b/integration-tests/ccip-tests/chaos/ccip_test.go index c681b366cb..d5940e11c1 100644 --- a/integration-tests/ccip-tests/chaos/ccip_test.go +++ b/integration-tests/ccip-tests/chaos/ccip_test.go @@ -116,8 +116,8 @@ func TestChaosCCIP(t *testing.T) { lane := setUpArgs.Lanes[0].ForwardLane tearDown := setUpArgs.TearDown - testEnvironment := lane.TestEnv.K8Env - testSetup := lane.TestEnv + testEnvironment := setUpArgs.Env.K8Env + testSetup := setUpArgs.Env testSetup.ChaosLabelForGeth(t, lane.SourceChain.GetNetworkName(), lane.DestChain.GetNetworkName()) testSetup.ChaosLabelForCLNodes(t) diff --git a/integration-tests/ccip-tests/contracts/contract_deployer.go b/integration-tests/ccip-tests/contracts/contract_deployer.go index ca645298ed..e243da5d66 100644 --- a/integration-tests/ccip-tests/contracts/contract_deployer.go +++ b/integration-tests/ccip-tests/contracts/contract_deployer.go @@ -17,6 +17,8 @@ import ( "github.com/rs/zerolog/log" "golang.org/x/crypto/curve25519" + "github.com/smartcontractkit/chainlink-common/pkg/config" + ocrconfighelper2 "github.com/smartcontractkit/libocr/offchainreporting2/confighelper" ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2/types" @@ -26,6 +28,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/wrappers" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/arm_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" @@ -75,7 +78,7 @@ func (e *CCIPContractsDeployer) DeployMultiCallContract() (common.Address, error auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - address, tx, contract, err := bind.DeployContract(auth, multiCallABI, common.FromHex(MultiCallBIN), backend) + address, tx, contract, err := bind.DeployContract(auth, multiCallABI, common.FromHex(MultiCallBIN), wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return common.Address{}, nil, nil, err } @@ -99,7 +102,7 @@ func (e *CCIPContractsDeployer) DeployTokenMessenger(tokenTransmitter common.Add auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - address, tx, contract, err := mock_usdc_token_messenger.DeployMockE2EUSDCTokenMessenger(auth, backend, 0, tokenTransmitter) + address, tx, contract, err := mock_usdc_token_messenger.DeployMockE2EUSDCTokenMessenger(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil), 0, tokenTransmitter) if err != nil { return common.Address{}, nil, nil, err } @@ -110,7 +113,7 @@ func (e *CCIPContractsDeployer) DeployTokenMessenger(tokenTransmitter common.Add } func (e *CCIPContractsDeployer) NewTokenTransmitter(addr common.Address) (*TokenTransmitter, error) { - transmitter, err := mock_usdc_token_transmitter.NewMockE2EUSDCTransmitter(addr, e.evmClient.Backend()) + transmitter, err := mock_usdc_token_transmitter.NewMockE2EUSDCTransmitter(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, err @@ -133,7 +136,7 @@ func (e *CCIPContractsDeployer) DeployTokenTransmitter(domain uint32) (*TokenTra auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - address, tx, contract, err := mock_usdc_token_transmitter.DeployMockE2EUSDCTransmitter(auth, backend, 0, domain) + address, tx, contract, err := mock_usdc_token_transmitter.DeployMockE2EUSDCTransmitter(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil), 0, domain) if err != nil { return common.Address{}, nil, nil, err } @@ -152,7 +155,7 @@ func (e *CCIPContractsDeployer) DeployLinkTokenContract() (*LinkToken, error) { auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - return link_token_interface.DeployLinkToken(auth, backend) + return link_token_interface.DeployLinkToken(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) }) if err != nil { @@ -171,7 +174,7 @@ func (e *CCIPContractsDeployer) DeployBurnMintERC677(ownerMintingAmount *big.Int auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - return burn_mint_erc677.DeployBurnMintERC677(auth, backend, "Test Token ERC677", "TERC677", 6, new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e9))) + return burn_mint_erc677.DeployBurnMintERC677(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil), "Test Token ERC677", "TERC677", 6, new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e9))) }) if err != nil { return nil, err @@ -213,7 +216,7 @@ func (e *CCIPContractsDeployer) DeployERC20TokenContract(deployerFn blockchain.C } func (e *CCIPContractsDeployer) NewLinkTokenContract(addr common.Address) (*LinkToken, error) { - token, err := link_token_interface.NewLinkToken(addr, e.evmClient.Backend()) + token, err := link_token_interface.NewLinkToken(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, err @@ -232,7 +235,7 @@ func (e *CCIPContractsDeployer) NewLinkTokenContract(addr common.Address) (*Link } func (e *CCIPContractsDeployer) NewERC20TokenContract(addr common.Address) (*ERC20Token, error) { - token, err := erc20.NewERC20(addr, e.evmClient.Backend()) + token, err := erc20.NewERC20(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, err @@ -254,7 +257,7 @@ func (e *CCIPContractsDeployer) NewLockReleaseTokenPoolContract(addr common.Addr *TokenPool, error, ) { - pool, err := lock_release_token_pool.NewLockReleaseTokenPool(addr, e.evmClient.Backend()) + pool, err := lock_release_token_pool.NewLockReleaseTokenPool(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, err @@ -265,7 +268,7 @@ func (e *CCIPContractsDeployer) NewLockReleaseTokenPoolContract(addr common.Addr Str("From", e.evmClient.GetDefaultWallet().Address()). Str("Network Name", e.evmClient.GetNetworkConfig().Name). Msg("New contract") - poolInstance, err := token_pool.NewTokenPool(addr, e.evmClient.Backend()) + poolInstance, err := token_pool.NewTokenPool(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, err } @@ -281,7 +284,7 @@ func (e *CCIPContractsDeployer) NewUSDCTokenPoolContract(addr common.Address) ( *TokenPool, error, ) { - pool, err := usdc_token_pool.NewUSDCTokenPool(addr, e.evmClient.Backend()) + pool, err := usdc_token_pool.NewUSDCTokenPool(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, err @@ -292,7 +295,7 @@ func (e *CCIPContractsDeployer) NewUSDCTokenPoolContract(addr common.Address) ( Str("From", e.evmClient.GetDefaultWallet().Address()). Str("Network Name", e.evmClient.GetNetworkConfig().Name). Msg("New contract") - poolInterface, err := token_pool.NewTokenPool(addr, e.evmClient.Backend()) + poolInterface, err := token_pool.NewTokenPool(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, err } @@ -316,7 +319,7 @@ func (e *CCIPContractsDeployer) DeployUSDCTokenPoolContract(tokenAddr string, to ) (common.Address, *types.Transaction, interface{}, error) { return usdc_token_pool.DeployUSDCTokenPool( auth, - backend, + wrappers.MustNewWrappedContractBackend(e.evmClient, nil), tokenMessenger, token, []common.Address{}, @@ -343,7 +346,7 @@ func (e *CCIPContractsDeployer) DeployLockReleaseTokenPoolContract(tokenAddr str ) (common.Address, *types.Transaction, interface{}, error) { return lock_release_token_pool.DeployLockReleaseTokenPool( auth, - backend, + wrappers.MustNewWrappedContractBackend(e.evmClient, nil), token, []common.Address{}, armProxy, @@ -362,13 +365,13 @@ func (e *CCIPContractsDeployer) DeployMockARMContract() (*common.Address, error) auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - return mock_arm_contract.DeployMockARMContract(auth, backend) + return mock_arm_contract.DeployMockARMContract(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) }) return address, err } func (e *CCIPContractsDeployer) NewARMContract(addr common.Address) (*ARM, error) { - arm, err := arm_contract.NewARMContract(addr, e.evmClient.Backend()) + arm, err := arm_contract.NewARMContract(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, err } @@ -390,7 +393,7 @@ func (e *CCIPContractsDeployer) NewCommitStore(addr common.Address) ( *CommitStore, error, ) { - ins, err := commit_store.NewCommitStore(addr, e.evmClient.Backend()) + ins, err := commit_store.NewCommitStore(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) log.Info(). Str("Contract Address", addr.Hex()). Str("Contract Name", "CommitStore"). @@ -411,7 +414,7 @@ func (e *CCIPContractsDeployer) DeployCommitStore(sourceChainSelector, destChain ) (common.Address, *types.Transaction, interface{}, error) { return commit_store.DeployCommitStore( auth, - backend, + wrappers.MustNewWrappedContractBackend(e.evmClient, nil), commit_store.CommitStoreStaticConfig{ ChainSelector: destChainSelector, SourceChainSelector: sourceChainSelector, @@ -438,7 +441,7 @@ func (e *CCIPContractsDeployer) DeployReceiverDapp(revert bool) ( auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - return maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver(auth, backend, revert) + return maybe_revert_message_receiver.DeployMaybeRevertMessageReceiver(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil), revert) }) if err != nil { return nil, err @@ -454,7 +457,7 @@ func (e *CCIPContractsDeployer) NewReceiverDapp(addr common.Address) ( *ReceiverDapp, error, ) { - ins, err := maybe_revert_message_receiver.NewMaybeRevertMessageReceiver(addr, e.evmClient.Backend()) + ins, err := maybe_revert_message_receiver.NewMaybeRevertMessageReceiver(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) log.Info(). Str("Contract Address", addr.Hex()). Str("Contract Name", "ReceiverDapp"). @@ -476,7 +479,7 @@ func (e *CCIPContractsDeployer) DeployRouter(wrappedNative common.Address, armAd auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - return router.DeployRouter(auth, backend, wrappedNative, armAddress) + return router.DeployRouter(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil), wrappedNative, armAddress) }) if err != nil { return nil, err @@ -492,7 +495,7 @@ func (e *CCIPContractsDeployer) NewRouter(addr common.Address) ( *Router, error, ) { - r, err := router.NewRouter(addr, e.evmClient.Backend()) + r, err := router.NewRouter(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) log.Info(). Str("Contract Address", addr.Hex()). Str("Contract Name", "Router"). @@ -513,7 +516,7 @@ func (e *CCIPContractsDeployer) NewPriceRegistry(addr common.Address) ( *PriceRegistry, error, ) { - ins, err := price_registry.NewPriceRegistry(addr, e.evmClient.Backend()) + ins, err := price_registry.NewPriceRegistry(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) log.Info(). Str("Contract Address", addr.Hex()). Str("Contract Name", "PriceRegistry"). @@ -532,7 +535,7 @@ func (e *CCIPContractsDeployer) DeployPriceRegistry(tokens []common.Address) (*P auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - return price_registry.DeployPriceRegistry(auth, backend, nil, tokens, 60*60*24*14) + return price_registry.DeployPriceRegistry(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil), nil, tokens, 60*60*24*14) }) if err != nil { return nil, err @@ -548,7 +551,7 @@ func (e *CCIPContractsDeployer) NewOnRamp(addr common.Address) ( *OnRamp, error, ) { - ins, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(addr, e.evmClient.Backend()) + ins, err := evm_2_evm_onramp.NewEVM2EVMOnRamp(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) log.Info(). Str("Contract Address", addr.Hex()). Str("Contract Name", "OnRamp"). @@ -580,7 +583,7 @@ func (e *CCIPContractsDeployer) DeployOnRamp( ) (common.Address, *types.Transaction, interface{}, error) { return evm_2_evm_onramp.DeployEVM2EVMOnRamp( auth, - backend, + wrappers.MustNewWrappedContractBackend(e.evmClient, nil), evm_2_evm_onramp.EVM2EVMOnRampStaticConfig{ LinkToken: linkTokenAddress, ChainSelector: sourceChainSelector, // source chain id @@ -626,7 +629,7 @@ func (e *CCIPContractsDeployer) NewOffRamp(addr common.Address) ( *OffRamp, error, ) { - ins, err := evm_2_evm_offramp.NewEVM2EVMOffRamp(addr, e.evmClient.Backend()) + ins, err := evm_2_evm_offramp.NewEVM2EVMOffRamp(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) log.Info(). Str("Contract Address", addr.Hex()). Str("Contract Name", "OffRamp"). @@ -647,7 +650,7 @@ func (e *CCIPContractsDeployer) DeployOffRamp(sourceChainSelector, destChainSele ) (common.Address, *types.Transaction, interface{}, error) { return evm_2_evm_offramp.DeployEVM2EVMOffRamp( auth, - backend, + wrappers.MustNewWrappedContractBackend(e.evmClient, nil), evm_2_evm_offramp.EVM2EVMOffRampStaticConfig{ CommitStore: commitStore, ChainSelector: destChainSelector, @@ -680,7 +683,7 @@ func (e *CCIPContractsDeployer) DeployWrappedNative() (*common.Address, error) { auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - return weth9.DeployWETH9(auth, backend) + return weth9.DeployWETH9(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) }) if err != nil { return nil, err @@ -693,7 +696,7 @@ func (e *CCIPContractsDeployer) DeployMockAggregator(decimals uint8, initialAns auth *bind.TransactOpts, backend bind.ContractBackend, ) (common.Address, *types.Transaction, interface{}, error) { - return mock_v3_aggregator_contract.DeployMockV3Aggregator(auth, backend, decimals, initialAns) + return mock_v3_aggregator_contract.DeployMockV3Aggregator(auth, wrappers.MustNewWrappedContractBackend(e.evmClient, nil), decimals, initialAns) }) if err != nil { return nil, fmt.Errorf("deploying mock aggregator: %w", err) @@ -712,7 +715,7 @@ func (e *CCIPContractsDeployer) DeployMockAggregator(decimals uint8, initialAns } func (e *CCIPContractsDeployer) NewMockAggregator(addr common.Address) (*MockAggregator, error) { - ins, err := mock_v3_aggregator_contract.NewMockV3Aggregator(addr, e.evmClient.Backend()) + ins, err := mock_v3_aggregator_contract.NewMockV3Aggregator(addr, wrappers.MustNewWrappedContractBackend(e.evmClient, nil)) if err != nil { return nil, fmt.Errorf("creating mock aggregator: %w", err) } @@ -724,27 +727,29 @@ func (e *CCIPContractsDeployer) NewMockAggregator(addr common.Address) (*MockAgg } var OCR2ParamsForCommit = contracts.OffChainAggregatorV2Config{ - DeltaProgress: 2 * time.Minute, - DeltaResend: 5 * time.Second, - DeltaRound: 75 * time.Second, - DeltaGrace: 5 * time.Second, - MaxDurationQuery: 100 * time.Millisecond, - MaxDurationObservation: 35 * time.Second, - MaxDurationReport: 10 * time.Second, - MaxDurationShouldAcceptFinalizedReport: 5 * time.Second, - MaxDurationShouldTransmitAcceptedReport: 10 * time.Second, + DeltaProgress: config.MustNewDuration(2 * time.Minute), + DeltaResend: config.MustNewDuration(5 * time.Second), + DeltaRound: config.MustNewDuration(60 * time.Second), + DeltaGrace: config.MustNewDuration(5 * time.Second), + DeltaStage: config.MustNewDuration(25 * time.Second), + MaxDurationQuery: config.MustNewDuration(100 * time.Millisecond), + MaxDurationObservation: config.MustNewDuration(35 * time.Second), + MaxDurationReport: config.MustNewDuration(10 * time.Second), + MaxDurationShouldAcceptFinalizedReport: config.MustNewDuration(5 * time.Second), + MaxDurationShouldTransmitAcceptedReport: config.MustNewDuration(10 * time.Second), } var OCR2ParamsForExec = contracts.OffChainAggregatorV2Config{ - DeltaProgress: 100 * time.Second, - DeltaResend: 5 * time.Second, - DeltaRound: 40 * time.Second, - DeltaGrace: 5 * time.Second, - MaxDurationQuery: 100 * time.Millisecond, - MaxDurationObservation: 20 * time.Second, - MaxDurationReport: 8 * time.Second, - MaxDurationShouldAcceptFinalizedReport: 5 * time.Second, - MaxDurationShouldTransmitAcceptedReport: 8 * time.Second, + DeltaProgress: config.MustNewDuration(100 * time.Second), + DeltaResend: config.MustNewDuration(5 * time.Second), + DeltaRound: config.MustNewDuration(30 * time.Second), + DeltaGrace: config.MustNewDuration(5 * time.Second), + DeltaStage: config.MustNewDuration(10 * time.Second), + MaxDurationQuery: config.MustNewDuration(100 * time.Millisecond), + MaxDurationObservation: config.MustNewDuration(20 * time.Second), + MaxDurationReport: config.MustNewDuration(8 * time.Second), + MaxDurationShouldAcceptFinalizedReport: config.MustNewDuration(5 * time.Second), + MaxDurationShouldTransmitAcceptedReport: config.MustNewDuration(8 * time.Second), } func OffChainAggregatorV2ConfigWithNodes(numberNodes int, inflightExpiry time.Duration, cfg contracts.OffChainAggregatorV2Config) contracts.OffChainAggregatorV2Config { @@ -764,12 +769,15 @@ func OffChainAggregatorV2ConfigWithNodes(numberNodes int, inflightExpiry time.Du if faultyNodes == 0 { faultyNodes = 1 } + if cfg.DeltaStage == nil { + cfg.DeltaStage = config.MustNewDuration(inflightExpiry) + } return contracts.OffChainAggregatorV2Config{ DeltaProgress: cfg.DeltaProgress, DeltaResend: cfg.DeltaResend, DeltaRound: cfg.DeltaRound, DeltaGrace: cfg.DeltaGrace, - DeltaStage: inflightExpiry, + DeltaStage: cfg.DeltaStage, RMax: 3, S: s, F: faultyNodes, @@ -854,20 +862,20 @@ func NewOffChainAggregatorV2ConfigForCCIPPlugin[T ccipconfig.OffchainConfig]( } _, _, f_, onchainConfig_, offchainConfigVersion, offchainConfig, err = ocrconfighelper2.ContractSetConfigArgsForTests( - ocrConfig.DeltaProgress, - ocrConfig.DeltaResend, - ocrConfig.DeltaRound, - ocrConfig.DeltaGrace, - ocrConfig.DeltaStage, + ocrConfig.DeltaProgress.Duration(), + ocrConfig.DeltaResend.Duration(), + ocrConfig.DeltaRound.Duration(), + ocrConfig.DeltaGrace.Duration(), + ocrConfig.DeltaStage.Duration(), ocrConfig.RMax, ocrConfig.S, ocrConfig.Oracles, ocrConfig.ReportingPluginConfig, - ocrConfig.MaxDurationQuery, - ocrConfig.MaxDurationObservation, - ocrConfig.MaxDurationReport, - ocrConfig.MaxDurationShouldAcceptFinalizedReport, - ocrConfig.MaxDurationShouldTransmitAcceptedReport, + ocrConfig.MaxDurationQuery.Duration(), + ocrConfig.MaxDurationObservation.Duration(), + ocrConfig.MaxDurationReport.Duration(), + ocrConfig.MaxDurationShouldAcceptFinalizedReport.Duration(), + ocrConfig.MaxDurationShouldTransmitAcceptedReport.Duration(), ocrConfig.F, ocrConfig.OnchainConfig, ) diff --git a/integration-tests/ccip-tests/contracts/contract_models.go b/integration-tests/ccip-tests/contracts/contract_models.go index a49e83bd9a..8b284ee3b2 100644 --- a/integration-tests/ccip-tests/contracts/contract_models.go +++ b/integration-tests/ccip-tests/contracts/contract_models.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/rs/zerolog/log" + "golang.org/x/exp/rand" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" @@ -376,30 +377,26 @@ func (pool *TokenPool) AddLiquidity(approveFn tokenApproveFn, tokenAddr string, return pool.client.ProcessTransaction(tx) } -func (pool *TokenPool) SetRemoteChainOnPool(remoteChainSelector uint64) error { +func (pool *TokenPool) SetRemoteChainOnPool(remoteChainSelectors []uint64) error { log.Info(). Str("Token Pool", pool.Address()). Msg("Setting remote chain on pool") - isSupported, err := pool.PoolInterface.IsSupportedChain(nil, remoteChainSelector) - if err != nil { - return fmt.Errorf("failed to get if chain is supported: %w", err) - } - // Check if remote chain is already supported , if yes return - if isSupported { - log.Info(). - Str("Token Pool", pool.Address()). - Str(Network, pool.client.GetNetworkName()). - Uint64("Remote Chain Selector", remoteChainSelector). - Msg("Remote chain is already supported") - return nil - } - // If remote chain is not supported , add it - opts, err := pool.client.TransactionOpts(pool.client.GetDefaultWallet()) - if err != nil { - return fmt.Errorf("failed to get transaction opts: %w", err) - } - tx, err := pool.PoolInterface.ApplyChainUpdates(opts, []token_pool.TokenPoolChainUpdate{ - { + var selectorsToUpdate []token_pool.TokenPoolChainUpdate + for _, remoteChainSelector := range remoteChainSelectors { + isSupported, err := pool.PoolInterface.IsSupportedChain(nil, remoteChainSelector) + if err != nil { + return fmt.Errorf("failed to get if chain is supported: %w", err) + } + // Check if remote chain is already supported , if yes continue + if isSupported { + log.Info(). + Str("Token Pool", pool.Address()). + Str(Network, pool.client.GetNetworkName()). + Uint64("Remote Chain Selector", remoteChainSelector). + Msg("Remote chain is already supported") + continue + } + selectorsToUpdate = append(selectorsToUpdate, token_pool.TokenPoolChainUpdate{ RemoteChainSelector: remoteChainSelector, Allowed: true, InboundRateLimiterConfig: token_pool.RateLimiterConfig{ @@ -412,8 +409,18 @@ func (pool *TokenPool) SetRemoteChainOnPool(remoteChainSelector uint64) error { Capacity: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e9)), Rate: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e5)), }, - }, - }) + }) + } + // if none to update return + if len(selectorsToUpdate) == 0 { + return nil + } + // If remote chain is not supported , add it + opts, err := pool.client.TransactionOpts(pool.client.GetDefaultWallet()) + if err != nil { + return fmt.Errorf("failed to get transaction opts: %w", err) + } + tx, err := pool.PoolInterface.ApplyChainUpdates(opts, selectorsToUpdate) if err != nil { return fmt.Errorf("failed to set chain updates on token pool: %w", err) @@ -421,9 +428,9 @@ func (pool *TokenPool) SetRemoteChainOnPool(remoteChainSelector uint64) error { log.Info(). Str("Token Pool", pool.Address()). - Str("Chain selector", strconv.FormatUint(remoteChainSelector, 10)). + Uints64("Chain selectors", remoteChainSelectors). Str(Network, pool.client.GetNetworkConfig().Name). - Msg("Remote chain set on token pool") + Msg("Remote chains set on token pool") return pool.client.ProcessTransaction(tx) } @@ -905,13 +912,29 @@ func (a *MockAggregator) UpdateRoundData(answer *big.Int) error { Str("Contract Address", a.ContractAddress.Hex()). Str("Network Name", a.client.GetNetworkConfig().Name). Msg("Updating Round Data") - tx, err := a.Instance.UpdateRoundData(opts, big.NewInt(50), answer, big.NewInt(time.Now().UTC().UnixNano()), big.NewInt(time.Now().UTC().UnixNano())) + // we get the round from latest round data + // if there is any error in fetching the round , we set the round with a random number + // otherwise increase the latest round by 1 and set the value for the next round + round, err := a.Instance.LatestRound(nil) + if err != nil { + rand.Seed(uint64(time.Now().UnixNano())) + round = big.NewInt(rand.Int63n(2000)) + } + round = new(big.Int).Add(round, big.NewInt(1)) + tx, err := a.Instance.UpdateRoundData(opts, round, answer, big.NewInt(time.Now().UTC().UnixNano()), big.NewInt(time.Now().UTC().UnixNano())) if err != nil { return fmt.Errorf("unable to update round data: %w", err) } - return a.client.ProcessTransaction(tx) -} + log.Info(). + Str("Contract Address", a.ContractAddress.Hex()). + Str("Network Name", a.client.GetNetworkConfig().Name). + Str("Round", round.String()). + Str("Answer", answer.String()). + Msg("Updated Round Data") + _, err = bind.WaitMined(context.Background(), a.client.DeployBackend(), tx) + if err != nil { + return fmt.Errorf("error waiting for tx %s to be mined", tx.Hash().Hex()) + } -func (a *MockAggregator) WaitForTxConfirmations() error { - return a.client.WaitForEvents() + return a.client.MarkTxAsSentOnL2(tx) } diff --git a/integration-tests/ccip-tests/contracts/laneconfig/contracts.json b/integration-tests/ccip-tests/contracts/laneconfig/contracts.json index 622fca0c3d..4d20e2a4d5 100644 --- a/integration-tests/ccip-tests/contracts/laneconfig/contracts.json +++ b/integration-tests/ccip-tests/contracts/laneconfig/contracts.json @@ -238,432 +238,6 @@ "receiver_dapp": "0xAFa2c441a83bBCEDc2E8c5c6f66248aFD8b9af3d" } } - }, - "Arbitrum Sepolia": { - "fee_token": "0xb1D4538B4571d411F07960EF2838Ce337FE1E80E", - "bridge_tokens": [], - "bridge_tokens_pools": [], - "arm": "0x5EF7a726Fd21Fd9D77D34E3C56cfDD8691F7F0ac", - "router": "0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165", - "price_registry": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", - "wrapped_native": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", - "multicall": "0x13931F0642C06B121Bd195CBD292Cac0e65F661c", - "src_contracts": { - "Sepolia Testnet": { - "on_ramp": "0x4205E1Ca0202A248A5D42F5975A8FE56F3E302e9", - "deployed_at": 2155183 - } - }, - "dest_contracts": { - "Sepolia Testnet": { - "off_ramp": "0x1c71f141b4630EBE52d6aF4894812960abE207eB", - "commit_store": "0xaB0c8Ba51E7Fa3E5693a4Fbb39473520FD85d173", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } - }, - "Avalanche Fuji": { - "fee_token": "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846", - "bridge_tokens": [], - "bridge_tokens_pools": [], - "arm": "0x0ea0D7B2b78DD3A926fC76d6875a287F0AEB158F", - "router": "0xF694E193200268f9a4868e4Aa017A0118C9a8177", - "price_registry": "0x19e157E5fb1DAec1aE4BaB113fdf077F980704AA", - "wrapped_native": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", - "multicall": "0x88b9782c2b271cf3d016867454e493144098E8AA", - "src_contracts": { - "BSC Testnet": { - "on_ramp": "0xF25ECF1Aad9B2E43EDc2960cF66f325783245535", - "deployed_at": 27801953 - }, - "Base Goerli": { - "on_ramp": "0x51E8D6CE070d2548A82a43C6Ca13C59D550845fF", - "deployed_at": 27803512 - }, - "Optimism Goerli": { - "on_ramp": "0xeBB38E5F6637D539Ac7518EAf9EB4cF64c80C6B4", - "deployed_at": 27798411 - }, - "Polygon Mumbai": { - "on_ramp": "0xcA2C3196047FE0E31547B7214E5B7c49413fE9a8", - "deployed_at": 27802223 - }, - "Sepolia Testnet": { - "on_ramp": "0x5724B4Cc39a9690135F7273b44Dfd3BA6c0c69aD", - "deployed_at": 27797585 - } - }, - "dest_contracts": { - "BSC Testnet": { - "off_ramp": "0x10b28009E5D776F1f5AAA73941CE8953B8f42d26", - "commit_store": "0xacDD582F271eCF22FAd6764cCDe1c4a534b732A8", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Base Goerli": { - "off_ramp": "0x11eD4b25816110CC6ae1a91330A50Fd3A02fb399", - "commit_store": "0x2F7340969809d37389ab99AB351683f138499c30", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Optimism Goerli": { - "off_ramp": "0xF9c5Edb7FdEa5cF489860E4D7fD1a573d54ea30E", - "commit_store": "0x4F7A4B1125c35ffa1F91eF561287B701e9BC3947", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Polygon Mumbai": { - "off_ramp": "0x299E1DAFc6758aAA1930301966DAf3562e2002D3", - "commit_store": "0xc381a0C3e52FA85601B032C90DFE83105989006b", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Sepolia Testnet": { - "off_ramp": "0x9e5e4324F8608D54A50a317832d456a392E4F8C2", - "commit_store": "0x92A51eD3F041B39EbD1e464C1f7cb1e8f8A8c63f", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } - }, - "BSC Testnet": { - "fee_token": "0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06", - "bridge_tokens": [ - "0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06" - ], - "bridge_tokens_pools": [ - "0x8a710bBd77661D168D5A6725bD2E514ba1bFf59d" - ], - "arm": "0xF9a21B587111e7E8745Fb8b13750014f19DB0014", - "router": "0xE1053aE1857476f36A3C62580FF9b016E8EE8F6f", - "price_registry": "0xCCDf022c9d31DC26Ebab4FB92432724a5b79809a", - "wrapped_native": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", - "multicall": "0x4E63F845A1F42C318bC151cc2c9C7B2144b5dB86", - "src_contracts": { - "Avalanche Fuji": { - "on_ramp": "0xa2515683E99F50ADbE177519A46bb20FfdBaA5de", - "deployed_at": 35152179 - }, - "Base Goerli": { - "on_ramp": "0x8aD923981D87Db69b1B607d12b2A93Ed1d6F7d16", - "deployed_at": 35152980 - }, - "Polygon Mumbai": { - "on_ramp": "0x2C513cF3066Ff38F6a7549a652899EbA23184Bb6", - "deployed_at": 35152808 - }, - "Sepolia Testnet": { - "on_ramp": "0xB1DE44B04C00eaFe9915a3C07a0CaeA4410537dF", - "deployed_at": 35150427 - } - }, - "dest_contracts": { - "Avalanche Fuji": { - "off_ramp": "0x6e6fFCb6B4BED91ff0CC8C2e57EC029dA7DB80C2", - "commit_store": "0x38Bc38Bd824b6eE87571f9D3CFbe6D6E28E3Dc62", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Base Goerli": { - "off_ramp": "0x53BF5022CbBbabAba05CeFa9D496aD51561Be7C9", - "commit_store": "0x3101f22eF7aE6F3eC6b2A5a70935985efF5A1747", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Polygon Mumbai": { - "off_ramp": "0x7d7E4BE678d045d79Be323c6B567a6e6B09Df8f9", - "commit_store": "0xAdc949435DD968dd6e5D1b2e926f39d0aF3c5092", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Sepolia Testnet": { - "off_ramp": "0xf1c128Fe52Ea78CcAAB407509292E61ce38C1523", - "commit_store": "0x59dFD870dC4bd76A7B879A4f705Fdcd2595f85f9", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } - }, - "Base Goerli": { - "fee_token": "0xD886E2286Fd1073df82462ea1822119600Af80b6", - "bridge_tokens": [], - "bridge_tokens_pools": [], - "arm": "0xe5BD6BCb6fa8e5236984D5ea127dE5047f93B5fF", - "router": "0x80AF2F44ed0469018922c9F483dc5A909862fdc2", - "price_registry": "0x6d875a24c43974935b4F3D79AA6CD242BDfcD70F", - "wrapped_native": "0x4200000000000000000000000000000000000006", - "multicall": "0xa1b97250844B089160a2Cd8dabEaCc4843993f24", - "src_contracts": { - "Avalanche Fuji": { - "on_ramp": "0x2665A5aa729DBD384c804d0640f235D997698363", - "deployed_at": 12482142 - }, - "BSC Testnet": { - "on_ramp": "0x1F77755730fd9bee2B23aad921E56A06b28306aC", - "deployed_at": 12481595 - }, - "Optimism Goerli": { - "on_ramp": "0x4D2591F40a3f5DDe31ebb0F64782C0aD4cb0eC5e", - "deployed_at": 12481134 - }, - "Sepolia Testnet": { - "on_ramp": "0x6D1B700B5D398A471BdEf448B8E02Ba1ce6E9aE8", - "deployed_at": 12478177 - } - }, - "dest_contracts": { - "Avalanche Fuji": { - "off_ramp": "0x5e2f9989cEC66D9f7436B8076Bd4dCb3e6D88eE5", - "commit_store": "0xf0bc96283C3Fd73FdDa8cb86990833919542bC16", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "BSC Testnet": { - "off_ramp": "0xAa0a7426d506272B33eE54724788ADe3270d2973", - "commit_store": "0xb4f787D7857ac29e8d2d0784F4f15FE8d5A7CC3e", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Optimism Goerli": { - "off_ramp": "0xD0B9920270B36c59EdECc694359274BF980aADE9", - "commit_store": "0x88330721106063CD54A39113568EF4380f26e252", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Sepolia Testnet": { - "off_ramp": "0xf6483C7327d59324764bF4a03D18F93E03cC2914", - "commit_store": "0xE7282599C2f70D020ED1aDd1802d5b0Bb183A2D6", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } - }, - "Kroma Sepolia": { - "fee_token": "0xa75cCA5b404ec6F4BB6EC4853D177FE7057085c8", - "bridge_tokens": [ - "0xa75cCA5b404ec6F4BB6EC4853D177FE7057085c8" - ], - "bridge_tokens_pools": [ - "0xd83437e1085C5410B9a95018599738875C581F67" - ], - "arm": "0x1E4e4e0d6f6631A45C616F71a1A5cF208DB9eCDe", - "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", - "price_registry": "0xa1ed3A3aA29166C9c8448654A8cA6b7916BC8379", - "wrapped_native": "0x4200000000000000000000000000000000000001", - "multicall": "0x260AF9b83e0d2Bb6C9015fC9f0BfF8858A0CCE68", - "src_contracts": { - "WeMix Testnet": { - "on_ramp": "0x6ea155Fc77566D9dcE01B8aa5D7968665dc4f0C5", - "deployed_at": 5632955 - } - }, - "dest_contracts": { - "WeMix Testnet": { - "off_ramp": "0xB602B6E5Caf08ac0C920EAE585aed100a8cF6f3B", - "commit_store": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } - }, - "Optimism Goerli": { - "fee_token": "0xdc2CC710e42857672E7907CF474a69B63B93089f", - "bridge_tokens": [], - "bridge_tokens_pools": [], - "arm": "0xEAf6968faB9C54aC31c3679F120705b5019d3546", - "router": "0xcc5a0B910D9E9504A7561934bed294c51285a78D", - "price_registry": "0x9fdF51832A473D874057a2fcE768F0bc1c0F6F75", - "wrapped_native": "0x4200000000000000000000000000000000000006", - "multicall": "0xA98DFA0e11e5B63C6fcbaEA5Fff49CfDf5765950", - "src_contracts": { - "Avalanche Fuji": { - "on_ramp": "0xE7234D8d807D61821A6bB90735Aa238E229cF167", - "deployed_at": 17359567 - }, - "Base Goerli": { - "on_ramp": "0xb82F98e2791D3d0Ab9732a7e824de20A223274e0", - "deployed_at": 17363908 - }, - "Polygon Mumbai": { - "on_ramp": "0x4a878289A152d2c02a2b8Ae90476003faf1FE74a", - "deployed_at": 17364632 - }, - "Sepolia Testnet": { - "on_ramp": "0x38F0d1749d2241fAC6E166C5264246F6E44Fd8D6", - "deployed_at": 17359244 - } - }, - "dest_contracts": { - "Avalanche Fuji": { - "off_ramp": "0xb4a1a2F0CA192D8f01437217C4b3d66616Eb5fb3", - "commit_store": "0x2ABAAf1A6442FB92027f03dfEf1101045aFc72E5", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Base Goerli": { - "off_ramp": "0x665e69fC1B056d8EFCEa5C2c2c363e8B267182C0", - "commit_store": "0x040067BE68e6E5d545FdD200A4ccC1FD2Db28CEF", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Polygon Mumbai": { - "off_ramp": "0xFFfB7132D25A8A3357A48Ff6F09E0ff9501E63D2", - "commit_store": "0x8D783D541f60A10D8073AD0b7e4A849707868d23", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Sepolia Testnet": { - "off_ramp": "0x74914c2d7D6B2dA72B831Fca7FB11c79194BC5E5", - "commit_store": "0xbC9c70284E1f41A941eF8dA6bA7f01658f3341e1", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } - }, - "Polygon Mumbai": { - "fee_token": "0x326C977E6efc84E512bB9C30f76E30c160eD06FB", - "bridge_tokens": [], - "bridge_tokens_pools": [], - "arm": "0x917A6913f785094f8B06785aa8a884f922A650d8", - "router": "0x1035CabC275068e0F4b745A29CEDf38E13aF41b1", - "price_registry": "0x6322B0AD5467C12fc11393D479b0452C7a55aD81", - "wrapped_native": "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889", - "multicall": "0xE8C13Eee68212cAD0eCD4E02ca1D34776296Fd27", - "src_contracts": { - "Avalanche Fuji": { - "on_ramp": "0x5e0AD6D742983Ca464Fef0c28fD2D788a320B1c3", - "deployed_at": 42477026 - }, - "BSC Testnet": { - "on_ramp": "0x971890dCcBe45D766Db8AB16CB76EDCDAB5bd70c", - "deployed_at": 42477607 - }, - "Optimism Goerli": { - "on_ramp": "0xCbe286E428FfF4c68Eadb056B2951B859446a0C0", - "deployed_at": 42478097 - }, - "Sepolia Testnet": { - "on_ramp": "0x70c04331fd35fC21DB54F64E0e77A5E423619e04", - "deployed_at": 42473803 - } - }, - "dest_contracts": { - "Avalanche Fuji": { - "off_ramp": "0x557Da53FdAe16B76142584516F3944fF9c9E299a", - "commit_store": "0xF2F6864F281e21D713F51CB7Cf3FB97384383C83", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "BSC Testnet": { - "off_ramp": "0xfcBaED431638aA070bF7E8FE1155d699140295e2", - "commit_store": "0x31252bf1d27a53347deFe4dA8442d61E1496F012", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Optimism Goerli": { - "off_ramp": "0xA6CAba8E99Dad2B2697B9cd262615F21E56c613D", - "commit_store": "0x6EE39f3092a2AE1E463760675C671b9E4435248c", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Sepolia Testnet": { - "off_ramp": "0xAC1456bafcc3403eb6e34f08FD2945cd7B3C8F0A", - "commit_store": "0x5BA71CcD924c91f58216BA633f7966f3AB2113ae", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } - }, - "Sepolia Testnet": { - "fee_token": "0x779877A7B0D9E8603169DdbD7836e478b4624789", - "bridge_tokens": [], - "bridge_tokens_pools": [], - "arm": "0xB4d360459F32Dd641Ef5A6985fFbAC5c4e5521aA", - "router": "0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59", - "price_registry": "0x9EF7D57a4ea30b9e37794E55b0C75F2A70275dCc", - "wrapped_native": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", - "multicall": "0x21CA8aC8df982eC644E7623463adAD52F993C5Aa", - "src_contracts": { - "Arbitrum Sepolia": { - "on_ramp": "0xe4Dd3B16E09c016402585a8aDFdB4A18f772a07e", - "deployed_at": 4834090 - }, - "Avalanche Fuji": { - "on_ramp": "0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4", - "deployed_at": 4699243 - }, - "BSC Testnet": { - "on_ramp": "0xD990f8aFA5BCB02f95eEd88ecB7C68f5998bD618", - "deployed_at": 4699243 - }, - "Base Goerli": { - "on_ramp": "0x66205151F6289eC93559C9437A2bEE7EdC2660A0", - "deployed_at": 4699243 - }, - "Optimism Goerli": { - "on_ramp": "0x897146677c63B52c645D827d4bD00fe0B927f60b", - "deployed_at": 4705792 - }, - "Polygon Mumbai": { - "on_ramp": "0x81660Dc846f0528A7Ce003c1F7774d7c4135F344", - "deployed_at": 4699243 - }, - "WeMix Testnet": { - "on_ramp": "0xedFc22336Eb0B9B11Ff37C07777db27BCcDe3C65", - "deployed_at": 4699243 - } - }, - "dest_contracts": { - "Arbitrum Sepolia": { - "off_ramp": "0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1", - "commit_store": "0x93Ff9Dd39Dc01eac1fc4d2c9211D95Ee458CAB94", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Avalanche Fuji": { - "off_ramp": "0x000b26f604eAadC3D874a4404bde6D64a97d95ca", - "commit_store": "0x2dD9273F8208B8393350508131270A6574A69784", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "BSC Testnet": { - "off_ramp": "0xdE2d8E126e08d675fCD7fFa5a6CE49925f3Dc692", - "commit_store": "0x0050ac355a82caB31194507f94174297bf0655A7", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Base Goerli": { - "off_ramp": "0x1dB714ECcA75053194f172ab39225E939EC735F4", - "commit_store": "0x92dC24ba33A2Ac8A5Af8D0aaAd1Bc97f69C416DC", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Optimism Goerli": { - "off_ramp": "0xc1091777000F1900e2559b720468ECc9c0c8646a", - "commit_store": "0x2a23738F6420bd9784AcE92296183B86F837AA08", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Polygon Mumbai": { - "off_ramp": "0x86C5A8A7Fb5ecf30cf2B42e2D1D74D0E9ce2F9A1", - "commit_store": "0x8fbCaBe31f6aaD1010634e1dCf8bB0A254ccD3a4", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "WeMix Testnet": { - "off_ramp": "0x46b639a3C1a4CBfD326b94a2dB7415c27157282f", - "commit_store": "0x7b74554678816b045c1e7409327E086bD436aa46", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } - }, - "WeMix Testnet": { - "fee_token": "0x3580c7A817cCD41f7e02143BFa411D4EeAE78093", - "bridge_tokens": [ - "0x3580c7A817cCD41f7e02143BFa411D4EeAE78093" - ], - "bridge_tokens_pools": [ - "0x86d1fED29fD3276B2413b2bE5f074581B0176b8a" - ], - "arm": "0x46fF31494651593973D9b38a872ED5B06f45A693", - "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", - "price_registry": "0x89D17571DB7C9540eeB36760E3c749C8fb984569", - "wrapped_native": "0xbE3686643c05f00eC46e73da594c78098F7a9Ae7", - "multicall": "0x8A06DB818EED9e37c712Fc00b30975b5941Df1a0", - "src_contracts": { - "Kroma Sepolia": { - "on_ramp": "0x428C4dc89b6Bf908B82d77C9CBceA786ea8cc7D0", - "deployed_at": 42001145 - }, - "Sepolia Testnet": { - "on_ramp": "0x4d57C6d8037C65fa66D6231844785a428310a735", - "deployed_at": 42006227 - } - }, - "dest_contracts": { - "Kroma Sepolia": { - "off_ramp": "0xD685D2d224dd6D0Db2D56497db6270D77D9a7966", - "commit_store": "0x7e062D6880779a0347e7742058C1b1Ee4AA0B137", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - }, - "Sepolia Testnet": { - "off_ramp": "0x8AB103843ED9D28D2C5DAf5FdB9c3e1CE2B6c876", - "commit_store": "0x7d5297c5506ee2A7Ef121Da9bE02b6a6AD30b392", - "receiver_dapp": "0xda9e8e71bB750a996Af33ebB8aBb18cd9EB9DC75" - } - } } } } diff --git a/integration-tests/ccip-tests/contracts/laneconfig/parse_contracts.go b/integration-tests/ccip-tests/contracts/laneconfig/parse_contracts.go index 0f8d6f05ba..73a80573bc 100644 --- a/integration-tests/ccip-tests/contracts/laneconfig/parse_contracts.go +++ b/integration-tests/ccip-tests/contracts/laneconfig/parse_contracts.go @@ -18,18 +18,19 @@ var ( ) type CommonContracts struct { - IsNativeFeeToken bool `json:"is_native_fee_token,omitempty"` - IsMockARM bool `json:"is_mock_arm,omitempty"` - FeeToken string `json:"fee_token"` - BridgeTokens []string `json:"bridge_tokens"` - BridgeTokenPools []string `json:"bridge_tokens_pools"` - ARM string `json:"arm"` - Router string `json:"router"` - PriceRegistry string `json:"price_registry"` - WrappedNative string `json:"wrapped_native"` - Multicall string `json:"multicall,omitempty"` - TokenTransmitter string `json:"token_transmitter,omitempty"` - TokenMessenger string `json:"token_messenger,omitempty"` + IsNativeFeeToken bool `json:"is_native_fee_token,omitempty"` + IsMockARM bool `json:"is_mock_arm,omitempty"` + FeeToken string `json:"fee_token"` + BridgeTokens []string `json:"bridge_tokens"` + BridgeTokenPools []string `json:"bridge_tokens_pools"` + PriceAggregators map[string]string `json:"price_aggregators"` + ARM string `json:"arm"` + Router string `json:"router"` + PriceRegistry string `json:"price_registry"` + WrappedNative string `json:"wrapped_native"` + Multicall string `json:"multicall,omitempty"` + TokenTransmitter string `json:"token_transmitter,omitempty"` + TokenMessenger string `json:"token_messenger,omitempty"` } type SourceContracts struct { diff --git a/integration-tests/ccip-tests/load/ccip_loadgen.go b/integration-tests/ccip-tests/load/ccip_loadgen.go index 2c7ce16b83..714478c86f 100644 --- a/integration-tests/ccip-tests/load/ccip_loadgen.go +++ b/integration-tests/ccip-tests/load/ccip_loadgen.go @@ -19,7 +19,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/atomic" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" @@ -29,27 +29,56 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testreporters" ) +// CCIPLaneOptimized is a light-weight version of CCIPLane, It only contains elements which are used during load triggering and validation +type CCIPLaneOptimized struct { + Logger zerolog.Logger + SourceNetworkName string + DestNetworkName string + Source *actions.SourceCCIPModule + Dest *actions.DestCCIPModule + Reports *testreporters.CCIPLaneStats +} + type CCIPE2ELoad struct { - t *testing.T - Lane *actions.CCIPLane - NoOfReq int64 // approx no of Request fired - CurrentMsgSerialNo *atomic.Int64 // current msg serial number in the load sequence - CallTimeOut time.Duration // max time to wait for various on-chain events - msg router.ClientEVM2AnyMessage - MaxDataBytes uint32 - SendMaxDataIntermittently bool - LastFinalizedTxBlock atomic.Uint64 - LastFinalizedTimestamp atomic.Time + t *testing.T + Lane *CCIPLaneOptimized + NoOfReq int64 // approx no of Request fired + CurrentMsgSerialNo *atomic.Int64 // current msg serial number in the load sequence + CallTimeOut time.Duration // max time to wait for various on-chain events + msg router.ClientEVM2AnyMessage + MaxDataBytes uint32 + SendMaxDataIntermittentlyInMsgCount int64 + SkipRequestIfAnotherRequestTriggeredWithin *config.Duration + LastFinalizedTxBlock atomic.Uint64 + LastFinalizedTimestamp atomic.Time } -func NewCCIPLoad(t *testing.T, lane *actions.CCIPLane, timeout time.Duration, noOfReq int64) *CCIPE2ELoad { +func NewCCIPLoad( + t *testing.T, + lane *actions.CCIPLane, + timeout time.Duration, + noOfReq int64, + sendMaxDataIntermittentlyInEveryMsgCount int64, + SkipRequestIfAnotherRequestTriggeredWithin *config.Duration, +) *CCIPE2ELoad { + // to avoid holding extra data + loadLane := &CCIPLaneOptimized{ + Logger: lane.Logger, + SourceNetworkName: lane.SourceNetworkName, + DestNetworkName: lane.DestNetworkName, + Source: lane.Source, + Dest: lane.Dest, + Reports: lane.Reports, + } + return &CCIPE2ELoad{ - t: t, - Lane: lane, - CurrentMsgSerialNo: atomic.NewInt64(1), - CallTimeOut: timeout, - NoOfReq: noOfReq, - SendMaxDataIntermittently: true, + t: t, + Lane: loadLane, + CurrentMsgSerialNo: atomic.NewInt64(1), + CallTimeOut: timeout, + NoOfReq: noOfReq, + SendMaxDataIntermittentlyInMsgCount: sendMaxDataIntermittentlyInEveryMsgCount, + SkipRequestIfAnotherRequestTriggeredWithin: SkipRequestIfAnotherRequestTriggeredWithin, } } @@ -61,7 +90,11 @@ func (c *CCIPE2ELoad) BeforeAllCall(msgType string, gasLimit *big.Int) { destCCIP := c.Lane.Dest var tokenAndAmounts []router.ClientEVMTokenAmount for i := range c.Lane.Source.TransferAmount { - token := sourceCCIP.Common.BridgeTokens[i] + // if length of sourceCCIP.TransferAmount is more than available bridge token use first bridge token + token := sourceCCIP.Common.BridgeTokens[0] + if i < len(sourceCCIP.Common.BridgeTokens) { + token = sourceCCIP.Common.BridgeTokens[i] + } tokenAndAmounts = append(tokenAndAmounts, router.ClientEVMTokenAmount{ Token: common.HexToAddress(token.Address()), Amount: c.Lane.Source.TransferAmount[i], }) @@ -84,7 +117,7 @@ func (c *CCIPE2ELoad) BeforeAllCall(msgType string, gasLimit *big.Int) { if msgType == actions.TokenTransfer { c.msg.TokenAmounts = tokenAndAmounts } - if c.SendMaxDataIntermittently { + if c.SendMaxDataIntermittentlyInMsgCount > 0 { dCfg, err := sourceCCIP.OnRamp.Instance.GetDynamicConfig(nil) require.NoError(c.t, err, "failed to fetch dynamic config") c.MaxDataBytes = dCfg.MaxDataBytes @@ -92,7 +125,11 @@ func (c *CCIPE2ELoad) BeforeAllCall(msgType string, gasLimit *big.Int) { // if the msg is sent via multicall, transfer the token transfer amount to multicall contract if sourceCCIP.Common.MulticallEnabled && sourceCCIP.Common.MulticallContract != (common.Address{}) { for i, amount := range sourceCCIP.TransferAmount { - token := sourceCCIP.Common.BridgeTokens[i] + // if length of sourceCCIP.TransferAmount is more than available bridge token use first bridge token + token := sourceCCIP.Common.BridgeTokens[0] + if i < len(sourceCCIP.Common.BridgeTokens) { + token = sourceCCIP.Common.BridgeTokens[i] + } amountToApprove := new(big.Int).Mul(amount, big.NewInt(c.NoOfReq)) bal, err := token.BalanceOf(context.Background(), sourceCCIP.Common.MulticallContract.Hex()) require.NoError(c.t, err, "Failed to get token balance") @@ -102,6 +139,13 @@ func (c *CCIPE2ELoad) BeforeAllCall(msgType string, gasLimit *big.Int) { } } } + // if it's not multicall set the tokens to nil to free up some space, + // we have already formed the msg to be sent in load, there is no need to store the bridge tokens anymore + // In case of multicall we still need the BridgeTokens to transfer amount from mutlicall to owner + if !sourceCCIP.Common.MulticallEnabled { + sourceCCIP.Common.BridgeTokens = nil + destCCIP.Common.BridgeTokens = nil + } // wait for any pending txs before moving on err = sourceCCIP.Common.ChainClient.WaitForEvents() require.NoError(c.t, err, "Failed to wait for events") @@ -112,19 +156,6 @@ func (c *CCIPE2ELoad) BeforeAllCall(msgType string, gasLimit *big.Int) { sourceCCIP.Common.ChainClient.ParallelTransactions(false) destCCIP.Common.ChainClient.ParallelTransactions(false) - // close all header subscriptions for dest chains - queuedEvents := destCCIP.Common.ChainClient.GetHeaderSubscriptions() - for subName := range queuedEvents { - destCCIP.Common.ChainClient.DeleteHeaderEventSubscription(subName) - } - // close all header subscriptions for source chains except for finalized header - queuedEvents = sourceCCIP.Common.ChainClient.GetHeaderSubscriptions() - for subName := range queuedEvents { - if subName == blockchain.FinalizedHeaderKey { - continue - } - sourceCCIP.Common.ChainClient.DeleteHeaderEventSubscription(subName) - } } func (c *CCIPE2ELoad) CCIPMsg() (router.ClientEVM2AnyMessage, *testreporters.RequestStat) { @@ -134,9 +165,9 @@ func (c *CCIPE2ELoad) CCIPMsg() (router.ClientEVM2AnyMessage, *testreporters.Req stats := testreporters.NewCCIPRequestStats(msgSerialNo, c.Lane.SourceNetworkName, c.Lane.DestNetworkName) // form the message for transfer msgStr := fmt.Sprintf("new message with Id %d", msgSerialNo) - if c.SendMaxDataIntermittently { - // every 100th message will have extra data with almost MaxDataBytes - if msgSerialNo%100 == 0 { + if c.SendMaxDataIntermittentlyInMsgCount > 0 { + // every SendMaxDataIntermittentlyInMsgCount message will have extra data with almost MaxDataBytes + if msgSerialNo%c.SendMaxDataIntermittentlyInMsgCount == 0 { length := c.MaxDataBytes - 1 b := make([]byte, c.MaxDataBytes-1) _, err := crypto_rand.Read(b) @@ -155,12 +186,18 @@ func (c *CCIPE2ELoad) CCIPMsg() (router.ClientEVM2AnyMessage, *testreporters.Req func (c *CCIPE2ELoad) Call(_ *wasp.Generator) *wasp.Response { res := &wasp.Response{} sourceCCIP := c.Lane.Source - + recentRequestFoundAt := sourceCCIP.IsRequestTriggeredWithinTimeframe(c.SkipRequestIfAnotherRequestTriggeredWithin) + if recentRequestFoundAt != nil { + c.Lane.Logger. + Info(). + Str("Found At=", recentRequestFoundAt.String()). + Msgf("Skipping ...Another Request found within given timeframe %s", c.SkipRequestIfAnotherRequestTriggeredWithin.String()) + return res + } msg, stats := c.CCIPMsg() msgSerialNo := stats.ReqNo lggr := c.Lane.Logger.With().Int64("msg Number", stats.ReqNo).Logger() - defer c.Lane.Reports.UpdatePhaseStatsForReq(stats) feeToken := sourceCCIP.Common.FeeToken.EthAddress // initiate the transfer lggr.Debug().Str("triggeredAt", time.Now().GoString()).Msg("triggering transfer") @@ -186,7 +223,8 @@ func (c *CCIPE2ELoad) Call(_ *wasp.Generator) *wasp.Response { if feeToken != common.HexToAddress("0x0") { sendTx, err = sourceCCIP.Common.Router.CCIPSend(destChainSelector, msg, nil) } else { - sendTx, err = sourceCCIP.Common.Router.CCIPSend(destChainSelector, msg, fee) + // add a bit buffer to fee + sendTx, err = sourceCCIP.Common.Router.CCIPSend(destChainSelector, msg, new(big.Int).Add(big.NewInt(1e2), fee)) } if err != nil { stats.UpdateState(lggr, 0, testreporters.TX, time.Since(startTime), testreporters.Failure) diff --git a/integration-tests/ccip-tests/load/ccip_multicall_loadgen.go b/integration-tests/ccip-tests/load/ccip_multicall_loadgen.go index 7aec7b7c6f..0f6045abf1 100644 --- a/integration-tests/ccip-tests/load/ccip_multicall_loadgen.go +++ b/integration-tests/ccip-tests/load/ccip_multicall_loadgen.go @@ -49,20 +49,20 @@ type MultiCallReturnValues struct { func NewMultiCallLoadGenerator(testCfg *testsetups.CCIPTestConfig, lanes []*actions.CCIPLane, noOfRequestsPerUnitTime int64, labels map[string]string) (*CCIPMultiCallLoadGenerator, error) { // check if all lanes are from same network - source := lanes[0].SourceChain.GetChainID() - multiCall := lanes[0].SrcNetworkLaneCfg.Multicall + source := lanes[0].Source.Common.ChainClient.GetChainID() + multiCall := lanes[0].Source.Common.MulticallContract.Hex() if multiCall == "" { return nil, fmt.Errorf("multicall address cannot be empty") } for i := 1; i < len(lanes); i++ { - if source.String() != lanes[i].SourceChain.GetChainID().String() { - return nil, fmt.Errorf("all lanes should be from same network; expected %s, got %s", source, lanes[i].SourceChain.GetChainID()) + if source.String() != lanes[i].Source.Common.ChainClient.GetChainID().String() { + return nil, fmt.Errorf("all lanes should be from same network; expected %s, got %s", source, lanes[i].Source.Common.ChainClient.GetChainID()) } - if lanes[i].SrcNetworkLaneCfg.Multicall != multiCall { + if lanes[i].Source.Common.MulticallContract.Hex() != multiCall { return nil, fmt.Errorf("multicall address should be same for all lanes") } } - client := lanes[0].SourceChain + client := lanes[0].Source.Common.ChainClient lggr := logging.GetTestLogger(testCfg.Test).With().Str("Source Network", client.GetNetworkName()).Logger() ls := wasp.LabelsMapToModel(labels) if err := ls.Validate(); err != nil { @@ -86,10 +86,13 @@ func NewMultiCallLoadGenerator(testCfg *testsetups.CCIPTestConfig, lanes []*acti Done: make(chan struct{}), } for _, lane := range lanes { - ccipLoad := NewCCIPLoad(testCfg.Test, lane, testCfg.TestGroupInput.PhaseTimeout.Duration(), 100000) // for multicall load generator, we don't want to send max data intermittently, it might // cause oversized data for multicall - ccipLoad.SendMaxDataIntermittently = false + ccipLoad := NewCCIPLoad( + testCfg.Test, lane, testCfg.TestGroupInput.PhaseTimeout.Duration(), + 100000, 0, + testCfg.TestGroupInput.SkipRequestIfAnotherRequestTriggeredWithin, + ) ccipLoad.BeforeAllCall(testCfg.TestGroupInput.MsgType, big.NewInt(*testCfg.TestGroupInput.DestGasLimit)) m.E2ELoads[fmt.Sprintf("%s-%s", lane.SourceNetworkName, lane.DestNetworkName)] = ccipLoad } @@ -104,7 +107,12 @@ func (m *CCIPMultiCallLoadGenerator) Stop() error { var tokens []*contracts.ERC20Token for _, e2eLoad := range m.E2ELoads { for i := range e2eLoad.Lane.Source.TransferAmount { - if _, ok := tokenMap[e2eLoad.Lane.Source.Common.BridgeTokens[i].Address()]; !ok { + // if length of sourceCCIP.TransferAmount is more than available bridge token use first bridge token + token := e2eLoad.Lane.Source.Common.BridgeTokens[0] + if i < len(e2eLoad.Lane.Source.Common.BridgeTokens) { + token = e2eLoad.Lane.Source.Common.BridgeTokens[i] + } + if _, ok := tokenMap[token.Address()]; !ok { tokens = append(tokens, e2eLoad.Lane.Source.Common.BridgeTokens[i]) } } @@ -211,7 +219,7 @@ func (m *CCIPMultiCallLoadGenerator) Call(_ *wasp.Generator) *wasp.Response { return res } - lggr = lggr.With().Str("Source Network", c.Lane.SourceChain.GetNetworkName()).Str("Dest Network", c.Lane.DestChain.GetNetworkName()).Logger() + lggr = lggr.With().Str("Source Network", c.Lane.Source.Common.ChainClient.GetNetworkName()).Str("Dest Network", c.Lane.Dest.Common.ChainClient.GetNetworkName()).Logger() stats := rValues.Stats txConfirmationTime := txConfirmationTime sendTx := sendTx diff --git a/integration-tests/ccip-tests/load/ccip_test.go b/integration-tests/ccip-tests/load/ccip_test.go index 1ee3995696..309a3ee155 100644 --- a/integration-tests/ccip-tests/load/ccip_test.go +++ b/integration-tests/ccip-tests/load/ccip_test.go @@ -295,3 +295,30 @@ func TestLoadCCIPStableWithPodChaosDiffCommitAndExec(t *testing.T) { }) } } + +// TestLoadCCIPStableRPSAfterARMCurseAndUncurse validates that after ARM curse is lifted +// all pending requests get delivered. +// The test pauses loadgen while ARM is cursed and resumes it when curse is lifted. +// There is a known limitation of this test - if the test is run on remote-runner with high frequency +// the remote-runner pod gets evicted after the loadgen is resumed. +// The recommended frequency for this test 2req/min +func TestLoadCCIPStableRPSAfterARMCurseAndUncurse(t *testing.T) { + t.Parallel() + lggr := logging.GetTestLogger(t) + testArgs := NewLoadArgs(t, lggr) + testArgs.Setup() + // if the test runs on remote runner + if len(testArgs.TestSetupArgs.Lanes) == 0 { + return + } + t.Cleanup(func() { + log.Info().Msg("Tearing down the environment") + require.NoError(t, testArgs.TestSetupArgs.TearDown()) + }) + testArgs.TriggerLoadByLane() + // wait for certain time so that few messages are sent + time.Sleep(2 * time.Minute) + // now validate the curse + testArgs.ValidateCurseFollowedByUncurse() + testArgs.Wait() +} diff --git a/integration-tests/ccip-tests/load/helper.go b/integration-tests/ccip-tests/load/helper.go index 65a289b40c..82811c8394 100644 --- a/integration-tests/ccip-tests/load/helper.go +++ b/integration-tests/ccip-tests/load/helper.go @@ -1,6 +1,7 @@ package load import ( + "context" "fmt" "math" "math/big" @@ -10,17 +11,18 @@ import ( "github.com/AlekSi/pointer" "github.com/rs/zerolog" + "github.com/smartcontractkit/wasp" "github.com/stretchr/testify/require" + "go.uber.org/atomic" "golang.org/x/sync/errgroup" - "github.com/smartcontractkit/wasp" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" ) type ChaosConfig struct { @@ -32,6 +34,7 @@ type ChaosConfig struct { type LoadArgs struct { t *testing.T + Ctx context.Context lggr zerolog.Logger schedules []*wasp.Segment RunnerWg *errgroup.Group // to wait on individual load generators run @@ -41,6 +44,7 @@ type LoadArgs struct { ChaosExps []ChaosConfig LoadgenTearDowns []func() Labels map[string]string + pauseLoad *atomic.Bool } func (l *LoadArgs) SetReportParams() { @@ -71,7 +75,6 @@ func (l *LoadArgs) Setup() { } l.Labels = map[string]string{ "test_group": "load", - "cluster": "sdlc", "test_id": "ccip", "namespace": namespace, } @@ -111,6 +114,115 @@ func (l *LoadArgs) SanityCheck() { } } +// ValidateCurseFollowedByUncurse assumes the lanes under test are bi-directional. +// It assumes requests in both direction are in flight when this is called. +// It assumes the ARM is not already cursed, it will fail the test if it is in cursed state. +// It curses source ARM for forward lanes so that destination curse is also validated for reverse lanes. +// It waits for 2 minutes for curse to be seen by ccip plugins and contracts. +// It captures the curse timestamp to verify no execution state changed event is emitted after the cure is applied. +// It uncurses the source ARM at the end so that it can be verified that rest of the requests are processed as expected. +// Validates that even after uncursing the lane should not function for 30 more minutes. +func (l *LoadArgs) ValidateCurseFollowedByUncurse() { + var lanes []*actions.CCIPLane + for _, lane := range l.TestSetupArgs.Lanes { + lanes = append(lanes, lane.ForwardLane) + } + // check if source is already cursed + for _, lane := range lanes { + cursed, err := lane.Source.Common.IsCursed() + require.NoError(l.t, err, "cannot get cursed state") + if cursed { + require.Fail(l.t, "test will not work if ARM is already cursed") + } + } + // before cursing set pause + l.pauseLoad.Store(true) + // wait for some time for pause to be active in wasp + l.lggr.Info().Msg("Waiting for 1 minute after applying pause on load") + time.Sleep(1 * time.Minute) + curseTimeStamps := make(map[string]time.Time) + for _, lane := range lanes { + if _, exists := curseTimeStamps[lane.SourceNetworkName]; exists { + continue + } + curseTx, err := lane.Source.Common.CurseARM() + require.NoError(l.t, err, "error in cursing arm") + require.NotNil(l.t, curseTx, "invalid cursetx") + receipt, err := lane.Source.Common.ChainClient.GetTxReceipt(curseTx.Hash()) + require.NoError(l.t, err) + hdr, err := lane.Source.Common.ChainClient.HeaderByNumber(context.Background(), receipt.BlockNumber) + require.NoError(l.t, err) + curseTimeStamps[lane.SourceNetworkName] = hdr.Timestamp + l.lggr.Info().Str("Source", lane.SourceNetworkName).Msg("Curse is applied on source") + l.lggr.Info().Str("Destination", lane.SourceNetworkName).Msg("Curse is applied on destination") + } + + l.lggr.Info().Msg("Curse is applied on all lanes. Waiting for 2 minutes") + time.Sleep(2 * time.Minute) + + for _, lane := range lanes { + // try to send requests on lanes on which curse is applied on source RMN and the request should revert + failedTx, _, _, err := lane.Source.SendRequest( + lane.Dest.ReceiverDapp.EthAddress, + actions.DataOnlyTransfer, "msg sent when ARM is cursed", + big.NewInt(600_000), // gas limit + ) + if lane.Source.Common.ChainClient.GetNetworkConfig().MinimumConfirmations > 0 { + require.Error(l.t, err) + } else { + require.NoError(l.t, err) + } + errReason, v, err := lane.Source.Common.ChainClient.RevertReasonFromTx(failedTx, router.RouterABI) + require.NoError(l.t, err) + require.Equal(l.t, "BadARMSignal", errReason) + lane.Logger.Info(). + Str("Revert Reason", errReason). + Interface("Args", v). + Str("FailedTx", failedTx.Hex()). + Msg("Msg sent while source ARM is cursed") + } + + // now uncurse all + for _, lane := range lanes { + require.NoError(l.t, lane.Source.Common.UnvoteToCurseARM(), "error to unvote in cursing arm") + } + l.lggr.Info().Msg("Curse is lifted on all lanes") + // lift the pause on load test + l.pauseLoad.Store(false) + + // now add the reverse lanes so that destination curse is also verified + // we add the reverse lanes now to verify absence of commit and execution for the reverse lanes + for _, lane := range l.TestSetupArgs.Lanes { + lanes = append(lanes, lane.ReverseLane) + } + + // verify that even after uncursing the lane should not function for 30 more minutes, + // i.e no execution state changed or commit report accepted event is generated + errGrp := &errgroup.Group{} + for _, lane := range lanes { + lane := lane + curseTimeStamp, exists := curseTimeStamps[lane.SourceNetworkName] + // if curse timestamp does not exist for source, it will exist for destination + if !exists { + curseTimeStamp, exists = curseTimeStamps[lane.DestNetworkName] + require.Truef(l.t, exists, "did not find curse time stamp for lane %s->%s", lane.SourceNetworkName, lane.DestNetworkName) + } + errGrp.Go(func() error { + lane.Logger.Info().Msg("Validating no CommitReportAccepted event is received for 29 minutes") + // we allow additional 1 minute after curse timestamp for curse to be visible by plugin + return lane.Dest.AssertNoReportAcceptedEventReceived(lane.Logger, 25*time.Minute, curseTimeStamp.Add(1*time.Minute)) + }) + errGrp.Go(func() error { + lane.Logger.Info().Msg("Validating no ExecutionStateChanged event is received for 25 minutes") + // we allow additional 1 minute after curse timestamp for curse to be visible by plugin + return lane.Dest.AssertNoExecutionStateChangedEventReceived(lane.Logger, 25*time.Minute, curseTimeStamp.Add(1*time.Minute)) + }) + } + l.lggr.Info().Msg("waiting for no commit/execution validation") + err := errGrp.Wait() + require.NoError(l.t, err, "error received to validate no commit/execution is generated after lane is cursed") +} + func (l *LoadArgs) TriggerLoadByLane() { l.setSchedule() l.TestSetupArgs.Reporter.SetDuration(l.TestCfg.TestGroupInput.TestDuration.Duration()) @@ -121,8 +233,12 @@ func (l *LoadArgs) TriggerLoadByLane() { Str("Source Network", lane.SourceNetworkName). Str("Destination Network", lane.DestNetworkName). Msg("Starting load for lane") - - ccipLoad := NewCCIPLoad(l.TestCfg.Test, lane, l.TestCfg.TestGroupInput.PhaseTimeout.Duration(), 100000) + sendMaxData := pointer.GetInt64(l.TestCfg.TestGroupInput.SendMaxDataInEveryMsgCount) + ccipLoad := NewCCIPLoad( + l.TestCfg.Test, lane, l.TestCfg.TestGroupInput.PhaseTimeout.Duration(), + 100000, sendMaxData, + l.TestCfg.TestGroupInput.SkipRequestIfAnotherRequestTriggeredWithin, + ) ccipLoad.BeforeAllCall(l.TestCfg.TestGroupInput.MsgType, big.NewInt(*l.TestCfg.TestGroupInput.DestGasLimit)) lokiConfig := l.TestCfg.EnvInput.Logging.Loki labels := make(map[string]string) @@ -131,7 +247,7 @@ func (l *LoadArgs) TriggerLoadByLane() { } labels["source_chain"] = lane.SourceNetworkName labels["dest_chain"] = lane.DestNetworkName - loadRunner, err := wasp.NewGenerator(&wasp.Config{ + waspCfg := &wasp.Config{ T: l.TestCfg.Test, GenName: fmt.Sprintf("lane %s-> %s", lane.SourceNetworkName, lane.DestNetworkName), Schedule: l.schedules, @@ -144,8 +260,10 @@ func (l *LoadArgs) TriggerLoadByLane() { SharedData: l.TestCfg.TestGroupInput.MsgType, LokiConfig: wasp.NewLokiConfig(lokiConfig.Endpoint, lokiConfig.TenantId, nil, nil), Labels: labels, - FailOnErr: true, - }) + FailOnErr: pointer.GetBool(l.TestCfg.TestGroupInput.FailOnFirstErrorInLoad), + } + waspCfg.LokiConfig.Timeout = time.Minute + loadRunner, err := wasp.NewGenerator(waspCfg) require.NoError(l.TestCfg.Test, err, "initiating loadgen for lane %s --> %s", lane.SourceNetworkName, lane.DestNetworkName) loadRunner.Run(false) @@ -170,6 +288,28 @@ func (l *LoadArgs) TriggerLoadByLane() { } func (l *LoadArgs) AddToRunnerGroup(gen *wasp.Generator) { + // watch for pause signal + go func(gen *wasp.Generator) { + ticker := time.NewTicker(time.Second) + pausedOnce := false + resumedAlready := false + for { + select { + case <-ticker.C: + if l.pauseLoad.Load() && !pausedOnce { + gen.Pause() + pausedOnce = true + continue + } + if pausedOnce && !resumedAlready && !l.pauseLoad.Load() { + gen.Resume() + resumedAlready = true + } + case <-l.Ctx.Done(): + return + } + } + }(gen) l.RunnerWg.Go(func() error { _, failed := gen.Wait() if failed { @@ -272,7 +412,7 @@ func (l *LoadArgs) TriggerLoadBySource() { Logger: multiCallGen.logger, LokiConfig: wasp.NewLokiConfig(lokiConfig.Endpoint, lokiConfig.TenantId, nil, nil), Labels: allLabels, - FailOnErr: true, + FailOnErr: pointer.GetBool(l.TestCfg.TestGroupInput.FailOnFirstErrorInLoad), }) require.NoError(l.TestCfg.Test, err, "initiating loadgen for source %s", source) loadRunner.Run(false) @@ -286,12 +426,15 @@ func (l *LoadArgs) TriggerLoadBySource() { func NewLoadArgs(t *testing.T, lggr zerolog.Logger, chaosExps ...ChaosConfig) *LoadArgs { wg, _ := errgroup.WithContext(testcontext.Get(t)) + ctx := testcontext.Get(t) return &LoadArgs{ t: t, + Ctx: ctx, lggr: lggr, RunnerWg: wg, TestCfg: testsetups.NewCCIPTestConfig(t, lggr, testconfig.Load), ChaosExps: chaosExps, LoadStarterWg: &sync.WaitGroup{}, + pauseLoad: atomic.NewBool(false), } } diff --git a/integration-tests/ccip-tests/smoke/ccip_test.go b/integration-tests/ccip-tests/smoke/ccip_test.go index fe8c858ebe..66b4a640f8 100644 --- a/integration-tests/ccip-tests/smoke/ccip_test.go +++ b/integration-tests/ccip-tests/smoke/ccip_test.go @@ -40,7 +40,7 @@ func TestSmokeCCIPForBidirectionalLane(t *testing.T) { t.Cleanup(func() { // If we are running a test that is a token transfer, we need to verify the balance. // For USDC deployment, the mock contracts cannot mint the token in destination, therefore skip the balance check. - if TestCfg.TestGroupInput.MsgType == actions.TokenTransfer && !pointer.GetBool(TestCfg.TestGroupInput.USDCMockDeployment) { + if TestCfg.TestGroupInput.MsgType == actions.TokenTransfer && TestCfg.TestGroupInput.NoOfUSDCMockTokens == nil { setUpOutput.Balance.Verify(t) } require.NoError(t, setUpOutput.TearDown()) diff --git a/integration-tests/ccip-tests/testconfig/ccip.go b/integration-tests/ccip-tests/testconfig/ccip.go index 9b287174ab..214c6a13fa 100644 --- a/integration-tests/ccip-tests/testconfig/ccip.go +++ b/integration-tests/ccip-tests/testconfig/ccip.go @@ -1,6 +1,7 @@ package testconfig import ( + "fmt" "os" "github.com/pelletier/go-toml/v2" @@ -12,39 +13,51 @@ import ( ctfconfig "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-common/pkg/config" + + "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) type CCIPTestConfig struct { - KeepEnvAlive *bool `toml:",omitempty"` - BiDirectionalLane *bool `toml:",omitempty"` - CommitAndExecuteOnSameDON *bool `toml:",omitempty"` - NoOfCommitNodes int `toml:",omitempty"` - MsgType string `toml:",omitempty"` - DestGasLimit *int64 `toml:",omitempty"` - MulticallInOneTx *bool `toml:",omitempty"` - NoOfSendsInMulticall int `toml:",omitempty"` - PhaseTimeout *config.Duration `toml:",omitempty"` - TestDuration *config.Duration `toml:",omitempty"` - LocalCluster *bool `toml:",omitempty"` - ExistingDeployment *bool `toml:",omitempty"` - TestRunName string `toml:",omitempty"` - ReuseContracts *bool `toml:",omitempty"` - NodeFunding float64 `toml:",omitempty"` - RequestPerUnitTime []int64 `toml:",omitempty"` - TimeUnit *config.Duration `toml:",omitempty"` - StepDuration []*config.Duration `toml:",omitempty"` - WaitBetweenChaosDuringLoad *config.Duration `toml:",omitempty"` - NetworkPairs []string `toml:",omitempty"` - NoOfNetworks int `toml:",omitempty"` - NoOfRoutersPerPair int `toml:",omitempty"` - Blockscout bool `toml:",omitempty"` - NoOfTokensPerChain int `toml:",omitempty"` - NoOfTokensInMsg int `toml:",omitempty"` - AmountPerToken int64 `toml:",omitempty"` - MaxNoOfLanes int `toml:",omitempty"` - ChaosDuration *config.Duration `toml:",omitempty"` - USDCMockDeployment *bool `toml:",omitempty"` - TimeoutForPriceUpdate *config.Duration `toml:",omitempty"` + KeepEnvAlive *bool `toml:",omitempty"` + BiDirectionalLane *bool `toml:",omitempty"` + CommitAndExecuteOnSameDON *bool `toml:",omitempty"` + NoOfCommitNodes int `toml:",omitempty"` + MsgType string `toml:",omitempty"` + DestGasLimit *int64 `toml:",omitempty"` + MulticallInOneTx *bool `toml:",omitempty"` + NoOfSendsInMulticall int `toml:",omitempty"` + PhaseTimeout *config.Duration `toml:",omitempty"` + TestDuration *config.Duration `toml:",omitempty"` + LocalCluster *bool `toml:",omitempty"` + ExistingDeployment *bool `toml:",omitempty"` + TestRunName string `toml:",omitempty"` + ReuseContracts *bool `toml:",omitempty"` + NodeFunding float64 `toml:",omitempty"` + RequestPerUnitTime []int64 `toml:",omitempty"` + TimeUnit *config.Duration `toml:",omitempty"` + StepDuration []*config.Duration `toml:",omitempty"` + WaitBetweenChaosDuringLoad *config.Duration `toml:",omitempty"` + NetworkPairs []string `toml:",omitempty"` + NoOfNetworks int `toml:",omitempty"` + NoOfRoutersPerPair int `toml:",omitempty"` + Blockscout bool `toml:",omitempty"` + NoOfTokensPerChain int `toml:",omitempty"` + NoOfTokensInMsg int `toml:",omitempty"` + AmountPerToken int64 `toml:",omitempty"` + MaxNoOfLanes int `toml:",omitempty"` + ChaosDuration *config.Duration `toml:",omitempty"` + NoOfUSDCMockTokens *int `toml:",omitempty"` + TimeoutForPriceUpdate *config.Duration `toml:",omitempty"` + WithPipeline *bool `toml:",omitempty"` + FailOnFirstErrorInLoad *bool `toml:",omitempty"` + DynamicPriceUpdateInterval *config.Duration `toml:",omitempty"` + SendMaxDataInEveryMsgCount *int64 `toml:",omitempty"` + CommitOCRParams *contracts.OffChainAggregatorV2Config `toml:",omitempty"` + ExecOCRParams *contracts.OffChainAggregatorV2Config `toml:",omitempty"` + CommitInflightExpiry *config.Duration `toml:",omitempty"` + ExecInflightExpiry *config.Duration `toml:",omitempty"` + OptimizeSpace *bool `toml:",omitempty"` + SkipRequestIfAnotherRequestTriggeredWithin *config.Duration `toml:",omitempty"` } func (c *CCIPTestConfig) SetTestRunName(name string) { @@ -63,7 +76,9 @@ func (c *CCIPTestConfig) Validate() error { if c.MsgType != "WithoutToken" && c.MsgType != "WithToken" { return errors.Errorf("msg type should be either WithoutToken or WithToken") } - + if c.NoOfCommitNodes < 4 { + return fmt.Errorf("insuffcient number of commit nodes provided") + } if c.MsgType == "WithToken" { if c.AmountPerToken == 0 { return errors.Errorf("token amount should be greater than 0") diff --git a/integration-tests/ccip-tests/testconfig/global.go b/integration-tests/ccip-tests/testconfig/global.go index 75cbc4078b..f820dfba2a 100644 --- a/integration-tests/ccip-tests/testconfig/global.go +++ b/integration-tests/ccip-tests/testconfig/global.go @@ -11,6 +11,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/pkg/errors" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/seth" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/networks" @@ -150,13 +151,18 @@ func NewConfig() (*Config, error) { // Common is the generic config struct which can be used with product specific configs. // It contains generic DON and networks config which can be applied to all product based tests. type Common struct { - EnvUser string `toml:",omitempty"` - TTL *config.Duration `toml:",omitempty"` - ExistingCLCluster *CLCluster `toml:",omitempty"` // ExistingCLCluster is the existing chainlink cluster to use, if specified it will be used instead of creating a new one - Mockserver *string `toml:",omitempty"` - NewCLCluster *ChainlinkDeployment `toml:",omitempty"` // NewCLCluster is the new chainlink cluster to create, if specified along with ExistingCLCluster this will be ignored - Network *ctfconfig.NetworkConfig `toml:",omitempty"` - Logging *ctfconfig.LoggingConfig `toml:"Logging"` + EnvUser string `toml:",omitempty"` + TTL *config.Duration `toml:",omitempty"` + ExistingCLCluster *CLCluster `toml:",omitempty"` // ExistingCLCluster is the existing chainlink cluster to use, if specified it will be used instead of creating a new one + Mockserver *string `toml:",omitempty"` + NewCLCluster *ChainlinkDeployment `toml:",omitempty"` // NewCLCluster is the new chainlink cluster to create, if specified along with ExistingCLCluster this will be ignored + Network *ctfconfig.NetworkConfig `toml:",omitempty"` + PrivateEthereumNetworks map[string]*test_env.EthereumNetwork `toml:",omitempty"` + Logging *ctfconfig.LoggingConfig `toml:"Logging"` +} + +func (p *Common) GetSethConfig() *seth.Config { + return nil } func (p *Common) Validate() error { @@ -180,6 +186,21 @@ func (p *Common) Validate() error { return errors.New("no chainlink or existing cluster specified") } + for k, v := range p.PrivateEthereumNetworks { + // this is the only value we need to generate dynamically before starting a new simulated chain + if v.EthereumChainConfig != nil { + p.PrivateEthereumNetworks[k].EthereumChainConfig.GenerateGenesisTimestamp() + } + + builder := test_env.NewEthereumNetworkBuilder() + config, err := builder.WithExistingConfig(*v).Build() + if err != nil { + return fmt.Errorf("error building private ethereum network config %w", err) + } + + p.PrivateEthereumNetworks[k] = &config + } + if p.ExistingCLCluster != nil { if err := p.ExistingCLCluster.Validate(); err != nil { return fmt.Errorf("error validating existing chainlink cluster config %w", err) @@ -282,16 +303,18 @@ func (c *CLCluster) Validate() error { } type ChainlinkDeployment struct { - Common *Node `toml:",omitempty"` - NodeMemory string `toml:",omitempty"` - NodeCPU string `toml:",omitempty"` - DBMemory string `toml:",omitempty"` - DBCPU string `toml:",omitempty"` - DBCapacity string `toml:",omitempty"` - IsStateful *bool `toml:",omitempty"` - DBArgs []string `toml:",omitempty"` - NoOfNodes *int `toml:",omitempty"` - Nodes []*Node `toml:",omitempty"` // to be mentioned only if diff nodes follow diff configs; not required if all nodes follow CommonConfig + Common *Node `toml:",omitempty"` + NodeMemory string `toml:",omitempty"` + NodeCPU string `toml:",omitempty"` + DBMemory string `toml:",omitempty"` + DBCPU string `toml:",omitempty"` + DBCapacity string `toml:",omitempty"` + DBStorageClass *string `toml:",omitempty"` + PromPgExporter *bool `toml:",omitempty"` + IsStateful *bool `toml:",omitempty"` + DBArgs []string `toml:",omitempty"` + NoOfNodes *int `toml:",omitempty"` + Nodes []*Node `toml:",omitempty"` // to be mentioned only if diff nodes follow diff configs; not required if all nodes follow CommonConfig } func (c *ChainlinkDeployment) Validate() error { diff --git a/integration-tests/ccip-tests/testconfig/override/mainnet-secondary.toml b/integration-tests/ccip-tests/testconfig/override/mainnet-secondary.toml new file mode 100644 index 0000000000..a8597db109 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/override/mainnet-secondary.toml @@ -0,0 +1,689 @@ +[CCIP] +[CCIP.Deployments] +Data = """ +{ + "lane_configs": { + "Arbitrum Mainnet": { + "is_native_fee_token": true, + "fee_token": "0xf97f4df75117a78c1A5a0DBb814Af92458539FB4", + "bridge_tokens": ["0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"], + "bridge_tokens_pools": ["0x34700F5faE61Ba628c4269BdCbA12DA53bbfa726"], + "arm": "0xe06b0e8c4bd455153e8794ad7Ea8Ff5A14B64E4b", + "router": "0x141fa059441E0ca23ce184B6A78bafD2A517DdE8", + "price_registry": "0x13015e4E6f839E1Aa1016DF521ea458ecA20438c", + "wrapped_native": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "src_contracts": { + "Avalanche Mainnet": { + "on_ramp": "0x05B723f3db92430FbE4395fD03E40Cc7e9D17988", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x77b60F85b25fD501E3ddED6C1fe7bF565C08A22A", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0x79f3ABeCe5A3AFFf32D47F4CFe45e7b65c9a2D91", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0xCe11020D56e5FDbfE46D9FC3021641FfbBB5AdEE", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0xC09b72E8128620C40D89649019d995Cc79f030C3", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x122F05F49e90508F089eE8D0d868d1a4f3E5a809", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x66a0046ac9FA104eB38B04cfF391CcD0122E6FbC", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Avalanche Mainnet": { + "off_ramp": "0xe0109912157d5B75ea8b3181123Cf32c73bc9920", + "commit_store": "0xDaa61b8Cd85977820f92d1e749E1D9F55Da6CCEA", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0xdB19F77F87661f9be0F557cf9a1ebeCf7D8F206c", + "commit_store": "0x6e37f4c82d9A31cc42B445874dd3c3De97AB553f", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0xB1b705c2315fced1B38baE463BE7DDef531e47fA", + "commit_store": "0x310cECbFf14Ad0307EfF762F461a487C1abb90bf", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0x542ba1902044069330e8c5b36A84EC503863722f", + "commit_store": "0x060331fEdA35691e54876D957B4F9e3b8Cb47d20", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xeeed4D86F3E0e6d32A6Ad29d8De6A0Dc91963A5f", + "commit_store": "0xbbB563c4d98020b9c0f3Cc34c2C0Ef9676806E35", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x9bDA7c8DCda4E39aFeB483cc0B7E3C1f6E0D5AB1", + "commit_store": "0x63a0AeaadAe851b990bBD9dc41f5C1B08b32026d", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0xEEf5Fb4c4953F9cA9ab1f25cE590776AfFc2c455", + "commit_store": "0xD268286A277095a9C3C90205110831a84505881c", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Avalanche Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x5947BB275c521040051D82396192181b413227A3", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0xdFD6C0dc67666DE3bB36b31eec5c7B1542A82C1E", + "router": "0xF4c7E640EdA248ef95972845a62bdC74237805dB", + "price_registry": "0xfA4edD04eaAcDB07c8D73621bc1790eC50D8c489", + "wrapped_native": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x98f51B041e493fc4d72B8BD33218480bA0c66DDF", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x268fb4311D2c6CB2bbA01CCA9AC073Fb3bfd1C7c", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0x8eaae6462816CB4957184c48B86afA7642D8Bf2B", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0xD0701FcC7818c31935331B02Eb21e91eC71a1704", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x8629008887E073260c5434D6CaCFc83C3001d211", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x97500490d9126f34cf9aA0126d64623E170319Ef", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x9b1ed9De069Be4d50957464b359f98eD0Bf34dd5", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0x770b1375F86E7a9bf30DBe3F97bea67193dC9135", + "commit_store": "0x23E2b34Ce8e12c53f8a39AD4b3FFCa845f8E617C", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0x4d6A796Bc85dcDF41ce9AaEB50B094C6b589748f", + "commit_store": "0xc4C4358FA01a04D6c6FE3b96a351946d4c2715C2", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x83F53Fc798FEbfFbdF84830AD403b9989187a06C", + "commit_store": "0xD8ceCE2D7794385E00Ce3EF94550E732b0A0B959", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0x5B833BD6456c604Eb396C0fBa477aD49e82B1A2a", + "commit_store": "0x23E23958D220B774680f91c2c91a6f2B2f610d7e", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xb68A3EE8bD0A09eE221cf1859Dd5a4d5765188Fe", + "commit_store": "0x83DCeeCf822981F9F8552925eEfd88CAc1905dEA", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x19250aBE66B88F214d02B6f3BF80F4118290C619", + "commit_store": "0x87A0935cE6254dB1252bBac90d1D07D04846aDCA", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0x317dE8bc5c3292E494b6496586696d4966A922B0", + "commit_store": "0x97Fbf3d6DEac16adC721aE9187CeEa1e610aC7Af", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Base Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x88Fb150BDc53A65fe94Dea0c9BA0a6dAf8C6e196", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0x38660c8CC222c0192b635c2ac09687B4F25cCE5F", + "router": "0x881e3A65B4d4a04dD529061dd0071cf975F58bCD", + "price_registry": "0x6337a58D4BD7Ba691B66341779e8f87d4679923a", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x1E5Ca70d1e7A1B26061125738a880BBeA42FeB21", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0xBE5a9E336D9614024B4Fa10D8112671fc9A42d96", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0xdd4Fb402d41Beb0eEeF6CfB1bf445f50bDC8c981", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0xDEA286dc0E01Cb4755650A6CF8d1076b454eA1cb", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0xd952FEAcDd5919Cc5E9454b53bF45d4E73dD6457", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x3DB8Bea142e41cA3633890d0e5640F99a895D6A5", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0x8531E63aE9279a1f0D09eba566CD1b092b95f3D5", + "commit_store": "0x327E13f54c7871a2416006B33B4822eAAD357916", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0x8345F2fF67e5A65e85dc955DE1414832608E00aD", + "commit_store": "0xd0b13be4c53A6262b47C5DDd36F0257aa714F562", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x48a51f5D38BE630Ddd6417Ea2D9052B8efc91a18", + "commit_store": "0xF97127e77252284EC9D4bc13C247c9D1A99F72B0", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0xEC0cFe335a4d53dBA70CB650Ab56eEc32788F0BB", + "commit_store": "0x0ae3c2c7FB789bd05A450CD3075D11f6c2Ca4F77", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xf50c0d2a8B6Db60f1D93E60f03d0413D56153E4F", + "commit_store": "0x16f72C15165f7C9d74c12fDF188E399d4d3724e4", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x75F29f058b31106F99caFdc17c9b26ADfcC7b5D7", + "commit_store": "0xb719616E732581B570232DfB13Ca49D27667Af9f", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "BSC Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x404460C6A5EdE2D891e8297795264fDe62ADBB75", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0x3DB43b96B2625F4232e9Df900d464dd2c64C0021", + "router": "0x34B03Cb9086d7D758AC55af71584F81A598759FE", + "price_registry": "0xd64aAbD70A71d9f0A00B99F6EFc1626aA2dD43C7", + "wrapped_native": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "src_contracts": { + "Avalanche Mainnet": { + "on_ramp": "0x6aa72a998859eF93356c6521B72155D355D0Cfd2", + "deployed_at": 11111111 + }, + "Arbitrum Mainnet": { + "on_ramp": "0x2788b46BAcFF49BD89562e6bA5c5FBbbE5Fa92F7", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x70bC7f7a6D936b289bBF5c0E19ECE35B437E2e36", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0x0Bf40b034872D0b364f3DCec04C7434a4Da1C8d9", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x4FEB11A454C9E8038A8d0aDF599Fe7612ce114bA", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x6bD4754D86fc87FE5b463D368f26a3587a08347c", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x1467fF8f249f5bc604119Af26a47035886f856BE", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Avalanche Mainnet": { + "off_ramp": "0x37a6fa55fe61061Ae97bF7314Ae270eCF71c5ED3", + "commit_store": "0x1f558F6dcf0224Ef1F78A24814FED548B9602c80", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Arbitrum Mainnet": { + "off_ramp": "0x3DA330fd8Ef10d93cFB7D4f8ecE7BC1F10811feC", + "commit_store": "0x86D55Ff492cfBBAf0c0D42D4EE615144E78b3D02", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0x574c697deab06B805D8780898B3F136a1F4892Dc", + "commit_store": "0x002B164b1dcf4E92F352DC625A01Be0E890EdEea", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0x181Bb1E97b0bDD1D85E741ad0943552D3682cc35", + "commit_store": "0x3fF27A34fF0FA77921C3438e67f58da1a83e9Ce1", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xE7E080C8d62d595a223C577C7C8d1f75d9A5E664", + "commit_store": "0xF4d53346bDb6d393C74B0B72Aa7D6689a3eAad79", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x26af2046Da85d7f6712D5edCa81B9E3b2e7A60Ab", + "commit_store": "0x4C1dA405a789AC2853A69D8290B8B9b47a0374F8", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0xC027C5AEb230008c243Be463A73571e581F94c13", + "commit_store": "0x2EB426C8C54D740d1FC856eB3Ff96feA03957978", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Ethereum Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "bridge_tokens": ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"], + "bridge_tokens_pools": ["0x69c24c970B65e22Ac26864aF10b2295B7d78f93A"], + "arm": "0x8B63b3DE93431C0f756A493644d128134291fA1b", + "router": "0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D", + "price_registry": "0x8c9b2Efb7c64C394119270bfecE7f54763b958Ad", + "wrapped_native": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x925228D7B82d883Dde340A55Fe8e6dA56244A22C", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0x3df8dAe2d123081c4D5E946E655F7c109B9Dd630", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0xe2c2AB221AA0b957805f229d2AA57fBE2f4dADf7", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0x91D25A56Db77aD5147437d8B83Eb563D46eBFa69", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x86B47d8411006874eEf8E4584BdFD7be8e5549d1", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x35F0ca9Be776E4B38659944c257bDd0ba75F1B8B", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0xCbE7e5DA76dC99Ac317adF6d99137005FDA4E2C4", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0xeFC4a18af59398FF23bfe7325F2401aD44286F4d", + "commit_store": "0x9B2EEd6A1e16cB50Ed4c876D2dD69468B21b7749", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0x569940e02D4425eac61A7601632eC00d69f75c17", + "commit_store": "0x2aa101BF99CaeF7fc1355D4c493a1fe187A007cE", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0xdf85c8381954694E74abD07488f452b4c2Cddfb3", + "commit_store": "0x8DC27D621c41a32140e22E2a4dAf1259639BAe04", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x7Afe7088aff57173565F4b034167643AA8b9171c", + "commit_store": "0x87c55D48DF6EF7B08153Ab079e76bFEcbb793D75", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xB095900fB91db00E6abD247A5A5AD1cee3F20BF7", + "commit_store": "0x4af4B497c998007eF83ad130318eB2b925a79dc8", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x0af338F0E314c7551bcE0EF516d46d855b0Ee395", + "commit_store": "0xD37a60E8C36E802D2E1a6321832Ee85556Beeb76", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0x3a129e6C18b23d18BA9E6Aa14Dc2e79d1f91c6c5", + "commit_store": "0x31f6ab382DDeb9A316Ab61C3945a5292a50a89AB", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Kroma Mainnet": { + "is_native_fee_token": true, + "fee_token": "0xC1F6f7622ad37C3f46cDF6F8AA0344ADE80BF450", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0xB59779d3364BC6d71168245f9ebb96469E5a5a98", + "router": "0xE93E8B0d1b1CEB44350C8758ed1E2799CCee31aB", + "price_registry": "0x8155B4710e7bbC90924E957104F94Afd4f95Eca2", + "wrapped_native": "0x4200000000000000000000000000000000000001", + "src_contracts": { + "WeMix Mainnet": { + "on_ramp": "0x3C5Ab46fA1dB1dECD854224654313a69bf9fcAD3", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "WeMix Mainnet": { + "off_ramp": "0x2B555774B3D1dcbcd76efb7751F3c5FbCFABC5C4", + "commit_store": "0x213124614aAf31eBCE7c612A12aac5f8aAD77DE4", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Optimism Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6", + "bridge_tokens": ["0x4200000000000000000000000000000000000006"], + "bridge_tokens_pools": ["0x86E715415D8C8435903d1e8204fA1e9784Aa7305"], + "arm": "0x8C7C2C3362a42308BB5c368677Ad321D11693b81", + "router": "0x3206695CaE29952f4b0c22a169725a865bc8Ce0f", + "price_registry": "0xb52545aECE8C73A97E52a146757EC15b90Ed8488", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x0C9BE7Cfd12c735E5aaE047C1dCB845d54E518C3", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0xD0D3E757bFBce7ae1881DDD7F6d798DDcE588445", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x0b1760A8112183303c5526C6b24569fd3A274f3B", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0xa3c9544B82846C45BE37593d5d9ACffbE61BF3A6", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0x55183Db1d2aE0b63e4c92A64bEF2CBfc2032B127", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x6B57145e322c877E7D91Ed8E31266eB5c02F7EfC", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x82e9f4C5ec4a84E310d60D462a12042E5cbA0954", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0x0C9BE7Cfd12c735E5aaE047C1dCB845d54E518C3", + "commit_store": "0x55028780918330FD00a34a61D9a7Efd3f43ca845", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0x8dc6490A6204dF846BaBE809cB695ba17Df1F9B1", + "commit_store": "0xA190660787B6B183Dd82B243eA10e609327c7308", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0xBAE6560eCa9B77Cb047158C783e36F7735C86037", + "commit_store": "0x6168aDF58e1Ad446BaD45c6275Bef60Ef4FFBAb8", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0xE14501F2838F2fA1Ceb52E78ABdA289EcE1705EA", + "commit_store": "0xa8DD25B29787527Df283211C24Ac72B17150A696", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0xd2D98Be6a1C241e86C807e51cED6ABb51d044203", + "commit_store": "0x4d75A5cE454b264b187BeE9e189aF1564a68408D", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x7c6221880A1D62506b1A08Dab3Bf695A49AcDD22", + "commit_store": "0x0684076EE3595221861C50cDb9Cb66402Ec11Cb9", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0x3e5B3b7559D39563a74434157b31781322dA712D", + "commit_store": "0x7954372FF6f80908e5A2dC2a19d796A1005f91D2", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Polygon Mainnet": { + "is_native_fee_token": true, + "fee_token": "0xb0897686c545045aFc77CF20eC7A532E3120E0F1", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0xD7AcF65dA1E1f34b663aB199a474F209bF2b0523", + "router": "0x849c5ED5a80F5B408Dd4969b78c2C8fdf0565Bfe", + "price_registry": "0x30D873664Ba766C983984C7AF9A921ccE36D34e1", + "wrapped_native": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0xD16D025330Edb91259EEA8ed499daCd39087c295", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0x5FA30697e90eB30954895c45b028F7C0dDD39b12", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x20B028A2e0F6CCe3A11f3CE5F2B8986F932e89b4", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0xF5b5A2fC11BF46B1669C3B19d98B19C79109Dca9", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0xFd77c53AA4eF0E3C01f5Ac012BF7Cc7A3ECf5168", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x3111cfbF5e84B5D9BD952dd8e957f4Ca75f728Cf", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x5060eF647a1F66BE6eE27FAe3046faf8D53CeB2d", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0xa8a9eDa2867c2E0CE0d5ECe273961F1EcC3CC25B", + "commit_store": "0xbD4480658dca8496a65046dfD1BDD44EF897Bdb5", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0xB9e3680639c9F0C4e0b02FD81C445094426244Ae", + "commit_store": "0x8c63d4e67f7c4af6FEd2f56A34fB4e01CB807CFF", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0xD0FA7DE2D18A0c59D3fD7dfC7aB4e913C6Aa7b68", + "commit_store": "0xF88053B9DAC8Dd3039a4eFa8639159aaa3F2D4Cb", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x592773924741F0Da889a0dfdab71171Dd11E054C", + "commit_store": "0xEC4d35E1A85f770f4D93BA43a462c9d87Ef7017e", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0x45320085fF051361D301eC1044318213A5387A15", + "commit_store": "0x4Dc771B5ef21ef60c33e2987E092345f2b63aE08", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xBa754ecd3CFA7E9093F688EAc3860cf9D07Fc0AC", + "commit_store": "0x04C0D5302E3D8Ca0A0019141a52a23B59cdb70e4", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0xd7c877ea02310Cce9278D9A048Aa1Bb9aF72F00d", + "commit_store": "0x92A1C927E8E10Ab6A40E5A5154e2300D278d1a67", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "WeMix Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x80f1FcdC96B55e459BF52b998aBBE2c364935d69", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0x07aaC8B69A62dB5bd3d244091916EbF2fac17b76", + "router": "0x7798b795Fde864f4Cd1b124a38Ba9619B7F8A442", + "price_registry": "0x252863688762aD86868D3d3076233Eacd80c7055", + "wrapped_native": "0x7D72b22a74A216Af4a002a1095C8C707d6eC1C5f", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x9aBfd6f4C865610692AB6fb1Be862575809fFabf", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0xbE0Cfae74677F8dd16a246a3a5c8cbB1973118f4", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0x56657ec4D15C71f7F3C17ba2b21C853A24Dc5381", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x70f3b0FD7e6a4B9B623e9AB859604A9EE03e48BD", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x777058C1e1dcE4eB8001F38631a1cd9450816e5a", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0x190bcE84CF2d500B878966F4Cf98a50d78f2675E", + "deployed_at": 11111111 + }, + "Kroma Mainnet": { + "on_ramp": "0x47E9AE0A815C94836202E696748A5d5476aD8735", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0x2ba68a395B72a6E3498D312efeD755ed2f3CF223", + "commit_store": "0xdAeC234DA83F68707Bb8AcB2ee6a01a5FD4c2391", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0xFac907F9a1087B846Faa75A14C5d34A8639233d8", + "commit_store": "0xF2812063446c7deD2CA306c67A68364BdDcbEfc5", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x6ec9ca4Cba62cA17c55F05ad2000B46192f02035", + "commit_store": "0x84534BE763366a69710E119c100832955795B34B", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0x87220D01DF0fF27149B47227897074653788fd23", + "commit_store": "0xF8dD2be2C6FA43e48A17146380CbEBBB4291807b", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x8f0229804513A9Bc00c1308414AB279Dbc718ae1", + "commit_store": "0x3A85D1b8641d83a87957C6ECF1b62151213e0842", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0xF92Fa796F5307b029c65CA26f322a6D86f211194", + "commit_store": "0xbeC110FF43D52be2066B06525304A9924E16b73b", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Kroma Mainnet": { + "off_ramp": "0xF886d8DC64E544af4835cbf91e5678A54D95B80e", + "commit_store": "0x8794C9534658fdCC44f2FF6645Bf31cf9F6d2d5D", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + } + } +} +""" + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks = [ + 'ETHEREUM_MAINNET', + 'ARBITRUM_MAINNET', + 'OPTIMISM_MAINNET' + ] + +[CCIP.Groups.load] +NetworkPairs = [ + 'ETHEREUM_MAINNET,OPTIMISM_MAINNET', + 'ETHEREUM_MAINNET,ARBITRUM_MAINNET', + 'ARBITRUM_MAINNET,OPTIMISM_MAINNET' # added as batch 1 +] + +MsgType = 'WithToken' +DestGasLimit = 0 +BiDirectionalLane = true +PhaseTimeout = '40m' +TestDuration = '24h' +ExistingDeployment = true +RequestPerUnitTime = [1] +TimeUnit = '3h' +NoOfTokensPerChain = 1 +NoOfTokensInMsg = 1 +AmountPerToken = 1 +TestRunName = 'mainnet-2.7-ccip1.2' +FailOnFirstErrorInLoad = true + +[CCIP.Groups.smoke] +MsgType = 'WithoutToken' +# these are all the valid network pairs +NetworkPairs = [ + 'ETHEREUM_MAINNET,OPTIMISM_MAINNET', + 'ETHEREUM_MAINNET,ARBITRUM_MAINNET', + 'ARBITRUM_MAINNET,OPTIMISM_MAINNET' +] + +BiDirectionalLane = true +DestGasLimit = 0 +PhaseTimeout = '20m' +LocalCluster = false +ExistingDeployment = true +ReuseContracts = true +NoOfTokensPerChain = 1 +NoOfTokensInMsg = 1 +AmountPerToken = 1 +TestRunName = 'mainnet-2.7-ccip1.2' diff --git a/integration-tests/ccip-tests/testconfig/override/mainnet.toml b/integration-tests/ccip-tests/testconfig/override/mainnet.toml index 997dd2cc83..fd404595d2 100644 --- a/integration-tests/ccip-tests/testconfig/override/mainnet.toml +++ b/integration-tests/ccip-tests/testconfig/override/mainnet.toml @@ -655,11 +655,11 @@ selected_networks = [ [CCIP.Groups.load] NetworkPairs = [ - 'ETHEREUM_MAINNET,OPTIMISM_MAINNET', + #'ETHEREUM_MAINNET,OPTIMISM_MAINNET', 'ETHEREUM_MAINNET,AVALANCHE_MAINNET', 'ETHEREUM_MAINNET,POLYGON_MAINNET', 'ETHEREUM_MAINNET,BSC_MAINNET', - 'ETHEREUM_MAINNET,ARBITRUM_MAINNET', + # 'ETHEREUM_MAINNET,ARBITRUM_MAINNET', 'ETHEREUM_MAINNET,BASE_MAINNET', 'ETHEREUM_MAINNET,WEMIX_MAINNET', 'AVALANCHE_MAINNET,POLYGON_MAINNET', @@ -671,7 +671,7 @@ NetworkPairs = [ 'BASE_MAINNET,BSC_MAINNET', 'POLYGON_MAINNET,ARBITRUM_MAINNET', # added as batch 1 'ARBITRUM_MAINNET,BSC_MAINNET', # added as batch 1 - 'ARBITRUM_MAINNET,OPTIMISM_MAINNET', # added as batch 1 + # 'ARBITRUM_MAINNET,OPTIMISM_MAINNET', # added as batch 1 'AVALANCHE_MAINNET,OPTIMISM_MAINNET', # added as batch 2 'AVALANCHE_MAINNET,ARBITRUM_MAINNET', # added as batch 2 'BASE_MAINNET,POLYGON_MAINNET', # added as batch 2 @@ -691,22 +691,24 @@ PhaseTimeout = '20m' TestDuration = '5h' ExistingDeployment = true RequestPerUnitTime = [1] -TimeUnit = '2h' +TimeUnit = '1h' NoOfTokensPerChain = 1 NoOfTokensInMsg = 1 AmountPerToken = 1 TestRunName = 'mainnet-2.7-ccip1.2' +FailOnFirstErrorInLoad = true +SkipRequestIfAnotherRequestTriggeredWithin = '40m' [CCIP.Groups.smoke] MsgType = 'WithoutToken' # these are all the valid network pairs NetworkPairs = [ - 'ETHEREUM_MAINNET,OPTIMISM_MAINNET', + 'ETHEREUM_MAINNET,OPTIMISM_MAINNET', 'ETHEREUM_MAINNET,AVALANCHE_MAINNET', 'ETHEREUM_MAINNET,POLYGON_MAINNET', 'ETHEREUM_MAINNET,BSC_MAINNET', - 'ETHEREUM_MAINNET,ARBITRUM_MAINNET', - 'ETHEREUM_MAINNET,BASE_MAINNET', + 'ETHEREUM_MAINNET,ARBITRUM_MAINNET', + 'ETHEREUM_MAINNET,BASE_MAINNET', 'ETHEREUM_MAINNET,WEMIX_MAINNET', 'AVALANCHE_MAINNET,POLYGON_MAINNET', 'BASE_MAINNET,OPTIMISM_MAINNET', @@ -717,7 +719,7 @@ NetworkPairs = [ 'BASE_MAINNET,BSC_MAINNET', 'POLYGON_MAINNET,ARBITRUM_MAINNET', # added as batch 1 'ARBITRUM_MAINNET,BSC_MAINNET', # added as batch 1 - 'ARBITRUM_MAINNET,OPTIMISM_MAINNET', # added as batch 1 + 'ARBITRUM_MAINNET,OPTIMISM_MAINNET', # added as batch 1 'AVALANCHE_MAINNET,OPTIMISM_MAINNET', # added as batch 2 'AVALANCHE_MAINNET,ARBITRUM_MAINNET', # added as batch 2 'BASE_MAINNET,POLYGON_MAINNET', # added as batch 2 @@ -740,4 +742,4 @@ ReuseContracts = true NoOfTokensPerChain = 1 NoOfTokensInMsg = 1 AmountPerToken = 1 -TestRunName = 'mainnet-2.7-ccip1.2' \ No newline at end of file +TestRunName = 'mainnet-2.7-ccip1.2' diff --git a/integration-tests/ccip-tests/testconfig/override/override.toml b/integration-tests/ccip-tests/testconfig/override/override.toml index 2613d88071..bc334727f8 100644 --- a/integration-tests/ccip-tests/testconfig/override/override.toml +++ b/integration-tests/ccip-tests/testconfig/override/override.toml @@ -1,215 +1,6 @@ [CCIP] -[CCIP.Deployments] -Data = """ -{ - "lane_configs": { - "Arbitrum Sepolia": { - "is_native_fee_token": true, - "fee_token": "", - "bridge_tokens": [ - ], - "bridge_tokens_pools": [ - ], - "arm": "0x5EF7a726Fd21Fd9D77D34E3C56cfDD8691F7F0ac", - "router": "0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165", - "price_registry": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", - "wrapped_native": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", - "src_contracts": { - "Base Sepolia": { - "on_ramp": "0x7854E73C73e7F9bb5b0D5B4861E997f4C6E8dcC6", - "deployed_at": 9199926 - } - }, - "dest_contracts": { - "Base Sepolia": { - "off_ramp": "0xc1982985720B959E66c19b64F783361Eb9B60F26", - "commit_store": "0x28F66bB336f6db713d6ad2a3bd1B7a531282A159", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - } - } - }, - "Avalanche Fuji": { - "is_native_fee_token": false, - "fee_token": "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846", - "bridge_tokens": [ - ], - "bridge_tokens_pools": [ - ], - "arm": "0x0ea0D7B2b78DD3A926fC76d6875a287F0AEB158F", - "router": "0xF694E193200268f9a4868e4Aa017A0118C9a8177", - "price_registry": "0x19e157E5fb1DAec1aE4BaB113fdf077F980704AA", - "wrapped_native": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", - "src_contracts": { - "Base Sepolia": { - "on_ramp": "0x1A674645f3EB4147543FCA7d40C5719cbd997362", - "deployed_at": 29483052 - } - }, - "dest_contracts": { - "Base Sepolia": { - "off_ramp": "0xdBdE8510226d1E060A3bf982b67705C67f5697e2", - "commit_store": "0x8Ee73BC9492b4182D289E5C1e66e40CD876CC00F", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - } - } - }, - "BSC Testnet": { - "is_native_fee_token": false, - "fee_token": "0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06", - "bridge_tokens": [ - ], - "bridge_tokens_pools": [ - ], - "arm": "0xF9a21B587111e7E8745Fb8b13750014f19DB0014", - "router": "0xE1053aE1857476f36A3C62580FF9b016E8EE8F6f", - "price_registry": "0xCCDf022c9d31DC26Ebab4FB92432724a5b79809a", - "wrapped_native": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", - "src_contracts": { - "Base Sepolia": { - "on_ramp": "0x3E807220Ca84b997c0d1928162227b46C618e0c5", - "deployed_at": 37115558 - } - }, - "dest_contracts": { - "Base Sepolia": { - "off_ramp": "0x2C61FD7E93Dc79422861282145c59B56dFbc3a8c", - "commit_store": "0x42fAe5B3605804CF6d08632d7A25864e24F792Ae", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - } - } - }, - "Base Sepolia": { - "is_native_fee_token": true, - "fee_token": "", - "bridge_tokens": [ - ], - "bridge_tokens_pools": [ - ], - "arm": "0x5aA82cA372782d6CC33AA4C830Df2a91017A7e1b", - "router": "0xD3b06cEbF099CE7DA4AcCf578aaebFDBd6e88a93", - "price_registry": "0x4D20536e60832bE579Cd38E89Dc03d11E1741FbA", - "wrapped_native": "0x4200000000000000000000000000000000000006", - "src_contracts": { - "Arbitrum Sepolia": { - "on_ramp": "0x58622a80c6DdDc072F2b527a99BE1D0934eb2b50", - "deployed_at": 5146539 - }, - "Avalanche Fuji": { - "on_ramp": "0xAbA09a1b7b9f13E05A6241292a66793Ec7d43357", - "deployed_at": 5140044 - }, - "BSC Testnet": { - "on_ramp": "0xD806966beAB5A3C75E5B90CDA4a6922C6A9F0c9d", - "deployed_at": 5144127 - }, - "Optimism Sepolia": { - "on_ramp": "0x3b39Cd9599137f892Ad57A4f54158198D445D147", - "deployed_at": 5147649 - }, - "Sepolia Testnet": { - "on_ramp": "0x6486906bB2d85A6c0cCEf2A2831C11A2059ebfea", - "deployed_at": 5142138 - } - }, - "dest_contracts": { - "Arbitrum Sepolia": { - "off_ramp": "0xd364C06ac99a82a00d3eFF9F2F78E4Abe4b9baAA", - "commit_store": "0xdE8d0f47a71eA3fDFBD3162271652f2847939097", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - }, - "Avalanche Fuji": { - "off_ramp": "0xAd91214efFee446500940c764DF77AF18427294F", - "commit_store": "0x1242b6c5e0e349b8d4BCf0938f961C4B4f7EA3Fa", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - }, - "BSC Testnet": { - "off_ramp": "0xd5E9508921434e8758f4540D55c1c066b7cc1598", - "commit_store": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - }, - "Optimism Sepolia": { - "off_ramp": "0x86a3910908eCaAA31Fcd9F0fC8841D8E98f1511d", - "commit_store": "0xE99a87C9b5ed4D2b6060195DEea5106ffF655736", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - }, - "Sepolia Testnet": { - "off_ramp": "0x189F61D9B886Dd2975D5Abc893c8Cf5f5effda71", - "commit_store": "0xEE7e27346DCD1e711348D0F7f7ECB53a9a3a08a7", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - } - } - }, - "Optimism Sepolia": { - "is_native_fee_token": false, - "fee_token": "0xE4aB69C077896252FAFBD49EFD26B5D171A32410", - "bridge_tokens": [ - ], - "bridge_tokens_pools": [ - ], - "arm": "0xf06Ff5D2084295909119ca541E93635E7D582FFc", - "router": "0x114A20A10b43D4115e5aeef7345a1A71d2a60C57", - "price_registry": "0x782a7Ba95215f2F7c3dD4C153cbB2Ae3Ec2d3215", - "wrapped_native": "0x4200000000000000000000000000000000000006", - "src_contracts": { - "Base Sepolia": { - "on_ramp": "0xe284D2315a28c4d62C419e8474dC457b219DB969", - "deployed_at": 7130524 - } - }, - "dest_contracts": { - "Base Sepolia": { - "off_ramp": "0x0a750ca77369e03613d7640548F4b2b1c695c3Bb", - "commit_store": "0x8fEBC74C26129C8d7E60288C6dCCc75eb494aA3C", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - } - } - }, - "Sepolia Testnet": { - "is_native_fee_token": false, - "fee_token": "0x779877A7B0D9E8603169DdbD7836e478b4624789", - "bridge_tokens": [ - ], - "bridge_tokens_pools": [ - ], - "arm": "0xB4d360459F32Dd641Ef5A6985fFbAC5c4e5521aA", - "router": "0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59", - "price_registry": "0x9EF7D57a4ea30b9e37794E55b0C75F2A70275dCc", - "wrapped_native": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", - "src_contracts": { - "Base Sepolia": { - "on_ramp": "0x2B70a05320cB069e0fB55084D402343F832556E7", - "deployed_at": 5142138 - } - }, - "dest_contracts": { - "Base Sepolia": { - "off_ramp": "0x31c0B81832B333419f0DfD36A69F502cF9094aed", - "commit_store": "0xDFcde9d698a2B32DB2537DC9B752Cadd1D846a52", - "receiver_dapp": "0xf89c26A4B5a71283250ae3a28e89dEb3Ed555a1c" - } - } - } - } -} -""" - [CCIP.Groups.load] -NetworkPairs = [ - 'BASE_SEPOLIA,SEPOLIA', - 'BASE_SEPOLIA,OPTIMISM_SEPOLIA', - 'BASE_SEPOLIA,ARBITRUM_SEPOLIA', - 'BASE_SEPOLIA,BSC_TESTNET', - 'BASE_SEPOLIA,AVALANCHE_FUJI', -] -MsgType = 'WithoutToken' -DestGasLimit = 0 -BiDirectionalLane = true -PhaseTimeout = '50m' -TestDuration = '1h' -ExistingDeployment = true -RequestPerUnitTime = [1] -TimeUnit = '1m' -NoOfTokensPerChain = 1 -NoOfTokensInMsg = 1 -AmountPerToken = 1 -TestRunName = 'Base Sepolia Soak Test 0.1' \ No newline at end of file +PhaseTimeout = '20m' +TestDuration = '5m' +TestRunName = 'dynamic token price' +DynamicPriceUpdateInterval = '15s' \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip-crib.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip-crib.toml index e05eb8343a..f66dbb5e67 100644 --- a/integration-tests/ccip-tests/testconfig/tomls/ccip-crib.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip-crib.toml @@ -5,61 +5,61 @@ Data = """ "lane_configs": { "geth_1337": { "is_mock_arm": true, - "fee_token": "0x40a42Baf86Fc821f972Ad2aC878729063CeEF403", + "fee_token": "0xf5059a5D33d5853360D16C683c16e67980206f36", "bridge_tokens": [ - "0xde2Bd2ffEA002b8E84ADeA96e5976aF664115E2c", - "0xD49a0e9A4CD5979aE36840f542D2d7f02C4817Be" + "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49", + "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" ], "bridge_tokens_pools": [ - "0xc582Bc0317dbb0908203541971a358c44b1F3766", - "0x74Cf9087AD26D541930BaC724B7ab21bA8F00a27" + "0x9d4454B023096f34B160D6B654540c56A1F81688", + "0x1291Be112d480055DaFd8a610b7d1e203891C274" ], - "arm": "0x6C2d83262fF84cBaDb3e416D527403135D757892", - "router": "0x0ed64d01D0B4B655E410EF1441dD677B695639E7", - "price_registry": "0x26B862f640357268Bd2d9E95bc81553a2Aa81D7E", - "wrapped_native": "0xFD6F7A6a5c21A3f503EBaE7a473639974379c351", + "arm": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690", + "router": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8", + "price_registry": "0x82e01223d51Eb87e16A03E24687EDF0F294da6f1", + "wrapped_native": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB", "multicall": "0x0000000000000000000000000000000000000000", "src_contracts": { "geth_2337": { - "on_ramp": "0xB06c856C8eaBd1d8321b687E188204C1018BC4E5", - "deployed_at": 1798 + "on_ramp": "0xc351628EB244ec633d5f21fBD6621e1a683B1181", + "deployed_at": 258 } }, "dest_contracts": { "geth_2337": { - "off_ramp": "0x1780bCf4103D3F501463AD3414c7f4b654bb7aFd", - "commit_store": "0x02df3a3F960393F5B349E40A599FEda91a7cc1A7", - "receiver_dapp": "0x8F4ec854Dd12F1fe79500a1f53D0cbB30f9b6134" + "off_ramp": "0x922D6956C99E12DFeB3224DEA977D0939758A1Fe", + "commit_store": "0xB0D4afd8879eD9F52b28595d31B441D079B2Ca07", + "receiver_dapp": "0xdbC43Ba45381e02825b14322cDdd15eC4B3164E6" } } }, "geth_2337": { "is_mock_arm": true, - "fee_token": "0x58AA941Ba0568DF0886B6eC41233cFe94bF32178", + "fee_token": "0x20Fbd46DeEd5EEDEB6e5c87eeB31924e9CA312ad", "bridge_tokens": [ - "0x5C3A8b1E8273F52EF64D70CEAB437AA6f7092f65", - "0xd5C9Febf02791Bd2AE746FAF9e51Ea4Af25Ad2cb" + "0xAd5d57aD9bB17d34Debb88566ab2F5dB879Cc46F", + "0x7290f72B5C67052DDE8e6E179F7803c493e90d3f" ], "bridge_tokens_pools": [ - "0x07F1599D3fCEC400eFf2528f29fD4f00E9A61062", - "0x87Ce66A0E46924a164E967159EDc7A2E45AF926A" + "0xc63d2a04762529edB649d7a4cC3E57A0085e8544", + "0xD499f5F7d3C918D0e553BA03954c4E02af16B6e4" ], - "arm": "0x8896Dce0E60a706244553ADA1aAc5CDCc40a0428", - "router": "0x61B626A562d39439Ab60fCbF19678Dac036D1209", - "price_registry": "0x6D1366b16EAafbB328432894981F71CE55b0FDF8", - "wrapped_native": "0x2575d6D30767149c99589cce743656fA3866ca2e", + "arm": "0xcEC91d876E8f003110D43381359b1bAd124e7F2b", + "router": "0x8613A4029EaA95dA61AE65380aC2e7366451bF2b", + "price_registry": "0x49FcbCC4E425add3a45AFC82F4dD0E5c227A0Ff8", + "wrapped_native": "0x5370F78c6af2Da9cF6642382A3a75F9D5aEc9cc1", "multicall": "0x0000000000000000000000000000000000000000", "src_contracts": { "geth_1337": { - "on_ramp": "0xf9FFCCdb71D68eeBa284e6b8cbF35146f018C9b9", - "deployed_at": 1795 + "on_ramp": "0xA82ED5224ba72f2f776e09B11DC99E30Ee65Da8d", + "deployed_at": 258 } }, "dest_contracts": { "geth_1337": { - "off_ramp": "0x181522bb19Ae83E9984c469f67DAe5602F0BC3de", - "commit_store": "0x2d0adf1795389d6fB7d231ea5D282F1b19be5495", - "receiver_dapp": "0xC7981C4AB397E174964C0A927a4dE71143A09708" + "off_ramp": "0x199c27B10a195ee79e02d50846e59A4aFB82CAD1", + "commit_store": "0xf69E1dFAc3D43F438Bae80090b8E186B0231CFeb", + "receiver_dapp": "0x7798A400cBe0Ca14a7D614ECa1CD15adE5055413" } } } @@ -76,8 +76,8 @@ selected_networks = ['geth_1337', 'geth_2337'] [CCIP.Env.Network.EVMNetworks.geth_1337] evm_name = 'geth_1337' evm_chain_id = 1337 -evm_urls = ['ws://127.1.27.6:8546'] -evm_http_urls = ['http://127.1.27.6:8544'] +evm_urls = ['wss://skudasov-crib-ccip-geth-1337-ws.main.stage.cldev.sh'] +evm_http_urls = ['https://skudasov-crib-ccip-geth-1337-ws.main.stage.cldev.sh'] evm_keys = ['ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'] evm_simulated = true client_implementation = 'Ethereum' @@ -87,12 +87,13 @@ evm_minimum_confirmations = 1 evm_gas_estimation_buffer = 10000 evm_supports_eip1559 = true evm_default_gas_limit = 6000000 +evm_finality_depth = 1 [CCIP.Env.Network.EVMNetworks.geth_2337] evm_name = 'geth_2337' evm_chain_id = 2337 -evm_urls = ['ws://127.1.27.5:8546'] -evm_http_urls = ['http://127.1.27.5:8544'] +evm_urls = ['wss://skudasov-crib-ccip-geth-2337-ws.main.stage.cldev.sh'] +evm_http_urls = ['https://skudasov-crib-ccip-geth-2337-ws.main.stage.cldev.sh'] evm_keys = ['59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'] evm_simulated = true client_implementation = 'Ethereum' @@ -102,45 +103,45 @@ evm_minimum_confirmations = 1 evm_gas_estimation_buffer = 10000 evm_supports_eip1559 = true evm_default_gas_limit = 6000000 - +evm_finality_depth = 1 [CCIP.Env.ExistingCLCluster] Name = 'crib-ani' NoOfNodes = 6 [[CCIP.Env.ExistingCLCluster.NodeConfigs]] -URL = 'https://crib-ani-node1.main.stage.cldev.sh/' +URL = 'https://skudasov-crib-ccip-node1.main.stage.cldev.sh/' Email = 'notreal@fakeemail.ch' Password = 'fj293fbBnlQ!f9vNs' InternalIP = 'app-node-1' [[CCIP.Env.ExistingCLCluster.NodeConfigs]] -URL = 'https://crib-ani-node2.main.stage.cldev.sh/' +URL = 'https://skudasov-crib-ccip-node2.main.stage.cldev.sh/' Email = 'notreal@fakeemail.ch' Password = 'fj293fbBnlQ!f9vNs' InternalIP = 'app-node-2' [[CCIP.Env.ExistingCLCluster.NodeConfigs]] -URL = 'https://crib-ani-node3.main.stage.cldev.sh/' +URL = 'https://skudasov-crib-ccip-node3.main.stage.cldev.sh/' Email = 'notreal@fakeemail.ch' Password = 'fj293fbBnlQ!f9vNs' InternalIP = 'app-node-3' [[CCIP.Env.ExistingCLCluster.NodeConfigs]] -URL = 'https://crib-ani-node4.main.stage.cldev.sh/' +URL = 'https://skudasov-crib-ccip-node4.main.stage.cldev.sh/' Email = 'notreal@fakeemail.ch' Password = 'fj293fbBnlQ!f9vNs' InternalIP = 'app-node-4' [[CCIP.Env.ExistingCLCluster.NodeConfigs]] -URL = 'https://crib-ani-node5.main.stage.cldev.sh/' +URL = 'https://skudasov-crib-ccip-node5.main.stage.cldev.sh/' Email = 'notreal@fakeemail.ch' Password = 'fj293fbBnlQ!f9vNs' InternalIP = 'app-node-5' [[CCIP.Env.ExistingCLCluster.NodeConfigs]] -URL = 'https://crib-ani-node6.main.stage.cldev.sh/' +URL = 'https://skudasov-crib-ccip-node6.main.stage.cldev.sh/' Email = 'notreal@fakeemail.ch' Password = 'fj293fbBnlQ!f9vNs' InternalIP = 'app-node-6' @@ -148,14 +149,15 @@ InternalIP = 'app-node-6' [CCIP.Groups] [CCIP.Groups.smoke] LocalCluster = false -TestRunName = 'crib-ani-smoke' +TestRunName = 'skudasov-crib-ccip' +NodeFunding = 1000.0 ExistingDeployment = true [CCIP.Groups.load] LocalCluster = false -TestRunName = 'crib-ani-load' +TestRunName = 'skudasov-crib-ccip' TimeUnit = '1s' -TestDuration = '1m' +TestDuration = '15m' RequestPerUnitTime = [1] -NodeFunding = 100.0 +NodeFunding = 1000.0 ExistingDeployment = true diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml index 7393edd42d..5fd7051966 100644 --- a/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml @@ -12,6 +12,54 @@ TTL = '5h' [CCIP.Env.Network] selected_networks= ['SIMULATED_1', 'SIMULATED_2'] +# PrivateEthereumNetworks.NETWORK_NAME contains the configuration of private ethereum network that includes ethereum version, evm node client, chain id, +# certain chain configurations, addresses to fund or custom docker images to be used. These are non-dev networks, but they all run just a single node. +[CCIP.Env.PrivateEthereumNetworks.SIMULATED_1] +# either eth1 or eth2 (for post-Merge); for eth2 Prysm is used for consensus layer. +ethereum_version="eth1" +# geth, besu, erigon or nethermind +execution_layer="geth" +# eth2-only, if set to true environment startup will wait until at least 1 epoch has been finalised +#wait_for_finalization=false + +[CCIP.Env.PrivateEthereumNetworks.SIMULATED_1.EthereumChainConfig] +# eth2-only, the lower the value the faster the block production (3 is minimum) +seconds_per_slot=3 +# eth2-only, the lower the value the faster the epoch finalisation (2 is minimum) +slots_per_epoch=2 +# eht2-only, the lower tha value the faster the chain starts (10 is minimum) +genesis_delay=15 +# eth2-only, number of validators +validator_count=4 +chain_id=1337 +# address that should be founded in genesis wih ETH +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"] + +[CCIP.Env.PrivateEthereumNetworks.SIMULATED_1.EthereumChainConfig.HardForkEpochs] +# eth2-only, epoch at which chain will upgrade do Dencun or Deneb/Cancun (1 is minimum) +Deneb=500 + +# [PrivateEthereumNetwork.CustomDockerImages] +# custom docker image that will be used for execution layer client. It has to be one of: hyperledger/besu, nethermind/nethermind, thorax/erigon or ethereum/client-go. +# instead of using a specific tag you can also use "latest_available" to use latest published tag in Github or "latest_stable" to use latest stable release from Github +# (if corresponding Docker image on Docker Hub has not been published environment creation will fail). +# execution_layer="hyperledger/besu:24.2.0-RC2" + +[CCIP.Env.PrivateEthereumNetworks.SIMULATED_2] +ethereum_version="eth1" +execution_layer="geth" + +[CCIP.Env.PrivateEthereumNetworks.SIMULATED_2.EthereumChainConfig] +seconds_per_slot=3 +slots_per_epoch=2 +genesis_delay=15 +validator_count=4 +chain_id=2337 +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"] + +[CCIP.Env.PrivateEthereumNetworks.SIMULATED_2.EthereumChainConfig.HardForkEpochs] +Deneb=500 + [CCIP.Env.Logging] test_log_collect=false # if set to true will save logs even if test did not fail @@ -39,7 +87,6 @@ DBCapacity = '10Gi' # disk space to be allocated to postgresql database ; only u IsStateful = true # if true, chainlink nodes and postgresql database will be deployed as stateful set in k8s DBArgs = ['shared_buffers=1536MB', 'effective_cache_size=4096MB', 'work_mem=64MB'] # postgresql database arguments ; only used if tests are in k8s - # these values will be used to set up chainlink DON, if all the chainlink nodes are deployed with same configuration [CCIP.Env.NewCLCluster.Common] Name = 'node1' # name of the chainlink node, used as prefix for all the chainlink node names , used for k8s deployment @@ -201,6 +248,9 @@ NoOfNetworks = 2 # this is used with Networks in `CCIP.Env`, Blockscout = false # if true, the test will use blockscout TimeoutForPriceUpdate = '15m' # Duration to wait for the price update to time-out. +# Now testing only with dynamic price getter (no pipeline). +# Could be removed once the pipeline is completely removed. +WithPipeline = false # uncomment the following if you want to run your tests with specific number of lanes; # in this case out of all the possible lane combinations, only the ones with the specified number of lanes will be considered @@ -238,9 +288,10 @@ AmountPerToken = 1 # same as above MulticallInOneTx = false # same as above NoOfSendsInMulticall = 5 # same as above RequestPerUnitTime = [1] # number of ccip requests to be sent per unit time -TimeUnit = '1s' # unit of time for RequestPerUnitTime +TimeUnit = '10s' # unit of time for RequestPerUnitTime NoOfNetworks = 2 # same as above Blockscout = false # same as above +WithPipeline = false TimeoutForPriceUpdate = '15m' # time to wait for price update # uncomment the following if you want to run your tests with specific number of lanes; @@ -249,6 +300,36 @@ TimeoutForPriceUpdate = '15m' # time to wait for price update # then only random combinations of 2 lanes from the following will be considered for the test : # ['SIMULATED_1', 'SIMULATED_2'], ['SIMULATED_1', 'SIMULATED_3'], ['SIMULATED_2', 'SIMULATED_3'] #MaxNoOfLanes = # maximum number of lanes to be added in the test; mainly used for scalability tests +# + +# Uncomment the following if you want to run your tests with updated OCR params +# otherwise test will use default OCR params from - +# https://github.com/smartcontractkit/ccip/blob/ccip-develop/integration-tests/ccip-tests/contracts/contract_deployer.go#L729-L751 +## OCR Params +#CommitInflightExpiry = '2m' +#ExecInflightExpiry = '2m' +# +#[CCIP.Groups.load.CommitOCRParams] +#DeltaProgress = '2m' +#DeltaResend = '5s' +#DeltaRound = '75s' +#DeltaGrace = '5s' +#MaxDurationQuery = '100ms' +#MaxDurationObservation = '35s' +#MaxDurationReport = '10s' +#MaxDurationShouldAcceptFinalizedReport = '5s' +#MaxDurationShouldTransmitAcceptedReport = '10s' +# +#[CCIP.Groups.load.ExecOCRParams] +#DeltaProgress = '100s' +#DeltaResend = '5s' +#DeltaRound = '40s' +#DeltaGrace = '5s' +#MaxDurationQuery = '100ms' +#MaxDurationObservation = '20s' +#MaxDurationReport = '8s' +#MaxDurationShouldAcceptFinalizedReport = '5s' +#MaxDurationShouldTransmitAcceptedReport = '8s' [CCIP.Groups.chaos] # uncomment the following with specific values of lane combinations to be tested, if you want to run your tests to run only on these specific network pairs @@ -281,6 +362,7 @@ Blockscout = false ChaosDuration = '10m' # Duration for whichever chaos will be injected; only valid for chaos tests WaitBetweenChaosDuringLoad = '2m' # Duration to wait between each chaos injection during load test; only valid for chaos tests TimeoutForPriceUpdate = '15m' # Timeout for price update +WithPipeline = false # uncomment the following if you want to run your tests with specific number of lanes; # in this case out of all the possible lane combinations, only the ones with the specified number of lanes will be considered # for example, if you have provided CCIP.Env.Networks = ['SIMULATED_1', 'SIMULATED_2', 'SIMULATED_3'] and CCIP.Groups..MaxNoOfLanes = 2, diff --git a/integration-tests/ccip-tests/testconfig/tomls/db-compatibility.toml b/integration-tests/ccip-tests/testconfig/tomls/db-compatibility.toml index fad575b0f6..4f6b7680c3 100644 --- a/integration-tests/ccip-tests/testconfig/tomls/db-compatibility.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/db-compatibility.toml @@ -6,17 +6,17 @@ NoOfNodes = 6 [[CCIP.Env.NewCLCluster.Nodes]] Name = 'node1' DBImage = 'postgres' -DBTag = '13.12' +DBTag = '13.14' [[CCIP.Env.NewCLCluster.Nodes]] Name = 'node2' DBImage = 'postgres' -DBTag = '11.22-alpine' +DBTag = '12.18' [[CCIP.Env.NewCLCluster.Nodes]] Name = 'node3' DBImage = 'postgres' -DBTag = '12.15' +DBTag = '14.11' [[CCIP.Env.NewCLCluster.Nodes]] Name = 'node4' @@ -26,9 +26,9 @@ DBTag = '14.8' [[CCIP.Env.NewCLCluster.Nodes]] Name = 'node5' DBImage = 'postgres' -DBTag = '15.5' +DBTag = '15.6' [[CCIP.Env.NewCLCluster.Nodes]] Name = 'node6' DBImage = 'postgres' -DBTag = '15.1' \ No newline at end of file +DBTag = '15.6' \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/tomls/load-with-arm-curse-uncurse.toml b/integration-tests/ccip-tests/testconfig/tomls/load-with-arm-curse-uncurse.toml new file mode 100644 index 0000000000..1f3029dc92 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/load-with-arm-curse-uncurse.toml @@ -0,0 +1,14 @@ +[CCIP] +[CCIP.Env] +TTL = '15h' + +[CCIP.Groups] +[CCIP.Groups.load] +KeepEnvAlive = true +MsgType = 'WithToken' +PhaseTimeout = '50m' +TestDuration = '30m' +NodeFunding = 1000.0 +RequestPerUnitTime = [2] +TimeUnit = '1m' +SendMaxDataInEveryMsgCount = 0 diff --git a/integration-tests/ccip-tests/testconfig/tomls/usdc_mock_deployment.toml b/integration-tests/ccip-tests/testconfig/tomls/usdc_mock_deployment.toml index 54b6a980ba..00af344940 100644 --- a/integration-tests/ccip-tests/testconfig/tomls/usdc_mock_deployment.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/usdc_mock_deployment.toml @@ -1,7 +1,7 @@ [CCIP] [CCIP.Groups] [CCIP.Groups.smoke] -USDCMockDeployment = true -NoOfTokensPerChain = 1 -NoOfTokensInMsg = 1 +NoOfUSDCMockTokens = 1 +NoOfTokensPerChain = 2 +NoOfTokensInMsg = 3 AmountPerToken = 1 \ No newline at end of file diff --git a/integration-tests/ccip-tests/testreporters/ccip.go b/integration-tests/ccip-tests/testreporters/ccip.go index 855bbaab05..4b49019954 100644 --- a/integration-tests/ccip-tests/testreporters/ccip.go +++ b/integration-tests/ccip-tests/testreporters/ccip.go @@ -31,6 +31,7 @@ const ( ReportBlessed Phase = "ReportBlessedByARM" Success Status = "✅" Failure Status = "❌" + Unsure = "⚠️" slackFile string = "payload_ccip.json" ) @@ -84,15 +85,15 @@ func (stat *RequestStat) UpdateState(lggr zerolog.Logger, seqNum uint64, step Ph event.Uint64("seq num", seqNum) } // if any of the phase fails mark the E2E as failed - if state == Failure { + if state == Failure || state == Unsure { stat.StatusByPhase[E2E] = PhaseStat{ SeqNum: seqNum, Status: state, } lggr.Info(). - Str(fmt.Sprint(E2E), string(Failure)). + Str(fmt.Sprint(E2E), string(state)). Msgf("reqNo %d", stat.ReqNo) - event.Str(string(step), string(Failure)).Msgf("reqNo %d", stat.ReqNo) + event.Str(string(step), string(state)).Msgf("reqNo %d", stat.ReqNo) } else { event.Str(string(step), string(Success)).Msgf("reqNo %d", stat.ReqNo) if step == Commit || step == ReportBlessed || step == ExecStateChanged { @@ -175,7 +176,10 @@ func (testStats *CCIPLaneStats) Finalize(lane string) { } return true }) - + // if no phase stats are found return + if testStats.TotalRequests <= 0 { + return + } testStats.lggr.Info().Int64("Total Requests Triggerred", testStats.TotalRequests).Msg("Test Run Completed") for _, phase := range phases { events[phase] = testStats.lggr.Info().Str("Phase", string(phase)) diff --git a/integration-tests/ccip-tests/testsetups/ccip.go b/integration-tests/ccip-tests/testsetups/ccip.go index d14e8435a4..91db9e609c 100644 --- a/integration-tests/ccip-tests/testsetups/ccip.go +++ b/integration-tests/ccip-tests/testsetups/ccip.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "dario.cat/mergo" "github.com/AlekSi/pointer" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" @@ -22,6 +23,8 @@ import ( "go.uber.org/zap/zapcore" "golang.org/x/sync/errgroup" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" ctftestenv "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" @@ -33,6 +36,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/contracts" + integrationactions "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/contracts/laneconfig" @@ -76,6 +81,10 @@ func (c *CCIPTestConfig) useExistingDeployment() bool { return pointer.GetBool(c.TestGroupInput.ExistingDeployment) } +func (c *CCIPTestConfig) MultiCallEnabled() bool { + return pointer.GetBool(c.TestGroupInput.MulticallInOneTx) +} + func (c *CCIPTestConfig) localCluster() bool { return pointer.GetBool(c.TestGroupInput.LocalCluster) } @@ -184,31 +193,49 @@ func (c *CCIPTestConfig) SetNetworkPairs(lggr zerolog.Logger) error { // If provided networks is lesser than the required number of networks // and the provided networks are simulated network, create replicas of the provided networks with // different chain ids - if len(c.SelectedNetworks) < c.TestGroupInput.NoOfNetworks { - if simulated { - actualNoOfNetworks := len(c.SelectedNetworks) - n := c.SelectedNetworks[0] - var chainIDs []int64 - for _, id := range chainselectors.TestChainIds() { - if id == 2337 { - continue - } - chainIDs = append(chainIDs, int64(id)) + if simulated && len(c.SelectedNetworks) < c.TestGroupInput.NoOfNetworks { + actualNoOfNetworks := len(c.SelectedNetworks) + n := c.SelectedNetworks[0] + var chainIDs []int64 + for _, id := range chainselectors.TestChainIds() { + if id == 2337 { + continue } - for i := 0; i < c.TestGroupInput.NoOfNetworks-actualNoOfNetworks; i++ { - chainID := chainIDs[i] - c.SelectedNetworks = append(c.SelectedNetworks, blockchain.EVMNetwork{ - Name: fmt.Sprintf("simulated-non-dev%d", len(c.SelectedNetworks)+1), - ChainID: chainID, - Simulated: true, - PrivateKeys: []string{networks.AdditionalSimulatedPvtKeys[i]}, - ChainlinkTransactionLimit: n.ChainlinkTransactionLimit, - Timeout: n.Timeout, - MinimumConfirmations: n.MinimumConfirmations, - GasEstimationBuffer: n.GasEstimationBuffer + 1000, - ClientImplementation: n.ClientImplementation, - DefaultGasLimit: n.DefaultGasLimit, - }) + chainIDs = append(chainIDs, int64(id)) + } + for i := 0; i < c.TestGroupInput.NoOfNetworks-actualNoOfNetworks; i++ { + chainID := chainIDs[i] + // if i is greater than the number of simulated pvt keys, rotate the keys + if i > len(networks.AdditionalSimulatedPvtKeys)-1 { + networks.AdditionalSimulatedPvtKeys = append(networks.AdditionalSimulatedPvtKeys, networks.AdditionalSimulatedPvtKeys...) + } + c.SelectedNetworks = append(c.SelectedNetworks, blockchain.EVMNetwork{ + Name: fmt.Sprintf("simulated-non-dev%d", len(c.SelectedNetworks)+1), + ChainID: chainID, + Simulated: true, + PrivateKeys: []string{networks.AdditionalSimulatedPvtKeys[i]}, + ChainlinkTransactionLimit: n.ChainlinkTransactionLimit, + Timeout: n.Timeout, + MinimumConfirmations: n.MinimumConfirmations, + GasEstimationBuffer: n.GasEstimationBuffer + 1000, + ClientImplementation: n.ClientImplementation, + DefaultGasLimit: n.DefaultGasLimit, + FinalityDepth: n.FinalityDepth, + }) + chainConfig := &ctftestenv.EthereumChainConfig{} + err := chainConfig.Default() + if err != nil { + allError = multierr.Append(allError, fmt.Errorf("failed to get default chain config: %w", err)) + } else { + chainConfig.ChainID = int(chainID) + eth1 := ctftestenv.EthereumVersion_Eth1 + geth := ctftestenv.ExecutionLayer_Geth + + c.EnvInput.PrivateEthereumNetworks[fmt.Sprint(chainID)] = &ctftestenv.EthereumNetwork{ + EthereumVersion: ð1, + ExecutionLayer: &geth, + EthereumChainConfig: chainConfig, + } } } } @@ -243,6 +270,28 @@ func (c *CCIPTestConfig) FormNetworkPairCombinations() { } } +func (c *CCIPTestConfig) SetOCRParams() error { + if c.TestGroupInput.CommitOCRParams != nil { + err := mergo.Merge(&contracts.OCR2ParamsForCommit, c.TestGroupInput.CommitOCRParams, mergo.WithOverride) + if err != nil { + return err + } + } + if c.TestGroupInput.ExecOCRParams != nil { + err := mergo.Merge(&contracts.OCR2ParamsForExec, c.TestGroupInput.ExecOCRParams, mergo.WithOverride) + if err != nil { + return err + } + } + if c.TestGroupInput.ExecInflightExpiry != nil && c.TestGroupInput.ExecInflightExpiry.Duration() > 0 { + actions.InflightExpiryExec = c.TestGroupInput.ExecInflightExpiry.Duration() + } + if c.TestGroupInput.CommitInflightExpiry != nil && c.TestGroupInput.CommitInflightExpiry.Duration() > 0 { + actions.InflightExpiryCommit = c.TestGroupInput.CommitInflightExpiry.Duration() + } + return nil +} + func NewCCIPTestConfig(t *testing.T, lggr zerolog.Logger, tType string) *CCIPTestConfig { testCfg := testconfig.GlobalTestConfig() groupCfg, exists := testCfg.CCIP.Groups[tType] @@ -264,7 +313,11 @@ func NewCCIPTestConfig(t *testing.T, lggr zerolog.Logger, tType string) *CCIPTes TestGroupInput: groupCfg, GethResourceProfile: GethResourceProfile, } - err := ccipTestConfig.SetNetworkPairs(lggr) + err := ccipTestConfig.SetOCRParams() + if err != nil { + t.Fatal(err) + } + err = ccipTestConfig.SetNetworkPairs(lggr) if err != nil { t.Fatal(err) } @@ -279,19 +332,19 @@ type BiDirectionalLaneConfig struct { } type CCIPTestSetUpOutputs struct { - Cfg *CCIPTestConfig - LaneContractsByNetwork sync.Map - laneMutex *sync.Mutex - Lanes []*BiDirectionalLaneConfig - CommonContractsByNetwork sync.Map - Reporter *testreporters.CCIPTestReporter - LaneConfigFile string - LaneConfig *laneconfig.Lanes - TearDown func() error - Env *actions.CCIPTestEnv - Balance *actions.BalanceSheet - BootstrapAdded *atomic.Bool - JobAddGrp *errgroup.Group + SetUpContext context.Context + Cfg *CCIPTestConfig + LaneContractsByNetwork *sync.Map + laneMutex *sync.Mutex + Lanes []*BiDirectionalLaneConfig + Reporter *testreporters.CCIPTestReporter + LaneConfigFile string + LaneConfig *laneconfig.Lanes + TearDown func() error + Env *actions.CCIPTestEnv + Balance *actions.BalanceSheet + BootstrapAdded *atomic.Bool + JobAddGrp *errgroup.Group } func (o *CCIPTestSetUpOutputs) AddToLanes(lane *BiDirectionalLaneConfig) { @@ -307,11 +360,12 @@ func (o *CCIPTestSetUpOutputs) ReadLanes() []*BiDirectionalLaneConfig { } func (o *CCIPTestSetUpOutputs) DeployChainContracts( + lggr zerolog.Logger, chainClient blockchain.EVMClient, networkCfg blockchain.EVMNetwork, noOfTokens int, tokenDeployerFns []blockchain.ContractDeployer, - lggr zerolog.Logger, + selectors []uint64, ) error { var k8Env *environment.Environment ccipEnv := o.Env @@ -331,21 +385,45 @@ func (o *CCIPTestSetUpOutputs) DeployChainContracts( defer chain.Close() ccipCommon, err := actions.DefaultCCIPModule( lggr, chain, - pointer.GetBool(o.Cfg.TestGroupInput.ExistingDeployment), - pointer.GetBool(o.Cfg.TestGroupInput.MulticallInOneTx), - pointer.GetBool(o.Cfg.TestGroupInput.USDCMockDeployment)) + o.Cfg.useExistingDeployment(), + o.Cfg.MultiCallEnabled(), + o.Cfg.TestGroupInput.NoOfUSDCMockTokens) if err != nil { return errors.WithStack(fmt.Errorf("failed to create ccip common module for %s: %w", networkCfg.Name, err)) } - + ccipCommon.RemoteChains = selectors cfg := o.LaneConfig.ReadLaneConfig(networkCfg.Name) err = ccipCommon.DeployContracts(noOfTokens, tokenDeployerFns, cfg) if err != nil { return errors.WithStack(fmt.Errorf("failed to deploy common ccip contracts for %s: %w", networkCfg.Name, err)) } + ccipCommon.WriteLaneConfig(cfg) o.LaneContractsByNetwork.Store(networkCfg.Name, cfg) - o.CommonContractsByNetwork.Store(networkCfg.Name, ccipCommon) + + return nil +} + +func (o *CCIPTestSetUpOutputs) SetupDynamicTokenPriceUpdates() error { + interval := o.Cfg.TestGroupInput.DynamicPriceUpdateInterval.Duration() + covered := make(map[string]struct{}) + for _, lanes := range o.ReadLanes() { + lane := lanes.ForwardLane + if _, exists := covered[lane.SourceNetworkName]; !exists { + covered[lane.SourceNetworkName] = struct{}{} + err := lane.Source.Common.UpdateTokenPricesAtRegularInterval(lane.Context, interval, o.LaneConfig.ReadLaneConfig(lane.SourceNetworkName)) + if err != nil { + return err + } + } + if _, exists := covered[lane.DestNetworkName]; !exists { + covered[lane.DestNetworkName] = struct{}{} + err := lane.Dest.Common.UpdateTokenPricesAtRegularInterval(lane.Context, interval, o.LaneConfig.ReadLaneConfig(lane.SourceNetworkName)) + if err != nil { + return err + } + } + } return nil } @@ -354,8 +432,7 @@ func (o *CCIPTestSetUpOutputs) AddLanesForNetworkPair( networkA, networkB blockchain.EVMNetwork, chainClientA, chainClientB blockchain.EVMClient, transferAmounts []*big.Int, - numOfCommitNodes int, - commitAndExecOnSameDON, bidirectional bool, + commitAndExecOnSameDON, bidirectional, withPipeline bool, ) error { var allErrors atomic.Error t := o.Cfg.Test @@ -369,7 +446,7 @@ func (o *CCIPTestSetUpOutputs) AddLanesForNetworkPair( } } configureCLNode := !pointer.GetBool(o.Cfg.TestGroupInput.ExistingDeployment) - setUpFuncs, ctx := errgroup.WithContext(context.Background()) + setUpFuncs, ctx := errgroup.WithContext(testcontext.Get(t)) // Use new set of clients(sourceChainClient,destChainClient) // with new header subscriptions(otherwise transactions @@ -391,7 +468,6 @@ func (o *CCIPTestSetUpOutputs) AddLanesForNetworkPair( ccipLaneA2B := &actions.CCIPLane{ Test: t, - TestEnv: ccipEnv, SourceChain: sourceChainClientA2B, DestChain: destChainClientA2B, SourceNetworkName: actions.NetworkName(networkA.Name), @@ -400,7 +476,7 @@ func (o *CCIPTestSetUpOutputs) AddLanesForNetworkPair( SentReqs: make(map[common.Hash][]actions.CCIPRequest), TotalFee: big.NewInt(0), Balance: o.Balance, - Context: ctx, + Context: testcontext.Get(t), } contractsA, ok := o.LaneContractsByNetwork.Load(networkA.Name) if !ok { @@ -443,7 +519,6 @@ func (o *CCIPTestSetUpOutputs) AddLanesForNetworkPair( ccipLaneB2A = &actions.CCIPLane{ Test: t, - TestEnv: ccipEnv, SourceNetworkName: actions.NetworkName(networkB.Name), DestNetworkName: actions.NetworkName(networkA.Name), SourceChain: sourceChainClientB2A, @@ -452,7 +527,7 @@ func (o *CCIPTestSetUpOutputs) AddLanesForNetworkPair( Balance: o.Balance, SentReqs: make(map[common.Hash][]actions.CCIPRequest), TotalFee: big.NewInt(0), - Context: ctx, + Context: testcontext.Get(t), SrcNetworkLaneCfg: ccipLaneA2B.DstNetworkLaneCfg, DstNetworkLaneCfg: ccipLaneA2B.SrcNetworkLaneCfg, } @@ -464,72 +539,70 @@ func (o *CCIPTestSetUpOutputs) AddLanesForNetworkPair( } o.AddToLanes(bidirectionalLane) - c1, ok := o.CommonContractsByNetwork.Load(networkA.Name) - var networkACmn *actions.CCIPCommon - if ok { - networkACmn = c1.(*actions.CCIPCommon) - } - if networkACmn == nil { - return errors.WithStack(fmt.Errorf("chain contracts for network %s not found", networkA.Name)) - } - c2, ok := o.CommonContractsByNetwork.Load(networkB.Name) - var networkBCmn *actions.CCIPCommon - if ok { - networkBCmn = c2.(*actions.CCIPCommon) - } - if networkBCmn == nil { - return errors.WithStack(fmt.Errorf("chain contracts for network %s not found", networkB.Name)) - } - - // Now testing only with dynamic price getter (no pipeline). - // Could be removed once the pipeline is completely removed. - withPipeline := false - + staticPrice := o.Cfg.TestGroupInput.DynamicPriceUpdateInterval == nil setUpFuncs.Go(func() error { lggr.Info().Msgf("Setting up lane %s to %s", networkA.Name, networkB.Name) - srcConfig, destConfig, err := ccipLaneA2B.DeployNewCCIPLane(numOfCommitNodes, commitAndExecOnSameDON, networkACmn, networkBCmn, - transferAmounts, o.BootstrapAdded, configureCLNode, o.JobAddGrp, withPipeline) + err := ccipLaneA2B.DeployNewCCIPLane(o.SetUpContext, o.Env, commitAndExecOnSameDON, + transferAmounts, o.BootstrapAdded, configureCLNode, o.JobAddGrp, + withPipeline, staticPrice, + o.Cfg.useExistingDeployment(), + o.Cfg.MultiCallEnabled(), + o.Cfg.TestGroupInput.NoOfUSDCMockTokens, + ) if err != nil { allErrors.Store(multierr.Append(allErrors.Load(), fmt.Errorf("deploying lane %s to %s; err - %w", networkA.Name, networkB.Name, errors.WithStack(err)))) return err } - err = o.LaneConfig.WriteLaneConfig(networkA.Name, srcConfig) + err = o.LaneConfig.WriteLaneConfig(networkA.Name, ccipLaneA2B.SrcNetworkLaneCfg) if err != nil { lggr.Error().Err(err).Msgf("error deploying lane %s to %s", networkA.Name, networkB.Name) allErrors.Store(multierr.Append(allErrors.Load(), fmt.Errorf("writing lane config for %s; err - %w", networkA.Name, errors.WithStack(err)))) return err } - err = o.LaneConfig.WriteLaneConfig(networkB.Name, destConfig) + err = o.LaneConfig.WriteLaneConfig(networkB.Name, ccipLaneA2B.DstNetworkLaneCfg) if err != nil { allErrors.Store(multierr.Append(allErrors.Load(), fmt.Errorf("writing lane config for %s; err - %w", networkB.Name, errors.WithStack(err)))) return err } lggr.Info().Msgf("done setting up lane %s to %s", networkA.Name, networkB.Name) + if pointer.GetBool(o.Cfg.TestGroupInput.OptimizeSpace) { + // This is to optimize memory space for load tests with high number of networks, lanes, tokens + ccipLaneA2B.OptimizeStorage() + } return nil }) setUpFuncs.Go(func() error { if bidirectional { lggr.Info().Msgf("Setting up lane %s to %s", networkB.Name, networkA.Name) - srcConfig, destConfig, err := ccipLaneB2A.DeployNewCCIPLane(numOfCommitNodes, commitAndExecOnSameDON, networkBCmn, networkACmn, - transferAmounts, o.BootstrapAdded, configureCLNode, o.JobAddGrp, withPipeline) + err := ccipLaneB2A.DeployNewCCIPLane(o.SetUpContext, o.Env, commitAndExecOnSameDON, + transferAmounts, o.BootstrapAdded, configureCLNode, o.JobAddGrp, + withPipeline, staticPrice, + o.Cfg.useExistingDeployment(), + o.Cfg.MultiCallEnabled(), + o.Cfg.TestGroupInput.NoOfUSDCMockTokens, + ) if err != nil { lggr.Error().Err(err).Msgf("error deploying lane %s to %s", networkB.Name, networkA.Name) allErrors.Store(multierr.Append(allErrors.Load(), fmt.Errorf("deploying lane %s to %s; err - %w", networkB.Name, networkA.Name, errors.WithStack(err)))) return err } - err = o.LaneConfig.WriteLaneConfig(networkB.Name, srcConfig) + err = o.LaneConfig.WriteLaneConfig(networkB.Name, ccipLaneB2A.SrcNetworkLaneCfg) if err != nil { allErrors.Store(multierr.Append(allErrors.Load(), fmt.Errorf("writing lane config for %s; err - %w", networkA.Name, errors.WithStack(err)))) return err } - err = o.LaneConfig.WriteLaneConfig(networkA.Name, destConfig) + err = o.LaneConfig.WriteLaneConfig(networkA.Name, ccipLaneB2A.DstNetworkLaneCfg) if err != nil { allErrors.Store(multierr.Append(allErrors.Load(), fmt.Errorf("writing lane config for %s; err - %w", networkB.Name, errors.WithStack(err)))) return err } lggr.Info().Msgf("done setting up lane %s to %s", networkB.Name, networkA.Name) + if pointer.GetBool(o.Cfg.TestGroupInput.OptimizeSpace) { + // This is to optimize memory space for load tests with high number of networks, lanes, tokens + ccipLaneB2A.OptimizeStorage() + } return nil } return nil @@ -565,37 +638,29 @@ func (o *CCIPTestSetUpOutputs) StartEventWatchers() { } } -func (o *CCIPTestSetUpOutputs) WaitForPriceUpdates(ctx context.Context) { +func (o *CCIPTestSetUpOutputs) WaitForPriceUpdates() { t := o.Cfg.Test - priceUpdateGrp, _ := errgroup.WithContext(ctx) - priceUpdateTracker := sync.Map{} + priceUpdateGrp, _ := errgroup.WithContext(o.SetUpContext) for _, lanes := range o.ReadLanes() { lanes := lanes - waitForUpdate := func(lane actions.CCIPLane) error { - if id, ok := priceUpdateTracker.Load(lane.Source.Common.PriceRegistry.Address()); ok && - id.(uint64) == lane.Source.DestinationChainId { - return nil - } - priceUpdateTracker.Store(lane.Source.Common.PriceRegistry.Address(), lane.Source.DestinationChainId) - lane.Logger.Info(). - Str("source_chain", lane.Source.Common.ChainClient.GetNetworkName()). - Uint64("dest_chain", lane.Source.DestinationChainId). - Str("price_registry", lane.Source.Common.PriceRegistry.Address()). - Msgf("Waiting for price update") - err := lane.Source.Common.WatchForPriceUpdates() - if err != nil { - return err - } + forwardLane := lanes.ForwardLane + reverseLane := lanes.ReverseLane + waitForUpdate := func(lane *actions.CCIPLane) error { defer func() { lane.Logger.Info(). Str("source_chain", lane.Source.Common.ChainClient.GetNetworkName()). Uint64("dest_chain", lane.Source.DestinationChainId). Str("price_registry", lane.Source.Common.PriceRegistry.Address()). Msg("Stopping price update watch") - lane.Source.Common.StopWatchingPriceUpdates() + }() - err = lane.Source.Common.WaitForPriceUpdates( - lane.Logger, + lane.Logger.Info(). + Str("source_chain", lane.Source.Common.ChainClient.GetNetworkName()). + Uint64("dest_chain", lane.Source.DestinationChainId). + Str("price_registry", lane.Source.Common.PriceRegistry.Address()). + Msgf("Waiting for price update") + err := lane.Source.Common.WaitForPriceUpdates( + o.SetUpContext, lane.Logger, o.Cfg.TestGroupInput.TimeoutForPriceUpdate.Duration(), lane.Source.DestinationChainId, ) @@ -606,11 +671,11 @@ func (o *CCIPTestSetUpOutputs) WaitForPriceUpdates(ctx context.Context) { } priceUpdateGrp.Go(func() error { - return waitForUpdate(*lanes.ForwardLane) + return waitForUpdate(forwardLane) }) if lanes.ReverseLane != nil { priceUpdateGrp.Go(func() error { - return waitForUpdate(*lanes.ReverseLane) + return waitForUpdate(reverseLane) }) } } @@ -649,27 +714,29 @@ func CCIPDefaultTestSetUp( transferAmounts = append(transferAmounts, big.NewInt(testConfig.TestGroupInput.AmountPerToken)) } } + parent, cancel := context.WithCancel(context.Background()) + defer cancel() setUpArgs := &CCIPTestSetUpOutputs{ - Cfg: testConfig, - Reporter: testreporters.NewCCIPTestReporter(t, lggr), - LaneConfigFile: filename, - Balance: actions.NewBalanceSheet(), - BootstrapAdded: atomic.NewBool(false), - JobAddGrp: &errgroup.Group{}, - laneMutex: &sync.Mutex{}, + SetUpContext: parent, + Cfg: testConfig, + Reporter: testreporters.NewCCIPTestReporter(t, lggr), + LaneConfigFile: filename, + LaneContractsByNetwork: &sync.Map{}, + Balance: actions.NewBalanceSheet(), + BootstrapAdded: atomic.NewBool(false), + JobAddGrp: &errgroup.Group{}, + laneMutex: &sync.Mutex{}, } - parent, cancel := context.WithCancel(context.Background()) - defer cancel() - chainByChainID := setUpArgs.CreateEnvironment(parent, lggr, envName) + chainByChainID := setUpArgs.CreateEnvironment(lggr, envName) if setUpArgs.Env != nil { ccipEnv := setUpArgs.Env if ccipEnv.K8Env != nil && ccipEnv.K8Env.WillUseRemoteRunner() { return setUpArgs } } - _, err = os.Stat(setUpArgs.LaneConfigFile) - if err == nil { + laneCfgFile, err := os.Stat(setUpArgs.LaneConfigFile) + if err == nil && laneCfgFile.Size() > 1 { // remove the existing lane config file err = os.Remove(setUpArgs.LaneConfigFile) require.NoError(t, err, "error while removing existing lane config file - %s", setUpArgs.LaneConfigFile) @@ -703,29 +770,46 @@ func CCIPDefaultTestSetUp( } // deploy all chain specific common contracts - chainAddGrp, _ := errgroup.WithContext(parent) + chainAddGrp, _ := errgroup.WithContext(setUpArgs.SetUpContext) lggr.Info().Msg("Deploying common contracts") + chainSelectors := make(map[int64]uint64) + for _, net := range testConfig.AllNetworks { + if _, exists := chainSelectors[net.ChainID]; !exists { + chainSelectors[net.ChainID], err = chainselectors.SelectorFromChainId(uint64(net.ChainID)) + require.NoError(t, err) + } + } for _, net := range testConfig.AllNetworks { chain := chainByChainID[net.ChainID] net := net net.HTTPURLs = chain.GetNetworkConfig().HTTPURLs net.URLs = chain.GetNetworkConfig().URLs + var selectors []uint64 + for chainId, selector := range chainSelectors { + if chainId != net.ChainID { + selectors = append(selectors, selector) + } + } chainAddGrp.Go(func() error { - return setUpArgs.DeployChainContracts(chain, net, testConfig.TestGroupInput.NoOfTokensPerChain, tokenDeployerFns, lggr) + return setUpArgs.DeployChainContracts(lggr, chain, net, testConfig.TestGroupInput.NoOfTokensPerChain, tokenDeployerFns, selectors) }) } require.NoError(t, chainAddGrp.Wait(), "Deploying common contracts shouldn't fail") + withPipeline := pointer.GetBool(setUpArgs.Cfg.TestGroupInput.WithPipeline) + // set up mock server for price pipeline and usdc attestation if not using existing deployment if !pointer.GetBool(setUpArgs.Cfg.TestGroupInput.ExistingDeployment) { var killgrave *ctftestenv.Killgrave if setUpArgs.Env.LocalCluster != nil { killgrave = setUpArgs.Env.LocalCluster.MockAdapter } - // set up mock server for price pipeline. need to set it once for all the lanes as the price pipeline path uses - // regex to match the path for all tokens across all lanes - actions.SetMockserverWithTokenPriceValue(killgrave, setUpArgs.Env.MockServer) - if pointer.GetBool(setUpArgs.Cfg.TestGroupInput.USDCMockDeployment) { + if withPipeline { + // set up mock server for price pipeline. need to set it once for all the lanes as the price pipeline path uses + // regex to match the path for all tokens across all lanes + actions.SetMockserverWithTokenPriceValue(killgrave, setUpArgs.Env.MockServer) + } + if setUpArgs.Cfg.TestGroupInput.NoOfUSDCMockTokens != nil { // if it's a new USDC deployment, set up mock server for attestation, // we need to set it only once for all the lanes as the attestation path uses regex to match the path for // all messages across all lanes @@ -735,7 +819,9 @@ func CCIPDefaultTestSetUp( } // deploy all lane specific contracts lggr.Info().Msg("Deploying chain specific contracts") - laneAddGrp, _ := errgroup.WithContext(parent) + laneAddGrp, _ := errgroup.WithContext(setUpArgs.SetUpContext) + // for memory management set a batch size for active lane deployment group + laneAddGrp.SetLimit(200) for _, networkPair := range testConfig.NetworkPairs { n := networkPair var ok bool @@ -753,9 +839,9 @@ func CCIPDefaultTestSetUp( return setUpArgs.AddLanesForNetworkPair( lggr, n.NetworkA, n.NetworkB, chainByChainID[n.NetworkA.ChainID], chainByChainID[n.NetworkB.ChainID], transferAmounts, - testConfig.TestGroupInput.NoOfCommitNodes, pointer.GetBool(testConfig.TestGroupInput.CommitAndExecuteOnSameDON), pointer.GetBool(testConfig.TestGroupInput.BiDirectionalLane), + withPipeline, ) }) } @@ -764,13 +850,19 @@ func CCIPDefaultTestSetUp( require.NoError(t, err) require.Equal(t, len(setUpArgs.Lanes), len(testConfig.NetworkPairs), "Number of bi-directional lanes should be equal to number of network pairs") + // only required for env set up + setUpArgs.LaneContractsByNetwork = nil if configureCLNode { // wait for all jobs to get created lggr.Info().Msg("Waiting for jobs to be created") require.NoError(t, setUpArgs.JobAddGrp.Wait(), "Creating jobs shouldn't fail") - // wait for price updates to be available and start event watchers - setUpArgs.WaitForPriceUpdates(parent) + // wait for price updates to be available + setUpArgs.WaitForPriceUpdates() + // if dynamic price update is required + if setUpArgs.Cfg.TestGroupInput.DynamicPriceUpdateInterval != nil { + require.NoError(t, setUpArgs.SetupDynamicTokenPriceUpdates(), "setting up dynamic price update should not fail") + } } // start event watchers for all lanes @@ -801,7 +893,6 @@ func CCIPDefaultTestSetUp( // CreateEnvironment creates the environment for the test and registers the test clean-up function to tear down the set-up environment // It returns the map of chainID to EVMClient func (o *CCIPTestSetUpOutputs) CreateEnvironment( - parent context.Context, lggr zerolog.Logger, envName string, ) map[int64]blockchain.EVMClient { @@ -850,7 +941,7 @@ func (o *CCIPTestSetUpOutputs) CreateEnvironment( ClusterURL: mockserverURL, }) } - ccipEnv.CLNodeWithKeyReady, _ = errgroup.WithContext(parent) + ccipEnv.CLNodeWithKeyReady, _ = errgroup.WithContext(o.SetUpContext) o.Env = ccipEnv if ccipEnv.K8Env != nil && ccipEnv.K8Env.WillUseRemoteRunner() { return nil @@ -872,11 +963,13 @@ func (o *CCIPTestSetUpOutputs) CreateEnvironment( chainByChainID := make(map[int64]blockchain.EVMClient) if pointer.GetBool(testConfig.TestGroupInput.LocalCluster) { require.NotNil(t, ccipEnv.LocalCluster, "Local cluster shouldn't be nil") - for _, n := range ccipEnv.LocalCluster.PrivateChain { - primaryNode := n.GetPrimaryNode() - require.NotNil(t, primaryNode, "Primary node is nil in PrivateChain interface") - chainByChainID[primaryNode.GetEVMClient().GetChainID().Int64()] = primaryNode.GetEVMClient() - chains = append(chains, primaryNode.GetEVMClient()) + for _, n := range ccipEnv.LocalCluster.EVMNetworks { + if evmClient, err := blockchain.NewEVMClientFromNetwork(*n, lggr); err == nil { + chainByChainID[evmClient.GetChainID().Int64()] = evmClient + chains = append(chains, evmClient) + } else { + lggr.Error().Err(err).Msgf("EVMClient for chainID %d not found", n.ChainID) + } } } else { for _, n := range testConfig.SelectedNetworks { @@ -896,6 +989,7 @@ func (o *CCIPTestSetUpOutputs) CreateEnvironment( } if configureCLNode { ccipEnv.CLNodeWithKeyReady.Go(func() error { + var totalNodes int if !o.Cfg.ExistingCLCluster() { if ccipEnv.LocalCluster != nil { err = deployCL() @@ -907,13 +1001,41 @@ func (o *CCIPTestSetUpOutputs) CreateEnvironment( if err != nil { return fmt.Errorf("error connecting to chainlink nodes: %w", err) } + totalNodes = pointer.GetInt(testConfig.EnvInput.NewCLCluster.NoOfNodes) } else { + totalNodes = pointer.GetInt(testConfig.EnvInput.ExistingCLCluster.NoOfNodes) err = ccipEnv.ConnectToExistingNodes(o.Cfg.EnvInput) if err != nil { return fmt.Errorf("error deploying and connecting to chainlink nodes: %w", err) } } - return ccipEnv.SetUpNodeKeysAndFund(lggr, big.NewFloat(testConfig.TestGroupInput.NodeFunding), chains) + err = ccipEnv.SetUpNodeKeysAndFund(lggr, big.NewFloat(testConfig.TestGroupInput.NodeFunding), chains) + if err != nil { + return fmt.Errorf("error setting up nodes and keys %w", err) + } + // first node is the bootstrapper + ccipEnv.CommitNodeStartIndex = 1 + ccipEnv.ExecNodeStartIndex = 1 + ccipEnv.NumOfCommitNodes = testConfig.TestGroupInput.NoOfCommitNodes + ccipEnv.NumOfExecNodes = ccipEnv.NumOfCommitNodes + if !pointer.GetBool(testConfig.TestGroupInput.CommitAndExecuteOnSameDON) { + if len(ccipEnv.CLNodesWithKeys) < 11 { + return fmt.Errorf("not enough CL nodes for separate commit and execution nodes") + } + if testConfig.TestGroupInput.NoOfCommitNodes >= totalNodes { + return fmt.Errorf("number of commit nodes can not be greater than total number of nodes in DON") + } + // first two nodes are reserved for bootstrap commit and bootstrap exec + ccipEnv.CommitNodeStartIndex = 2 + ccipEnv.ExecNodeStartIndex = 2 + testConfig.TestGroupInput.NoOfCommitNodes + ccipEnv.NumOfExecNodes = totalNodes - (2 + testConfig.TestGroupInput.NoOfCommitNodes) + if ccipEnv.NumOfExecNodes < 4 { + return fmt.Errorf("insufficient number of exec nodes") + } + } + ccipEnv.NumOfAllowedFaultyExec = (ccipEnv.NumOfExecNodes - 1) / 3 + ccipEnv.NumOfAllowedFaultyCommit = (ccipEnv.NumOfCommitNodes - 1) / 3 + return nil }) } @@ -943,8 +1065,9 @@ func (o *CCIPTestSetUpOutputs) CreateEnvironment( func createEnvironmentConfig(t *testing.T, envName string, testConfig *CCIPTestConfig) *environment.Config { envConfig := &environment.Config{ - NamespacePrefix: envName, - Test: t, + NamespacePrefix: envName, + Test: t, + PreventPodEviction: true, } if testConfig.EnvInput.TTL != nil { envConfig.TTL = testConfig.EnvInput.TTL.Duration() diff --git a/integration-tests/ccip-tests/testsetups/test_env.go b/integration-tests/ccip-tests/testsetups/test_env.go index e24edaed94..a928bd5703 100644 --- a/integration-tests/ccip-tests/testsetups/test_env.go +++ b/integration-tests/ccip-tests/testsetups/test_env.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "strconv" + "strings" "testing" "github.com/AlekSi/pointer" @@ -16,7 +17,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctftestenv "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/client" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" @@ -99,10 +99,12 @@ func ChainlinkChart( } } clProps["db"] = map[string]interface{}{ - "resources": SetResourceProfile(testInputs.EnvInput.NewCLCluster.DBCPU, testInputs.EnvInput.NewCLCluster.DBMemory), - "additionalArgs": formattedArgs, - "stateful": pointer.GetBool(testInputs.EnvInput.NewCLCluster.IsStateful), - "capacity": testInputs.EnvInput.NewCLCluster.DBCapacity, + "resources": SetResourceProfile(testInputs.EnvInput.NewCLCluster.DBCPU, testInputs.EnvInput.NewCLCluster.DBMemory), + "additionalArgs": formattedArgs, + "stateful": pointer.GetBool(testInputs.EnvInput.NewCLCluster.IsStateful), + "capacity": testInputs.EnvInput.NewCLCluster.DBCapacity, + "storageClassName": pointer.GetString(testInputs.EnvInput.NewCLCluster.DBStorageClass), + "enablePrometheusPostgresExporter": pointer.GetBool(testInputs.EnvInput.NewCLCluster.PromPgExporter), "image": map[string]any{ "image": testInputs.EnvInput.NewCLCluster.Common.DBImage, "version": testInputs.EnvInput.NewCLCluster.Common.DBTag, @@ -149,6 +151,7 @@ func ChainlinkChart( "image": clNode.DBImage, "version": clNode.DBTag, }, + "storageClassName": "gp3", }, "toml": tomlStr, }) @@ -173,25 +176,66 @@ func DeployLocalCluster( testInputs *CCIPTestConfig, ) (*test_env.CLClusterTestEnv, func() error) { selectedNetworks := testInputs.SelectedNetworks + + privateEthereumNetworks := []*ctftestenv.EthereumNetwork{} + for _, network := range testInputs.EnvInput.PrivateEthereumNetworks { + privateEthereumNetworks = append(privateEthereumNetworks, network) + } + + if len(selectedNetworks) > len(privateEthereumNetworks) { + seen := make(map[int64]bool) + missing := []blockchain.EVMNetwork{} + + for _, network := range privateEthereumNetworks { + seen[int64(network.EthereumChainConfig.ChainID)] = true + } + + for _, network := range selectedNetworks { + if !seen[network.ChainID] { + missing = append(missing, network) + } + } + + for _, network := range missing { + chainConfig := &ctftestenv.EthereumChainConfig{} + err := chainConfig.Default() + if err != nil { + require.NoError(t, err, "failed to get default chain config: %w", err) + } else { + chainConfig.ChainID = int(network.ChainID) + eth1 := ctftestenv.EthereumVersion_Eth1 + geth := ctftestenv.ExecutionLayer_Geth + + privateEthereumNetworks = append(privateEthereumNetworks, &ctftestenv.EthereumNetwork{ + EthereumVersion: ð1, + ExecutionLayer: &geth, + EthereumChainConfig: chainConfig, + }) + } + } + + require.Equal(t, len(selectedNetworks), len(privateEthereumNetworks), "failed to create undefined selected networks. Maybe some of them had the same chain ids?") + } + env, err := test_env.NewCLTestEnvBuilder(). WithTestConfig(testInputs.EnvInput). WithTestInstance(t). - WithPrivateGethChains(selectedNetworks). + WithPrivateEthereumNetworks(privateEthereumNetworks). WithMockAdapter(). WithoutCleanup(). Build() require.NoError(t, err) - for _, n := range env.PrivateChain { - primaryNode := n.GetPrimaryNode() - require.NotNil(t, primaryNode, "Primary node is nil in PrivateChain interface") - for i, networkCfg := range selectedNetworks { - if networkCfg.ChainID == n.GetNetworkConfig().ChainID { - selectedNetworks[i].URLs = []string{primaryNode.GetInternalWsUrl()} - selectedNetworks[i].HTTPURLs = []string{primaryNode.GetInternalHttpUrl()} - } - } - testInputs.SelectedNetworks = selectedNetworks + for i, networkCfg := range selectedNetworks { + rpcProvider, err := env.GetRpcProvider(networkCfg.ChainID) + require.NoError(t, err, "Error getting rpc provider") + selectedNetworks[i].URLs = rpcProvider.PrivateWsUrsl() + selectedNetworks[i].HTTPURLs = rpcProvider.PrivateHttpUrls() + newNetwork := networkCfg + newNetwork.URLs = rpcProvider.PublicWsUrls() + newNetwork.HTTPURLs = rpcProvider.PublicHttpUrls() + env.EVMNetworks = append(env.EVMNetworks, &newNetwork) } + testInputs.SelectedNetworks = selectedNetworks // a func to start the CL nodes asynchronously deployCL := func() error { @@ -209,7 +253,7 @@ func DeployLocalCluster( return err } ccipNode, err := test_env.NewClNode( - []string{env.Network.Name}, + []string{env.DockerNetwork.Name}, pointer.GetString(clNode.ChainlinkImage.Image), pointer.GetString(clNode.ChainlinkImage.Version), toml, test_env.WithPgDBOptions( @@ -236,7 +280,7 @@ func DeployLocalCluster( return err } ccipNode, err := test_env.NewClNode( - []string{env.Network.Name}, + []string{env.DockerNetwork.Name}, pointer.GetString(testInputs.EnvInput.NewCLCluster.Common.ChainlinkImage.Image), pointer.GetString(testInputs.EnvInput.NewCLCluster.Common.ChainlinkImage.Version), toml, test_env.WithPgDBOptions( @@ -382,16 +426,11 @@ func DeployEnvironments( if !network.Simulated { return network.URLs, network.HTTPURLs } - networkName := network.Name + networkName := strings.ReplaceAll(strings.ToLower(network.Name), " ", "-") var internalWsURLs, internalHttpURLs []string for i := 0; i < numOfTxNodes; i++ { - podName := fmt.Sprintf("%s-ethereum-geth:%d", networkName, i) - txNodeInternalWs, err := testEnvironment.Fwd.FindPort(podName, "geth", "ws-rpc").As(client.RemoteConnection, client.WS) - require.NoError(t, err, "Error finding WS ports") - internalWsURLs = append(internalWsURLs, txNodeInternalWs) - txNodeInternalHttp, err := testEnvironment.Fwd.FindPort(podName, "geth", "http-rpc").As(client.RemoteConnection, client.HTTP) - require.NoError(t, err, "Error finding HTTP ports") - internalHttpURLs = append(internalHttpURLs, txNodeInternalHttp) + internalWsURLs = append(internalWsURLs, fmt.Sprintf("ws://%s-ethereum-geth:8546", networkName)) + internalHttpURLs = append(internalHttpURLs, fmt.Sprintf("http://%s-ethereum-geth:8544", networkName)) } return internalWsURLs, internalHttpURLs } diff --git a/integration-tests/ccip-tests/types/config/node/core.go b/integration-tests/ccip-tests/types/config/node/core.go index b1fa4f1878..eb12598f94 100644 --- a/integration-tests/ccip-tests/types/config/node/core.go +++ b/integration-tests/ccip-tests/types/config/node/core.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" itutils "github.com/smartcontractkit/chainlink/integration-tests/utils" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -42,6 +43,7 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork, commonChainConfig *evmcfg evmConfig := &evmcfg.EVMConfig{ ChainID: ubig.New(big.NewInt(network.ChainID)), Nodes: evmNodes, + Chain: evmcfg.Chain{}, } if commonChainConfig != nil { evmConfig.Chain = *commonChainConfig @@ -51,7 +53,12 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork, commonChainConfig *evmcfg evmConfig.Chain = overriddenChainCfg } } - + if evmConfig.Chain.FinalityDepth == nil && network.FinalityDepth > 0 { + evmConfig.Chain.FinalityDepth = ptr.Ptr(uint32(network.FinalityDepth)) + } + if evmConfig.Chain.FinalityTagEnabled == nil && network.FinalityTag { + evmConfig.Chain.FinalityTagEnabled = ptr.Ptr(network.FinalityTag) + } evmConfigs = append(evmConfigs, evmConfig) } return func(c *chainlink.Config) { diff --git a/integration-tests/ccip-tests/utils/common.go b/integration-tests/ccip-tests/utils/common.go index 8883b66163..afa8158e45 100644 --- a/integration-tests/ccip-tests/utils/common.go +++ b/integration-tests/ccip-tests/utils/common.go @@ -3,9 +3,30 @@ package utils import ( "path/filepath" "runtime" + "sync" ) func ProjectRoot() string { _, b, _, _ := runtime.Caller(0) return filepath.Join(filepath.Dir(b), "/..") } + +// DeleteNilEntriesFromMap checks for nil entry in map, store all not-nil entries to another map and deallocates previous map +// Deleting keys from a map actually does not delete the key, It just sets the corresponding value to nil. +func DeleteNilEntriesFromMap(inputMap *sync.Map) *sync.Map { + newMap := &sync.Map{} + foundNil := false + inputMap.Range(func(key, value any) bool { + if value != nil { + newMap.Store(key, value) + } + if value == nil { + foundNil = true + } + return true + }) + if foundNil { + runtime.GC() + } + return newMap +} diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 711a355730..abfc026efc 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -126,6 +126,7 @@ func TestAutomationChaos(t *testing.T) { registryVersions := map[string]eth_contracts.KeeperRegistryVersion{ "registry_2_0": eth_contracts.RegistryVersion_2_0, "registry_2_1": eth_contracts.RegistryVersion_2_1, + "registry_2_2": eth_contracts.RegistryVersion_2_2, } for name, registryVersion := range registryVersions { @@ -139,8 +140,8 @@ func TestAutomationChaos(t *testing.T) { } var overrideFn = func(_ interface{}, target interface{}) { - ctf_config.MustConfigOverrideChainlinkVersion(config.ChainlinkImage, target) - ctf_config.MightConfigOverridePyroscopeKey(config.Pyroscope, target) + ctf_config.MustConfigOverrideChainlinkVersion(config.GetChainlinkImageConfig(), target) + ctf_config.MightConfigOverridePyroscopeKey(config.GetPyroscopeConfig(), target) } chainlinkCfg := chainlink.NewWithOverride(0, getDefaultAutomationSettings(&config), config.ChainlinkImage, overrideFn) @@ -276,17 +277,23 @@ func TestAutomationChaos(t *testing.T) { actions.CreateOCRKeeperJobs(t, chainlinkNodes, registry.Address(), network.ChainID, 0, registryVersion) nodesWithoutBootstrap := chainlinkNodes[1:] - ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 30*time.Second) + defaultOCRRegistryConfig.RegistryVersion = registryVersion + ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 30*time.Second, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) require.NoError(t, err, "Error building OCR config vars") err = registry.SetConfig(defaultOCRRegistryConfig, ocrConfig) require.NoError(t, err, "Registry config should be be set successfully") require.NoError(t, chainClient.WaitForEvents(), "Waiting for config to be set") - consumers_conditional, upkeepIDs_conditional := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, false, false) - consumers_logtrigger, upkeepIDs_logtrigger := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, true, false) + consumersConditional, upkeepidsConditional := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, false, false) + consumersLogtrigger, upkeepidsLogtrigger := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, true, false) - consumers := append(consumers_conditional, consumers_logtrigger...) - upkeepIDs := append(upkeepIDs_conditional, upkeepIDs_logtrigger...) + consumers := append(consumersConditional, consumersLogtrigger...) + upkeepIDs := append(upkeepidsConditional, upkeepidsLogtrigger...) + + for _, c := range consumersLogtrigger { + err = c.Start() + require.NoError(t, err, "Error starting consumer") + } l.Info().Msg("Waiting for all upkeeps to be performed") diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index eda60a37f3..4869dbcabd 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -57,8 +57,8 @@ func TestOCR2VRFChaos(t *testing.T) { } var overrideFn = func(_ interface{}, target interface{}) { - ctf_config.MustConfigOverrideChainlinkVersion(testconfig.ChainlinkImage, target) - ctf_config.MightConfigOverridePyroscopeKey(testconfig.Pyroscope, target) + ctf_config.MustConfigOverrideChainlinkVersion(testconfig.GetChainlinkImageConfig(), target) + ctf_config.MightConfigOverridePyroscopeKey(testconfig.GetPyroscopeConfig(), target) } chainlinkCfg := chainlink.NewWithOverride(0, defaultOCR2VRFSettings, testconfig.ChainlinkImage, overrideFn) diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 97f7c67d1b..b2adec0411 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -6,10 +6,9 @@ import ( "testing" "github.com/onsi/gomega" + "github.com/smartcontractkit/seth" "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" @@ -22,10 +21,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -60,13 +61,11 @@ func TestOCRChaos(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) config, err := tc.GetConfig("Chaos", tc.OCR) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err, "Error getting config") var overrideFn = func(_ interface{}, target interface{}) { - ctf_config.MustConfigOverrideChainlinkVersion(config.ChainlinkImage, target) - ctf_config.MightConfigOverridePyroscopeKey(config.Pyroscope, target) + ctf_config.MustConfigOverrideChainlinkVersion(config.GetChainlinkImageConfig(), target) + ctf_config.MightConfigOverridePyroscopeKey(config.GetPyroscopeConfig(), target) } chainlinkCfg := chainlink.NewWithOverride(0, getDefaultOcrSettings(&config), config.ChainlinkImage, overrideFn) @@ -164,39 +163,37 @@ func TestOCRChaos(t *testing.T) { err = testEnvironment.Client.LabelChaosGroup(testEnvironment.Cfg.Namespace, "instance=node-", 2, 5, ChaosGroupMajorityPlus) require.NoError(t, err) - chainClient, err := blockchain.NewEVMClient(blockchain.SimulatedEVMNetwork, testEnvironment, l) - require.NoError(t, err, "Connecting to blockchain nodes shouldn't fail") - cd, err := contracts.NewContractDeployer(chainClient, l) - require.NoError(t, err, "Deploying contracts shouldn't fail") + cfg := config.MustCopy().(tc.TestConfig) + readSethCfg := cfg.GetSethConfig() + require.NotNil(t, readSethCfg, "Seth config shouldn't be nil") + + network := networks.MustGetSelectedNetworkConfig(cfg.GetNetworkConfig())[0] + network = utils.MustReplaceSimulatedNetworkUrlWithK8(l, network, *testEnvironment) + + sethCfg := utils.MergeSethAndEvmNetworkConfigs(l, network, *readSethCfg) + seth, err := seth.NewClientWithConfig(&sethCfg) + require.NoError(t, err, "Error creating seth client") chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) require.NoError(t, err, "Connecting to chainlink nodes shouldn't fail") bootstrapNode, workerNodes := chainlinkNodes[0], chainlinkNodes[1:] t.Cleanup(func() { - if chainClient != nil { - chainClient.GasStats().PrintStats() - } - err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, &config, chainClient) + err := actions_seth.TeardownRemoteSuite(t, seth, testEnvironment.Cfg.Namespace, chainlinkNodes, nil, &cfg) require.NoError(t, err, "Error tearing down environment") }) ms, err := ctfClient.ConnectMockServer(testEnvironment) require.NoError(t, err, "Creating mockserver clients shouldn't fail") - chainClient.ParallelTransactions(true) - require.NoError(t, err) - - lt, err := cd.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + linkDeploymentData, err := contracts.DeployLinkTokenContract(seth) + require.NoError(t, err, "Error deploying link token contract") - err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(10)) + err = actions_seth.FundChainlinkNodesFromRootAddress(l, seth, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(chainlinkNodes), big.NewFloat(10)) require.NoError(t, err) - ocrInstances, err := actions.DeployOCRContracts(1, lt, cd, workerNodes, chainClient) - require.NoError(t, err) - err = chainClient.WaitForEvents() + ocrInstances, err := actions_seth.DeployOCRv1Contracts(l, seth, 1, linkDeploymentData.Address, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes)) require.NoError(t, err) - err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, ms, chainClient.GetChainID().String()) + err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, ms, fmt.Sprint(seth.ChainID)) require.NoError(t, err) chaosApplied := false diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index b6dce73862..af1a540b9a 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -84,6 +84,7 @@ func initRestyClient(url string, email string, password string, timeout *time.Du return nil, fmt.Errorf("error connecting to chainlink node after %d attempts: %w", retryCount, err) } rc.SetCookies(resp.Cookies()) + log.Debug().Str("URL", url).Msg("Connected to Chainlink node") return rc, nil } diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index 0f53331fec..f95089f36f 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -380,8 +380,8 @@ type TxKeyData struct { // TxKeyAttributes is the model that represents the created keys when read type TxKeyAttributes struct { PublicKey string `json:"publicKey"` - - StarkKey string `json:"starkPubKey,omitempty"` + Address string `json:"address"` + StarkKey string `json:"starkPubKey,omitempty"` } type SingleTransactionDataWrapper struct { @@ -627,11 +627,23 @@ func (d *PipelineSpec) String() (string, error) { return MarshallTemplate(d, "API call pipeline template", sourceString) } +func getOptionalSimBlock(simBlock *string) (string, error) { + optionalSimBlock := "" + if simBlock != nil { + if *simBlock != "latest" && *simBlock != "pending" { + return "", fmt.Errorf("invalid simulation block value: %s", *simBlock) + } + optionalSimBlock = fmt.Sprintf("block=\"%s\"", *simBlock) + } + return optionalSimBlock, nil +} + // VRFV2TxPipelineSpec VRFv2 request with tx callback type VRFV2PlusTxPipelineSpec struct { Address string EstimateGasMultiplier float64 FromAddress string + SimulationBlock *string // can be nil, "latest" or "pending". } // Type returns the type of the pipeline @@ -641,7 +653,11 @@ func (d *VRFV2PlusTxPipelineSpec) Type() string { // String representation of the pipeline func (d *VRFV2PlusTxPipelineSpec) String() (string, error) { - sourceString := ` + optionalSimBlock, err := getOptionalSimBlock(d.SimulationBlock) + if err != nil { + return "", err + } + sourceTemplate := ` decode_log [type=ethabidecodelog abi="RandomWordsRequested(bytes32 indexed keyHash,uint256 requestId,uint256 preSeed,uint256 indexed subId,uint16 minimumRequestConfirmations,uint32 callbackGasLimit,uint32 numWords,bytes extraArgs,address indexed sender)" data="$(jobRun.logData)" @@ -654,7 +670,8 @@ generate_proof [type=vrfv2plus estimate_gas [type=estimategaslimit to="{{ .Address }}" multiplier="{{ .EstimateGasMultiplier }}" - data="$(generate_proof.output)"] + data="$(generate_proof.output)" + %s] simulate_fulfillment [type=ethcall from="{{ .FromAddress }}" to="{{ .Address }}" @@ -662,8 +679,11 @@ simulate_fulfillment [type=ethcall gasPrice="$(jobSpec.maxGasPrice)" extractRevertReason=true contract="{{ .Address }}" - data="$(generate_proof.output)"] + data="$(generate_proof.output)" + %s] decode_log->generate_proof->estimate_gas->simulate_fulfillment` + + sourceString := fmt.Sprintf(sourceTemplate, optionalSimBlock, optionalSimBlock) return MarshallTemplate(d, "VRFV2 Plus pipeline template", sourceString) } @@ -672,6 +692,7 @@ type VRFV2TxPipelineSpec struct { Address string EstimateGasMultiplier float64 FromAddress string + SimulationBlock *string // can be nil, "latest" or "pending". } // Type returns the type of the pipeline @@ -681,7 +702,11 @@ func (d *VRFV2TxPipelineSpec) Type() string { // String representation of the pipeline func (d *VRFV2TxPipelineSpec) String() (string, error) { - sourceString := ` + optionalSimBlock, err := getOptionalSimBlock(d.SimulationBlock) + if err != nil { + return "", err + } + sourceTemplate := ` decode_log [type=ethabidecodelog abi="RandomWordsRequested(bytes32 indexed keyHash,uint256 requestId,uint256 preSeed,uint64 indexed subId,uint16 minimumRequestConfirmations,uint32 callbackGasLimit,uint32 numWords,address indexed sender)" data="$(jobRun.logData)" @@ -694,7 +719,8 @@ vrf [type=vrfv2 estimate_gas [type=estimategaslimit to="{{ .Address }}" multiplier="{{ .EstimateGasMultiplier }}" - data="$(vrf.output)"] + data="$(vrf.output)" + %s] simulate [type=ethcall from="{{ .FromAddress }}" to="{{ .Address }}" @@ -702,8 +728,11 @@ simulate [type=ethcall gasPrice="$(jobSpec.maxGasPrice)" extractRevertReason=true contract="{{ .Address }}" - data="$(vrf.output)"] + data="$(vrf.output)" + %s] decode_log->vrf->estimate_gas->simulate` + + sourceString := fmt.Sprintf(sourceTemplate, optionalSimBlock, optionalSimBlock) return MarshallTemplate(d, "VRFV2 pipeline template", sourceString) } @@ -1261,14 +1290,18 @@ observationSource = """ // BlockhashStoreJobSpec represents a blockhashstore job type BlockhashStoreJobSpec struct { - Name string `toml:"name"` - CoordinatorV2Address string `toml:"coordinatorV2Address"` // Address of the VRF CoordinatorV2 contract - WaitBlocks int `toml:"waitBlocks"` - LookbackBlocks int `toml:"lookbackBlocks"` - BlockhashStoreAddress string `toml:"blockhashStoreAddress"` - PollPeriod string `toml:"pollPeriod"` - RunTimeout string `toml:"runTimeout"` - EVMChainID string `toml:"evmChainID"` + Name string `toml:"name"` + CoordinatorV2Address string `toml:"coordinatorV2Address"` + CoordinatorV2PlusAddress string `toml:"coordinatorV2PlusAddress"` + BlockhashStoreAddress string `toml:"blockhashStoreAddress"` + ExternalJobID string `toml:"externalJobID"` + FromAddresses []string `toml:"fromAddresses"` + EVMChainID string `toml:"evmChainID"` + ForwardingAllowed bool `toml:"forwardingAllowed"` + PollPeriod time.Duration `toml:"pollPeriod"` + RunTimeout time.Duration `toml:"runTimeout"` + WaitBlocks int `toml:"waitBlocks"` + LookbackBlocks int `toml:"lookbackBlocks"` } // Type returns the type of the job @@ -1280,13 +1313,17 @@ func (b *BlockhashStoreJobSpec) String() (string, error) { type = "blockhashstore" schemaVersion = 1 name = "{{.Name}}" -coordinatorV2Address = "{{.CoordinatorV2Address}}" -waitBlocks = {{.WaitBlocks}} -lookbackBlocks = {{.LookbackBlocks}} -blockhashStoreAddress = "{{.BlockhashStoreAddress}}" -pollPeriod = "{{.PollPeriod}}" -runTimeout = "{{.RunTimeout}}" +forwardingAllowed = {{.ForwardingAllowed}} +coordinatorV2Address = "{{.CoordinatorV2Address}}" +coordinatorV2PlusAddress = "{{.CoordinatorV2PlusAddress}}" +blockhashStoreAddress = "{{.BlockhashStoreAddress}}" +fromAddresses = [{{range .FromAddresses}}"{{.}}",{{end}}] evmChainID = "{{.EVMChainID}}" +externalJobID = "{{.ExternalJobID}}" +waitBlocks = {{.WaitBlocks}} +lookbackBlocks = {{.LookbackBlocks}} +pollPeriod = "{{.PollPeriod}}" +runTimeout = "{{.RunTimeout}}" ` return MarshallTemplate(b, "BlockhashStore Job", vrfTemplateString) } diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 2acc0a2109..958607a097 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -21,6 +21,7 @@ import ( ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_mock_ethlink_aggregator" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_load_test_client" @@ -624,7 +625,7 @@ func (e *EthereumContractDeployer) DeployOffChainAggregator( if err != nil { return nil, err } - return &EthereumOffchainAggregator{ + return &LegacyEthereumOffchainAggregator{ client: e.client, ocr: instance.(*offchainaggregator.OffchainAggregator), address: address, @@ -643,7 +644,7 @@ func (e *EthereumContractDeployer) LoadOffChainAggregator(address *common.Addres if err != nil { return nil, err } - return &EthereumOffchainAggregator{ + return &LegacyEthereumOffchainAggregator{ address: address, client: e.client, ocr: instance.(*offchainaggregator.OffchainAggregator), @@ -1538,7 +1539,7 @@ func (e *EthereumContractDeployer) DeployOperatorFactory(linkAddr string) (Opera if err != nil { return nil, err } - return &EthereumOperatorFactory{ + return &LegacyEthereumOperatorFactory{ address: addr, client: e.client, operatorFactory: instance.(*operator_factory.OperatorFactory), @@ -1605,7 +1606,7 @@ func (e *EthereumContractDeployer) DeployOffchainAggregatorV2( if err != nil { return nil, err } - return &EthereumOffchainAggregatorV2{ + return &LegacyEthereumOffchainAggregatorV2{ client: e.client, contract: instance.(*ocr2aggregator.OCR2Aggregator), address: address, @@ -1624,7 +1625,7 @@ func (e *EthereumContractDeployer) LoadOffChainAggregatorV2(address *common.Addr if err != nil { return nil, err } - return &EthereumOffchainAggregatorV2{ + return &LegacyEthereumOffchainAggregatorV2{ client: e.client, contract: instance.(*ocr2aggregator.OCR2Aggregator), address: address, diff --git a/integration-tests/contracts/contract_loader.go b/integration-tests/contracts/contract_loader.go index 0fec424426..a2a4fb60be 100644 --- a/integration-tests/contracts/contract_loader.go +++ b/integration-tests/contracts/contract_loader.go @@ -81,6 +81,8 @@ func NewContractLoader(bcClient blockchain.EVMClient, logger zerolog.Logger) (Co return &FantomContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil case *blockchain.BSCClient: return &BSCContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil + case *blockchain.GnosisClient: + return &GnosisContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil } return nil, errors.New("unknown blockchain client implementation for contract Loader, register blockchain client in NewContractLoader") } @@ -154,6 +156,11 @@ type BSCContractLoader struct { *EthereumContractLoader } +// GnosisContractLoader wraps for Gnosis +type GnosisContractLoader struct { + *EthereumContractLoader +} + // NewEthereumContractLoader returns an instantiated instance of the ETH contract Loader func NewEthereumContractLoader(ethClient blockchain.EVMClient, logger zerolog.Logger) *EthereumContractLoader { return &EthereumContractLoader{ @@ -247,7 +254,7 @@ func (e *EthereumContractLoader) LoadOperatorContract(address common.Address) (O if err != nil { return nil, err } - return &EthereumOperator{ + return &LegacyEthereumOperator{ address: address, client: e.client, operator: instance.(*operator_wrapper.Operator), @@ -266,7 +273,7 @@ func (e *EthereumContractLoader) LoadAuthorizedForwarder(address common.Address) if err != nil { return nil, err } - return &EthereumAuthorizedForwarder{ + return &LegacyEthereumAuthorizedForwarder{ address: address, client: e.client, authorizedForwarder: instance.(*authorized_forwarder.AuthorizedForwarder), diff --git a/integration-tests/contracts/contract_models.go b/integration-tests/contracts/contract_models.go index 3d738033d6..a381748e62 100644 --- a/integration-tests/contracts/contract_models.go +++ b/integration-tests/contracts/contract_models.go @@ -13,6 +13,8 @@ import ( ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" ocrConfigHelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/functions_billing_registry_events_mock" @@ -112,34 +114,44 @@ type OffChainAggregatorConfig struct { } type OffChainAggregatorV2Config struct { - DeltaProgress time.Duration - DeltaResend time.Duration - DeltaRound time.Duration - DeltaGrace time.Duration - DeltaStage time.Duration - RMax uint8 - S []int - Oracles []ocrConfigHelper2.OracleIdentityExtra - ReportingPluginConfig []byte - MaxDurationQuery time.Duration - MaxDurationObservation time.Duration - MaxDurationReport time.Duration - MaxDurationShouldAcceptFinalizedReport time.Duration - MaxDurationShouldTransmitAcceptedReport time.Duration - F int - OnchainConfig []byte + DeltaProgress *config.Duration `toml:",omitempty"` + DeltaResend *config.Duration `toml:",omitempty"` + DeltaRound *config.Duration `toml:",omitempty"` + DeltaGrace *config.Duration `toml:",omitempty"` + DeltaStage *config.Duration `toml:",omitempty"` + RMax uint8 `toml:"-"` + S []int `toml:"-"` + Oracles []ocrConfigHelper2.OracleIdentityExtra `toml:"-"` + ReportingPluginConfig []byte `toml:"-"` + MaxDurationQuery *config.Duration `toml:",omitempty"` + MaxDurationObservation *config.Duration `toml:",omitempty"` + MaxDurationReport *config.Duration `toml:",omitempty"` + MaxDurationShouldAcceptFinalizedReport *config.Duration `toml:",omitempty"` + MaxDurationShouldTransmitAcceptedReport *config.Duration `toml:",omitempty"` + F int `toml:"-"` + OnchainConfig []byte `toml:"-"` } type OffchainAggregatorData struct { LatestRoundData RoundData // Data about the latest round } +type ChainlinkNodeWithKeysAndAddress interface { + MustReadOCRKeys() (*client.OCRKeys, error) + MustReadP2PKeys() (*client.P2PKeys, error) + ExportEVMKeysForChain(string) ([]*client.ExportedEVMKey, error) + PrimaryEthAddress() (string, error) +} + +type OffChainAggregatorWithRounds interface { + Address() string + GetLatestRound(ctx context.Context) (*RoundData, error) + RequestNewRound() error +} + type OffchainAggregator interface { Address() string - Fund(nativeAmount *big.Float) error - GetContractData(ctx context.Context) (*OffchainAggregatorData, error) - SetConfig(chainlinkNodes []*client.ChainlinkK8sClient, ocrConfig OffChainAggregatorConfig, transmitters []common.Address) error - SetConfigLocal(chainlinkNodes []*client.ChainlinkClient, ocrConfig OffChainAggregatorConfig, transmitters []common.Address) error + SetConfig(chainlinkNodes []ChainlinkNodeWithKeysAndAddress, ocrConfig OffChainAggregatorConfig, transmitters []common.Address) error SetPayees([]string, []string) error RequestNewRound() error GetLatestAnswer(ctx context.Context) (*big.Int, error) @@ -151,10 +163,8 @@ type OffchainAggregator interface { type OffchainAggregatorV2 interface { Address() string - Fund(nativeAmount *big.Float) error RequestNewRound() error SetConfig(ocrConfig *OCRv2Config) error - GetConfig(ctx context.Context) ([32]byte, uint32, error) SetPayees(transmitters, payees []string) error GetLatestAnswer(ctx context.Context) (*big.Int, error) GetLatestRound(ctx context.Context) (*RoundData, error) @@ -210,6 +220,7 @@ type MockGasFeed interface { type BlockHashStore interface { Address() string + GetBlockHash(ctx context.Context, blockNumber *big.Int) ([32]byte, error) } type Staking interface { @@ -386,6 +397,7 @@ type MercuryVerifierProxy interface { type MercuryFeeManager interface { Address() common.Address + UpdateSubscriberDiscount(subscriber common.Address, feedId [32]byte, token common.Address, discount uint64) (*types.Transaction, error) } type MercuryRewardManager interface { diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 2faf97df16..0fa171fb24 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -87,10 +87,14 @@ type VRFCoordinatorV2_5 interface { stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, - feeConfig vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig, + fulfillmentFlatFeeNativePPM uint32, + fulfillmentFlatFeeLinkDiscountPPM uint32, + nativePremiumPercentage uint8, + linkPremiumPercentage uint8, ) error RegisterProvingKey( publicProvingKey [2]*big.Int, + gasLaneMaxGas uint64, ) error HashOfKey(ctx context.Context, pubKey [2]*big.Int) ([32]byte, error) CreateSubscription() (*types.Transaction, error) @@ -127,7 +131,10 @@ type VRFCoordinatorV2PlusUpgradedVersion interface { stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, - feeConfig vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionFeeConfig, + fulfillmentFlatFeeNativePPM uint32, + fulfillmentFlatFeeLinkDiscountPPM uint32, + nativePremiumPercentage uint8, + linkPremiumPercentage uint8, ) error RegisterProvingKey( publicProvingKey [2]*big.Int, @@ -159,6 +166,8 @@ type VRFV2PlusWrapper interface { Address() string SetConfig(wrapperGasOverhead uint32, coordinatorGasOverhead uint32, wrapperPremiumPercentage uint8, keyHash [32]byte, maxNumWords uint8, stalenessSeconds uint32, fallbackWeiPerUnitLink *big.Int, fulfillmentFlatFeeLinkPPM uint32, fulfillmentFlatFeeNativePPM uint32) error GetSubID(ctx context.Context) (*big.Int, error) + Migrate(newCoordinator common.Address) error + Coordinator(ctx context.Context) (common.Address, error) } type VRFOwner interface { @@ -331,9 +340,12 @@ type LoadTestRequestStatus struct { } type VRFLoadTestMetrics struct { - RequestCount *big.Int - FulfilmentCount *big.Int - AverageFulfillmentInMillions *big.Int - SlowestFulfillment *big.Int - FastestFulfillment *big.Int + RequestCount *big.Int + FulfilmentCount *big.Int + AverageFulfillmentInMillions *big.Int + SlowestFulfillment *big.Int + FastestFulfillment *big.Int + AverageResponseTimeInSecondsMillions *big.Int + SlowestResponseTimeInSeconds *big.Int + FastestResponseTimeInSeconds *big.Int } diff --git a/integration-tests/contracts/ethereum/KeeperRegistryVersions.go b/integration-tests/contracts/ethereum/KeeperRegistryVersions.go index f15e43524d..4aee8d75d2 100644 --- a/integration-tests/contracts/ethereum/KeeperRegistryVersions.go +++ b/integration-tests/contracts/ethereum/KeeperRegistryVersions.go @@ -18,4 +18,5 @@ const ( RegistryVersion_1_3 RegistryVersion_2_0 RegistryVersion_2_1 + RegistryVersion_2_2 ) diff --git a/integration-tests/contracts/ethereum_contracts.go b/integration-tests/contracts/ethereum_contracts.go index 9cb858fe00..f78e64d82b 100644 --- a/integration-tests/contracts/ethereum_contracts.go +++ b/integration-tests/contracts/ethereum_contracts.go @@ -1289,45 +1289,17 @@ func (l *EthereumLinkToken) TransferAndCall(to string, amount *big.Int, data []b return tx, l.client.ProcessTransaction(tx) } -// EthereumOffchainAggregator represents the offchain aggregation contract -type EthereumOffchainAggregator struct { +// LegacyEthereumOffchainAggregator represents the offchain aggregation contract +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumOffchainAggregator instead +type LegacyEthereumOffchainAggregator struct { client blockchain.EVMClient ocr *offchainaggregator.OffchainAggregator address *common.Address l zerolog.Logger } -// Fund sends specified currencies to the contract -func (o *EthereumOffchainAggregator) Fund(ethAmount *big.Float) error { - gasEstimates, err := o.client.EstimateGas(ethereum.CallMsg{ - To: o.address, - }) - if err != nil { - return err - } - return o.client.Fund(o.address.Hex(), ethAmount, gasEstimates) -} - -// GetContractData retrieves basic data for the offchain aggregator contract -func (o *EthereumOffchainAggregator) GetContractData(ctxt context.Context) (*OffchainAggregatorData, error) { - opts := &bind.CallOpts{ - From: common.HexToAddress(o.client.GetDefaultWallet().Address()), - Context: ctxt, - } - - lr, err := o.ocr.LatestRoundData(opts) - if err != nil { - return &OffchainAggregatorData{}, err - } - latestRound := RoundData(lr) - - return &OffchainAggregatorData{ - LatestRoundData: latestRound, - }, nil -} - // SetPayees sets wallets for the contract to pay out to? -func (o *EthereumOffchainAggregator) SetPayees( +func (o *LegacyEthereumOffchainAggregator) SetPayees( transmitters, payees []string, ) error { opts, err := o.client.TransactionOpts(o.client.GetDefaultWallet()) @@ -1356,8 +1328,8 @@ func (o *EthereumOffchainAggregator) SetPayees( } // SetConfig sets the payees and the offchain reporting protocol configuration -func (o *EthereumOffchainAggregator) SetConfig( - chainlinkNodes []*client.ChainlinkK8sClient, +func (o *LegacyEthereumOffchainAggregator) SetConfig( + chainlinkNodes []ChainlinkNodeWithKeysAndAddress, ocrConfig OffChainAggregatorConfig, transmitters []common.Address, ) error { @@ -1441,7 +1413,7 @@ func (o *EthereumOffchainAggregator) SetConfig( } // RequestNewRound requests the OCR contract to create a new round -func (o *EthereumOffchainAggregator) RequestNewRound() error { +func (o *LegacyEthereumOffchainAggregator) RequestNewRound() error { opts, err := o.client.TransactionOpts(o.client.GetDefaultWallet()) if err != nil { return err @@ -1456,7 +1428,7 @@ func (o *EthereumOffchainAggregator) RequestNewRound() error { } // GetLatestAnswer returns the latest answer from the OCR contract -func (o *EthereumOffchainAggregator) GetLatestAnswer(ctxt context.Context) (*big.Int, error) { +func (o *LegacyEthereumOffchainAggregator) GetLatestAnswer(ctxt context.Context) (*big.Int, error) { opts := &bind.CallOpts{ From: common.HexToAddress(o.client.GetDefaultWallet().Address()), Context: ctxt, @@ -1464,12 +1436,12 @@ func (o *EthereumOffchainAggregator) GetLatestAnswer(ctxt context.Context) (*big return o.ocr.LatestAnswer(opts) } -func (o *EthereumOffchainAggregator) Address() string { +func (o *LegacyEthereumOffchainAggregator) Address() string { return o.address.Hex() } // GetLatestRound returns data from the latest round -func (o *EthereumOffchainAggregator) GetLatestRound(ctx context.Context) (*RoundData, error) { +func (o *LegacyEthereumOffchainAggregator) GetLatestRound(ctx context.Context) (*RoundData, error) { opts := &bind.CallOpts{ From: common.HexToAddress(o.client.GetDefaultWallet().Address()), Context: ctx, @@ -1489,7 +1461,7 @@ func (o *EthereumOffchainAggregator) GetLatestRound(ctx context.Context) (*Round }, err } -func (o *EthereumOffchainAggregator) LatestRoundDataUpdatedAt() (*big.Int, error) { +func (o *LegacyEthereumOffchainAggregator) LatestRoundDataUpdatedAt() (*big.Int, error) { data, err := o.ocr.LatestRoundData(&bind.CallOpts{ From: common.HexToAddress(o.client.GetDefaultWallet().Address()), Context: context.Background(), @@ -1501,7 +1473,7 @@ func (o *EthereumOffchainAggregator) LatestRoundDataUpdatedAt() (*big.Int, error } // GetRound retrieves an OCR round by the round ID -func (o *EthereumOffchainAggregator) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { +func (o *LegacyEthereumOffchainAggregator) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { opts := &bind.CallOpts{ From: common.HexToAddress(o.client.GetDefaultWallet().Address()), Context: ctx, @@ -1521,7 +1493,7 @@ func (o *EthereumOffchainAggregator) GetRound(ctx context.Context, roundID *big. } // ParseEventAnswerUpdated parses the log for event AnswerUpdated -func (o *EthereumOffchainAggregator) ParseEventAnswerUpdated(eventLog types.Log) (*offchainaggregator.OffchainAggregatorAnswerUpdated, error) { +func (o *LegacyEthereumOffchainAggregator) ParseEventAnswerUpdated(eventLog types.Log) (*offchainaggregator.OffchainAggregatorAnswerUpdated, error) { return o.ocr.ParseAnswerUpdated(eventLog) } @@ -1808,26 +1780,27 @@ func (e *EthereumFlags) GetFlag(ctx context.Context, addr string) (bool, error) return flag, nil } -// EthereumOperatorFactory represents operator factory contract -type EthereumOperatorFactory struct { +// LegacyEthereumOperatorFactory represents operator factory contract +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumOperatorFactory instead +type LegacyEthereumOperatorFactory struct { address *common.Address client blockchain.EVMClient operatorFactory *operator_factory.OperatorFactory } -func (e *EthereumOperatorFactory) ParseAuthorizedForwarderCreated(eventLog types.Log) (*operator_factory.OperatorFactoryAuthorizedForwarderCreated, error) { +func (e *LegacyEthereumOperatorFactory) ParseAuthorizedForwarderCreated(eventLog types.Log) (*operator_factory.OperatorFactoryAuthorizedForwarderCreated, error) { return e.operatorFactory.ParseAuthorizedForwarderCreated(eventLog) } -func (e *EthereumOperatorFactory) ParseOperatorCreated(eventLog types.Log) (*operator_factory.OperatorFactoryOperatorCreated, error) { +func (e *LegacyEthereumOperatorFactory) ParseOperatorCreated(eventLog types.Log) (*operator_factory.OperatorFactoryOperatorCreated, error) { return e.operatorFactory.ParseOperatorCreated(eventLog) } -func (e *EthereumOperatorFactory) Address() string { +func (e *LegacyEthereumOperatorFactory) Address() string { return e.address.Hex() } -func (e *EthereumOperatorFactory) DeployNewOperatorAndForwarder() (*types.Transaction, error) { +func (e *LegacyEthereumOperatorFactory) DeployNewOperatorAndForwarder() (*types.Transaction, error) { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return nil, err @@ -1839,19 +1812,20 @@ func (e *EthereumOperatorFactory) DeployNewOperatorAndForwarder() (*types.Transa return tx, nil } -// EthereumOperator represents operator contract -type EthereumOperator struct { +// LegacyEthereumOperator represents operator contract +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumOperator instead +type LegacyEthereumOperator struct { address common.Address client blockchain.EVMClient operator *operator_wrapper.Operator l zerolog.Logger } -func (e *EthereumOperator) Address() string { +func (e *LegacyEthereumOperator) Address() string { return e.address.Hex() } -func (e *EthereumOperator) AcceptAuthorizedReceivers(forwarders []common.Address, eoa []common.Address) error { +func (e *LegacyEthereumOperator) AcceptAuthorizedReceivers(forwarders []common.Address, eoa []common.Address) error { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return err @@ -1867,15 +1841,16 @@ func (e *EthereumOperator) AcceptAuthorizedReceivers(forwarders []common.Address return e.client.ProcessTransaction(tx) } -// EthereumAuthorizedForwarder represents authorized forwarder contract -type EthereumAuthorizedForwarder struct { +// LegacyEthereumAuthorizedForwarder represents authorized forwarder contract +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumAuthorizedForwarder instead +type LegacyEthereumAuthorizedForwarder struct { address common.Address client blockchain.EVMClient authorizedForwarder *authorized_forwarder.AuthorizedForwarder } // Owner return authorized forwarder owner address -func (e *EthereumAuthorizedForwarder) Owner(ctx context.Context) (string, error) { +func (e *LegacyEthereumAuthorizedForwarder) Owner(ctx context.Context) (string, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -1885,7 +1860,7 @@ func (e *EthereumAuthorizedForwarder) Owner(ctx context.Context) (string, error) return owner.Hex(), err } -func (e *EthereumAuthorizedForwarder) GetAuthorizedSenders(ctx context.Context) ([]string, error) { +func (e *LegacyEthereumAuthorizedForwarder) GetAuthorizedSenders(ctx context.Context) ([]string, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -1901,7 +1876,7 @@ func (e *EthereumAuthorizedForwarder) GetAuthorizedSenders(ctx context.Context) return sendersAddrs, nil } -func (e *EthereumAuthorizedForwarder) Address() string { +func (e *LegacyEthereumAuthorizedForwarder) Address() string { return e.address.Hex() } @@ -1949,7 +1924,8 @@ func channelClosed(ch <-chan struct{}) bool { return false } -type EthereumOffchainAggregatorV2 struct { +// Deprecated: we are moving away from blockchain.EVMClient, use EthereumOffchainAggregatorV2 instead +type LegacyEthereumOffchainAggregatorV2 struct { address *common.Address client blockchain.EVMClient contract *ocr2aggregator.OCR2Aggregator @@ -1966,21 +1942,11 @@ type OCRv2Config struct { OffchainConfig []byte } -func (e *EthereumOffchainAggregatorV2) Address() string { +func (e *LegacyEthereumOffchainAggregatorV2) Address() string { return e.address.Hex() } -func (e *EthereumOffchainAggregatorV2) Fund(nativeAmount *big.Float) error { - gasEstimates, err := e.client.EstimateGas(ethereum.CallMsg{ - To: e.address, - }) - if err != nil { - return err - } - return e.client.Fund(e.address.Hex(), nativeAmount, gasEstimates) -} - -func (e *EthereumOffchainAggregatorV2) RequestNewRound() error { +func (e *LegacyEthereumOffchainAggregatorV2) RequestNewRound() error { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return err @@ -1992,7 +1958,7 @@ func (e *EthereumOffchainAggregatorV2) RequestNewRound() error { return e.client.ProcessTransaction(tx) } -func (e *EthereumOffchainAggregatorV2) GetLatestAnswer(ctx context.Context) (*big.Int, error) { +func (e *LegacyEthereumOffchainAggregatorV2) GetLatestAnswer(ctx context.Context) (*big.Int, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -2000,7 +1966,7 @@ func (e *EthereumOffchainAggregatorV2) GetLatestAnswer(ctx context.Context) (*bi return e.contract.LatestAnswer(opts) } -func (e *EthereumOffchainAggregatorV2) GetLatestRound(ctx context.Context) (*RoundData, error) { +func (e *LegacyEthereumOffchainAggregatorV2) GetLatestRound(ctx context.Context) (*RoundData, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -2018,7 +1984,7 @@ func (e *EthereumOffchainAggregatorV2) GetLatestRound(ctx context.Context) (*Rou }, nil } -func (e *EthereumOffchainAggregatorV2) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { +func (e *LegacyEthereumOffchainAggregatorV2) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { opts := &bind.CallOpts{ From: common.HexToAddress(e.client.GetDefaultWallet().Address()), Context: ctx, @@ -2036,7 +2002,7 @@ func (e *EthereumOffchainAggregatorV2) GetRound(ctx context.Context, roundID *bi }, nil } -func (e *EthereumOffchainAggregatorV2) SetPayees(transmitters, payees []string) error { +func (e *LegacyEthereumOffchainAggregatorV2) SetPayees(transmitters, payees []string) error { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return err @@ -2062,7 +2028,7 @@ func (e *EthereumOffchainAggregatorV2) SetPayees(transmitters, payees []string) return e.client.ProcessTransaction(tx) } -func (e *EthereumOffchainAggregatorV2) SetConfig(ocrConfig *OCRv2Config) error { +func (e *LegacyEthereumOffchainAggregatorV2) SetConfig(ocrConfig *OCRv2Config) error { opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) if err != nil { return err @@ -2091,19 +2057,7 @@ func (e *EthereumOffchainAggregatorV2) SetConfig(ocrConfig *OCRv2Config) error { return e.client.ProcessTransaction(tx) } -func (e *EthereumOffchainAggregatorV2) GetConfig(ctx context.Context) ([32]byte, uint32, error) { - opts := &bind.CallOpts{ - From: common.HexToAddress(e.client.GetDefaultWallet().Address()), - Context: ctx, - } - details, err := e.contract.LatestConfigDetails(opts) - if err != nil { - return [32]byte{}, 0, err - } - return details.ConfigDigest, details.BlockNumber, err -} - -func (e *EthereumOffchainAggregatorV2) ParseEventAnswerUpdated(log types.Log) (*ocr2aggregator.OCR2AggregatorAnswerUpdated, error) { +func (e *LegacyEthereumOffchainAggregatorV2) ParseEventAnswerUpdated(log types.Log) (*ocr2aggregator.OCR2AggregatorAnswerUpdated, error) { return e.contract.ParseAnswerUpdated(log) } @@ -2408,6 +2362,19 @@ func (e *EthereumMercuryFeeManager) Address() common.Address { return e.address } +func (e *EthereumMercuryFeeManager) UpdateSubscriberDiscount(subscriber common.Address, feedId [32]byte, token common.Address, discount uint64) (*types.Transaction, error) { + opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := e.instance.UpdateSubscriberDiscount(opts, subscriber, feedId, token, discount) + e.l.Info().Err(err).Msg("Called EthereumMercuryFeeManager.UpdateSubscriberDiscount()") + if err != nil { + return nil, err + } + return tx, e.client.ProcessTransaction(tx) +} + type EthereumMercuryRewardManager struct { address common.Address client blockchain.EVMClient @@ -2506,3 +2473,39 @@ func (e *EthereumWERC20Mock) Mint(account common.Address, amount *big.Int) (*typ } return tx, e.client.ProcessTransaction(tx) } + +func ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(k8sNodes []*client.ChainlinkK8sClient) []ChainlinkNodeWithKeysAndAddress { + var nodesAsInterface = make([]ChainlinkNodeWithKeysAndAddress, len(k8sNodes)) + for i, node := range k8sNodes { + nodesAsInterface[i] = node + } + + return nodesAsInterface +} + +func ChainlinkClientToChainlinkNodeWithKeysAndAddress(k8sNodes []*client.ChainlinkClient) []ChainlinkNodeWithKeysAndAddress { + var nodesAsInterface = make([]ChainlinkNodeWithKeysAndAddress, len(k8sNodes)) + for i, node := range k8sNodes { + nodesAsInterface[i] = node + } + + return nodesAsInterface +} + +func V2OffChainAgrregatorToOffChainAggregatorWithRounds(contracts []OffchainAggregatorV2) []OffChainAggregatorWithRounds { + var contractsAsInterface = make([]OffChainAggregatorWithRounds, len(contracts)) + for i, contract := range contracts { + contractsAsInterface[i] = contract + } + + return contractsAsInterface +} + +func V1OffChainAgrregatorToOffChainAggregatorWithRounds(contracts []OffchainAggregator) []OffChainAggregatorWithRounds { + var contractsAsInterface = make([]OffChainAggregatorWithRounds, len(contracts)) + for i, contract := range contracts { + contractsAsInterface[i] = contract + } + + return contractsAsInterface +} diff --git a/integration-tests/contracts/ethereum_contracts_seth.go b/integration-tests/contracts/ethereum_contracts_seth.go new file mode 100644 index 0000000000..237d689623 --- /dev/null +++ b/integration-tests/contracts/ethereum_contracts_seth.go @@ -0,0 +1,581 @@ +package contracts + +import ( + "context" + "encoding/hex" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/seth" + + "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" + ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" + ocrTypes "github.com/smartcontractkit/libocr/offchainreporting/types" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/link_token" +) + +// EthereumOffchainAggregator represents the offchain aggregation contract +type EthereumOffchainAggregator struct { + client *seth.Client + ocr *offchainaggregator.OffchainAggregator + address *common.Address + l zerolog.Logger +} + +func LoadOffchainAggregator(l zerolog.Logger, seth *seth.Client, contractAddress common.Address) (EthereumOffchainAggregator, error) { + oAbi, err := offchainaggregator.OffchainAggregatorMetaData.GetAbi() + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + } + seth.ContractStore.AddABI("OffChainAggregator", *oAbi) + seth.ContractStore.AddBIN("OffChainAggregator", common.FromHex(offchainaggregator.OffchainAggregatorMetaData.Bin)) + + ocr, err := offchainaggregator.NewOffchainAggregator(contractAddress, seth.Client) + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + + return EthereumOffchainAggregator{ + client: seth, + ocr: ocr, + address: &contractAddress, + l: l, + }, nil +} + +func DeployOffchainAggregator(l zerolog.Logger, seth *seth.Client, linkTokenAddress common.Address, offchainOptions OffchainOptions) (EthereumOffchainAggregator, error) { + oAbi, err := offchainaggregator.OffchainAggregatorMetaData.GetAbi() + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + } + + ocrDeploymentData, err := seth.DeployContract( + seth.NewTXOpts(), + "OffChainAggregator", + *oAbi, + common.FromHex(offchainaggregator.OffchainAggregatorMetaData.Bin), + offchainOptions.MaximumGasPrice, + offchainOptions.ReasonableGasPrice, + offchainOptions.MicroLinkPerEth, + offchainOptions.LinkGweiPerObservation, + offchainOptions.LinkGweiPerTransmission, + linkTokenAddress, + offchainOptions.MinimumAnswer, + offchainOptions.MaximumAnswer, + offchainOptions.BillingAccessController, + offchainOptions.RequesterAccessController, + offchainOptions.Decimals, + offchainOptions.Description) + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("OCR instance deployment have failed: %w", err) + } + + ocr, err := offchainaggregator.NewOffchainAggregator(ocrDeploymentData.Address, seth.Client) + if err != nil { + return EthereumOffchainAggregator{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + + return EthereumOffchainAggregator{ + client: seth, + ocr: ocr, + address: &ocrDeploymentData.Address, + l: l, + }, nil +} + +// SetPayees sets wallets for the contract to pay out to? +func (o *EthereumOffchainAggregator) SetPayees( + transmitters, payees []string, +) error { + var transmittersAddr, payeesAddr []common.Address + for _, tr := range transmitters { + transmittersAddr = append(transmittersAddr, common.HexToAddress(tr)) + } + for _, p := range payees { + payeesAddr = append(payeesAddr, common.HexToAddress(p)) + } + + o.l.Info(). + Str("Transmitters", fmt.Sprintf("%v", transmitters)). + Str("Payees", fmt.Sprintf("%v", payees)). + Str("OCR Address", o.Address()). + Msg("Setting OCR Payees") + + _, err := o.client.Decode(o.ocr.SetPayees(o.client.NewTXOpts(), transmittersAddr, payeesAddr)) + return err +} + +// SetConfig sets the payees and the offchain reporting protocol configuration +func (o *EthereumOffchainAggregator) SetConfig( + chainlinkNodes []ChainlinkNodeWithKeysAndAddress, + ocrConfig OffChainAggregatorConfig, + transmitters []common.Address, +) error { + // Gather necessary addresses and keys from our chainlink nodes to properly configure the OCR contract + log.Info().Str("Contract Address", o.address.Hex()).Msg("Configuring OCR Contract") + for i, node := range chainlinkNodes { + ocrKeys, err := node.MustReadOCRKeys() + if err != nil { + return err + } + if len(ocrKeys.Data) == 0 { + return fmt.Errorf("no OCR keys found for node %v", node) + } + primaryOCRKey := ocrKeys.Data[0] + if err != nil { + return err + } + p2pKeys, err := node.MustReadP2PKeys() + if err != nil { + return err + } + primaryP2PKey := p2pKeys.Data[0] + + // Need to convert the key representations + var onChainSigningAddress [20]byte + var configPublicKey [32]byte + offchainSigningAddress, err := hex.DecodeString(primaryOCRKey.Attributes.OffChainPublicKey) + if err != nil { + return err + } + decodeConfigKey, err := hex.DecodeString(primaryOCRKey.Attributes.ConfigPublicKey) + if err != nil { + return err + } + + // https://stackoverflow.com/questions/8032170/how-to-assign-string-to-bytes-array + copy(onChainSigningAddress[:], common.HexToAddress(primaryOCRKey.Attributes.OnChainSigningAddress).Bytes()) + copy(configPublicKey[:], decodeConfigKey) + + oracleIdentity := ocrConfigHelper.OracleIdentity{ + TransmitAddress: transmitters[i], + OnChainSigningAddress: onChainSigningAddress, + PeerID: primaryP2PKey.Attributes.PeerID, + OffchainPublicKey: offchainSigningAddress, + } + oracleIdentityExtra := ocrConfigHelper.OracleIdentityExtra{ + OracleIdentity: oracleIdentity, + SharedSecretEncryptionPublicKey: ocrTypes.SharedSecretEncryptionPublicKey(configPublicKey), + } + + ocrConfig.OracleIdentities = append(ocrConfig.OracleIdentities, oracleIdentityExtra) + } + + signers, transmitters, threshold, encodedConfigVersion, encodedConfig, err := ocrConfigHelper.ContractSetConfigArgs( + ocrConfig.DeltaProgress, + ocrConfig.DeltaResend, + ocrConfig.DeltaRound, + ocrConfig.DeltaGrace, + ocrConfig.DeltaC, + ocrConfig.AlphaPPB, + ocrConfig.DeltaStage, + ocrConfig.RMax, + ocrConfig.S, + ocrConfig.OracleIdentities, + ocrConfig.F, + ) + if err != nil { + 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 +} + +// RequestNewRound requests the OCR contract to create a new round +func (o *EthereumOffchainAggregator) RequestNewRound() error { + o.l.Info().Str("Contract Address", o.address.Hex()).Msg("New OCR round requested") + _, err := o.client.Decode(o.ocr.RequestNewRound(o.client.NewTXOpts())) + return err +} + +// GetLatestAnswer returns the latest answer from the OCR contract +func (o *EthereumOffchainAggregator) GetLatestAnswer(ctx context.Context) (*big.Int, error) { + return o.ocr.LatestAnswer(&bind.CallOpts{ + From: o.client.Addresses[0], + Context: ctx, + }) +} + +func (o *EthereumOffchainAggregator) Address() string { + return o.address.Hex() +} + +// GetLatestRound returns data from the latest round +func (o *EthereumOffchainAggregator) GetLatestRound(ctx context.Context) (*RoundData, error) { + roundData, err := o.ocr.LatestRoundData(&bind.CallOpts{ + From: o.client.Addresses[0], + Context: ctx, + }) + if err != nil { + return nil, err + } + + return &RoundData{ + RoundId: roundData.RoundId, + Answer: roundData.Answer, + AnsweredInRound: roundData.AnsweredInRound, + StartedAt: roundData.StartedAt, + UpdatedAt: roundData.UpdatedAt, + }, err +} + +func (o *EthereumOffchainAggregator) LatestRoundDataUpdatedAt() (*big.Int, error) { + data, err := o.ocr.LatestRoundData(o.client.NewCallOpts()) + if err != nil { + return nil, err + } + return data.UpdatedAt, nil +} + +// GetRound retrieves an OCR round by the round ID +func (o *EthereumOffchainAggregator) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { + roundData, err := o.ocr.GetRoundData(&bind.CallOpts{ + From: o.client.Addresses[0], + Context: ctx, + }, roundID) + if err != nil { + return nil, err + } + + return &RoundData{ + RoundId: roundData.RoundId, + Answer: roundData.Answer, + AnsweredInRound: roundData.AnsweredInRound, + StartedAt: roundData.StartedAt, + UpdatedAt: roundData.UpdatedAt, + }, nil +} + +// ParseEventAnswerUpdated parses the log for event AnswerUpdated +func (o *EthereumOffchainAggregator) ParseEventAnswerUpdated(eventLog types.Log) (*offchainaggregator.OffchainAggregatorAnswerUpdated, error) { + return o.ocr.ParseAnswerUpdated(eventLog) +} + +// LegacyEthereumOperatorFactory represents operator factory contract +type EthereumOperatorFactory struct { + address *common.Address + client *seth.Client + operatorFactory *operator_factory.OperatorFactory +} + +func DeployEthereumOperatorFactory(seth *seth.Client, linkTokenAddress common.Address) (EthereumOperatorFactory, error) { + operatorAbi, err := operator_factory.OperatorFactoryMetaData.GetAbi() + if err != nil { + return EthereumOperatorFactory{}, fmt.Errorf("failed to get OperatorFactory ABI: %w", err) + } + operatorData, err := seth.DeployContract(seth.NewTXOpts(), "OperatorFactory", *operatorAbi, common.FromHex(operator_factory.OperatorFactoryMetaData.Bin), linkTokenAddress) + if err != nil { + return EthereumOperatorFactory{}, fmt.Errorf("OperatorFactory instance deployment have failed: %w", err) + } + + operatorFactory, err := operator_factory.NewOperatorFactory(operatorData.Address, seth.Client) + if err != nil { + return EthereumOperatorFactory{}, fmt.Errorf("failed to instantiate OperatorFactory instance: %w", err) + } + + return EthereumOperatorFactory{ + address: &operatorData.Address, + client: seth, + operatorFactory: operatorFactory, + }, nil +} + +func (e *EthereumOperatorFactory) ParseAuthorizedForwarderCreated(eventLog types.Log) (*operator_factory.OperatorFactoryAuthorizedForwarderCreated, error) { + return e.operatorFactory.ParseAuthorizedForwarderCreated(eventLog) +} + +func (e *EthereumOperatorFactory) ParseOperatorCreated(eventLog types.Log) (*operator_factory.OperatorFactoryOperatorCreated, error) { + return e.operatorFactory.ParseOperatorCreated(eventLog) +} + +func (e *EthereumOperatorFactory) Address() string { + return e.address.Hex() +} + +func (e *EthereumOperatorFactory) DeployNewOperatorAndForwarder() (*types.Transaction, error) { + return e.operatorFactory.DeployNewOperatorAndForwarder(e.client.NewTXOpts()) +} + +// EthereumOperator represents operator contract +type EthereumOperator struct { + address *common.Address + client *seth.Client + operator *operator_wrapper.Operator + l zerolog.Logger +} + +func LoadEthereumOperator(logger zerolog.Logger, seth *seth.Client, contractAddress common.Address) (EthereumOperator, error) { + + abi, err := operator_wrapper.OperatorMetaData.GetAbi() + if err != nil { + return EthereumOperator{}, err + } + seth.ContractStore.AddABI("EthereumOperator", *abi) + seth.ContractStore.AddBIN("EthereumOperator", common.FromHex(operator_wrapper.OperatorMetaData.Bin)) + + operator, err := operator_wrapper.NewOperator(contractAddress, seth.Client) + if err != nil { + return EthereumOperator{}, err + } + + return EthereumOperator{ + address: &contractAddress, + client: seth, + operator: operator, + l: logger, + }, nil +} + +func (e *EthereumOperator) Address() string { + return e.address.Hex() +} + +func (e *EthereumOperator) AcceptAuthorizedReceivers(forwarders []common.Address, eoa []common.Address) error { + e.l.Info(). + Str("ForwardersAddresses", fmt.Sprint(forwarders)). + Str("EoaAddresses", fmt.Sprint(eoa)). + Msg("Accepting Authorized Receivers") + _, err := e.client.Decode(e.operator.AcceptAuthorizedReceivers(e.client.NewTXOpts(), forwarders, eoa)) + return err +} + +// EthereumAuthorizedForwarder represents authorized forwarder contract +type EthereumAuthorizedForwarder struct { + address *common.Address + client *seth.Client + authorizedForwarder *authorized_forwarder.AuthorizedForwarder +} + +func LoadEthereumAuthorizedForwarder(seth *seth.Client, contractAddress common.Address) (EthereumAuthorizedForwarder, error) { + abi, err := authorized_forwarder.AuthorizedForwarderMetaData.GetAbi() + if err != nil { + return EthereumAuthorizedForwarder{}, err + } + seth.ContractStore.AddABI("AuthorizedForwarder", *abi) + seth.ContractStore.AddBIN("AuthorizedForwarder", common.FromHex(authorized_forwarder.AuthorizedForwarderMetaData.Bin)) + + authorizedForwarder, err := authorized_forwarder.NewAuthorizedForwarder(contractAddress, seth.Client) + if err != nil { + return EthereumAuthorizedForwarder{}, fmt.Errorf("failed to instantiate AuthorizedForwarder instance: %w", err) + } + + return EthereumAuthorizedForwarder{ + address: &contractAddress, + client: seth, + authorizedForwarder: authorizedForwarder, + }, nil +} + +// Owner return authorized forwarder owner address +func (e *EthereumAuthorizedForwarder) Owner(_ context.Context) (string, error) { + owner, err := e.authorizedForwarder.Owner(e.client.NewCallOpts()) + + return owner.Hex(), err +} + +func (e *EthereumAuthorizedForwarder) GetAuthorizedSenders(ctx context.Context) ([]string, error) { + opts := &bind.CallOpts{ + From: e.client.Addresses[0], + Context: ctx, + } + authorizedSenders, err := e.authorizedForwarder.GetAuthorizedSenders(opts) + if err != nil { + return nil, err + } + var sendersAddrs []string + for _, o := range authorizedSenders { + sendersAddrs = append(sendersAddrs, o.Hex()) + } + return sendersAddrs, nil +} + +func (e *EthereumAuthorizedForwarder) Address() string { + return e.address.Hex() +} + +type EthereumOffchainAggregatorV2 struct { + address *common.Address + client *seth.Client + contract *ocr2aggregator.OCR2Aggregator + l zerolog.Logger +} + +func LoadOffChainAggregatorV2(l zerolog.Logger, seth *seth.Client, contractAddress common.Address) (EthereumOffchainAggregatorV2, error) { + oAbi, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi() + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + } + seth.ContractStore.AddABI("OffChainAggregatorV2", *oAbi) + seth.ContractStore.AddBIN("OffChainAggregatorV2", common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin)) + + ocr2, err := ocr2aggregator.NewOCR2Aggregator(contractAddress, seth.Client) + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + + return EthereumOffchainAggregatorV2{ + client: seth, + contract: ocr2, + address: &contractAddress, + l: l, + }, nil +} + +func DeployOffchainAggregatorV2(l zerolog.Logger, seth *seth.Client, linkTokenAddress common.Address, offchainOptions OffchainOptions) (EthereumOffchainAggregatorV2, error) { + oAbi, err := ocr2aggregator.OCR2AggregatorMetaData.GetAbi() + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to get OffChain Aggregator ABI: %w", err) + } + seth.ContractStore.AddABI("OffChainAggregatorV2", *oAbi) + seth.ContractStore.AddBIN("OffChainAggregatorV2", common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin)) + + ocrDeploymentData2, err := seth.DeployContract(seth.NewTXOpts(), "OffChainAggregatorV2", *oAbi, common.FromHex(ocr2aggregator.OCR2AggregatorMetaData.Bin), + linkTokenAddress, + offchainOptions.MinimumAnswer, + offchainOptions.MaximumAnswer, + offchainOptions.BillingAccessController, + offchainOptions.RequesterAccessController, + offchainOptions.Decimals, + offchainOptions.Description, + ) + + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("OCR instance deployment have failed: %w", err) + } + + ocr2, err := ocr2aggregator.NewOCR2Aggregator(ocrDeploymentData2.Address, seth.Client) + if err != nil { + return EthereumOffchainAggregatorV2{}, fmt.Errorf("failed to instantiate OCR instance: %w", err) + } + + return EthereumOffchainAggregatorV2{ + client: seth, + contract: ocr2, + address: &ocrDeploymentData2.Address, + l: l, + }, nil +} + +func (e *EthereumOffchainAggregatorV2) Address() string { + return e.address.Hex() +} + +func (e *EthereumOffchainAggregatorV2) RequestNewRound() error { + _, err := e.client.Decode(e.contract.RequestNewRound(e.client.NewTXOpts())) + return err +} + +func (e *EthereumOffchainAggregatorV2) GetLatestAnswer(ctx context.Context) (*big.Int, error) { + return e.contract.LatestAnswer(&bind.CallOpts{ + From: e.client.Addresses[0], + Context: ctx, + }) +} + +func (e *EthereumOffchainAggregatorV2) GetLatestRound(ctx context.Context) (*RoundData, error) { + data, err := e.contract.LatestRoundData(&bind.CallOpts{ + From: e.client.Addresses[0], + Context: ctx, + }) + if err != nil { + return nil, err + } + return &RoundData{ + RoundId: data.RoundId, + StartedAt: data.StartedAt, + UpdatedAt: data.UpdatedAt, + AnsweredInRound: data.AnsweredInRound, + Answer: data.Answer, + }, nil +} + +func (e *EthereumOffchainAggregatorV2) GetRound(ctx context.Context, roundID *big.Int) (*RoundData, error) { + data, err := e.contract.GetRoundData(&bind.CallOpts{ + From: e.client.Addresses[0], + Context: ctx, + }, roundID) + if err != nil { + return nil, err + } + return &RoundData{ + RoundId: data.RoundId, + StartedAt: data.StartedAt, + UpdatedAt: data.UpdatedAt, + AnsweredInRound: data.AnsweredInRound, + Answer: data.Answer, + }, nil +} + +func (e *EthereumOffchainAggregatorV2) SetPayees(transmitters, payees []string) error { + e.l.Info(). + Str("Transmitters", fmt.Sprintf("%v", transmitters)). + Str("Payees", fmt.Sprintf("%v", payees)). + Str("OCRv2 Address", e.Address()). + Msg("Setting OCRv2 Payees") + + var addTransmitters, addrPayees []common.Address + for _, t := range transmitters { + addTransmitters = append(addTransmitters, common.HexToAddress(t)) + } + for _, p := range payees { + addrPayees = append(addrPayees, common.HexToAddress(p)) + } + + _, err := e.client.Decode(e.contract.SetPayees(e.client.NewTXOpts(), addTransmitters, addrPayees)) + return err +} + +func (e *EthereumOffchainAggregatorV2) SetConfig(ocrConfig *OCRv2Config) error { + e.l.Info(). + Str("Address", e.Address()). + Interface("Signers", ocrConfig.Signers). + Interface("Transmitters", ocrConfig.Transmitters). + Uint8("F", ocrConfig.F). + Bytes("OnchainConfig", ocrConfig.OnchainConfig). + Uint64("OffchainConfigVersion", ocrConfig.OffchainConfigVersion). + Bytes("OffchainConfig", ocrConfig.OffchainConfig). + Msg("Setting OCRv2 Config") + + _, err := e.client.Decode(e.contract.SetConfig( + e.client.NewTXOpts(), + ocrConfig.Signers, + ocrConfig.Transmitters, + ocrConfig.F, + ocrConfig.OnchainConfig, + ocrConfig.OffchainConfigVersion, + ocrConfig.OffchainConfig, + )) + return err +} + +func (e *EthereumOffchainAggregatorV2) ParseEventAnswerUpdated(log types.Log) (*ocr2aggregator.OCR2AggregatorAnswerUpdated, error) { + return e.contract.ParseAnswerUpdated(log) +} + +func DeployLinkTokenContract(client *seth.Client) (seth.DeploymentData, error) { + linkTokenAbi, err := link_token.LinkTokenMetaData.GetAbi() + if err != nil { + return seth.DeploymentData{}, fmt.Errorf("failed to get LinkToken ABI: %w", err) + } + linkDeploymentData, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *linkTokenAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + if err != nil { + return seth.DeploymentData{}, fmt.Errorf("LinkToken instance deployment have failed: %w", err) + } + + return linkDeploymentData, nil +} diff --git a/integration-tests/contracts/ethereum_vrf_contracts.go b/integration-tests/contracts/ethereum_vrf_contracts.go index 427ac4ccbf..ea8a4f9481 100644 --- a/integration-tests/contracts/ethereum_vrf_contracts.go +++ b/integration-tests/contracts/ethereum_vrf_contracts.go @@ -134,6 +134,18 @@ func (v *EthereumBlockhashStore) Address() string { return v.address.Hex() } +func (v *EthereumBlockhashStore) GetBlockHash(ctx context.Context, blockNumber *big.Int) ([32]byte, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + blockHash, err := v.blockHashStore.GetBlockhash(opts, blockNumber) + if err != nil { + return [32]byte{}, err + } + return blockHash, nil +} + func (v *EthereumVRFCoordinator) Address() string { return v.address.Hex() } diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index 7501521ac3..fc7a5a7a13 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -913,6 +913,9 @@ func (v *EthereumVRFv2LoadTestConsumer) GetLoadTestMetrics(ctx context.Context) averageFulfillmentInMillions, slowestFulfillment, fastestFulfillment, + nil, + nil, + nil, }, nil } @@ -1036,6 +1039,9 @@ func (v *EthereumVRFV2WrapperLoadTestConsumer) GetLoadTestMetrics(ctx context.Co averageFulfillmentInMillions, slowestFulfillment, fastestFulfillment, + nil, + nil, + nil, }, nil } diff --git a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go index 31c7f1e4f4..64afb4c466 100644 --- a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go @@ -50,6 +50,69 @@ type EthereumVRFV2PlusWrapper struct { wrapper *vrfv2plus_wrapper.VRFV2PlusWrapper } +func (v *EthereumVRFV2PlusWrapper) Address() string { + return v.address.Hex() +} + +func (v *EthereumVRFV2PlusWrapper) SetConfig(wrapperGasOverhead uint32, + coordinatorGasOverhead uint32, + wrapperPremiumPercentage uint8, + keyHash [32]byte, + maxNumWords uint8, + stalenessSeconds uint32, + fallbackWeiPerUnitLink *big.Int, + fulfillmentFlatFeeLinkPPM uint32, + fulfillmentFlatFeeNativePPM uint32, +) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.wrapper.SetConfig( + opts, + wrapperGasOverhead, + coordinatorGasOverhead, + wrapperPremiumPercentage, + keyHash, + maxNumWords, + stalenessSeconds, + fallbackWeiPerUnitLink, + fulfillmentFlatFeeLinkPPM, + fulfillmentFlatFeeNativePPM, + ) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFV2PlusWrapper) GetSubID(ctx context.Context) (*big.Int, error) { + return v.wrapper.SUBSCRIPTIONID(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) +} + +func (v *EthereumVRFV2PlusWrapper) Migrate(newCoordinator common.Address) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.wrapper.Migrate(opts, newCoordinator) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFV2PlusWrapper) Coordinator(ctx context.Context) (common.Address, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + return v.wrapper.SVrfCoordinator(opts) +} + // DeployVRFCoordinatorV2_5 deploys VRFV2_5 coordinator contract func (e *EthereumContractDeployer) DeployVRFCoordinatorV2_5(bhsAddr string) (VRFCoordinatorV2_5, error) { address, _, instance, err := e.client.DeployContract("VRFCoordinatorV2Plus", func( @@ -210,7 +273,16 @@ func (v *EthereumVRFCoordinatorV2_5) WithdrawNative(recipient common.Address) er return v.client.ProcessTransaction(tx) } -func (v *EthereumVRFCoordinatorV2_5) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig) error { +func (v *EthereumVRFCoordinatorV2_5) SetConfig( + minimumRequestConfirmations uint16, + maxGasLimit uint32, + stalenessSeconds uint32, + gasAfterPaymentCalculation uint32, + fallbackWeiPerUnitLink *big.Int, + fulfillmentFlatFeeNativePPM uint32, + fulfillmentFlatFeeLinkDiscountPPM uint32, + nativePremiumPercentage uint8, + linkPremiumPercentage uint8) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err @@ -222,7 +294,10 @@ func (v *EthereumVRFCoordinatorV2_5) SetConfig(minimumRequestConfirmations uint1 stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, - feeConfig, + fulfillmentFlatFeeNativePPM, + fulfillmentFlatFeeLinkDiscountPPM, + nativePremiumPercentage, + linkPremiumPercentage, ) if err != nil { return err @@ -248,12 +323,13 @@ func (v *EthereumVRFCoordinatorV2_5) SetLINKAndLINKNativeFeed(linkAddress string func (v *EthereumVRFCoordinatorV2_5) RegisterProvingKey( publicProvingKey [2]*big.Int, + gasLaneMaxGas uint64, ) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err } - tx, err := v.coordinator.RegisterProvingKey(opts, publicProvingKey) + tx, err := v.coordinator.RegisterProvingKey(opts, publicProvingKey, gasLaneMaxGas) if err != nil { return err } @@ -509,14 +585,14 @@ func (v *EthereumVRFv2PlusLoadTestConsumer) GetLoadTestMetrics(ctx context.Conte if err != nil { return nil, err } - averageFulfillmentInMillions, err := v.consumer.SAverageFulfillmentInMillions(&bind.CallOpts{ + averageFulfillmentInMillions, err := v.consumer.SAverageResponseTimeInBlocksMillions(&bind.CallOpts{ From: common.HexToAddress(v.client.GetDefaultWallet().Address()), Context: ctx, }) if err != nil { return nil, err } - slowestFulfillment, err := v.consumer.SSlowestFulfillment(&bind.CallOpts{ + slowestFulfillment, err := v.consumer.SSlowestResponseTimeInBlocks(&bind.CallOpts{ From: common.HexToAddress(v.client.GetDefaultWallet().Address()), Context: ctx, }) @@ -524,7 +600,30 @@ func (v *EthereumVRFv2PlusLoadTestConsumer) GetLoadTestMetrics(ctx context.Conte if err != nil { return nil, err } - fastestFulfillment, err := v.consumer.SFastestFulfillment(&bind.CallOpts{ + fastestFulfillment, err := v.consumer.SFastestResponseTimeInBlocks(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) + if err != nil { + return nil, err + } + + averageResponseTimeInSeconds, err := v.consumer.SAverageResponseTimeInSecondsMillions(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) + if err != nil { + return nil, err + } + slowestResponseTimeInSeconds, err := v.consumer.SSlowestResponseTimeInSeconds(&bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + }) + + if err != nil { + return nil, err + } + fastestResponseTimeInSeconds, err := v.consumer.SFastestResponseTimeInSeconds(&bind.CallOpts{ From: common.HexToAddress(v.client.GetDefaultWallet().Address()), Context: ctx, }) @@ -533,11 +632,14 @@ func (v *EthereumVRFv2PlusLoadTestConsumer) GetLoadTestMetrics(ctx context.Conte } return &VRFLoadTestMetrics{ - requestCount, - fulfilmentCount, - averageFulfillmentInMillions, - slowestFulfillment, - fastestFulfillment, + RequestCount: requestCount, + FulfilmentCount: fulfilmentCount, + AverageFulfillmentInMillions: averageFulfillmentInMillions, + SlowestFulfillment: slowestFulfillment, + FastestFulfillment: fastestFulfillment, + AverageResponseTimeInSecondsMillions: averageResponseTimeInSeconds, + SlowestResponseTimeInSeconds: slowestResponseTimeInSeconds, + FastestResponseTimeInSeconds: fastestResponseTimeInSeconds, }, nil } @@ -598,7 +700,16 @@ func (v *EthereumVRFCoordinatorV2PlusUpgradedVersion) GetSubscription(ctx contex return subscription, nil } -func (v *EthereumVRFCoordinatorV2PlusUpgradedVersion) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionFeeConfig) error { +func (v *EthereumVRFCoordinatorV2PlusUpgradedVersion) SetConfig( + minimumRequestConfirmations uint16, + maxGasLimit uint32, + stalenessSeconds uint32, + gasAfterPaymentCalculation uint32, + fallbackWeiPerUnitLink *big.Int, + fulfillmentFlatFeeNativePPM uint32, + fulfillmentFlatFeeLinkDiscountPPM uint32, + nativePremiumPercentage uint8, + linkPremiumPercentage uint8) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err @@ -610,7 +721,10 @@ func (v *EthereumVRFCoordinatorV2PlusUpgradedVersion) SetConfig(minimumRequestCo stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, - feeConfig, + fulfillmentFlatFeeNativePPM, + fulfillmentFlatFeeLinkDiscountPPM, + nativePremiumPercentage, + linkPremiumPercentage, ) if err != nil { return err @@ -850,10 +964,6 @@ func (e *EthereumContractDeployer) DeployVRFV2PlusWrapper(linkAddr string, linkE }, err } -func (v *EthereumVRFV2PlusWrapper) Address() string { - return v.address.Hex() -} - func (e *EthereumContractDeployer) DeployVRFV2PlusWrapperLoadTestConsumer(linkAddr string, vrfV2PlusWrapperAddr string) (VRFv2PlusWrapperLoadTestConsumer, error) { address, _, instance, err := e.client.DeployContract("VRFV2PlusWrapperLoadTestConsumer", func( auth *bind.TransactOpts, @@ -875,45 +985,6 @@ func (v *EthereumVRFV2PlusWrapperLoadTestConsumer) Address() string { return v.address.Hex() } -func (v *EthereumVRFV2PlusWrapper) SetConfig(wrapperGasOverhead uint32, - coordinatorGasOverhead uint32, - wrapperPremiumPercentage uint8, - keyHash [32]byte, - maxNumWords uint8, - stalenessSeconds uint32, - fallbackWeiPerUnitLink *big.Int, - fulfillmentFlatFeeLinkPPM uint32, - fulfillmentFlatFeeNativePPM uint32, -) error { - opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) - if err != nil { - return err - } - tx, err := v.wrapper.SetConfig( - opts, - wrapperGasOverhead, - coordinatorGasOverhead, - wrapperPremiumPercentage, - keyHash, - maxNumWords, - stalenessSeconds, - fallbackWeiPerUnitLink, - fulfillmentFlatFeeLinkPPM, - fulfillmentFlatFeeNativePPM, - ) - if err != nil { - return err - } - return v.client.ProcessTransaction(tx) -} - -func (v *EthereumVRFV2PlusWrapper) GetSubID(ctx context.Context) (*big.Int, error) { - return v.wrapper.SUBSCRIPTIONID(&bind.CallOpts{ - From: common.HexToAddress(v.client.GetDefaultWallet().Address()), - Context: ctx, - }) -} - func (v *EthereumVRFV2PlusWrapperLoadTestConsumer) Fund(ethAmount *big.Float) error { gasEstimates, err := v.client.EstimateGas(ethereum.CallMsg{ To: v.address, @@ -1011,10 +1082,13 @@ func (v *EthereumVRFV2PlusWrapperLoadTestConsumer) GetLoadTestMetrics(ctx contex } return &VRFLoadTestMetrics{ - requestCount, - fulfilmentCount, - averageFulfillmentInMillions, - slowestFulfillment, - fastestFulfillment, + RequestCount: requestCount, + FulfilmentCount: fulfilmentCount, + AverageFulfillmentInMillions: averageFulfillmentInMillions, + SlowestFulfillment: slowestFulfillment, + FastestFulfillment: fastestFulfillment, + AverageResponseTimeInSecondsMillions: nil, + SlowestResponseTimeInSeconds: nil, + FastestResponseTimeInSeconds: nil, }, nil } diff --git a/integration-tests/docker/README.md b/integration-tests/docker/README.md index b8ef1fc49d..7df481832e 100644 --- a/integration-tests/docker/README.md +++ b/integration-tests/docker/README.md @@ -1,13 +1,14 @@ -## Docker environment -This folder contains Chainlink cluster environment created with `testcontainers-go` +# Docker environment -### CLI for Local Testing Environment +This folder contains a Chainlink cluster environment created with [testcontainers-go](https://github.com/testcontainers/testcontainers-go/tree/main). + +## CLI for Local Testing Environment The command-line interface (CLI) located at `./integration-tests/docker/cmd/test_env.go` can be utilized to initiate a local testing environment. It is intended to replace Docker Compose in the near future. +Example: -Example: -``` +```sh # Set required envs export CHAINLINK_IMAGE="" export CHAINLINK_VERSION="" @@ -18,4 +19,4 @@ export LOKI_URL=https://${loki_host}/loki/api/v1/push cd ./integration-tests/docker/cmd go run test_env.go start-env cl-cluster -``` \ No newline at end of file +``` diff --git a/integration-tests/docker/cmd/test_env.go b/integration-tests/docker/cmd/test_env.go index 5fe2001350..bbe8612833 100644 --- a/integration-tests/docker/cmd/test_env.go +++ b/integration-tests/docker/cmd/test_env.go @@ -12,6 +12,8 @@ import ( "github.com/spf13/cobra" "github.com/testcontainers/testcontainers-go" + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) @@ -35,8 +37,18 @@ func main() { log.Logger = logging.GetLogger(nil, "CORE_DOCKER_ENV_LOG_LEVEL") log.Info().Msg("Starting CL cluster test environment..") - _, err := test_env.NewCLTestEnvBuilder(). - WithGeth(). + ethBuilder := ctf_test_env.NewEthereumNetworkBuilder() + network, err := ethBuilder. + WithEthereumVersion(ctf_test_env.EthereumVersion_Eth1). + WithExecutionLayer(ctf_test_env.ExecutionLayer_Geth). + Build() + + if err != nil { + return err + } + + _, err = test_env.NewCLTestEnvBuilder(). + WithPrivateEthereumNetwork(network). WithMockAdapter(). WithCLNodes(6). Build() diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 2ffd49b877..09da97b40f 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -43,6 +43,11 @@ var ( ErrStartCLNodeContainer = "failed to start CL node container" ) +const ( + RestartContainer = true + StartNewContainer = false +) + type ClNode struct { test_env.EnvComponent API *client.ChainlinkClient `json:"-"` @@ -158,7 +163,7 @@ func (n *ClNode) Restart(cfg *chainlink.Config) error { return err } n.NodeConfig = cfg - return n.StartContainer() + return n.RestartContainer() } // UpgradeVersion restarts the cl node with new image and version @@ -275,6 +280,10 @@ func (n *ClNode) Fund(evmClient blockchain.EVMClient, amount *big.Float) error { if err != nil { return err } + n.l.Debug(). + Str("ChainId", evmClient.GetChainID().String()). + Str("Address", toAddress). + Msg("Funding Chainlink Node") toAddr := common.HexToAddress(toAddress) gasEstimates, err := evmClient.EstimateGas(ethereum.CallMsg{ To: &toAddr, @@ -285,8 +294,13 @@ func (n *ClNode) Fund(evmClient blockchain.EVMClient, amount *big.Float) error { return evmClient.Fund(toAddress, amount, gasEstimates) } -func (n *ClNode) StartContainer() error { - err := n.PostgresDb.StartContainer() +func (n *ClNode) containerStartOrRestart(restartDb bool) error { + var err error + if restartDb { + err = n.PostgresDb.RestartContainer() + } else { + err = n.PostgresDb.StartContainer() + } if err != nil { return err } @@ -318,7 +332,7 @@ func (n *ClNode) StartContainer() error { container, err := docker.StartContainerWithRetry(n.l, tc.GenericContainerRequest{ ContainerRequest: *cReq, Started: true, - Reuse: true, + Reuse: false, Logger: l, }) if err != nil { @@ -359,6 +373,14 @@ func (n *ClNode) StartContainer() error { return nil } +func (n *ClNode) RestartContainer() error { + return n.containerStartOrRestart(RestartContainer) +} + +func (n *ClNode) StartContainer() error { + return n.containerStartOrRestart(StartNewContainer) +} + func (n *ClNode) ExecGetVersion() (string, error) { cmd := []string{"chainlink", "--version"} _, output, err := n.Container.Exec(context.Background(), cmd) diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index e6d1451627..23570724b3 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -4,12 +4,12 @@ import ( "encoding/json" "fmt" "math/big" - "runtime/debug" "testing" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/seth" tc "github.com/testcontainers/testcontainers-go" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" @@ -21,9 +21,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - core_testconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -32,21 +32,23 @@ var ( ) type CLClusterTestEnv struct { - Cfg *TestEnvConfig - Network *tc.DockerNetwork - LogStream *logstream.LogStream + Cfg *TestEnvConfig + DockerNetwork *tc.DockerNetwork + LogStream *logstream.LogStream /* components */ - ClCluster *ClCluster - PrivateChain []test_env.PrivateChain // for tests using non-dev networks -- unify it with new approach - MockAdapter *test_env.Killgrave - EVMClient blockchain.EVMClient - ContractDeployer contracts.ContractDeployer - ContractLoader contracts.ContractLoader - RpcProvider test_env.RpcProvider - PrivateEthereumConfig *test_env.EthereumNetwork // new approach to private chains, supporting eth1 and eth2 - l zerolog.Logger - t *testing.T + ClCluster *ClCluster + MockAdapter *test_env.Killgrave + evmClients map[int64]blockchain.EVMClient + SethClient *seth.Client + ContractDeployer contracts.ContractDeployer + ContractLoader contracts.ContractLoader + PrivateEthereumConfigs []*test_env.EthereumNetwork // new approach to private chains, supporting eth1 and eth2 + EVMNetworks []*blockchain.EVMNetwork + rpcProviders map[int64]*test_env.RpcProvider + l zerolog.Logger + t *testing.T + isSimulatedNetwork bool } func NewTestEnv() (*CLClusterTestEnv, error) { @@ -56,8 +58,8 @@ func NewTestEnv() (*CLClusterTestEnv, error) { return nil, err } return &CLClusterTestEnv{ - Network: network, - l: log.Logger, + DockerNetwork: network, + l: log.Logger, }, nil } @@ -66,7 +68,7 @@ func NewTestEnv() (*CLClusterTestEnv, error) { func (te *CLClusterTestEnv) WithTestEnvConfig(cfg *TestEnvConfig) *CLClusterTestEnv { te.Cfg = cfg if cfg.MockAdapter.ContainerName != "" { - n := []string{te.Network.Name} + n := []string{te.DockerNetwork.Name} te.MockAdapter = test_env.NewKillgrave(n, te.Cfg.MockAdapter.ImpostersPath, test_env.WithContainerName(te.Cfg.MockAdapter.ContainerName), test_env.WithLogStream(te.LogStream)) } return te @@ -82,47 +84,9 @@ func (te *CLClusterTestEnv) WithTestInstance(t *testing.T) *CLClusterTestEnv { } func (te *CLClusterTestEnv) ParallelTransactions(enabled bool) { - te.EVMClient.ParallelTransactions(enabled) -} - -func (te *CLClusterTestEnv) WithPrivateChain(evmNetworks []blockchain.EVMNetwork) *CLClusterTestEnv { - var chains []test_env.PrivateChain - for _, evmNetwork := range evmNetworks { - n := evmNetwork - pgc := test_env.NewPrivateGethChain(&n, []string{te.Network.Name}) - if te.t != nil { - pgc.GetPrimaryNode().WithTestInstance(te.t) - } - chains = append(chains, pgc) - var privateChain test_env.PrivateChain - switch n.SimulationType { - case "besu": - privateChain = test_env.NewPrivateBesuChain(&n, []string{te.Network.Name}) - default: - privateChain = test_env.NewPrivateGethChain(&n, []string{te.Network.Name}) - } - chains = append(chains, privateChain) - } - te.PrivateChain = chains - return te -} - -func (te *CLClusterTestEnv) StartPrivateChain() error { - for _, chain := range te.PrivateChain { - primaryNode := chain.GetPrimaryNode() - if primaryNode == nil { - return fmt.Errorf("primary node is nil in PrivateChain interface, stack: %s", string(debug.Stack())) - } - err := primaryNode.Start() - if err != nil { - return err - } - err = primaryNode.ConnectToClient() - if err != nil { - return err - } + for _, evmClient := range te.evmClients { + evmClient.ParallelTransactions(enabled) } - return nil } func (te *CLClusterTestEnv) StartEthereumNetwork(cfg *test_env.EthereumNetwork) (blockchain.EVMNetwork, test_env.RpcProvider, error) { @@ -138,6 +102,13 @@ func (te *CLClusterTestEnv) StartEthereumNetwork(cfg *test_env.EthereumNetwork) } cfg = &c } + + te.l.Info(). + Str("Execution Layer", string(*cfg.ExecutionLayer)). + Str("Ethereum Version", string(*cfg.EthereumVersion)). + Str("Custom Docker Images", fmt.Sprintf("%v", cfg.CustomDockerImages)). + Msg("Starting Ethereum network") + n, rpc, err := cfg.Start() if err != nil { @@ -156,10 +127,18 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i if te.Cfg != nil && te.Cfg.ClCluster != nil { te.ClCluster = te.Cfg.ClCluster } else { + // prepend the postgres version option from the toml config + if testconfig.GetChainlinkImageConfig().PostgresVersion != nil && *testconfig.GetChainlinkImageConfig().PostgresVersion != "" { + opts = append([]func(c *ClNode){ + func(c *ClNode) { + c.PostgresDb.EnvComponent.ContainerVersion = *testconfig.GetChainlinkImageConfig().PostgresVersion + }, + }, opts...) + } opts = append(opts, WithSecrets(secretsConfig), WithLogStream(te.LogStream)) te.ClCluster = &ClCluster{} for i := 0; i < count; i++ { - ocrNode, err := NewClNode([]string{te.Network.Name}, *testconfig.GetChainlinkImageConfig().Image, *testconfig.GetChainlinkImageConfig().Version, nodeConfig, opts...) + ocrNode, err := NewClNode([]string{te.DockerNetwork.Name}, *testconfig.GetChainlinkImageConfig().Image, *testconfig.GetChainlinkImageConfig().Version, nodeConfig, opts...) if err != nil { return err } @@ -180,12 +159,19 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i // FundChainlinkNodes will fund all the provided Chainlink nodes with a set amount of native currency func (te *CLClusterTestEnv) FundChainlinkNodes(amount *big.Float) error { - for _, cl := range te.ClCluster.Nodes { - if err := cl.Fund(te.EVMClient, amount); err != nil { - return fmt.Errorf("%s, err: %w", ErrFundCLNode, err) + for _, evmClient := range te.evmClients { + for _, cl := range te.ClCluster.Nodes { + if err := cl.Fund(evmClient, amount); err != nil { + return fmt.Errorf("%s, err: %w", ErrFundCLNode, err) + } + } + err := evmClient.WaitForEvents() + if err != nil { + return err } } - return te.EVMClient.WaitForEvents() + + return nil } func (te *CLClusterTestEnv) Terminate() error { @@ -213,11 +199,10 @@ func (te *CLClusterTestEnv) Cleanup() error { te.logWhetherAllContainersAreRunning() - if te.EVMClient == nil { - return fmt.Errorf("evm client is nil, unable to return funds from chainlink nodes during cleanup") - } else if te.EVMClient.NetworkSimulated() { + if len(te.evmClients) == 0 && te.SethClient == nil { + return fmt.Errorf("both EVMClients and SethClient are nil, unable to return funds from chainlink nodes during cleanup") + } else if te.isSimulatedNetwork { te.l.Info(). - Str("Network Name", te.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return.") } else { if err := te.returnFunds(); err != nil { @@ -226,11 +211,15 @@ func (te *CLClusterTestEnv) Cleanup() error { } // close EVMClient connections - if te.EVMClient != nil { - err := te.EVMClient.Close() + for _, evmClient := range te.evmClients { + err := evmClient.Close() return err } + if te.SethClient != nil { + te.SethClient.Client.Close() + } + return nil } @@ -255,29 +244,63 @@ func (te *CLClusterTestEnv) logWhetherAllContainersAreRunning() { func (te *CLClusterTestEnv) returnFunds() error { te.l.Info().Msg("Attempting to return Chainlink node funds to default network wallets") - for _, chainlinkNode := range te.ClCluster.Nodes { - fundedKeys, err := chainlinkNode.API.ExportEVMKeysForChain(te.EVMClient.GetChainID().String()) - if err != nil { - return err - } - for _, key := range fundedKeys { - keyToDecrypt, err := json.Marshal(key) - if err != nil { - return err - } - // This can take up a good bit of RAM and time. When running on the remote-test-runner, this can lead to OOM - // issues. So we avoid running in parallel; slower, but safer. - decryptedKey, err := keystore.DecryptKey(keyToDecrypt, client.ChainlinkKeyPassword) + + if len(te.evmClients) == 0 && te.SethClient == nil { + return fmt.Errorf("both EVMClients and SethClient are nil, unable to return funds from chainlink nodes") + } + + for _, evmClient := range te.evmClients { + for _, chainlinkNode := range te.ClCluster.Nodes { + fundedKeys, err := chainlinkNode.API.ExportEVMKeysForChain(te.evmClients[0].GetChainID().String()) if err != nil { return err } - if err = te.EVMClient.ReturnFunds(decryptedKey.PrivateKey); err != nil { - // If we fail to return funds from one, go on to try the others anyway - te.l.Error().Err(err).Str("Node", chainlinkNode.ContainerName).Msg("Error returning funds from node") + for _, key := range fundedKeys { + keyToDecrypt, err := json.Marshal(key) + if err != nil { + return err + } + // This can take up a good bit of RAM and time. When running on the remote-test-runner, this can lead to OOM + // issues. So we avoid running in parallel; slower, but safer. + decryptedKey, err := keystore.DecryptKey(keyToDecrypt, client.ChainlinkKeyPassword) + if err != nil { + return err + } + if te.evmClients[0] != nil { + te.l.Debug(). + Str("ChainId", evmClient.GetChainID().String()). + Msg("Returning funds from chainlink node") + if err = evmClient.ReturnFunds(decryptedKey.PrivateKey); err != nil { + // If we fail to return funds from one, go on to try the others anyway + te.l.Error().Err(err).Str("Node", chainlinkNode.ContainerName).Msg("Error returning funds from node") + } + } } } } + if te.SethClient != nil { + if err := actions_seth.ReturnFunds(te.l, te.SethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(te.ClCluster.NodeAPIs())); err != nil { + te.l.Error().Err(err).Msg("Error returning funds from node") + } + } + te.l.Info().Msg("Returned funds from Chainlink nodes") return nil } + +func (te *CLClusterTestEnv) GetEVMClient(chainId int64) (blockchain.EVMClient, error) { + if evmClient, ok := te.evmClients[chainId]; ok { + return evmClient, nil + } + + return nil, fmt.Errorf("no EVMClient available for chain ID %d", chainId) +} + +func (te *CLClusterTestEnv) GetRpcProvider(chainId int64) (*test_env.RpcProvider, error) { + if rpc, ok := te.rpcProviders[chainId]; ok { + return rpc, nil + } + + return nil, fmt.Errorf("no RPC provider available for chain ID %d", chainId) +} diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 0b1cd30c1b..0b30ad860a 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -1,14 +1,15 @@ package test_env import ( + "errors" "fmt" "math/big" "os" - "runtime/debug" "testing" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/seth" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" @@ -17,14 +18,14 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) type CleanUpType string @@ -36,26 +37,27 @@ const ( ) type CLTestEnvBuilder struct { - hasLogStream bool - hasKillgrave bool - hasForwarders bool - clNodeConfig *chainlink.Config - secretsConfig string - nonDevGethNetworks []blockchain.EVMNetwork - clNodesCount int - clNodesOpts []func(*ClNode) - customNodeCsaKeys []string - defaultNodeCsaKeys []string - l zerolog.Logger - t *testing.T - te *CLClusterTestEnv - isNonEVM bool - cleanUpType CleanUpType - cleanUpCustomFn func() - chainOptionsFn []ChainOption - evmClientNetworkOption []EVMClientNetworkOption - ethereumNetwork *test_env.EthereumNetwork - testConfig tc.GlobalTestConfig + hasLogStream bool + hasKillgrave bool + hasForwarders bool + hasSeth bool + hasEVMClient bool + clNodeConfig *chainlink.Config + secretsConfig string + clNodesCount int + clNodesOpts []func(*ClNode) + customNodeCsaKeys []string + defaultNodeCsaKeys []string + l zerolog.Logger + t *testing.T + te *CLClusterTestEnv + isNonEVM bool + cleanUpType CleanUpType + cleanUpCustomFn func() + chainOptionsFn []ChainOption + evmClientNetworkOption []EVMClientNetworkOption + privateEthereumNetworks []*test_env.EthereumNetwork + testConfig tc.GlobalTestConfig /* funding */ ETHFunds *big.Float @@ -65,6 +67,7 @@ func NewCLTestEnvBuilder() *CLTestEnvBuilder { return &CLTestEnvBuilder{ l: log.Logger, hasLogStream: true, + hasEVMClient: true, } } @@ -137,32 +140,19 @@ func (b *CLTestEnvBuilder) WithFunding(eth *big.Float) *CLTestEnvBuilder { return b } -// deprecated -// left only for backward compatibility -func (b *CLTestEnvBuilder) WithGeth() *CLTestEnvBuilder { - ethBuilder := test_env.NewEthereumNetworkBuilder() - cfg, err := ethBuilder. - WithConsensusType(test_env.ConsensusType_PoW). - WithExecutionLayer(test_env.ExecutionLayer_Geth). - WithTest(b.t). - Build() - - if err != nil { - panic(err) - } - - b.ethereumNetwork = &cfg - +func (b *CLTestEnvBuilder) WithSeth() *CLTestEnvBuilder { + b.hasSeth = true + b.hasEVMClient = false return b } func (b *CLTestEnvBuilder) WithPrivateEthereumNetwork(en test_env.EthereumNetwork) *CLTestEnvBuilder { - b.ethereumNetwork = &en + b.privateEthereumNetworks = append(b.privateEthereumNetworks, &en) return b } -func (b *CLTestEnvBuilder) WithPrivateGethChains(evmNetworks []blockchain.EVMNetwork) *CLTestEnvBuilder { - b.nonDevGethNetworks = evmNetworks +func (b *CLTestEnvBuilder) WithPrivateEthereumNetworks(ens []*test_env.EthereumNetwork) *CLTestEnvBuilder { + b.privateEthereumNetworks = ens return b } @@ -247,11 +237,11 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } if b.hasKillgrave { - if b.te.Network == nil { + if b.te.DockerNetwork == nil { return nil, fmt.Errorf("test environment builder failed: %w", fmt.Errorf("cannot start mock adapter without a network")) } - b.te.MockAdapter = test_env.NewKillgrave([]string{b.te.Network.Name}, "", test_env.WithLogStream(b.te.LogStream)) + b.te.MockAdapter = test_env.NewKillgrave([]string{b.te.DockerNetwork.Name}, "", test_env.WithLogStream(b.te.LogStream)) err = b.te.StartMockAdapter() if err != nil { @@ -296,46 +286,55 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { }) } - if b.nonDevGethNetworks != nil { - b.te.WithPrivateChain(b.nonDevGethNetworks) - err := b.te.StartPrivateChain() - if err != nil { - return b.te, err - } - var nonDevNetworks []blockchain.EVMNetwork - for i, n := range b.te.PrivateChain { - primaryNode := n.GetPrimaryNode() - if primaryNode == nil { - return b.te, fmt.Errorf("primary node is nil in PrivateChain interface, stack: %s", string(debug.Stack())) + // in this case we will use the builder only to start chains, not the cluster, because currently we support only 1 network config per cluster + if len(b.privateEthereumNetworks) > 1 { + b.te.rpcProviders = make(map[int64]*test_env.RpcProvider) + b.te.EVMNetworks = make([]*blockchain.EVMNetwork, 0) + b.te.evmClients = make(map[int64]blockchain.EVMClient) + for _, en := range b.privateEthereumNetworks { + en.DockerNetworkNames = []string{b.te.DockerNetwork.Name} + _, rpcProvider, err := b.te.StartEthereumNetwork(en) + if err != nil { + return nil, err } - nonDevNetworks = append(nonDevNetworks, *n.GetNetworkConfig()) - nonDevNetworks[i].URLs = []string{primaryNode.GetInternalWsUrl()} - nonDevNetworks[i].HTTPURLs = []string{primaryNode.GetInternalHttpUrl()} - } - if nonDevNetworks == nil { - return nil, fmt.Errorf("cannot create nodes with custom config without nonDevNetworks") - } + b.te.rpcProviders[int64(en.EthereumChainConfig.ChainID)] = &rpcProvider + } err = b.te.StartClCluster(b.clNodeConfig, b.clNodesCount, b.secretsConfig, b.testConfig, b.clNodesOpts...) if err != nil { return nil, err } + + b.te.isSimulatedNetwork = true + return b.te, nil } networkConfig := networks.MustGetSelectedNetworkConfig(b.testConfig.GetNetworkConfig())[0] - var rpcProvider test_env.RpcProvider - if b.ethereumNetwork != nil && networkConfig.Simulated { + if len(b.privateEthereumNetworks) == 1 { + b.te.rpcProviders = make(map[int64]*test_env.RpcProvider) // TODO here we should save the ethereum network config to te.Cfg, but it doesn't exist at this point // in general it seems we have no methods for saving config to file and we only load it from file // but I don't know how that config file is to be created or whether anyone ever done that - b.ethereumNetwork.DockerNetworkNames = []string{b.te.Network.Name} - networkConfig, rpcProvider, err = b.te.StartEthereumNetwork(b.ethereumNetwork) + var rpcProvider test_env.RpcProvider + + b.privateEthereumNetworks[0].DockerNetworkNames = []string{b.te.DockerNetwork.Name} + networkConfig, rpcProvider, err = b.te.StartEthereumNetwork(b.privateEthereumNetworks[0]) if err != nil { return nil, err } - b.te.RpcProvider = rpcProvider - b.te.PrivateEthereumConfig = b.ethereumNetwork + b.te.rpcProviders[networkConfig.ChainID] = &rpcProvider + b.te.PrivateEthereumConfigs = b.privateEthereumNetworks + + b.te.isSimulatedNetwork = true + } + + if !b.hasSeth && !b.hasEVMClient { + return nil, errors.New("you need to specify, which evm client to use: Seth or EMVClient") + } + + if b.hasSeth && b.hasEVMClient { + return nil, errors.New("you can't use both Seth and EMVClient at the same time") } if !b.isNonEVM { @@ -344,23 +343,42 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { fn(&networkConfig) } } - bc, err := blockchain.NewEVMClientFromNetwork(networkConfig, b.l) - if err != nil { - return nil, err - } + if b.hasEVMClient { + bc, err := blockchain.NewEVMClientFromNetwork(networkConfig, b.l) + if err != nil { + return nil, err + } - b.te.EVMClient = bc - cd, err := contracts.NewContractDeployer(bc, b.l) - if err != nil { - return nil, err + b.te.evmClients = make(map[int64]blockchain.EVMClient) + b.te.evmClients[networkConfig.ChainID] = bc + + cd, err := contracts.NewContractDeployer(bc, b.l) + if err != nil { + return nil, err + } + b.te.ContractDeployer = cd + + cl, err := contracts.NewContractLoader(bc, b.l) + if err != nil { + return nil, err + } + b.te.ContractLoader = cl } - b.te.ContractDeployer = cd - cl, err := contracts.NewContractLoader(bc, b.l) - if err != nil { - return nil, err + if b.hasSeth { + readSethCfg := b.testConfig.GetSethConfig() + sethCfg := utils.MergeSethAndEvmNetworkConfigs(b.l, networkConfig, *readSethCfg) + err = utils.ValidateSethNetworkConfig(sethCfg.Network) + if err != nil { + return nil, err + } + seth, err := seth.NewClientWithConfig(&sethCfg) + if err != nil { + return nil, err + } + + b.te.SethClient = seth } - b.te.ContractLoader = cl } var nodeCsaKeys []string @@ -380,6 +398,10 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { if !b.isNonEVM { var httpUrls []string var wsUrls []string + rpcProvider, ok := b.te.rpcProviders[networkConfig.ChainID] + if !ok { + return nil, fmt.Errorf("rpc provider for chain %d not found", networkConfig.ChainID) + } if networkConfig.Simulated { httpUrls = rpcProvider.PrivateHttpUrls() wsUrls = rpcProvider.PrivateWsUrsl() @@ -411,17 +433,26 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { b.defaultNodeCsaKeys = nodeCsaKeys } - if b.ethereumNetwork != nil && b.clNodesCount > 0 && b.ETHFunds != nil { - b.te.ParallelTransactions(true) - defer b.te.ParallelTransactions(false) - if err := b.te.FundChainlinkNodes(b.ETHFunds); err != nil { - return nil, err + if len(b.privateEthereumNetworks) > 0 && b.clNodesCount > 0 && b.ETHFunds != nil { + if b.hasEVMClient { + b.te.ParallelTransactions(true) + defer b.te.ParallelTransactions(false) + if err := b.te.FundChainlinkNodes(b.ETHFunds); err != nil { + return nil, err + } + } + if b.hasSeth { + if err := actions_seth.FundChainlinkNodesFromRootAddress(b.l, b.te.SethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(b.te.ClCluster.NodeAPIs()), b.ETHFunds); err != nil { + return nil, err + } } } var enDesc string - if b.te.PrivateEthereumConfig != nil { - enDesc = b.te.PrivateEthereumConfig.Describe() + if len(b.te.PrivateEthereumConfigs) > 0 { + for _, en := range b.te.PrivateEthereumConfigs { + enDesc += en.Describe() + } } else { enDesc = "none" } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c7b55fb9e0..2f297a05f3 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -1,13 +1,13 @@ module github.com/smartcontractkit/ccip/integration-tests -go 1.21.4 +go 1.21.7 // Make sure we're working with the latest chainlink libs replace github.com/smartcontractkit/chainlink/v2 => ../ require ( + dario.cat/mergo v1.0.0 github.com/AlekSi/pointer v1.1.0 - github.com/K-Phoen/grabana v0.21.17 github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df github.com/cli/go-gh/v2 v2.0.0 github.com/ethereum/go-ethereum v1.13.8 @@ -26,27 +26,27 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chain-selectors v1.0.13 - github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa - github.com/smartcontractkit/chainlink-testing-framework v1.24.3 + github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240311111125-22812a072c35 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240326183122-8012c0f08116 + github.com/smartcontractkit/chainlink-testing-framework v1.28.1-0.20240402041143-d05d8fef4769 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 - github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 - github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 - github.com/smartcontractkit/wasp v0.4.1 - github.com/spf13/cobra v1.7.0 - github.com/stretchr/testify v1.8.4 + github.com/smartcontractkit/libocr v0.0.0-20240229181116-bfb2432a7a66 + github.com/smartcontractkit/seth v0.1.2 + github.com/smartcontractkit/wasp v0.4.5 + github.com/spf13/cobra v1.8.0 + github.com/stretchr/testify v1.9.0 github.com/test-go/testify v1.1.4 github.com/testcontainers/testcontainers-go v0.28.0 github.com/umbracle/ethgo v0.1.3 go.dedis.ch/kyber/v3 v3.1.0 go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 - go.uber.org/ratelimit v0.2.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.17.0 - golang.org/x/sync v0.5.0 + golang.org/x/crypto v0.19.0 + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a + golang.org/x/sync v0.6.0 golang.org/x/text v0.14.0 gopkg.in/guregu/null.v4 v4.0.0 ) @@ -68,7 +68,6 @@ require ( cosmossdk.io/depinject v1.0.0-alpha.3 // indirect cosmossdk.io/errors v1.0.0 // indirect cosmossdk.io/math v1.0.1 // indirect - dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect @@ -82,7 +81,8 @@ require ( github.com/CosmWasm/wasmd v0.40.1 // indirect github.com/CosmWasm/wasmvm v1.2.4 // indirect github.com/DataDog/zstd v1.5.2 // indirect - github.com/K-Phoen/sdk v0.12.2 // indirect + github.com/K-Phoen/grabana v0.22.1 // indirect + github.com/K-Phoen/sdk v0.12.4 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect @@ -91,7 +91,6 @@ require ( github.com/Microsoft/hcsshim v0.11.4 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/avast/retry-go v3.0.0+incompatible // indirect @@ -100,6 +99,7 @@ require ( github.com/aws/constructs-go/constructs/v10 v10.1.255 // indirect github.com/aws/jsii-runtime-go v1.75.0 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect @@ -255,7 +255,7 @@ require ( github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect github.com/hashicorp/consul/api v1.25.1 // indirect - github.com/hashicorp/consul/sdk v0.14.1 // indirect + github.com/hashicorp/consul/sdk v0.16.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-envparse v0.1.0 // indirect @@ -263,14 +263,14 @@ require ( github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.5.2 // indirect + github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/memberlist v0.5.0 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect @@ -331,6 +331,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect @@ -338,6 +339,8 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect + github.com/naoina/go-stringutil v0.1.0 // indirect + github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect @@ -376,12 +379,13 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect + github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sony/gobreaker v0.5.0 // indirect @@ -391,7 +395,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.16.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect @@ -436,16 +440,16 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect go.uber.org/goleak v1.3.0 // indirect + go.uber.org/ratelimit v0.3.0 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.6.0 // indirect - golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.16.0 // indirect + golang.org/x/tools v0.18.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect @@ -453,7 +457,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/guregu/null.v2 v2.1.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -488,7 +492,7 @@ replace ( github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 // until merged upstream: https://github.com/hashicorp/go-plugin/pull/257 - github.com/hashicorp/go-plugin => github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 + github.com/hashicorp/go-plugin => github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 // until merged upstream: https://github.com/mwitkow/grpc-proxy/pull/69 github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 46fcc3fae2..8c2404316e 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -130,10 +130,10 @@ github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtix github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/K-Phoen/grabana v0.21.17 h1:mO/9DvJWC/qpTF/X5jQDm5eKgCBaCGypP/tEfXAvKfg= -github.com/K-Phoen/grabana v0.21.17/go.mod h1:vbASQt9UiQhX4lC3/opLpJMJ8m+hsTUU2FwkQMytHK4= -github.com/K-Phoen/sdk v0.12.2 h1:0QofDlKE+lloyBOzhjEEMW21061zts/WIpfpQ5NLLAs= -github.com/K-Phoen/sdk v0.12.2/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= +github.com/K-Phoen/grabana v0.22.1 h1:b/O+C3H2H6VNYSeMCYUO4X4wYuwFXgBcRkvYa+fjpQA= +github.com/K-Phoen/grabana v0.22.1/go.mod h1:3LTXrTzQzTKTgvKSXdRjlsJbizSOW/V23Q3iX00R5bU= +github.com/K-Phoen/sdk v0.12.4 h1:j2EYuBJm3zDTD0fGKACVFWxAXtkR0q5QzfVqxmHSeGQ= +github.com/K-Phoen/sdk v0.12.4/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -183,7 +183,6 @@ github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6u github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -217,6 +216,8 @@ github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -379,8 +380,8 @@ github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHf github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= @@ -887,8 +888,8 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= -github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= +github.com/hashicorp/consul/sdk v0.16.0 h1:SE9m0W6DEfgIVCJX7xU+iv/hUl4m/nxqMTnCdMxDpJ8= +github.com/hashicorp/consul/sdk v0.16.0/go.mod h1:7pxqqhqoaPqnBnzXD1StKed62LqJeClzVsUEy85Zr0A= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -954,8 +955,8 @@ github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e/go.mod h1:O23q github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce h1:7UnVY3T/ZnHUrfviiAgIUjg2PXxsQfs5bphsG8F7Keo= -github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= @@ -1261,6 +1262,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= @@ -1279,6 +1282,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= @@ -1499,36 +1506,38 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chain-selectors v1.0.13 h1:vHMbh7Wu+W+/DSD88feiwMMSXmwxa5fZPRE3dZ+1zio= github.com/smartcontractkit/chain-selectors v1.0.13/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= -github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 h1:xkejUBZhcBpBrTSfxc91Iwzadrb6SXw8ks69bHIQ9Ww= -github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429/go.mod h1:wJmVvDf4XSjsahWtfUq3wvIAYEAuhr7oxmxYnEL/LGQ= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa h1:9g7e1C3295ALDK8Gs42fIKSSJfI+H1RoBmivGWTvIZo= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240124161023-948579cbaffa/go.mod h1:05rRF84QKlIOF5LfTBPkHdw4UpBI2G3zxRcuZ65bPjk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0 h1:NALwENz6vQ972DuD9AZjqRjyNSxH9ptNapizQGLI+2s= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240120192246-4bb04c997ca0/go.mod h1:NcVAT/GETDBvIoAej5K6OYqAtDOkF6vO5pYw/hLuYVU= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1/go.mod h1:GuPvyXryvbiUZIHmPeLBz4L+yJKeyGUjrDfd1KNne+o= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240311111125-22812a072c35 h1:GNhRKD3izyzAoGMXDvVUAwEuzz4Atdj3U3RH7eak5Is= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240311111125-22812a072c35/go.mod h1:2I0dWdYdK6jHPnSYYy7Y7Xp7L0YTnJ3KZtkhLQflsTU= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240326183122-8012c0f08116 h1:+ncHpL0jLpdCXXOm4JanT1NJVXimysKhyQbK4X22A9g= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240326183122-8012c0f08116/go.mod h1://xWphjmC6GWJtT8l86J2VpnG21xNwFCb0thzz4ItEk= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 h1:1BcjXuviSAKttOX7BZoVHRZZGfxqoA2+AL8tykmkdoc= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8/go.mod h1:vy1L7NybTy2F/Yv7BOh+oZBa1MACD6gzd1+DkcSkfp8= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba h1:6rnQrD8NaLfLOPHszW1hbpviqpU8011gzdZk6wKP1xY= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240122152632-38444d2ad8ba/go.mod h1:OZfzyayUdwsVBqxvbEMqwUntQT8HbFbgyqoudvwfVN0= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007 h1:KwB0H2P/gxJgt823Ku1fTcFLDKMj6zsP3wbQGlBOm4U= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240119162652-3a7274645007/go.mod h1:EbZAlb/2K6mKr26u3+3cLBe/caJaqCHw786On94C43g= -github.com/smartcontractkit/chainlink-testing-framework v1.24.3 h1:CMOneLuOLBN+cDgyIYE/5iT1XnCv3pDDBlAUo8OGi7c= -github.com/smartcontractkit/chainlink-testing-framework v1.24.3/go.mod h1:gkmsafC85u6hIqWbxKjynKf4NuFuFJDRcgxIEFsSq6E= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e h1:k8HS3GsAFZnxXIW3141VsQP2+EL1XrTtOi/HDt7sdBE= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e/go.mod h1:JiykN+8W5TA4UD2ClrzQCVvcH3NcyLEVv7RwY0busrw= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0 h1:7m9PVtccb8/pvKTXMaGuyceFno1icRyC2SFH7KG7+70= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0/go.mod h1:SZ899lZYQ0maUulWbZg+SWqabHQ1wTbyk3jT8wJfyo8= +github.com/smartcontractkit/chainlink-testing-framework v1.28.1-0.20240402041143-d05d8fef4769 h1:EeKY/xZ2AbNGP46OI1lEldwE4Dqxnhjkk88icrj92vI= +github.com/smartcontractkit/chainlink-testing-framework v1.28.1-0.20240402041143-d05d8fef4769/go.mod h1:jN+HgXbriq6fKRlIqLw9F3I81aYImV6kBJkIfz0mdIA= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= -github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= -github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= +github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1 h1:3y9WsXkZ5lxFrmfH7DQHs/q308lylKId5l/3VC0QAdM= -github.com/smartcontractkit/libocr v0.0.0-20240112202000-6359502d2ff1/go.mod h1:kC0qmVPUaVkFqGiZMNhmRmjdphuUmeyLEdlWFOQzFWI= +github.com/smartcontractkit/libocr v0.0.0-20240229181116-bfb2432a7a66 h1:xsU00JB9GJxEiN6tDbqgN+fT98ySdxkUwTw6CfBXscw= +github.com/smartcontractkit/libocr v0.0.0-20240229181116-bfb2432a7a66/go.mod h1:SJEZCHgMCAzzBvo9vMV2DQ9onfEcIJCYSViyP4JI6c4= +github.com/smartcontractkit/seth v0.1.2 h1:ImXJmniuq6yWB6b3eezjV+lkYb1GfQuaJkwRvrCfTKQ= +github.com/smartcontractkit/seth v0.1.2/go.mod h1:aOaGwrIVFG/MYaLSj9UUMyE5QJnYQoAgnxm5cKfT9Ng= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= -github.com/smartcontractkit/wasp v0.4.1 h1:qgIx2s+eCwH0OaBKaHEAHUQ1Z47bAgDu+ICS9IOqvGQ= -github.com/smartcontractkit/wasp v0.4.1/go.mod h1:3qiofyI3pkbrc48a3CVshbMfgl74SiuPL/tm30d9Wb4= +github.com/smartcontractkit/wasp v0.4.5 h1:pgiXwBci2m15eo33AzspzhpNG/gxg+8QGxl+I5LpfsQ= +github.com/smartcontractkit/wasp v0.4.5/go.mod h1:eVhBVLbVv0qORUlN7aR5C4aTN/lTYO3KnN1erO4ROOI= github.com/smartcontractkit/wsrpc v0.7.2 h1:iBXzMeg7vc5YoezIQBq896y25BARw7OKbhrb6vPbtRQ= github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -1551,8 +1560,8 @@ github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cA github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -1571,8 +1580,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -1584,8 +1594,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= @@ -1778,8 +1789,9 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -1826,8 +1838,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1838,8 +1850,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= -golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1867,8 +1879,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1934,8 +1946,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1963,8 +1975,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2067,8 +2079,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2078,8 +2090,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2173,8 +2185,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2313,8 +2325,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/integration-tests/k8s/connect.go b/integration-tests/k8s/connect.go index db9ccecb17..d9a4223c07 100644 --- a/integration-tests/k8s/connect.go +++ b/integration-tests/k8s/connect.go @@ -6,17 +6,15 @@ import ( "time" "github.com/pelletier/go-toml/v2" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" client2 "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) const ( - DefaultConfigFilePath = "../../../charts/chainlink-cluster/connect.toml" + DefaultConfigFilePath = "../connect.toml" ErrReadConnectionConfig = "failed to read TOML environment connection config" ErrUnmarshalConnectionConfig = "failed to unmarshal TOML environment connection config" ) @@ -37,10 +35,10 @@ type ConnectionVars struct { } // ConnectRemote connects to a local environment, see charts/chainlink-cluster -func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverClient, contracts.ContractDeployer, *client.ChainlinkK8sClient, []*client.ChainlinkK8sClient, error) { +func ConnectRemote() (*blockchain.EVMNetwork, *client2.MockserverClient, *client.ChainlinkK8sClient, []*client.ChainlinkK8sClient, error) { cfg, err := ReadConfig() if err != nil { - return nil, nil, nil, nil, nil, err + return &blockchain.EVMNetwork{}, nil, nil, nil, err } net := &blockchain.EVMNetwork{ Name: cfg.NetworkName, @@ -58,14 +56,6 @@ func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverC MinimumConfirmations: 1, GasEstimationBuffer: 10000, } - cc, err := blockchain.NewEVMClientFromNetwork(*net, l) - if err != nil { - return nil, nil, nil, nil, nil, err - } - cd, err := contracts.NewContractDeployer(cc, l) - if err != nil { - return nil, nil, nil, nil, nil, err - } clClients := make([]*client.ChainlinkK8sClient, 0) for i := 1; i <= cfg.CLNodesNum; i++ { c, err := client.NewChainlinkK8sClient(&client.ChainlinkConfig{ @@ -75,7 +65,7 @@ func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverC Password: cfg.CLNodePassword, }, fmt.Sprintf(cfg.CLNodeInternalDNSRecordTemplate, i), cfg.Namespace) if err != nil { - return nil, nil, nil, nil, nil, err + return &blockchain.EVMNetwork{}, nil, nil, nil, err } clClients = append(clClients, c) } @@ -83,7 +73,7 @@ func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverC LocalURL: cfg.MockServerURL, ClusterURL: cfg.MockServerURL, }) - return cc, msClient, cd, clClients[0], clClients[1:], nil + return net, msClient, clClients[0], clClients[1:], nil } func ReadConfig() (*ConnectionVars, error) { diff --git a/integration-tests/load/README.md b/integration-tests/load/README.md index 3738a1d9ac..afcf633e5c 100644 --- a/integration-tests/load/README.md +++ b/integration-tests/load/README.md @@ -68,3 +68,20 @@ To implement a standard e2e performance suite for a new product please look at ` Gun should be working with one instance of your product. VU(Virtual user) creates a new instance of your product and works with it in `Call()` + +### Cluster mode (k8s) +Add configuration to `overrides.toml` +``` +[WaspAutoBuild] +namespace = "wasp" +update_image = true +repo_image_version_uri = "${staging_ecr_registry}/wasp-tests:wb-core" +test_binary_name = "ocr.test" +test_name = "TestOCRLoad" +test_timeout = "24h" +wasp_log_level = "debug" +wasp_jobs = "1" +keep_jobs = true +``` + +And run your tests using `go test -v -run TestClusterEntrypoint` diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index ffce754c9d..fc7166b218 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -245,8 +245,8 @@ Load Config: nodeTOML = networks.AddNetworksConfig(nodeTOML, loadedTestConfig.Pyroscope, testNetwork) var overrideFn = func(_ interface{}, target interface{}) { - ctfconfig.MustConfigOverrideChainlinkVersion(loadedTestConfig.ChainlinkImage, target) - ctfconfig.MightConfigOverridePyroscopeKey(loadedTestConfig.Pyroscope, target) + ctfconfig.MustConfigOverrideChainlinkVersion(loadedTestConfig.GetChainlinkImageConfig(), target) + ctfconfig.MightConfigOverridePyroscopeKey(loadedTestConfig.GetPyroscopeConfig(), target) } cd := chainlink.NewWithOverride(i, map[string]any{ diff --git a/integration-tests/load/connect.toml b/integration-tests/load/connect.toml new file mode 100644 index 0000000000..919c5102c8 --- /dev/null +++ b/integration-tests/load/connect.toml @@ -0,0 +1,13 @@ +# this is a static configuration to connect with CRIB k8s environment (Default Geth) +namespace = "cl-cluster" +network_name = "geth" +network_chain_id = 1337 +network_private_key = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +network_ws_url = "ws://geth-1337:8546" +network_http_url = "http://geth:8544" +cl_nodes_num = 6 +cl_node_url_template = "http://app-node-%d:6688" +cl_node_internal_dns_record_template = "app-node-%d" +cl_node_user = "notreal@fakeemail.ch" +cl_node_password = "fj293fbBnlQ!f9vNs" +mockserver_url = "http://mockserver:1080" \ No newline at end of file diff --git a/integration-tests/load/functions/functionscmd/dashboard.go b/integration-tests/load/functions/functionscmd/dashboard.go new file mode 100644 index 0000000000..ccf5674023 --- /dev/null +++ b/integration-tests/load/functions/functionscmd/dashboard.go @@ -0,0 +1,34 @@ +package main + +import ( + "github.com/K-Phoen/grabana/dashboard" + "github.com/K-Phoen/grabana/logs" + "github.com/K-Phoen/grabana/row" + db "github.com/smartcontractkit/wasp/dashboard" +) + +func main() { + lokiDS := "grafanacloud-logs" + d, err := db.NewDashboard(nil, + []dashboard.Option{ + dashboard.Row("DON logs (errors)", + row.Collapse(), + row.WithLogs( + "DON logs", + logs.DataSource(lokiDS), + logs.Span(12), + logs.Height("300px"), + logs.Transparent(), + logs.WithLokiTarget(` + { cluster="staging-us-west-2-main", app=~"clc-ocr2-dr-matic-testnet" } | json | level="error" + `), + )), + }, + ) + if err != nil { + panic(err) + } + if _, err := d.Deploy(); err != nil { + panic(err) + } +} diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod new file mode 100644 index 0000000000..f5e15ac5f2 --- /dev/null +++ b/integration-tests/load/go.mod @@ -0,0 +1,503 @@ +module github.com/smartcontractkit/chainlink/load-tests + +go 1.21.7 + +// Make sure we're working with the latest chainlink libs +replace github.com/smartcontractkit/chainlink/v2 => ../../ + +replace github.com/smartcontractkit/chainlink/integration-tests => ../ + +require ( + github.com/K-Phoen/grabana v0.22.1 + github.com/ethereum/go-ethereum v1.13.8 + github.com/go-resty/resty/v2 v2.11.0 + github.com/pelletier/go-toml/v2 v2.1.1 + github.com/rs/zerolog v1.30.0 + github.com/slack-go/slack v0.12.2 + github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240311111125-22812a072c35 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240326183122-8012c0f08116 + github.com/smartcontractkit/chainlink-testing-framework v1.28.1-0.20240402041143-d05d8fef4769 + github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c + github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 + github.com/smartcontractkit/libocr v0.0.0-20240229181116-bfb2432a7a66 + github.com/smartcontractkit/seth v0.1.2 + github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 + github.com/smartcontractkit/wasp v0.4.6 + github.com/stretchr/testify v1.9.0 + go.uber.org/ratelimit v0.3.0 +) + +// avoids ambigious imports of indirect dependencies +exclude github.com/hashicorp/consul v1.2.1 + +require ( + contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect + cosmossdk.io/api v0.3.1 // indirect + cosmossdk.io/core v0.5.1 // indirect + cosmossdk.io/depinject v1.0.0-alpha.3 // indirect + cosmossdk.io/errors v1.0.0 // indirect + cosmossdk.io/math v1.0.1 // indirect + dario.cat/mergo v1.0.0 // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect + github.com/99designs/keyring v1.2.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect + github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect + github.com/CosmWasm/wasmd v0.40.1 // indirect + github.com/CosmWasm/wasmvm v1.2.4 // indirect + github.com/DataDog/zstd v1.5.2 // indirect + github.com/K-Phoen/sdk v0.12.4 // indirect + github.com/MakeNowJust/heredoc v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.11.4 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect + github.com/XSAM/otelsql v0.27.0 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/avast/retry-go v3.0.0+incompatible // indirect + github.com/avast/retry-go/v4 v4.5.1 // indirect + github.com/aws/aws-sdk-go v1.45.25 // indirect + github.com/aws/constructs-go/constructs/v10 v10.1.255 // indirect + github.com/aws/jsii-runtime-go v1.75.0 // indirect + github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect + github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/blendle/zapdriver v1.3.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/bytedance/sonic v1.10.1 // indirect + github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect + github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect + github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 // indirect + github.com/cenkalti/backoff v2.2.1+incompatible // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect + github.com/cockroachdb/errors v1.9.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect + github.com/cockroachdb/redact v1.1.3 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft v0.37.2 // indirect + github.com/cometbft/cometbft-db v0.8.0 // indirect + github.com/confio/ics23/go v0.9.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/containerd/containerd v1.7.12 // indirect + github.com/containerd/continuity v0.4.3 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect + github.com/cosmos/cosmos-sdk v0.47.4 // indirect + github.com/cosmos/go-bip39 v1.0.0 // indirect + github.com/cosmos/gogoproto v1.4.11 // indirect + github.com/cosmos/iavl v0.20.0 // indirect + github.com/cosmos/ibc-go/v7 v7.2.0 // indirect + github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect + github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/deckarep/golang-set/v2 v2.3.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/dennwc/varint v1.0.0 // indirect + github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 // indirect + github.com/dgraph-io/badger/v2 v2.2007.4 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v25.0.2+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/dvsekhvalnov/jose2go v1.5.0 // indirect + github.com/edsrzf/mmap-go v1.1.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.2 // indirect + github.com/esote/minmaxheap v1.0.0 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect + github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect + github.com/fatih/camelcase v1.0.0 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fvbommel/sortorder v1.0.2 // indirect + github.com/fxamacker/cbor/v2 v2.5.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gagliardetto/binary v0.7.1 // indirect + github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 // indirect + github.com/gagliardetto/treeout v0.1.4 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect + github.com/getsentry/sentry-go v0.19.0 // indirect + github.com/gin-contrib/sessions v0.0.5 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.9.1 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect + github.com/go-kit/kit v0.12.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-ldap/ldap/v3 v3.4.6 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/analysis v0.21.4 // indirect + github.com/go-openapi/errors v0.20.4 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/loads v0.21.2 // indirect + github.com/go-openapi/spec v0.20.9 // indirect + github.com/go-openapi/strfmt v0.21.7 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/validate v0.22.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.15.5 // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/go-webauthn/webauthn v0.9.4 // indirect + github.com/go-webauthn/x v0.1.5 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gofrs/flock v0.8.1 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.3 // indirect + github.com/gogo/status v1.1.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.0 // indirect + github.com/golang/glog v1.1.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-github/v41 v41.0.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/go-tpm v0.9.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/gorilla/context v1.1.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gorilla/sessions v1.2.2 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/gosimple/slug v1.13.1 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect + github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f // indirect + github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 // indirect + github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503 // indirect + github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4 // indirect + github.com/grafana/pyroscope-go v1.0.4 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.4 // indirect + github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect + github.com/gtank/merlin v0.1.1 // indirect + github.com/gtank/ristretto255 v0.1.2 // indirect + github.com/hashicorp/consul/api v1.25.1 // indirect + github.com/hashicorp/consul/sdk v0.16.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-envparse v0.1.0 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.6.0 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/memberlist v0.5.0 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect + github.com/hdevalence/ed25519consensus v0.1.0 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/huandu/skiplist v1.2.0 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.14.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.2 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgx/v4 v4.18.1 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/linxGnu/grocksdb v1.7.16 // indirect + github.com/logrusorgru/aurora v2.0.3+incompatible // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/miekg/dns v1.1.56 // indirect + github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/mwitkow/grpc-proxy v0.0.0-20230212185441-f345521cb9c9 // indirect + github.com/naoina/go-stringutil v0.1.0 // indirect + github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 // indirect + github.com/oklog/run v1.1.0 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/onsi/ginkgo/v2 v2.13.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/opencontainers/runc v1.1.10 // indirect + github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect + github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/otiai10/copy v1.14.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/alertmanager v0.26.0 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/common/sigv4 v0.1.0 // indirect + github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/prometheus v0.48.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/russross/blackfriday v1.6.0 // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/scylladb/go-reflectx v1.0.1 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect + github.com/segmentio/ksuid v1.0.4 // indirect + github.com/sercand/kuberesolver/v5 v5.1.1 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect + github.com/smartcontractkit/chain-selectors v1.0.13 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect + github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0 // indirect + github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240227164431-18a7065e23ea // indirect + github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 // indirect + github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect + github.com/smartcontractkit/wsrpc v0.7.2 // indirect + github.com/soheilhy/cmux v0.1.5 // indirect + github.com/sony/gobreaker v0.5.0 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.16.0 // indirect + github.com/status-im/keycard-go v0.2.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tendermint/go-amino v0.16.0 // indirect + github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect + github.com/test-go/testify v1.1.4 // indirect + github.com/testcontainers/testcontainers-go v0.28.0 // indirect + github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect + github.com/tidwall/btree v1.6.0 // indirect + github.com/tidwall/gjson v1.17.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect + github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/umbracle/ethgo v0.1.3 // indirect + github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 // indirect + github.com/valyala/fastjson v1.4.1 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/xlab/treeprint v1.1.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + github.com/zondax/hid v0.9.1 // indirect + github.com/zondax/ledger-go v0.14.1 // indirect + go.dedis.ch/fixbuf v1.0.3 // indirect + go.dedis.ch/kyber/v3 v3.1.0 // indirect + go.etcd.io/bbolt v1.3.7 // indirect + go.etcd.io/etcd/api/v3 v3.5.9 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect + go.etcd.io/etcd/client/v3 v3.5.9 // indirect + go.mongodb.org/mongo-driver v1.12.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/collector/pdata v1.0.0-rcv0016 // indirect + go.opentelemetry.io/collector/semconv v0.87.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/sdk v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/goleak v1.3.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect + golang.org/x/arch v0.7.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/term v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.18.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect + gonum.org/v1/gonum v0.14.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/grpc v1.59.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect + gopkg.in/guregu/null.v4 v4.0.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.28.2 // indirect + k8s.io/apiextensions-apiserver v0.25.3 // indirect + k8s.io/apimachinery v0.28.2 // indirect + k8s.io/cli-runtime v0.25.11 // indirect + k8s.io/client-go v0.28.2 // indirect + k8s.io/component-base v0.26.2 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + k8s.io/kubectl v0.25.11 // indirect + k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect + nhooyr.io/websocket v1.8.7 // indirect + pgregory.net/rapid v0.5.5 // indirect + rsc.io/tmplfunc v0.0.3 // indirect + sigs.k8s.io/controller-runtime v0.13.0 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.12.1 // indirect + sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +replace ( + github.com/go-kit/log => github.com/go-kit/log v0.2.1 + + // replicating the replace directive on cosmos SDK + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + + // until merged upstream: https://github.com/hashicorp/go-plugin/pull/257 + github.com/hashicorp/go-plugin => github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 + + // until merged upstream: https://github.com/mwitkow/grpc-proxy/pull/69 + github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f + + // type func(a Label, b Label) bool of func(a, b Label) bool {…} does not match inferred type func(a Label, b Label) int for func(a E, b E) int + github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510 + + // Pin K8s versions as their updates are highly disruptive and go mod keeps wanting to update them + k8s.io/api => k8s.io/api v0.25.11 + k8s.io/client-go => k8s.io/client-go v0.25.11 + k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d +) diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum new file mode 100644 index 0000000000..2c6aa1f3ab --- /dev/null +++ b/integration-tests/load/go.sum @@ -0,0 +1,2418 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.110.9 h1:e7ITSqGFFk4rbz/JFIqZh3G4VEHguhAL4BQcFlWtU68= +cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/iam v1.1.4 h1:K6n/GZHFTtEoKT5aUG3l9diPi0VduZNQ1PfdnpkkIFk= +cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +contrib.go.opencensus.io/exporter/stackdriver v0.12.6/go.mod h1:8x999/OcIPy5ivx/wDiV7Gx4D+VUPODf0mWRGRc5kSk= +contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= +contrib.go.opencensus.io/exporter/stackdriver v0.13.5 h1:TNaexHK16gPUoc7uzELKOU7JULqccn1NDuqUxmxSqfo= +contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= +cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE= +cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw= +cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI= +cosmossdk.io/core v0.5.1/go.mod h1:KZtwHCLjcFuo0nmDc24Xy6CRNEL9Vl/MeimQ2aC7NLE= +cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw= +cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU= +cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= +cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= +cosmossdk.io/log v1.1.1-0.20230704160919-88f2c830b0ca h1:msenprh2BLLRwNT7zN56TbBHOGk/7ARQckXHxXyvjoQ= +cosmossdk.io/log v1.1.1-0.20230704160919-88f2c830b0ca/go.mod h1:PkIAKXZvaxrTRc++z53XMRvFk8AcGGWYHcMIPzVYX9c= +cosmossdk.io/math v1.0.1 h1:Qx3ifyOPaMLNH/89WeZFH268yCvU4xEcnPLu3sJqPPg= +cosmossdk.io/math v1.0.1/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k= +cosmossdk.io/tools/rosetta v0.2.1 h1:ddOMatOH+pbxWbrGJKRAawdBkPYLfKXutK9IETnjYxw= +cosmossdk.io/tools/rosetta v0.2.1/go.mod h1:Pqdc1FdvkNV3LcNIkYWt2RQY6IP1ge6YWZk8MhhO9Hw= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI= +github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= +github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 h1:9kDVnTz3vbfweTqAUmk/a/pH5pWFCHtvRpHYC0G/dcA= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= +github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= +github.com/CosmWasm/wasmd v0.40.1 h1:LxbO78t/6S8TkeQlUrJ0m5O87HtAwLx4RGHq3rdrOEU= +github.com/CosmWasm/wasmd v0.40.1/go.mod h1:6EOwnv7MpuFaEqxcUOdFV9i4yvrdOciaY6VQ1o7A3yg= +github.com/CosmWasm/wasmvm v1.2.4 h1:6OfeZuEcEH/9iqwrg2pkeVtDCkMoj9U6PpKtcrCyVrQ= +github.com/CosmWasm/wasmvm v1.2.4/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8J7KFtkc= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Depado/ginprom v1.8.0 h1:zaaibRLNI1dMiiuj1MKzatm8qrcHzikMlCc1anqOdyo= +github.com/Depado/ginprom v1.8.0/go.mod h1:XBaKzeNBqPF4vxJpNLincSQZeMDnZp1tIbU0FU0UKgg= +github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= +github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/K-Phoen/grabana v0.22.1 h1:b/O+C3H2H6VNYSeMCYUO4X4wYuwFXgBcRkvYa+fjpQA= +github.com/K-Phoen/grabana v0.22.1/go.mod h1:3LTXrTzQzTKTgvKSXdRjlsJbizSOW/V23Q3iX00R5bU= +github.com/K-Phoen/sdk v0.12.4 h1:j2EYuBJm3zDTD0fGKACVFWxAXtkR0q5QzfVqxmHSeGQ= +github.com/K-Phoen/sdk v0.12.4/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd5wAKUHEO/k= +github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs= +github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= +github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI= +github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo= +github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= +github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= +github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= +github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= +github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.45.25 h1:c4fLlh5sLdK2DCRTY1z0hyuJZU4ygxX8m1FswL6/nF4= +github.com/aws/aws-sdk-go v1.45.25/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/constructs-go/constructs/v10 v10.1.255 h1:5hARfEmhBqHSTQf/C3QLA3sWOxO2Dfja0iA1W7ZcI7g= +github.com/aws/constructs-go/constructs/v10 v10.1.255/go.mod h1:DCdBSjN04Ck2pajCacTD4RKFqSA7Utya8d62XreYctI= +github.com/aws/jsii-runtime-go v1.75.0 h1:NhpUfyiL7/wsRuUekFsz8FFBCYLfPD/l61kKg9kL/a4= +github.com/aws/jsii-runtime-go v1.75.0/go.mod h1:TKCyrtM0pygEPo4rDZzbMSDNCDNTSYSN6/mGyHI6O3I= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= +github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= +github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= +github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ= +github.com/btcsuite/btcd/btcutil v1.1.2/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3 h1:SDlJ7bAm4ewvrmZtR0DaiYbQGdKPeaaIm7bM+qRhFeU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.3/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA= +github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= +github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= +github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 h1:rvc39Ol6z3MvaBzXkxFC6Nfsnixq/dRypushKDd7Nc0= +github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5/go.mod h1:R/pdNYDYFQk+tuuOo7QES1kkv6OLmp5ze2XBZQIVffM= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= +github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657 h1:CyuI+igIjadM/GRnE2o0q+WCwipDh0n2cUYFPAvxziM= +github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657/go.mod h1:JRiumF+RFsH1mrrP8FUsi9tExPylKkO/oSRWeQEUdLE= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= +github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= +github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w= +github.com/cockroachdb/apd/v3 v3.1.0/go.mod h1:6qgPBMXjATAdD/VefbRP9NoSLKjbB4LCoA7gN4LpHs4= +github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= +github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= +github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= +github.com/cometbft/cometbft v0.37.2 h1:XB0yyHGT0lwmJlFmM4+rsRnczPlHoAKFX6K8Zgc2/Jc= +github.com/cometbft/cometbft v0.37.2/go.mod h1:Y2MMMN//O5K4YKd8ze4r9jmk4Y7h0ajqILXbH5JQFVs= +github.com/cometbft/cometbft-db v0.8.0 h1:vUMDaH3ApkX8m0KZvOFFy9b5DZHBAjsnEuo9AKVZpjo= +github.com/cometbft/cometbft-db v0.8.0/go.mod h1:6ASCP4pfhmrCBpfk01/9E1SI29nD3HfVHrY4PG8x5c0= +github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= +github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= +github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= +github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= +github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-proto v1.0.0-beta.2 h1:X3OKvWgK9Gsejo0F1qs5l8Qn6xJV/AzgIWR2wZ8Nua8= +github.com/cosmos/cosmos-proto v1.0.0-beta.2/go.mod h1:+XRCLJ14pr5HFEHIUcn51IKXD1Fy3rkEQqt4WqmN4V0= +github.com/cosmos/cosmos-sdk v0.47.4 h1:FVUpEprm58nMmBX4xkRdMDaIG5Nr4yy92HZAfGAw9bg= +github.com/cosmos/cosmos-sdk v0.47.4/go.mod h1:R5n+uM7vguVPFap4pgkdvQCT1nVo/OtPwrlAU40rvok= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= +github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= +github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= +github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= +github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= +github.com/cosmos/ibc-go/v7 v7.2.0 h1:dx0DLUl7rxdyZ8NiT6UsrbzKOJx/w7s+BOaewFRH6cg= +github.com/cosmos/ibc-go/v7 v7.2.0/go.mod h1:OOcjKIRku/j1Xs1RgKK0yvKRrJ5iFuZYMetR1n3yMlc= +github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= +github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w= +github.com/cosmos/ledger-cosmos-go v0.12.1/go.mod h1:dhO6kj+Y+AHIOgAe4L9HL/6NDdyyth4q238I9yFpD2g= +github.com/cosmos/rosetta-sdk-go v0.10.0 h1:E5RhTruuoA7KTIXUcMicL76cffyeoyvNybzUGSKFTcM= +github.com/cosmos/rosetta-sdk-go v0.10.0/go.mod h1:SImAZkb96YbwvoRkzSMQB6noNJXFgWl/ENIznEoYQI4= +github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= +github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/creachadair/taskgroup v0.4.2 h1:jsBLdAJE42asreGss2xZGZ8fJra7WtwnHWeJFxv2Li8= +github.com/creachadair/taskgroup v0.4.2/go.mod h1:qiXUOSrbwAY3u0JPGTzObbE3yf9hcXHDKBZ2ZjpCbgM= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbdT9+Q5kgLpmmsHYl0= +github.com/cucumber/common/gherkin/go/v22 v22.0.0/go.mod h1:3mJT10B2GGn3MvVPd3FwR7m2u4tLhSRhWUqJU4KN4Fg= +github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts= +github.com/cucumber/common/messages/go/v17 v17.1.1/go.mod h1:bpGxb57tDE385Rb2EohgUadLkAbhoC4IyCFi89u/JQI= +github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= +github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e h1:5jVSh2l/ho6ajWhSPNN84eHEdq3dp0T7+f6r3Tc6hsk= +github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e/go.mod h1:IJgIiGUARc4aOr4bOQ85klmjsShkEEfiRc6q/yBSfo8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g= +github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= +github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dfuse-io/logging v0.0.0-20201110202154-26697de88c79/go.mod h1:V+ED4kT/t/lKtH99JQmKIb0v9WL3VaYkJ36CfHlVECI= +github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70 h1:CuJS05R9jmNlUK8GOxrEELPbfXm0EuGh/30LjkjN5vo= +github.com/dfuse-io/logging v0.0.0-20210109005628-b97a57253f70/go.mod h1:EoK/8RFbMEteaCaz89uessDTnCWjbbcr+DXcBh4el5o= +github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/digitalocean/godo v1.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E= +github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v25.0.2+incompatible h1:/OaKeauroa10K4Nqavw4zlhcDq/WBcPMc5DbjOGgozY= +github.com/docker/docker v25.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= +github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA= +github.com/esote/minmaxheap v1.0.0/go.mod h1:Ln8+i7fS1k3PLgZI2JAo0iA1as95QnIYiGCrqSJ5FZk= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg= +github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo= +github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= +github.com/gagliardetto/binary v0.7.1 h1:6ggDQ26vR+4xEvl/S13NcdLK3MUCi4oSy73pS9aI1cI= +github.com/gagliardetto/binary v0.7.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= +github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw= +github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY= +github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 h1:q2IztKyRQUxJ6abXRsawaBtvDFvM+szj4jDqV4od1gs= +github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27/go.mod h1:NFuoDwHPvw858ZMHUJr6bkhN8qHt4x6e+U3EYHxAwNY= +github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw= +github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSGFplbWBSHaGolEQlNLgMgSE3ccnIQ= +github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM= +github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/getsentry/sentry-go v0.19.0 h1:BcCH3CN5tXt5aML+gwmbFwVptLLQA+eT866fCO9wVOM= +github.com/getsentry/sentry-go v0.19.0/go.mod h1:y3+lGEFEFexZtpbG1GUE2WD/f9zGyKYwpEqryTOC/nE= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk= +github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI= +github.com/gin-contrib/expvar v0.0.1 h1:IuU5ArEgihz50vG8Onrwz22kJr7Mcvgv9xSSpfU5g+w= +github.com/gin-contrib/expvar v0.0.1/go.mod h1:8o2CznfQi1JjktORdHr2/abg3wSV6OCnXh0yGypvvVw= +github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= +github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY= +github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 h1:Z9J0PVIt1PuibOShaOw1jH8hUYz+Ak8NLsR/GI0Hv5I= +github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4/go.mod h1:CEPcgZiz8998l9E8fDm16h8UfHRL7b+5oG0j/0koeVw= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= +github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg= +github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= +github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= +github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M= +github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= +github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= +github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= +github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= +github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-webauthn/webauthn v0.9.4 h1:YxvHSqgUyc5AK2pZbqkWWR55qKeDPhP8zLDr6lpIc2g= +github.com/go-webauthn/webauthn v0.9.4/go.mod h1:LqupCtzSef38FcxzaklmOn7AykGKhAhr9xlRbdbgnTw= +github.com/go-webauthn/x v0.1.5 h1:V2TCzDU2TGLd0kSZOXdrqDVV5JB9ILnKxA9S53CSBw0= +github.com/go-webauthn/x v0.1.5/go.mod h1:qbzWwcFcv4rTwtCLOZd+icnr6B7oSsAGZJqlt8cukqY= +github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= +github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg= +github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= +github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= +github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gophercloud/gophercloud v1.5.0 h1:cDN6XFCLKiiqvYpjQLq9AiM7RDRbIC9450WpPH+yvXo= +github.com/gophercloud/gophercloud v1.5.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= +github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= +github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f h1:gyojr97YeWZ70pKNakWv5/tKwBHuLy3icnIeCo9gQr4= +github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f/go.mod h1:8dsy5tQOkeNQyjXpm5mQsbCu3H5uzeBD35MzRQFznKU= +github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ= +github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= +github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503 h1:gdrsYbmk8822v6qvPwZO5DC6QjnAW7uKJ9YXnoUmV8c= +github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503/go.mod h1:d8seWXCEXkL42mhuIJYcGi6DxfehzoIpLrMQWJojvOo= +github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4 h1:wQ0FnSeebhJIBkgYOD06Mxk9HV2KhtEG0hp/7R+5RUQ= +github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4/go.mod h1:f3JSoxBTPXX5ec4FxxeC19nTBSxoTz+cBgS3cYLMcr0= +github.com/grafana/pyroscope-go v1.0.4 h1:oyQX0BOkL+iARXzHuCdIF5TQ7/sRSel1YFViMHC7Bm0= +github.com/grafana/pyroscope-go v1.0.4/go.mod h1:0d7ftwSMBV/Awm7CCiYmHQEG8Y44Ma3YSjt+nWcWztY= +github.com/grafana/pyroscope-go/godeltaprof v0.1.4 h1:mDsJ3ngul7UfrHibGQpV66PbZ3q1T8glz/tK3bQKKEk= +github.com/grafana/pyroscope-go/godeltaprof v0.1.4/go.mod h1:1HSPtjU8vLG0jE9JrTdzjgFqdJ/VgN7fvxBNq3luJko= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EKYrXrXXUNJHOgbRt+U6jOug= +github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= +github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 h1:f4tggROQKKcnh4eItay6z/HbHLqghBxS8g7pyMhmDio= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0/go.mod h1:hKAkSgNkL0FII46ZkJcpVEAai4KV+swlIWCKfekd1pA= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3 h1:o95KDiV/b1xdkumY5YbLR0/n2+wBxUpgf3HgfKgTyLI= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.3/go.mod h1:hTxjzRcX49ogbTGVJ1sM5mz5s+SSgiGIyL3jjPxl32E= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= +github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.16.0 h1:SE9m0W6DEfgIVCJX7xU+iv/hUl4m/nxqMTnCdMxDpJ8= +github.com/hashicorp/consul/sdk v0.16.0/go.mod h1:7pxqqhqoaPqnBnzXD1StKed62LqJeClzVsUEy85Zr0A= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY= +github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc= +github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= +github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e h1:sr4lujmn9heD030xx/Pd4B/JSmvRhFzuotNXaaV0WLs= +github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= +github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/hetznercloud/hcloud-go/v2 v2.0.0 h1:Sg1DJ+MAKvbYAqaBaq9tPbwXBS2ckPIaMtVdUjKu+4g= +github.com/hetznercloud/hcloud-go/v2 v2.0.0/go.mod h1:4iUG2NG8b61IAwNx6UsMWQ6IfIf/i1RsG0BbsKAyR5Q= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw= +github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= +github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/ionos-cloud/sdk-go/v6 v6.1.8 h1:493wE/BkZxJf7x79UCE0cYGPZoqQcPiEBALvt7uVGY0= +github.com/ionos-cloud/sdk-go/v6 v6.1.8/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= +github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4= +github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= +github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= +github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= +github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= +github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= +github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= +github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a h1:dHCfT5W7gghzPtfsW488uPmEOm85wewI+ypUwibyTdU= +github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/linode/linodego v1.19.0 h1:n4WJrcr9+30e9JGZ6DI0nZbm5SdAj1kSwvvt/998YUw= +github.com/linode/linodego v1.19.0/go.mod h1:XZFR+yJ9mm2kwf6itZ6SCpu+6w3KnIevV0Uu5HNWJgQ= +github.com/linxGnu/grocksdb v1.7.16 h1:Q2co1xrpdkr5Hx3Fp+f+f7fRGhQFQhvi/+226dtLmA8= +github.com/linxGnu/grocksdb v1.7.16/go.mod h1:JkS7pl5qWpGpuVb3bPqTz8nC12X3YtPZT+Xq7+QfQo4= +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/manyminds/api2go v0.0.0-20171030193247-e7b693844a6f h1:tVvGiZQFjOXP+9YyGqSA6jE55x1XVxmoPYudncxrZ8U= +github.com/manyminds/api2go v0.0.0-20171030193247-e7b693844a6f/go.mod h1:Z60vy0EZVSu0bOugCHdcN5ZxFMKSpjRgsnh0XKPFqqk= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= +github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= +github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= +github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE= +github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= +github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= +github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= +github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= +github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= +github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/ovh/go-ovh v1.4.1 h1:VBGa5wMyQtTP7Zb+w97zRCh9sLtM/2YKRyy+MEJmWaM= +github.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= +github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pressly/goose/v3 v3.16.0 h1:xMJUsZdHLqSnCqESyKSqEfcYVYsUuup1nrOhaEFftQg= +github.com/pressly/goose/v3 v3.16.0/go.mod h1:JwdKVnmCRhnF6XLQs2mHEQtucFD49cQBdRM4UiwkxsM= +github.com/prometheus/alertmanager v0.26.0 h1:uOMJWfIwJguc3NaM3appWNbbrh6G/OjvaHMk22aBBYc= +github.com/prometheus/alertmanager v0.26.0/go.mod h1:rVcnARltVjavgVaNnmevxK7kOn7IZavyf0KNgHkbEpU= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= +github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= +github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97 h1:oHcfzdJnM/SFppy2aUlvomk37GI33x9vgJULihE5Dt8= +github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97/go.mod h1:LoBCZeRh+5hX+fSULNyFnagYlQG/gBsyA/deNzROkq8= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510 h1:6ksZ7t1hNOzGPPs8DK7SvXQf6UfWzi+W5Z7PCBl8gx4= +github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510/go.mod h1:UC0TwJiF90m2T3iYPQBKnGu8gv3s55dF/EgpTq8gyvo= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/pyroscope-io/client v0.7.1 h1:yFRhj3vbgjBxehvxQmedmUWJQ4CAfCHhn+itPsuWsHw= +github.com/pyroscope-io/client v0.7.1/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU= +github.com/pyroscope-io/godeltaprof v0.1.2 h1:MdlEmYELd5w+lvIzmZvXGNMVzW2Qc9jDMuJaPOR75g4= +github.com/pyroscope-io/godeltaprof v0.1.2/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= +github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= +github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGnWA97M= +github.com/regen-network/gocuke v0.6.2/go.mod h1:zYaqIHZobHyd0xOrHGPQjbhGJsuZ1oElx150u2o1xuk= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= +github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20 h1:a9hSJdJcd16e0HoMsnFvaHvxB3pxSD+SC7+CISp7xY0= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= +github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= +github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= +github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY= +github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec= +github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= +github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= +github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= +github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= +github.com/smartcontractkit/chain-selectors v1.0.13 h1:vHMbh7Wu+W+/DSD88feiwMMSXmwxa5fZPRE3dZ+1zio= +github.com/smartcontractkit/chain-selectors v1.0.13/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240311111125-22812a072c35 h1:GNhRKD3izyzAoGMXDvVUAwEuzz4Atdj3U3RH7eak5Is= +github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240311111125-22812a072c35/go.mod h1:2I0dWdYdK6jHPnSYYy7Y7Xp7L0YTnJ3KZtkhLQflsTU= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240326183122-8012c0f08116 h1:+ncHpL0jLpdCXXOm4JanT1NJVXimysKhyQbK4X22A9g= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240326183122-8012c0f08116/go.mod h1://xWphjmC6GWJtT8l86J2VpnG21xNwFCb0thzz4ItEk= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 h1:1BcjXuviSAKttOX7BZoVHRZZGfxqoA2+AL8tykmkdoc= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8/go.mod h1:vy1L7NybTy2F/Yv7BOh+oZBa1MACD6gzd1+DkcSkfp8= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e h1:k8HS3GsAFZnxXIW3141VsQP2+EL1XrTtOi/HDt7sdBE= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e/go.mod h1:JiykN+8W5TA4UD2ClrzQCVvcH3NcyLEVv7RwY0busrw= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0 h1:7m9PVtccb8/pvKTXMaGuyceFno1icRyC2SFH7KG7+70= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0/go.mod h1:SZ899lZYQ0maUulWbZg+SWqabHQ1wTbyk3jT8wJfyo8= +github.com/smartcontractkit/chainlink-testing-framework v1.28.1-0.20240402041143-d05d8fef4769 h1:EeKY/xZ2AbNGP46OI1lEldwE4Dqxnhjkk88icrj92vI= +github.com/smartcontractkit/chainlink-testing-framework v1.28.1-0.20240402041143-d05d8fef4769/go.mod h1:jN+HgXbriq6fKRlIqLw9F3I81aYImV6kBJkIfz0mdIA= +github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240227164431-18a7065e23ea h1:ZdLmNAfKRjH8AYUvjiiDGUgiWQfq/7iNpxyTkvjx/ko= +github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240227164431-18a7065e23ea/go.mod h1:gCKC9w6XpNk6jm+XIk2psrkkfxhi421N9NSiFceXW88= +github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= +github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= +github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= +github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= +github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= +github.com/smartcontractkit/libocr v0.0.0-20240229181116-bfb2432a7a66 h1:xsU00JB9GJxEiN6tDbqgN+fT98ySdxkUwTw6CfBXscw= +github.com/smartcontractkit/libocr v0.0.0-20240229181116-bfb2432a7a66/go.mod h1:SJEZCHgMCAzzBvo9vMV2DQ9onfEcIJCYSViyP4JI6c4= +github.com/smartcontractkit/seth v0.1.2 h1:ImXJmniuq6yWB6b3eezjV+lkYb1GfQuaJkwRvrCfTKQ= +github.com/smartcontractkit/seth v0.1.2/go.mod h1:aOaGwrIVFG/MYaLSj9UUMyE5QJnYQoAgnxm5cKfT9Ng= +github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= +github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= +github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= +github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= +github.com/smartcontractkit/wasp v0.4.6 h1:s6J8HgpxMHORl19nCpZPxc5jaVUQv8EXB6QjTuLXXnw= +github.com/smartcontractkit/wasp v0.4.6/go.mod h1:+ViWdUf1ap6powiEiwPskpZfH/Q1sG29YoVav7zGOIo= +github.com/smartcontractkit/wsrpc v0.7.2 h1:iBXzMeg7vc5YoezIQBq896y25BARw7OKbhrb6vPbtRQ= +github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= +github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= +github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 h1:3SNcvBmEPE1YlB1JpVZouslJpI3GBNoiqW7+wb0Rz7w= +github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= +github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= +github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= +github.com/testcontainers/testcontainers-go v0.28.0 h1:1HLm9qm+J5VikzFDYhOd+Zw12NtOl+8drH2E8nTY1r8= +github.com/testcontainers/testcontainers-go v0.28.0/go.mod h1:COlDpUXbwW3owtpMkEB1zo9gwb1CoKVKlyrVPejF4AU= +github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a h1:YuO+afVc3eqrjiCUizNCxI53bl/BnPiVwXqLzqYTqgU= +github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47zCZp9FrtGcWyo1VjbgDaodxX9ovZvgLb/MxaA= +github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= +github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA= +github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI= +github.com/umbracle/ethgo v0.1.3 h1:s8D7Rmphnt71zuqrgsGTMS5gTNbueGO1zKLh7qsFzTM= +github.com/umbracle/ethgo v0.1.3/go.mod h1:g9zclCLixH8liBI27Py82klDkW7Oo33AxUOr+M9lzrU= +github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 h1:10Nbw6cACsnQm7r34zlpJky+IzxVLRk6MKTS2d3Vp0E= +github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722/go.mod h1:c8J0h9aULj2i3umrfyestM6jCq0LK0U6ly6bWy96nd4= +github.com/unrolled/secure v1.13.0 h1:sdr3Phw2+f8Px8HE5sd1EHdj1aV3yUwed/uZXChLFsk= +github.com/unrolled/secure v1.13.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= +github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= +github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= +github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= +github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= +github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= +github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c= +github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= +go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= +go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= +go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ= +go.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg= +go.dedis.ch/kyber/v3 v3.1.0 h1:ghu+kiRgM5JyD9TJ0hTIxTLQlJBR/ehjWvWwYW3XsC0= +go.dedis.ch/kyber/v3 v3.1.0/go.mod h1:kXy7p3STAurkADD+/aZcsznZGKVHEqbtmdIzvPfrs1U= +go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo= +go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4= +go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= +go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/etcd/api/v3 v3.5.9 h1:4wSsluwyTbGGmyjJktOf3wFQoTBIURXHnq9n/G/JQHs= +go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= +go.etcd.io/etcd/client/pkg/v3 v3.5.9 h1:oidDC4+YEuSIQbsR94rY9gur91UPL6DnxDCIYd2IGsE= +go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= +go.etcd.io/etcd/client/v3 v3.5.9 h1:r5xghnU7CwbUxD/fbUtRyJGaYNfDun8sp/gTr1hew6E= +go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= +go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0016 h1:qCPXSQCoD3qeWFb1RuIks8fw9Atxpk78bmtVdi15KhE= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0016/go.mod h1:OdN0alYOlYhHXu6BDlGehrZWgtBuiDsz/rlNeJeXiNg= +go.opentelemetry.io/collector/semconv v0.87.0 h1:BsG1jdLLRCBRlvUujk4QA86af7r/ZXnizczQpEs/gg8= +go.opentelemetry.io/collector/semconv v0.87.0/go.mod h1:j/8THcqVxFna1FpvA2zYIsUperEtOaRaqoLYIN4doWw= +go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1 h1:mMv2jG58h6ZI5t5S9QCVGdzCmAsTakMa3oxVgpSD44g= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.46.1/go.mod h1:oqRuNKG0upTaDPbLVCG8AD0G2ETrfDtmh7jViy7ox6M= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= +go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11Us0AH2fTa9rmsbbF7g= +go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= +go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= +golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200601175630-2caf76543d99/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.132.0 h1:8t2/+qZ26kAOGSmOiHwVycqVaDg7q3JDILrNi/Z6rvc= +google.golang.org/api v0.132.0/go.mod h1:AeTBC6GpJnJSRJjktDcPX0QwtS8pGYZOV6MSuSCusw0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg= +gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +k8s.io/api v0.25.11 h1:4mjYDfE3yp22jrytjH0knwgzjXKkxHX4D01ZCAazvZM= +k8s.io/api v0.25.11/go.mod h1:bK4UvD4bthtutNlvensrfBX21PRQ/vs2cIYggHkOOAo= +k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= +k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= +k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= +k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= +k8s.io/cli-runtime v0.25.11 h1:GE2yNZm1tN+MJtw1SGMOLesLF7Kp7NVAVqRSTbXfu4o= +k8s.io/cli-runtime v0.25.11/go.mod h1:r/nEINuHVEpgGhcd2WamU7hD1t/lMnSz8XM44Autltc= +k8s.io/client-go v0.25.11 h1:DJQ141UsbNRI6wYSlcYLP5J5BW5Wq7Bgm42Ztq2SW70= +k8s.io/client-go v0.25.11/go.mod h1:41Xs7p1SfhoReUnmjjYCfCNWFiq4xSkexwJfbxF2F7A= +k8s.io/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI= +k8s.io/component-base v0.26.2/go.mod h1:DxbuIe9M3IZPRxPIzhch2m1eT7uFrSBJUBuVCQEBivs= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d h1:VcFq5n7wCJB2FQMCIHfC+f+jNcGgNMar1uKd6rVlifU= +k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= +k8s.io/kubectl v0.25.11 h1:6bsft5Gan6BCvQ7cJbDRFjTm4Zfq8GuUYpsWAdVngYE= +k8s.io/kubectl v0.25.11/go.mod h1:8mIfgkFgT+yJ8/TlmPW1qoRh46H2si9q5nW8id7i9iM= +k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI5yiP5WwlfDoIyc= +k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= +pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= +sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= +sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= +sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= +sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= +sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/integration-tests/load/ocr/gun.go b/integration-tests/load/ocr/gun.go index 14fac84fc1..c4b79ceaf4 100644 --- a/integration-tests/load/ocr/gun.go +++ b/integration-tests/load/ocr/gun.go @@ -6,11 +6,9 @@ import ( "time" "github.com/rs/zerolog" - + "github.com/smartcontractkit/seth" "github.com/smartcontractkit/wasp" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) @@ -19,14 +17,14 @@ import ( type Gun struct { roundNum atomic.Int64 ocrInstances []contracts.OffchainAggregator - cc blockchain.EVMClient + seth *seth.Client l zerolog.Logger } -func NewGun(l zerolog.Logger, cc blockchain.EVMClient, ocrInstances []contracts.OffchainAggregator) *Gun { +func NewGun(l zerolog.Logger, seth *seth.Client, ocrInstances []contracts.OffchainAggregator) *Gun { return &Gun{ l: l, - cc: cc, + seth: seth, ocrInstances: ocrInstances, } } diff --git a/integration-tests/load/ocr/helper.go b/integration-tests/load/ocr/helper.go index c35dc384d1..c80cedb64f 100644 --- a/integration-tests/load/ocr/helper.go +++ b/integration-tests/load/ocr/helper.go @@ -1,49 +1,52 @@ package ocr import ( + "fmt" "math/big" "math/rand" "time" + "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/seth" client2 "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) func SetupCluster( - cc blockchain.EVMClient, - cd contracts.ContractDeployer, + l zerolog.Logger, + seth *seth.Client, workerNodes []*client.ChainlinkK8sClient, -) (contracts.LinkToken, error) { - err := actions.FundChainlinkNodes(workerNodes, cc, big.NewFloat(3)) +) (common.Address, error) { + err := actions_seth.FundChainlinkNodesFromRootAddress(l, seth, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes), big.NewFloat(3)) if err != nil { - return nil, err + return common.Address{}, err } - lt, err := cd.DeployLinkTokenContract() + linkDeploymentData, err := contracts.DeployLinkTokenContract(seth) if err != nil { - return nil, err + return common.Address{}, err } - return lt, nil + return linkDeploymentData.Address, nil } func SetupFeed( - cc blockchain.EVMClient, + l zerolog.Logger, + seth *seth.Client, + lta common.Address, msClient *client2.MockserverClient, - cd contracts.ContractDeployer, bootstrapNode *client.ChainlinkK8sClient, workerNodes []*client.ChainlinkK8sClient, - lt contracts.LinkToken, ) ([]contracts.OffchainAggregator, error) { - ocrInstances, err := actions.DeployOCRContracts(1, lt, cd, workerNodes, cc) + ocrInstances, err := actions_seth.DeployOCRv1Contracts(l, seth, 1, lta, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(workerNodes)) if err != nil { return nil, err } - err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, msClient, cc.GetChainID().String()) + err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, msClient, fmt.Sprint(seth.ChainID)) if err != nil { return nil, err } diff --git a/integration-tests/load/ocr/ocr_test.go b/integration-tests/load/ocr/ocr_test.go index c6edf9122b..0a06206e60 100644 --- a/integration-tests/load/ocr/ocr_test.go +++ b/integration-tests/load/ocr/ocr_test.go @@ -5,10 +5,12 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/seth" "github.com/smartcontractkit/wasp" "github.com/smartcontractkit/chainlink-testing-framework/logging" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/smartcontractkit/chainlink/integration-tests/k8s" ) @@ -22,14 +24,24 @@ var ( func TestOCRLoad(t *testing.T) { l := logging.GetTestLogger(t) - cc, msClient, cd, bootstrapNode, workerNodes, err := k8s.ConnectRemote(l) - require.NoError(t, err) - lt, err := SetupCluster(cc, cd, workerNodes) + + config, err := tc.GetConfig("Load", tc.OCR) require.NoError(t, err) - ocrInstances, err := SetupFeed(cc, msClient, cd, bootstrapNode, workerNodes, lt) + + evmNetwork, msClient, bootstrapNode, workerNodes, err := k8s.ConnectRemote() require.NoError(t, err) - config, err := tc.GetConfig("Load", tc.OCR) + readSethCfg := config.GetSethConfig() + require.NotNil(t, readSethCfg, "Seth config shouldn't be nil") + + sethCfg := utils.MergeSethAndEvmNetworkConfigs(l, *evmNetwork, *readSethCfg) + + seth, err := seth.NewClientWithConfig(&sethCfg) + require.NoError(t, err, "Error creating seth client") + + lta, err := SetupCluster(l, seth, workerNodes) + require.NoError(t, err) + ocrInstances, err := SetupFeed(l, seth, lta, msClient, bootstrapNode, workerNodes) require.NoError(t, err) cfg := config.OCR @@ -44,7 +56,7 @@ func TestOCRLoad(t *testing.T) { CallTimeout: cfg.Load.VerificationTimeout.Duration, RateLimitUnitDuration: cfg.Load.RateLimitUnitDuration.Duration, Schedule: wasp.Plain(*cfg.Load.Rate, cfg.Load.TestDuration.Duration), - Gun: NewGun(l, cc, ocrInstances), + Gun: NewGun(l, seth, ocrInstances), Labels: CommonTestLabels, LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), })) @@ -54,11 +66,21 @@ func TestOCRLoad(t *testing.T) { func TestOCRVolume(t *testing.T) { l := logging.GetTestLogger(t) - cc, msClient, cd, bootstrapNode, workerNodes, err := k8s.ConnectRemote(l) + config, err := tc.GetConfig("Volume", tc.OCR) require.NoError(t, err) - lt, err := SetupCluster(cc, cd, workerNodes) + + evmNetwork, msClient, bootstrapNode, workerNodes, err := k8s.ConnectRemote() require.NoError(t, err) - config, err := tc.GetConfig("Volume", tc.OCR) + + readSethCfg := config.GetSethConfig() + require.NotNil(t, readSethCfg, "Seth config shouldn't be nil") + + sethCfg := utils.MergeSethAndEvmNetworkConfigs(l, *evmNetwork, *readSethCfg) + + seth, err := seth.NewClientWithConfig(&sethCfg) + require.NoError(t, err, "Error creating seth client") + + lta, err := SetupCluster(l, seth, workerNodes) require.NoError(t, err) cfg := config.OCR @@ -71,7 +93,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, *cfg.Volume.VURequestsPerUnit, cfg.Volume.RateLimitUnitDuration.Duration, cc, lt, cd, bootstrapNode, workerNodes, msClient), + VU: NewVU(l, seth, *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 d113f7eb3f..83f43a94e4 100644 --- a/integration-tests/load/ocr/vu.go +++ b/integration-tests/load/ocr/vu.go @@ -2,12 +2,14 @@ package ocr import ( "context" + "fmt" "sync/atomic" "time" + "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/seth" "github.com/smartcontractkit/wasp" "go.uber.org/ratelimit" @@ -15,6 +17,7 @@ import ( client2 "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) @@ -22,40 +25,38 @@ import ( // VU is a virtual user for the OCR load test // it creates a feed and triggers new rounds type VU struct { + *wasp.VUControl rl ratelimit.Limiter rate int rateUnit time.Duration roundNum atomic.Int64 - cc blockchain.EVMClient - lt contracts.LinkToken - cd contracts.ContractDeployer + seth *seth.Client + lta common.Address bootstrapNode *client.ChainlinkK8sClient workerNodes []*client.ChainlinkK8sClient msClient *client2.MockserverClient l zerolog.Logger ocrInstances []contracts.OffchainAggregator - stop chan struct{} } func NewVU( l zerolog.Logger, + seth *seth.Client, rate int, rateUnit time.Duration, - cc blockchain.EVMClient, - lt contracts.LinkToken, - cd contracts.ContractDeployer, + lta common.Address, bootstrapNode *client.ChainlinkK8sClient, workerNodes []*client.ChainlinkK8sClient, msClient *client2.MockserverClient, ) *VU { return &VU{ + VUControl: wasp.NewVUControl(), rl: ratelimit.New(rate, ratelimit.Per(rateUnit)), rate: rate, rateUnit: rateUnit, l: l, - cc: cc, - lt: lt, - cd: cd, + seth: seth, + lta: lta, msClient: msClient, bootstrapNode: bootstrapNode, workerNodes: workerNodes, @@ -64,14 +65,13 @@ func NewVU( func (m *VU) Clone(_ *wasp.Generator) wasp.VirtualUser { return &VU{ - stop: make(chan struct{}, 1), + VUControl: wasp.NewVUControl(), rl: ratelimit.New(m.rate, ratelimit.Per(m.rateUnit)), rate: m.rate, rateUnit: m.rateUnit, l: m.l, - cc: m.cc, - lt: m.lt, - cd: m.cd, + seth: m.seth, + lta: m.lta, msClient: m.msClient, bootstrapNode: m.bootstrapNode, workerNodes: m.workerNodes, @@ -79,11 +79,11 @@ func (m *VU) Clone(_ *wasp.Generator) wasp.VirtualUser { } func (m *VU) Setup(_ *wasp.Generator) error { - ocrInstances, err := actions.DeployOCRContracts(1, m.lt, m.cd, m.workerNodes, m.cc) + ocrInstances, err := actions_seth.DeployOCRv1Contracts(m.l, m.seth, 1, m.lta, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(m.workerNodes)) if err != nil { return err } - err = actions.CreateOCRJobs(ocrInstances, m.bootstrapNode, m.workerNodes, 5, m.msClient, m.cc.GetChainID().String()) + err = actions.CreateOCRJobs(ocrInstances, m.bootstrapNode, m.workerNodes, 5, m.msClient, fmt.Sprint(m.seth.ChainID)) if err != nil { return err } @@ -119,11 +119,3 @@ func (m *VU) Call(l *wasp.Generator) { } } } - -func (m *VU) Stop(_ *wasp.Generator) { - m.stop <- struct{}{} -} - -func (m *VU) StopChan() chan struct{} { - return m.stop -} diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index 4746c73081..9bf34f70b9 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -6,6 +6,7 @@ import ( "github.com/rs/zerolog" "github.com/smartcontractkit/wasp" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" "github.com/smartcontractkit/chainlink/integration-tests/types" ) @@ -13,7 +14,7 @@ import ( /* SingleHashGun is a gun that constantly requests randomness for one feed */ type SingleHashGun struct { - contracts *vrfv2.VRFV2Contracts + contracts *vrfcommon.VRFContracts keyHash [32]byte subIDs []uint64 testConfig types.VRFv2TestConfig @@ -21,7 +22,7 @@ type SingleHashGun struct { } func NewSingleHashGun( - contracts *vrfv2.VRFV2Contracts, + contracts *vrfcommon.VRFContracts, keyHash [32]byte, subIDs []uint64, testConfig types.VRFv2TestConfig, @@ -46,11 +47,11 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { _, err := vrfv2.RequestRandomnessAndWaitForFulfillment( m.logger, //the same consumer is used for all requests and in all subs - m.contracts.LoadTestConsumers[0], - m.contracts.Coordinator, + m.contracts.VRFV2Consumer[0], + m.contracts.CoordinatorV2, //randomly pick a subID from pool of subIDs m.subIDs[randInRange(0, len(m.subIDs)-1)], - &vrfv2.VRFV2Data{VRFV2KeyData: vrfv2.VRFV2KeyData{KeyHash: m.keyHash}}, + &vrfcommon.VRFKeyData{KeyHash: m.keyHash}, *vrfv2Config.MinimumConfirmations, *vrfv2Config.CallbackGasLimit, *vrfv2Config.NumberOfWords, diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 3a29e729de..9fd748a8b9 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -14,8 +14,11 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -29,8 +32,8 @@ import ( var ( env *test_env.CLClusterTestEnv - vrfv2Contracts *vrfv2.VRFV2Contracts - vrfv2Data *vrfv2.VRFV2Data + vrfContracts *vrfcommon.VRFContracts + vrfKeyData *vrfcommon.VRFKeyData subIDs []uint64 eoaWalletAddress string @@ -59,6 +62,10 @@ func TestVRFV2Performance(t *testing.T) { return } + networkConfig := networks.MustGetSelectedNetworkConfig(testConfig.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + updatedLabels := UpdateLabels(labels, t) l.Info(). @@ -68,34 +75,24 @@ func TestVRFV2Performance(t *testing.T) { Str("RateLimitUnitDuration", vrfv2Config.Performance.RateLimitUnitDuration.String()). Uint16("RandomnessRequestCountPerRequest", *vrfv2Config.General.RandomnessRequestCountPerRequest). Uint16("RandomnessRequestCountPerRequestDeviation", *vrfv2Config.General.RandomnessRequestCountPerRequestDeviation). - Bool("UseExistingEnv", *vrfv2Config.Performance.UseExistingEnv). + Bool("UseExistingEnv", *vrfv2Config.General.UseExistingEnv). Msg("Performance Test Configuration") - if *vrfv2Config.Performance.UseExistingEnv { - //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - cfg := testConfig.VRFv2 - - vrfv2Config.Performance.CoordinatorAddress = cfg.ExistingEnvConfig.CoordinatorAddress - vrfv2Config.Performance.ConsumerAddress = cfg.ExistingEnvConfig.ConsumerAddress - vrfv2Config.Performance.LinkAddress = cfg.ExistingEnvConfig.LinkAddress - vrfv2Config.General.SubscriptionFundingAmountLink = cfg.ExistingEnvConfig.SubFunding.SubFundsLink - vrfv2Config.Performance.SubID = cfg.ExistingEnvConfig.SubID - vrfv2Config.Performance.KeyHash = cfg.ExistingEnvConfig.KeyHash - + if *vrfv2Config.General.UseExistingEnv { env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&testConfig). WithCustomCleanup( func() { - teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, string(testType), &testConfig) - if env.EVMClient.NetworkSimulated() { + teardown(t, vrfContracts.VRFV2Consumer[0], lc, updatedLabels, testReporter, string(testType), &testConfig) + if evmClient.NetworkSimulated() { l.Info(). - Str("Network Name", env.EVMClient.GetNetworkName()). + Str("Network Name", evmClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - if *vrfv2Config.Common.CancelSubsAfterTestRun { + if *vrfv2Config.General.CancelSubsAfterTestRun { //cancel subs and return funds to sub owner - cancelSubsAndReturnFunds(subIDs, l) + cancelSubsAndReturnFunds(testcontext.Get(t), subIDs, l) } } }). @@ -103,24 +100,25 @@ func TestVRFV2Performance(t *testing.T) { require.NoError(t, err, "error creating test env") - coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(*vrfv2Config.Performance.CoordinatorAddress) + coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(*vrfv2Config.ExistingEnvConfig.CoordinatorAddress) require.NoError(t, err) var consumers []contracts.VRFv2LoadTestConsumer - if *cfg.ExistingEnvConfig.CreateFundSubsAndAddConsumers { - linkToken, err := env.ContractLoader.LoadLINKToken(*vrfv2Config.Performance.LinkAddress) + if *vrfv2Config.ExistingEnvConfig.CreateFundSubsAndAddConsumers { + linkToken, err := env.ContractLoader.LoadLINKToken(*vrfv2Config.ExistingEnvConfig.LinkAddress) require.NoError(t, err) consumers, err = vrfv2.DeployVRFV2Consumers(env.ContractDeployer, coordinator.Address(), 1) require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2.ErrWaitTXsComplete) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) l.Info(). - Str("Coordinator", *cfg.ExistingEnvConfig.CoordinatorAddress). + Str("Coordinator", *vrfv2Config.ExistingEnvConfig.CoordinatorAddress). Int("Number of Subs to create", *vrfv2Config.General.NumberOfSubToCreate). Msg("Creating and funding subscriptions, deploying and adding consumers to subs") subIDs, err = vrfv2.CreateFundSubsAndAddConsumers( env, - big.NewFloat(*cfg.General.SubscriptionFundingAmountLink), + networkConfig.ChainID, + big.NewFloat(*vrfv2Config.General.SubscriptionFundingAmountLink), linkToken, coordinator, consumers, @@ -128,37 +126,28 @@ func TestVRFV2Performance(t *testing.T) { ) require.NoError(t, err) } else { - consumer, err := env.ContractLoader.LoadVRFv2LoadTestConsumer(*vrfv2Config.Performance.ConsumerAddress) + consumer, err := env.ContractLoader.LoadVRFv2LoadTestConsumer(*vrfv2Config.ExistingEnvConfig.ConsumerAddress) require.NoError(t, err) consumers = append(consumers, consumer) - subIDs = append(subIDs, *vrfv2Config.Performance.SubID) + subIDs = append(subIDs, *vrfv2Config.ExistingEnvConfig.SubID) } - err = FundNodesIfNeeded(&testConfig, env.EVMClient, l) + err = FundNodesIfNeeded(testcontext.Get(t), &testConfig, evmClient, l) require.NoError(t, err) - vrfv2Contracts = &vrfv2.VRFV2Contracts{ - Coordinator: coordinator, - LoadTestConsumers: consumers, - BHS: nil, + vrfContracts = &vrfcommon.VRFContracts{ + CoordinatorV2: coordinator, + VRFV2Consumer: consumers, + BHS: nil, } - vrfv2Data = &vrfv2.VRFV2Data{ - VRFV2KeyData: vrfv2.VRFV2KeyData{ - VRFKey: nil, - EncodedProvingKey: [2]*big.Int{}, - KeyHash: common.HexToHash(*vrfv2Config.Performance.KeyHash), - }, - VRFJob: nil, - PrimaryEthAddress: "", - ChainID: nil, + vrfKeyData = &vrfcommon.VRFKeyData{ + VRFKey: nil, + EncodedProvingKey: [2]*big.Int{}, + KeyHash: common.HexToHash(*vrfv2Config.ExistingEnvConfig.KeyHash), } } else { - //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - testConfig.Common.ChainlinkNodeFunding = testConfig.VRFv2.NewEnvConfig.NodeSendingKeyFunding - vrfv2Config.General.SubscriptionFundingAmountLink = testConfig.VRFv2.NewEnvConfig.Funding.SubFundsLink - network, err := actions.EthereumNetworkConfigFromConfig(l, &testConfig) require.NoError(t, err, "Error building ethereum network config") env, err = test_env.NewCLTestEnvBuilder(). @@ -169,16 +158,16 @@ func TestVRFV2Performance(t *testing.T) { WithFunding(big.NewFloat(*testConfig.Common.ChainlinkNodeFunding)). WithCustomCleanup( func() { - teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, string(testType), &testConfig) + teardown(t, vrfContracts.VRFV2Consumer[0], lc, updatedLabels, testReporter, string(testType), &testConfig) - if env.EVMClient.NetworkSimulated() { + if evmClient.NetworkSimulated() { l.Info(). - Str("Network Name", env.EVMClient.GetNetworkName()). + Str("Network Name", evmClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - if *testConfig.VRFv2.Common.CancelSubsAfterTestRun { + if *testConfig.VRFv2.General.CancelSubsAfterTestRun { //cancel subs and return funds to sub owner - cancelSubsAndReturnFunds(subIDs, l) + cancelSubsAndReturnFunds(testcontext.Get(t), subIDs, l) } } if err := env.Cleanup(); err != nil { @@ -200,15 +189,17 @@ func TestVRFV2Performance(t *testing.T) { useVRFOwner := true useTestCoordinator := true - vrfv2Contracts, subIDs, vrfv2Data, err = vrfv2.SetupVRFV2Environment( + vrfContracts, subIDs, vrfKeyData, _, err = vrfv2.SetupVRFV2Environment( env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, &testConfig, useVRFOwner, useTestCoordinator, linkToken, mockETHLinkFeed, //register proving key against EOA address in order to return funds to this address - env.EVMClient.GetDefaultWallet().Address(), + evmClient.GetDefaultWallet().Address(), 0, 1, *vrfv2Config.General.NumberOfSubToCreate, @@ -216,23 +207,22 @@ func TestVRFV2Performance(t *testing.T) { ) require.NoError(t, err, "error setting up VRF v2 env") } - eoaWalletAddress = env.EVMClient.GetDefaultWallet().Address() + eoaWalletAddress = evmClient.GetDefaultWallet().Address() l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") for _, subID := range subIDs { - subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information for subscription %d", subID) - vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2) } - singleFeedConfig := &wasp.Config{ T: t, LoadType: wasp.RPS, GenName: "gun", RateLimitUnitDuration: vrfv2Config.Performance.RateLimitUnitDuration.Duration, Gun: NewSingleHashGun( - vrfv2Contracts, - vrfv2Data.KeyHash, + vrfContracts, + vrfKeyData.KeyHash, subIDs, &testConfig, l, @@ -241,8 +231,8 @@ func TestVRFV2Performance(t *testing.T) { LokiConfig: lokiConfig, CallTimeout: 2 * time.Minute, } - require.Len(t, vrfv2Contracts.LoadTestConsumers, 1, "only one consumer should be created for Load Test") - consumer := vrfv2Contracts.LoadTestConsumers[0] + require.Len(t, vrfContracts.VRFV2Consumer, 1, "only one consumer should be created for Load Test") + consumer := vrfContracts.VRFV2Consumer[0] err = consumer.ResetMetrics() require.NoError(t, err) MonitorLoadStats(lc, consumer, updatedLabels) @@ -262,7 +252,7 @@ func TestVRFV2Performance(t *testing.T) { var wg sync.WaitGroup wg.Add(1) //todo - timeout should be configurable depending on the perf test type - requestCount, fulfilmentCount, err := vrfv2.WaitForRequestCountEqualToFulfilmentCount(consumer, 2*time.Minute, &wg) + requestCount, fulfilmentCount, err := vrfcommon.WaitForRequestCountEqualToFulfilmentCount(testcontext.Get(t), consumer, 2*time.Minute, &wg) require.NoError(t, err) wg.Wait() @@ -274,18 +264,18 @@ func TestVRFV2Performance(t *testing.T) { } -func cancelSubsAndReturnFunds(subIDs []uint64, l zerolog.Logger) { +func cancelSubsAndReturnFunds(ctx context.Context, subIDs []uint64, l zerolog.Logger) { for _, subID := range subIDs { l.Info(). Uint64("Returning funds from SubID", subID). Str("Returning funds to", eoaWalletAddress). Msg("Canceling subscription and returning funds to subscription owner") - pendingRequestsExist, err := vrfv2Contracts.Coordinator.PendingRequestsExist(context.Background(), subID) + pendingRequestsExist, err := vrfContracts.CoordinatorV2.PendingRequestsExist(ctx, subID) if err != nil { l.Error().Err(err).Msg("Error checking if pending requests exist") } if !pendingRequestsExist { - _, err := vrfv2Contracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + _, err := vrfContracts.CoordinatorV2.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) if err != nil { l.Error().Err(err).Msg("Error canceling subscription") } @@ -295,12 +285,12 @@ func cancelSubsAndReturnFunds(subIDs []uint64, l zerolog.Logger) { } } -func FundNodesIfNeeded(vrfv2TestConfig tc.VRFv2TestConfig, client blockchain.EVMClient, l zerolog.Logger) error { +func FundNodesIfNeeded(ctx context.Context, vrfv2TestConfig tc.VRFv2TestConfig, client blockchain.EVMClient, l zerolog.Logger) error { cfg := vrfv2TestConfig.GetVRFv2Config() if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin != nil && *cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { for _, sendingKey := range cfg.ExistingEnvConfig.NodeSendingKeys { address := common.HexToAddress(sendingKey) - sendingKeyBalance, err := client.BalanceAt(context.Background(), address) + sendingKeyBalance, err := client.BalanceAt(ctx, address) if err != nil { return err } diff --git a/integration-tests/load/vrfv2/vrfv2cmd/dashboard.go b/integration-tests/load/vrfv2/vrfv2cmd/dashboard.go new file mode 100644 index 0000000000..e80d7516fd --- /dev/null +++ b/integration-tests/load/vrfv2/vrfv2cmd/dashboard.go @@ -0,0 +1,102 @@ +package main + +import ( + "os" + + db "github.com/smartcontractkit/wasp/dashboard" + + "github.com/K-Phoen/grabana/dashboard" + "github.com/K-Phoen/grabana/logs" + "github.com/K-Phoen/grabana/row" + "github.com/K-Phoen/grabana/target/prometheus" + "github.com/K-Phoen/grabana/timeseries" + "github.com/K-Phoen/grabana/timeseries/axis" +) + +func main() { + //TODO switch to TOML too? + lokiDS := os.Getenv("DATA_SOURCE_NAME") + d, err := db.NewDashboard(nil, + []dashboard.Option{ + dashboard.Row("LoadContractMetrics", + row.WithTimeSeries( + "RequestCount + FulfilmentCount", + timeseries.Span(12), + timeseries.Height("300px"), + timeseries.DataSource(lokiDS), + timeseries.Axis( + axis.Unit("Requests"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap RequestCount [$__interval]) by (node_id, go_test_name, gen_name) + `, prometheus.Legend("{{go_test_name}} requests"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap FulfilmentCount [$__interval]) by (node_id, go_test_name, gen_name) + `, prometheus.Legend("{{go_test_name}} fulfillments"), + ), + ), + row.WithTimeSeries( + "Fulfillment time (blocks)", + timeseries.Span(12), + timeseries.Height("300px"), + timeseries.DataSource(lokiDS), + timeseries.Axis( + axis.Unit("Blocks"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap AverageFulfillmentInMillions [$__interval]) by (node_id, go_test_name, gen_name) / 1e6 + `, prometheus.Legend("{{go_test_name}} avg"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap SlowestFulfillment [$__interval]) by (node_id, go_test_name, gen_name) + `, prometheus.Legend("{{go_test_name}} slowest"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap FastestFulfillment [$__interval]) by (node_id, go_test_name, gen_name) + `, prometheus.Legend("{{go_test_name}} fastest"), + ), + ), + ), + dashboard.Row("CL nodes logs", + row.Collapse(), + row.WithLogs( + "CL nodes logs", + logs.DataSource(lokiDS), + logs.Span(12), + logs.Height("300px"), + logs.Transparent(), + logs.WithLokiTarget(` + {type="log_watch"} + `), + )), + }, + ) + if err != nil { + panic(err) + } + // set env vars + //export GRAFANA_URL=... + //export GRAFANA_TOKEN=... + //export DATA_SOURCE_NAME=Loki + //export DASHBOARD_FOLDER=LoadTests + //export DASHBOARD_NAME=WaspVRFv2 + if _, err := d.Deploy(); err != nil { + panic(err) + } +} diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index faf5e6ef21..bfd8ff868b 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -8,6 +8,7 @@ import ( "github.com/rs/zerolog" "github.com/smartcontractkit/wasp" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2plus" vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/types" @@ -16,7 +17,7 @@ import ( /* SingleHashGun is a gun that constantly requests randomness for one feed */ type SingleHashGun struct { - contracts *vrfv2plus.VRFV2_5Contracts + contracts *vrfcommon.VRFContracts keyHash [32]byte subIDs []*big.Int testConfig types.VRFv2PlusTestConfig @@ -24,7 +25,7 @@ type SingleHashGun struct { } func NewSingleHashGun( - contracts *vrfv2plus.VRFV2_5Contracts, + contracts *vrfcommon.VRFContracts, keyHash [32]byte, subIDs []*big.Int, testConfig types.VRFv2PlusTestConfig, @@ -52,9 +53,9 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { randomnessRequestCountPerRequest := deviateValue(*m.testConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequest, *m.testConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequestDeviation) _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( //the same consumer is used for all requests and in all subs - m.contracts.LoadTestConsumers[0], - m.contracts.Coordinator, - &vrfv2plus.VRFV2PlusData{VRFV2PlusKeyData: vrfv2plus.VRFV2PlusKeyData{KeyHash: m.keyHash}}, + m.contracts.VRFV2PlusConsumer[0], + m.contracts.CoordinatorV2Plus, + &vrfcommon.VRFKeyData{KeyHash: m.keyHash}, //randomly pick a subID from pool of subIDs m.subIDs[randInRange(0, len(m.subIDs)-1)], //randomly pick payment type diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 51a3116dca..597965ef1a 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -15,25 +15,26 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" "github.com/smartcontractkit/chainlink/integration-tests/actions" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" ) var ( - env *test_env.CLClusterTestEnv - vrfv2PlusContracts *vrfv2plus.VRFV2_5Contracts - vrfv2PlusData *vrfv2plus.VRFV2PlusData - subIDs []*big.Int - eoaWalletAddress string + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + vrfv2PlusData *vrfcommon.VRFKeyData + subIDs []*big.Int + eoaWalletAddress string labels = map[string]string{ "branch": "vrfv2Plus_healthcheck", @@ -59,6 +60,10 @@ func TestVRFV2PlusPerformance(t *testing.T) { return } + networkConfig := networks.MustGetSelectedNetworkConfig(testConfig.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + updatedLabels := UpdateLabels(labels, t) l.Info(). @@ -68,33 +73,25 @@ func TestVRFV2PlusPerformance(t *testing.T) { Str("RateLimitUnitDuration", vrfv2PlusConfig.Performance.RateLimitUnitDuration.String()). Uint16("RandomnessRequestCountPerRequest", *vrfv2PlusConfig.General.RandomnessRequestCountPerRequest). Uint16("RandomnessRequestCountPerRequestDeviation", *vrfv2PlusConfig.General.RandomnessRequestCountPerRequestDeviation). - Bool("UseExistingEnv", *vrfv2PlusConfig.Performance.UseExistingEnv). + Bool("UseExistingEnv", *vrfv2PlusConfig.General.UseExistingEnv). Msg("Performance Test Configuration") - if *vrfv2PlusConfig.Performance.UseExistingEnv { - //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - vrfv2PlusConfig.Performance.CoordinatorAddress = testConfig.VRFv2Plus.ExistingEnvConfig.CoordinatorAddress - vrfv2PlusConfig.Performance.ConsumerAddress = testConfig.VRFv2Plus.ExistingEnvConfig.ConsumerAddress - vrfv2PlusConfig.Performance.LinkAddress = testConfig.VRFv2Plus.ExistingEnvConfig.LinkAddress - vrfv2PlusConfig.General.SubscriptionFundingAmountLink = testConfig.VRFv2Plus.ExistingEnvConfig.SubFunding.SubFundsLink - vrfv2PlusConfig.General.SubscriptionFundingAmountNative = testConfig.VRFv2Plus.ExistingEnvConfig.SubFunding.SubFundsNative - vrfv2PlusConfig.Performance.SubID = testConfig.VRFv2Plus.ExistingEnvConfig.SubID - vrfv2PlusConfig.Performance.KeyHash = testConfig.VRFv2Plus.ExistingEnvConfig.KeyHash + if *vrfv2PlusConfig.General.UseExistingEnv { env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&testConfig). WithCustomCleanup( func() { - teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, string(testType), &testConfig) - if env.EVMClient.NetworkSimulated() { + teardown(t, vrfContracts.VRFV2PlusConsumer[0], lc, updatedLabels, testReporter, string(testType), &testConfig) + if evmClient.NetworkSimulated() { l.Info(). - Str("Network Name", env.EVMClient.GetNetworkName()). + Str("Network Name", evmClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - if *testConfig.VRFv2Plus.Common.CancelSubsAfterTestRun { + if *testConfig.VRFv2Plus.General.CancelSubsAfterTestRun { //cancel subs and return funds to sub owner - cancelSubsAndReturnFunds(subIDs, l) + cancelSubsAndReturnFunds(testcontext.Get(t), subIDs, l) } } }). @@ -102,68 +99,58 @@ func TestVRFV2PlusPerformance(t *testing.T) { require.NoError(t, err, "error creating test env") - coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2_5(*vrfv2PlusConfig.Performance.CoordinatorAddress) + coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2_5(*vrfv2PlusConfig.ExistingEnvConfig.CoordinatorAddress) require.NoError(t, err) var consumers []contracts.VRFv2PlusLoadTestConsumer if *testConfig.VRFv2Plus.ExistingEnvConfig.CreateFundSubsAndAddConsumers { - linkToken, err := env.ContractLoader.LoadLINKToken(*vrfv2PlusConfig.Performance.LinkAddress) + linkToken, err := env.ContractLoader.LoadLINKToken(*vrfv2PlusConfig.ExistingEnvConfig.LinkAddress) require.NoError(t, err) consumers, err = vrfv2plus.DeployVRFV2PlusConsumers(env.ContractDeployer, coordinator, 1) require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) l.Info(). - Str("Coordinator", *vrfv2PlusConfig.Performance.CoordinatorAddress). + Str("Coordinator", *vrfv2PlusConfig.ExistingEnvConfig.CoordinatorAddress). Int("Number of Subs to create", *vrfv2PlusConfig.General.NumberOfSubToCreate). Msg("Creating and funding subscriptions, deploying and adding consumers to subs") subIDs, err = vrfv2plus.CreateFundSubsAndAddConsumers( env, + networkConfig.ChainID, big.NewFloat(*testConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), big.NewFloat(*testConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, coordinator, consumers, *vrfv2PlusConfig.General.NumberOfSubToCreate, - vrfv2plus_config.BillingType(*vrfv2PlusConfig.General.SubscriptionBillingType), ) require.NoError(t, err) } else { - consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(*vrfv2PlusConfig.Performance.ConsumerAddress) + consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(*vrfv2PlusConfig.ExistingEnvConfig.ConsumerAddress) require.NoError(t, err) consumers = append(consumers, consumer) var ok bool - subID := big.NewInt(int64(*vrfv2PlusConfig.Performance.SubID)) + subID, ok := new(big.Int).SetString(*vrfv2PlusConfig.ExistingEnvConfig.SubID, 10) require.True(t, ok) subIDs = append(subIDs, subID) } - err = FundNodesIfNeeded(&testConfig, env.EVMClient, l) + err = FundNodesIfNeeded(testcontext.Get(t), &testConfig, evmClient, l) require.NoError(t, err) - vrfv2PlusContracts = &vrfv2plus.VRFV2_5Contracts{ - Coordinator: coordinator, - LoadTestConsumers: consumers, + vrfContracts = &vrfcommon.VRFContracts{ + CoordinatorV2Plus: coordinator, + VRFV2PlusConsumer: consumers, BHS: nil, } - vrfv2PlusData = &vrfv2plus.VRFV2PlusData{ - VRFV2PlusKeyData: vrfv2plus.VRFV2PlusKeyData{ - VRFKey: nil, - EncodedProvingKey: [2]*big.Int{}, - KeyHash: common.HexToHash(*vrfv2PlusConfig.Performance.KeyHash), - }, - VRFJob: nil, - PrimaryEthAddress: "", - ChainID: nil, + vrfv2PlusData = &vrfcommon.VRFKeyData{ + VRFKey: nil, + EncodedProvingKey: [2]*big.Int{}, + KeyHash: common.HexToHash(*vrfv2PlusConfig.ExistingEnvConfig.KeyHash), } } else { - //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - testConfig.Common.ChainlinkNodeFunding = testConfig.VRFv2.NewEnvConfig.NodeSendingKeyFunding - vrfv2PlusConfig.General.SubscriptionFundingAmountLink = testConfig.VRFv2Plus.NewEnvConfig.Funding.SubFundsLink - vrfv2PlusConfig.General.SubscriptionFundingAmountNative = testConfig.VRFv2Plus.NewEnvConfig.Funding.SubFundsNative - network, err := actions.EthereumNetworkConfigFromConfig(l, &testConfig) require.NoError(t, err, "Error building ethereum network config") env, err = test_env.NewCLTestEnvBuilder(). @@ -174,16 +161,16 @@ func TestVRFV2PlusPerformance(t *testing.T) { WithFunding(big.NewFloat(*testConfig.Common.ChainlinkNodeFunding)). WithCustomCleanup( func() { - teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, string(testType), &testConfig) + teardown(t, vrfContracts.VRFV2PlusConsumer[0], lc, updatedLabels, testReporter, string(testType), &testConfig) - if env.EVMClient.NetworkSimulated() { + if evmClient.NetworkSimulated() { l.Info(). - Str("Network Name", env.EVMClient.GetNetworkName()). + Str("Network Name", evmClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - if *testConfig.VRFv2Plus.Common.CancelSubsAfterTestRun { + if *testConfig.VRFv2Plus.General.CancelSubsAfterTestRun { //cancel subs and return funds to sub owner - cancelSubsAndReturnFunds(subIDs, l) + cancelSubsAndReturnFunds(testcontext.Get(t), subIDs, l) } } if err := env.Cleanup(); err != nil { @@ -202,8 +189,10 @@ func TestVRFV2PlusPerformance(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err = vrfv2plus.SetupVRFV2_5Environment( + vrfContracts, subIDs, vrfv2PlusData, _, err = vrfv2plus.SetupVRFV2_5Environment( env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, &testConfig, linkToken, mockETHLinkFeed, @@ -214,13 +203,13 @@ func TestVRFV2PlusPerformance(t *testing.T) { ) require.NoError(t, err, "error setting up VRF v2_5 env") } - eoaWalletAddress = env.EVMClient.GetDefaultWallet().Address() + eoaWalletAddress = evmClient.GetDefaultWallet().Address() l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") for _, subID := range subIDs { - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) + subscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information for subscription %s", subID.String()) - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) + vrfv2plus.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2Plus) } singleFeedConfig := &wasp.Config{ @@ -229,7 +218,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { GenName: "gun", RateLimitUnitDuration: vrfv2PlusConfig.Performance.RateLimitUnitDuration.Duration, Gun: NewSingleHashGun( - vrfv2PlusContracts, + vrfContracts, vrfv2PlusData.KeyHash, subIDs, &testConfig, @@ -239,8 +228,8 @@ func TestVRFV2PlusPerformance(t *testing.T) { LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), CallTimeout: 2 * time.Minute, } - require.Len(t, vrfv2PlusContracts.LoadTestConsumers, 1, "only one consumer should be created for Load Test") - consumer := vrfv2PlusContracts.LoadTestConsumers[0] + require.Len(t, vrfContracts.VRFV2PlusConsumer, 1, "only one consumer should be created for Load Test") + consumer := vrfContracts.VRFV2PlusConsumer[0] err = consumer.ResetMetrics() require.NoError(t, err) MonitorLoadStats(lc, consumer, updatedLabels) @@ -259,7 +248,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { var wg sync.WaitGroup wg.Add(1) //todo - timeout should be configurable depending on the perf test type - requestCount, fulfilmentCount, err := vrfv2plus.WaitForRequestCountEqualToFulfilmentCount(consumer, 2*time.Minute, &wg) + requestCount, fulfilmentCount, err := vrfcommon.WaitForRequestCountEqualToFulfilmentCount(testcontext.Get(t), consumer, 2*time.Minute, &wg) require.NoError(t, err) wg.Wait() @@ -271,18 +260,18 @@ func TestVRFV2PlusPerformance(t *testing.T) { } -func cancelSubsAndReturnFunds(subIDs []*big.Int, l zerolog.Logger) { +func cancelSubsAndReturnFunds(ctx context.Context, subIDs []*big.Int, l zerolog.Logger) { for _, subID := range subIDs { l.Info(). Str("Returning funds from SubID", subID.String()). Str("Returning funds to", eoaWalletAddress). Msg("Canceling subscription and returning funds to subscription owner") - pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(context.Background(), subID) + pendingRequestsExist, err := vrfContracts.CoordinatorV2Plus.PendingRequestsExist(ctx, subID) if err != nil { l.Error().Err(err).Msg("Error checking if pending requests exist") } if !pendingRequestsExist { - _, err := vrfv2PlusContracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + _, err := vrfContracts.CoordinatorV2Plus.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) if err != nil { l.Error().Err(err).Msg("Error canceling subscription") } @@ -292,12 +281,12 @@ func cancelSubsAndReturnFunds(subIDs []*big.Int, l zerolog.Logger) { } } -func FundNodesIfNeeded(vrfv2plusTestConfig tc.VRFv2PlusTestConfig, client blockchain.EVMClient, l zerolog.Logger) error { +func FundNodesIfNeeded(ctx context.Context, vrfv2plusTestConfig tc.VRFv2PlusTestConfig, client blockchain.EVMClient, l zerolog.Logger) error { cfg := vrfv2plusTestConfig.GetVRFv2PlusConfig() - if *cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { + if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin != nil && *cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { for _, sendingKey := range cfg.ExistingEnvConfig.NodeSendingKeys { address := common.HexToAddress(sendingKey) - sendingKeyBalance, err := client.BalanceAt(context.Background(), address) + sendingKeyBalance, err := client.BalanceAt(ctx, address) if err != nil { return err } @@ -342,11 +331,16 @@ func teardown( //set report data for Slack notification testReporter.SetReportData( testType, - metrics.RequestCount, - metrics.FulfilmentCount, - metrics.AverageFulfillmentInMillions, - metrics.SlowestFulfillment, - metrics.FastestFulfillment, + testreporters.VRFLoadTestMetrics{ + RequestCount: metrics.RequestCount, + FulfilmentCount: metrics.FulfilmentCount, + AverageFulfillmentInMillions: metrics.AverageFulfillmentInMillions, + SlowestFulfillment: metrics.SlowestFulfillment, + FastestFulfillment: metrics.FastestFulfillment, + AverageResponseTimeInSecondsMillions: metrics.AverageResponseTimeInSecondsMillions, + SlowestResponseTimeInSeconds: metrics.SlowestResponseTimeInSeconds, + FastestResponseTimeInSeconds: metrics.FastestResponseTimeInSeconds, + }, testConfig, ) diff --git a/integration-tests/load/vrfv2plus/vrfv2pluscmd/dashboard.go b/integration-tests/load/vrfv2plus/vrfv2pluscmd/dashboard.go new file mode 100644 index 0000000000..75853e7e21 --- /dev/null +++ b/integration-tests/load/vrfv2plus/vrfv2pluscmd/dashboard.go @@ -0,0 +1,102 @@ +package main + +import ( + "os" + + db "github.com/smartcontractkit/wasp/dashboard" + + "github.com/K-Phoen/grabana/dashboard" + "github.com/K-Phoen/grabana/logs" + "github.com/K-Phoen/grabana/row" + "github.com/K-Phoen/grabana/target/prometheus" + "github.com/K-Phoen/grabana/timeseries" + "github.com/K-Phoen/grabana/timeseries/axis" +) + +func main() { + //TODO switch to TOML too? + lokiDS := os.Getenv("DATA_SOURCE_NAME") + d, err := db.NewDashboard(nil, + []dashboard.Option{ + dashboard.Row("LoadContractMetrics", + row.WithTimeSeries( + "RequestCount + FulfilmentCount", + timeseries.Span(12), + timeseries.Height("300px"), + timeseries.DataSource(lokiDS), + timeseries.Axis( + axis.Unit("Requests"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2plus_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap RequestCount [$__interval]) by (node_id, go_test_name, gen_name) + `, prometheus.Legend("{{go_test_name}} requests"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2plus_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap FulfilmentCount [$__interval]) by (node_id, go_test_name, gen_name) + `, prometheus.Legend("{{go_test_name}} fulfillments"), + ), + ), + row.WithTimeSeries( + "Fulfillment time (blocks)", + timeseries.Span(12), + timeseries.Height("300px"), + timeseries.DataSource(lokiDS), + timeseries.Axis( + axis.Unit("Blocks"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2plus_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap AverageFulfillmentInMillions [$__interval]) by (node_id, go_test_name, gen_name) / 1e6 + `, prometheus.Legend("{{go_test_name}} avg"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2plus_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap SlowestFulfillment [$__interval]) by (node_id, go_test_name, gen_name) + `, prometheus.Legend("{{go_test_name}} slowest"), + ), + timeseries.WithPrometheusTarget( + ` + last_over_time({type="vrfv2plus_contracts_load_summary", go_test_name=~"${go_test_name:pipe}", branch=~"${branch:pipe}", commit=~"${commit:pipe}", gen_name=~"${gen_name:pipe}"} + | json + | unwrap FastestFulfillment [$__interval]) by (node_id, go_test_name, gen_name) + `, prometheus.Legend("{{go_test_name}} fastest"), + ), + ), + ), + dashboard.Row("CL nodes logs", + row.Collapse(), + row.WithLogs( + "CL nodes logs", + logs.DataSource(lokiDS), + logs.Span(12), + logs.Height("300px"), + logs.Transparent(), + logs.WithLokiTarget(` + {type="log_watch"} + `), + )), + }, + ) + if err != nil { + panic(err) + } + // set env vars + //export GRAFANA_URL=... + //export GRAFANA_TOKEN=... + //export DATA_SOURCE_NAME=Loki + //export DASHBOARD_FOLDER=LoadTests + //export DASHBOARD_NAME=Waspvrfv2plus + if _, err := d.Deploy(); err != nil { + panic(err) + } +} diff --git a/integration-tests/load/zcluster/cluster_entrypoint_test.go b/integration-tests/load/zcluster/cluster_entrypoint_test.go new file mode 100644 index 0000000000..35b3ee422c --- /dev/null +++ b/integration-tests/load/zcluster/cluster_entrypoint_test.go @@ -0,0 +1,40 @@ +package zcluster + +import ( + "testing" + + "github.com/smartcontractkit/wasp" + "github.com/stretchr/testify/require" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" +) + +func TestClusterEntrypoint(t *testing.T) { + config, err := tc.GetConfig("Load", tc.OCR) + require.NoError(t, err) + cfgBase64, err := config.AsBase64() + require.NoError(t, err) + p, err := wasp.NewClusterProfile(&wasp.ClusterConfig{ + // you set up these only once, no need to configure through TOML + DockerCmdExecPath: "../../..", + BuildCtxPath: "integration-tests/load", + Namespace: *config.WaspConfig.Namespace, + KeepJobs: config.WaspConfig.KeepJobs, + UpdateImage: config.WaspConfig.UpdateImage, + HelmValues: map[string]string{ + "env.loki.url": *config.Logging.Loki.Endpoint, + "env.loki.tenant_id": *config.Logging.Loki.TenantId, + "image": *config.WaspConfig.RepoImageVersionURI, + "test.binaryName": *config.WaspConfig.TestBinaryName, + "test.name": *config.WaspConfig.TestName, + "test.timeout": *config.WaspConfig.TestTimeout, + "env.wasp.log_level": *config.WaspConfig.WaspLogLevel, + "jobs": *config.WaspConfig.WaspJobs, + // other test vars pass through + "test.BASE64_CONFIG_OVERRIDE": cfgBase64, + }, + }) + require.NoError(t, err) + err = p.Run() + require.NoError(t, err) +} diff --git a/integration-tests/migration/upgrade_version_test.go b/integration-tests/migration/upgrade_version_test.go index 584a9fbc75..d8abddf8e5 100644 --- a/integration-tests/migration/upgrade_version_test.go +++ b/integration-tests/migration/upgrade_version_test.go @@ -5,6 +5,8 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" @@ -13,17 +15,22 @@ import ( func TestVersionUpgrade(t *testing.T) { t.Parallel() + l := logging.GetTestLogger(t) + config, err := tc.GetConfig("Migration", tc.Node) require.NoError(t, err, "Error getting config") err = config.ChainlinkUpgradeImage.Validate() require.NoError(t, err, "Error validating upgrade image") + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestConfig(&config). WithTestInstance(t). WithStandardCleanup(). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithCLNodes(1). WithStandardCleanup(). Build() diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index 36b5d3eb98..aa1cb6bcde 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -25,7 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -129,14 +128,16 @@ func TestAutomationReorg(t *testing.T) { l := logging.GetTestLogger(t) registryVersions := map[string]ethereum.KeeperRegistryVersion{ - "registry_2_0": ethereum.RegistryVersion_2_0, - // "registry_2_1_conditional": ethereum.RegistryVersion_2_1, - // "registry_2_1_logtrigger": ethereum.RegistryVersion_2_1, + "registry_2_0": ethereum.RegistryVersion_2_0, + "registry_2_1_conditional": ethereum.RegistryVersion_2_1, + "registry_2_1_logtrigger": ethereum.RegistryVersion_2_1, + "registry_2_2_conditional": ethereum.RegistryVersion_2_2, + "registry_2_2_logtrigger": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() config, err := tc.GetConfig("Reorg", tc.Automation) @@ -150,8 +151,8 @@ func TestAutomationReorg(t *testing.T) { defaultAutomationSettings["toml"] = networks.AddNetworkDetailedConfig(baseTOML, config.Pyroscope, networkTOML, network) var overrideFn = func(_ interface{}, target interface{}) { - ctf_config.MustConfigOverrideChainlinkVersion(config.ChainlinkImage, target) - ctf_config.MightConfigOverridePyroscopeKey(config.Pyroscope, target) + ctf_config.MustConfigOverrideChainlinkVersion(config.GetChainlinkImageConfig(), target) + ctf_config.MightConfigOverridePyroscopeKey(config.GetPyroscopeConfig(), target) } cd := chainlink.NewWithOverride(0, defaultAutomationSettings, config.ChainlinkImage, overrideFn) @@ -213,7 +214,8 @@ func TestAutomationReorg(t *testing.T) { actions.CreateOCRKeeperJobs(t, chainlinkNodes, registry.Address(), network.ChainID, 0, registryVersion) nodesWithoutBootstrap := chainlinkNodes[1:] - ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 5*time.Second) + defaultOCRRegistryConfig.RegistryVersion = registryVersion + ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 5*time.Second, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) require.NoError(t, err, "OCR2 config should be built successfully") err = registry.SetConfig(defaultOCRRegistryConfig, ocrConfig) require.NoError(t, err, "Registry config should be be set successfully") diff --git a/integration-tests/scripts/buildTests b/integration-tests/scripts/buildTests index 749bb54511..b6033c1c41 100755 --- a/integration-tests/scripts/buildTests +++ b/integration-tests/scripts/buildTests @@ -21,6 +21,12 @@ OIFS=$IFS IFS=' ' for x in $tosplit do - go test -c -tags embed ./"${x}" + if [ "$x" = "load" ]; then + echo "Changing directory and executing go test -c ./... for 'load' package" + pushd "./load" && go test -c -tags embed -o .. ./... + popd + else + go test -c -tags embed ./"${x}" + fi done IFS=$OIFS diff --git a/integration-tests/scripts/check_base64_env_var.sh b/integration-tests/scripts/check_base64_env_var.sh new file mode 100755 index 0000000000..f1bd4632da --- /dev/null +++ b/integration-tests/scripts/check_base64_env_var.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +WHITE='\033[0;37m' +NC='\033[0m' # No Color + +echo +if [ ! -z "$BASE64_CONFIG_OVERRIDE" ]; then + echo "${GREEN}BASE64_CONFIG_OVERRIDE is set, it will be used.${NC}" + echo "Decoded content:" + echo + echo "$BASE64_CONFIG_OVERRIDE" | base64 --decode + echo + echo + echo "${GREEN}Press RETURN to confirm and continue...${NC} ${RED}or CTRL+C to exit${NC}" + read -r -n 1 key +else + echo "${YELLOW}BASE64_CONFIG_OVERRIDE is not set, checking for overrides.toml file...${NC}" + if [ -f "testconfig/overrides.toml" ]; then + echo "${GREEN}Found testconfig/overrides.toml file. Here's its content:${NC}" + echo + cat "testconfig/overrides.toml" + echo + echo + echo "${GREEN}Press RETURN to base64-encode it and run the test...${NC} ${RED}or CTRL+C to exit${NC}" + read -r -n 1 key + if [[ $key = "" ]]; then + export BASE64_CONFIG_OVERRIDE=$(cat testconfig/overrides.toml | base64) + fi + else + echo "${RED}testconfig/overrides.toml file does not exist. Please create it or set BASE64_CONFIG_OVERRIDE manually.${NC}" + echo + fi +fi \ No newline at end of file diff --git a/integration-tests/scripts/entrypoint b/integration-tests/scripts/entrypoint index 12ef90959b..d4ebe722a1 100755 --- a/integration-tests/scripts/entrypoint +++ b/integration-tests/scripts/entrypoint @@ -47,4 +47,4 @@ if [ -n "${UPLOAD_MEM_PROFILE}" ]; then fi echo "Exiting with 0 exit code as test is either completed, or failed and cannot be restarted" -exit 0 \ No newline at end of file +exit 0 diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 07f5c907b2..7f98958075 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -1128,11 +1128,15 @@ func setupAutomationTestDocker( require.NoError(t, err) l.Debug().Msgf("Funding amount: %f", *automationTestConfig.GetCommonConfig().ChainlinkNodeFunding) clNodesCount := 5 + + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, automationTestConfig) + require.NoError(t, err, "Error building ethereum network config") + if isMercuryV02 || isMercuryV03 { env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(automationTestConfig). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithFunding(big.NewFloat(*automationTestConfig.GetCommonConfig().ChainlinkNodeFunding)). WithStandardCleanup(). @@ -1148,11 +1152,14 @@ func setupAutomationTestDocker( Password = 'nodepass'` secretsConfig = fmt.Sprintf(secretsConfig, env.MockAdapter.InternalEndpoint, env.MockAdapter.InternalEndpoint) + rpcProvider, err := env.GetRpcProvider(network.ChainID) + require.NoError(t, err, "Error getting rpc provider") + var httpUrls []string var wsUrls []string if network.Simulated { - httpUrls = []string{env.RpcProvider.PrivateHttpUrls()[0]} - wsUrls = []string{env.RpcProvider.PrivateWsUrsl()[0]} + httpUrls = []string{rpcProvider.PrivateHttpUrls()[0]} + wsUrls = []string{rpcProvider.PrivateWsUrsl()[0]} } else { httpUrls = network.HTTPURLs wsUrls = network.URLs @@ -1169,7 +1176,7 @@ func setupAutomationTestDocker( env, err = test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(automationTestConfig). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithCLNodes(clNodesCount). WithCLNodeConfig(clNodeConfig). @@ -1179,10 +1186,12 @@ func setupAutomationTestDocker( require.NoError(t, err, "Error deploying test environment") } - env.ParallelTransactions(true) nodeClients := env.ClCluster.NodeAPIs() - a := automationv2.NewAutomationTestDocker(env.EVMClient, env.ContractDeployer, nodeClients) + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Error getting evm client") + + a := automationv2.NewAutomationTestDocker(evmClient, env.ContractDeployer, nodeClients) a.MercuryCredentialName = "cred1" a.RegistrySettings = registryConfig a.RegistrarSettings = contracts.KeeperRegistrarSettings{ diff --git a/integration-tests/smoke/automation_test.go_test_list.json b/integration-tests/smoke/automation_test.go_test_list.json index b88684599c..3e7a82effd 100644 --- a/integration-tests/smoke/automation_test.go_test_list.json +++ b/integration-tests/smoke/automation_test.go_test_list.json @@ -3,19 +3,31 @@ { "name": "TestAutomationBasic", "label": "ubuntu-latest", - "nodes": 2, + "nodes": 3, "run":[ {"name":"registry_2_0"}, - {"name":"registry_2_1_conditional"} + {"name":"registry_2_1_conditional"}, + {"name":"registry_2_1_logtrigger"} ] }, { "name": "TestAutomationBasic", "label": "ubuntu-latest", - "nodes": 2, + "nodes": 3, "run":[ - {"name":"registry_2_1_logtrigger"}, - {"name":"registry_2_1_with_mercury_v02"} + {"name":"registry_2_1_with_mercury_v02"}, + {"name":"registry_2_1_with_mercury_v03"}, + {"name":"registry_2_1_with_logtrigger_and_mercury_v02"} + ] + }, + { + "name": "TestAutomationBasic", + "label": "ubuntu-latest", + "nodes": 3, + "run":[ + {"name":"registry_2_2_conditional"}, + {"name":"registry_2_2_logtrigger"}, + {"name":"registry_2_2_with_mercury_v02"} ] }, { @@ -23,52 +35,54 @@ "label": "ubuntu-latest", "nodes": 2, "run":[ - {"name":"registry_2_1_with_mercury_v03"}, - {"name":"registry_2_1_with_logtrigger_and_mercury_v02"} + {"name":"registry_2_2_with_mercury_v03"}, + {"name":"registry_2_2_with_logtrigger_and_mercury_v02"} ] }, { - "name": "TestSetUpkeepTriggerConfig" + "name": "TestSetUpkeepTriggerConfig", + "label": "ubuntu-latest", + "nodes": 2 }, { "name": "TestAutomationAddFunds", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationPauseUnPause", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationRegisterUpkeep", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationPauseRegistry", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationKeeperNodesDown", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationPerformSimulation", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationCheckPerformGasLimit", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestUpdateCheckData", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 } ] } \ No newline at end of file diff --git a/integration-tests/smoke/cron_test.go b/integration-tests/smoke/cron_test.go index b6e04612ac..15934f4892 100644 --- a/integration-tests/smoke/cron_test.go +++ b/integration-tests/smoke/cron_test.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" @@ -25,10 +26,13 @@ func TestCronBasic(t *testing.T) { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithCLNodes(1). WithStandardCleanup(). @@ -77,10 +81,13 @@ func TestCronJobReplacement(t *testing.T) { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithCLNodes(1). WithStandardCleanup(). diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index c8cec4e385..a10efb3f39 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -31,10 +32,13 @@ func TestFluxBasic(t *testing.T) { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithCLNodes(3). WithStandardCleanup(). @@ -43,7 +47,12 @@ func TestFluxBasic(t *testing.T) { nodeAddresses, err := env.ClCluster.NodeAddresses() require.NoError(t, err, "Retrieving on-chain wallet addresses for chainlink nodes shouldn't fail") - env.EVMClient.ParallelTransactions(true) + + network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + + evmClient.ParallelTransactions(true) adapterUUID := uuid.NewString() adapterPath := fmt.Sprintf("/variable-%s", adapterUUID) @@ -54,12 +63,12 @@ func TestFluxBasic(t *testing.T) { require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") fluxInstance, err := env.ContractDeployer.DeployFluxAggregatorContract(lt.Address(), contracts.DefaultFluxAggregatorOptions()) require.NoError(t, err, "Deploying Flux Aggregator Contract shouldn't fail") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Failed waiting for deployment of flux aggregator contract") err = lt.Transfer(fluxInstance.Address(), big.NewInt(1e18)) require.NoError(t, err, "Funding Flux Aggregator Contract shouldn't fail") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Failed waiting for funding of flux aggregator contract") err = fluxInstance.UpdateAvailableFunds() @@ -79,7 +88,7 @@ func TestFluxBasic(t *testing.T) { }) require.NoError(t, err, "Setting oracle options in the Flux Aggregator contract shouldn't fail") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") oracles, err := fluxInstance.GetOracles(testcontext.Get(t)) require.NoError(t, err, "Getting oracle details from the Flux aggregator contract shouldn't fail") @@ -98,7 +107,7 @@ func TestFluxBasic(t *testing.T) { fluxSpec := &client.FluxMonitorJobSpec{ Name: fmt.Sprintf("flux-monitor-%s", adapterUUID), ContractAddress: fluxInstance.Address(), - EVMChainID: env.EVMClient.GetChainID().String(), + EVMChainID: evmClient.GetChainID().String(), Threshold: 0, AbsoluteThreshold: 0, PollTimerPeriod: 15 * time.Second, // min 15s @@ -112,8 +121,8 @@ func TestFluxBasic(t *testing.T) { // initial value set is performed before jobs creation fluxRoundTimeout := 1 * time.Minute fluxRound := contracts.NewFluxAggregatorRoundConfirmer(fluxInstance, big.NewInt(1), fluxRoundTimeout, l) - env.EVMClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) - err = env.EVMClient.WaitForEvents() + evmClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) + err = evmClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") data, err := fluxInstance.GetContractData(testcontext.Get(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") @@ -129,10 +138,10 @@ func TestFluxBasic(t *testing.T) { "Expected allocated funds to be %d, but found %d", int64(3), data.AllocatedFunds.Int64()) fluxRound = contracts.NewFluxAggregatorRoundConfirmer(fluxInstance, big.NewInt(2), fluxRoundTimeout, l) - env.EVMClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) + evmClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) err = env.MockAdapter.SetAdapterBasedIntValuePath(adapterPath, []string{http.MethodPost}, 1e10) require.NoError(t, err, "Setting value path in mock server shouldn't fail") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") data, err = fluxInstance.GetContractData(testcontext.Get(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index 5c603c5b08..11a772255f 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -24,10 +25,13 @@ func TestForwarderOCRBasic(t *testing.T) { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithForwarders(). WithCLNodes(6). @@ -47,20 +51,26 @@ func TestForwarderOCRBasic(t *testing.T) { linkTokenContract, err := env.ContractDeployer.DeployLinkTokenContract() require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) + network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + + err = actions.FundChainlinkNodesLocal(workerNodes, evmClient, big.NewFloat(.05)) require.NoError(t, err, "Error funding Chainlink nodes") + //nolint:staticcheck //ignore SA1019 we will migrate that test later operators, authorizedForwarders, _ := actions.DeployForwarderContracts( - t, env.ContractDeployer, linkTokenContract, env.EVMClient, len(workerNodes), + t, env.ContractDeployer, linkTokenContract, evmClient, len(workerNodes), ) for i := range workerNodes { + //nolint:staticcheck //ignore SA1019 we will migrate that test later actions.AcceptAuthorizedReceiversOperator( - t, operators[i], authorizedForwarders[i], []common.Address{workerNodeAddresses[i]}, env.EVMClient, env.ContractLoader, + t, operators[i], authorizedForwarders[i], []common.Address{workerNodeAddresses[i]}, evmClient, env.ContractLoader, ) require.NoError(t, err, "Accepting Authorize Receivers on Operator shouldn't fail") - err = actions.TrackForwarderLocal(env.EVMClient, authorizedForwarders[i], workerNodes[i], l) + err = actions.TrackForwarderLocal(evmClient, authorizedForwarders[i], workerNodes[i], l) require.NoError(t, err) - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() } ocrInstances, err := actions.DeployOCRContractsForwarderFlowLocal( 1, @@ -68,15 +78,15 @@ func TestForwarderOCRBasic(t *testing.T) { env.ContractDeployer, workerNodes, authorizedForwarders, - env.EVMClient, + evmClient, ) require.NoError(t, err, "Error deploying OCR contracts") - err = actions.CreateOCRJobsWithForwarderLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID().String()) + err = actions.CreateOCRJobsWithForwarderLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, evmClient.GetChainID().String()) require.NoError(t, err, "failed to setup forwarder jobs") - err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(1, ocrInstances, evmClient, l) require.NoError(t, err) - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) @@ -85,9 +95,9 @@ func TestForwarderOCRBasic(t *testing.T) { err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) require.NoError(t, err) - err = actions.WatchNewRound(2, ocrInstances, env.EVMClient, l) + err = actions.WatchNewRound(2, ocrInstances, evmClient, l) require.NoError(t, err) - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index 9385fb4f9a..bf573e89db 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -30,10 +31,13 @@ func TestForwarderOCR2Basic(t *testing.T) { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), node.WithOCR2(), @@ -57,19 +61,25 @@ func TestForwarderOCR2Basic(t *testing.T) { linkTokenContract, err := env.ContractDeployer.DeployLinkTokenContract() require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) + network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + + err = actions.FundChainlinkNodesLocal(workerNodes, evmClient, big.NewFloat(.05)) require.NoError(t, err, "Error funding Chainlink nodes") + //nolint:staticcheck //ignore SA1019 we will migrate that test later operators, authorizedForwarders, _ := actions.DeployForwarderContracts( - t, env.ContractDeployer, linkTokenContract, env.EVMClient, len(workerNodes), + t, env.ContractDeployer, linkTokenContract, evmClient, len(workerNodes), ) for i := range workerNodes { - actions.AcceptAuthorizedReceiversOperator(t, operators[i], authorizedForwarders[i], []common.Address{workerNodeAddresses[i]}, env.EVMClient, env.ContractLoader) + //nolint:staticcheck //ignore SA1019 we will migrate that test later + actions.AcceptAuthorizedReceiversOperator(t, operators[i], authorizedForwarders[i], []common.Address{workerNodeAddresses[i]}, evmClient, env.ContractLoader) require.NoError(t, err, "Accepting Authorized Receivers on Operator shouldn't fail") - err = actions.TrackForwarderLocal(env.EVMClient, authorizedForwarders[i], workerNodes[i], l) + err = actions.TrackForwarderLocal(evmClient, authorizedForwarders[i], workerNodes[i], l) require.NoError(t, err, "failed to track forwarders") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") } @@ -80,24 +90,25 @@ func TestForwarderOCR2Basic(t *testing.T) { } ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - ocrInstances, err := actions.DeployOCRv2Contracts(1, linkTokenContract, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) + ocrInstances, err := actions.DeployOCRv2Contracts(1, linkTokenContract, env.ContractDeployer, transmitters, evmClient, ocrOffchainOptions) require.NoError(t, err, "Error deploying OCRv2 contracts with forwarders") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - err = actions.CreateOCRv2JobsLocal(ocrInstances, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), true, false) + err = actions.CreateOCRv2JobsLocal(ocrInstances, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, evmClient.GetChainID().Uint64(), true, false) require.NoError(t, err, "Error creating OCRv2 jobs with forwarders") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) require.NoError(t, err, "Error building OCRv2 config") ocrv2Config.Transmitters = authorizedForwarders - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, ocrInstances) + //nolint:staticcheck //ignore SA1019 we will migrate that test later + err = actions.ConfigureOCRv2AggregatorContracts(evmClient, ocrv2Config, ocrInstances) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.WatchNewOCR2Round(1, ocrInstances, env.EVMClient, time.Minute*10, l) + err = actions.WatchNewOCR2Round(1, ocrInstances, evmClient, time.Minute*10, l) require.NoError(t, err) answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) @@ -108,7 +119,7 @@ func TestForwarderOCR2Basic(t *testing.T) { ocrRoundVal := (5 + i) % 10 err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, ocrRoundVal) require.NoError(t, err) - err = actions.WatchNewOCR2Round(int64(i), ocrInstances, env.EVMClient, time.Minute*10, l) + err = actions.WatchNewOCR2Round(int64(i), ocrInstances, evmClient, time.Minute*10, l) require.NoError(t, err) answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 0ebbc1e083..7f2183faea 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -9,11 +9,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/onsi/gomega" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -93,7 +95,7 @@ func TestKeeperBasicSmoke(t *testing.T) { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -174,7 +176,7 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -283,7 +285,7 @@ func TestKeeperSimulation(t *testing.T) { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumersPerformance, upkeepIDs := actions.DeployPerformanceKeeperContracts( t, registryVersion, @@ -360,7 +362,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumersPerformance, upkeepIDs := actions.DeployPerformanceKeeperContracts( t, registryVersion, @@ -477,7 +479,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, registrar, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -570,7 +572,7 @@ func TestKeeperAddFunds(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -637,7 +639,7 @@ func TestKeeperRemove(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -719,7 +721,7 @@ func TestKeeperPauseRegistry(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -784,7 +786,7 @@ func TestKeeperMigrateRegistry(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, ethereum.RegistryVersion_1_2, @@ -880,7 +882,7 @@ func TestKeeperNodeDown(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, @@ -990,7 +992,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, ethereum.RegistryVersion_1_3, @@ -1084,7 +1086,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { if err != nil { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, performDataChecker, upkeepIDs := actions.DeployPerformDataCheckerContracts( t, ethereum.RegistryVersion_1_3, @@ -1143,7 +1145,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { }, "3m", "1s").Should(gomega.Succeed()) } -func setupKeeperTest(t *testing.T, config *tc.TestConfig) ( +func setupKeeperTest(l zerolog.Logger, t *testing.T, config *tc.TestConfig) ( blockchain.EVMClient, []*client.ChainlinkClient, contracts.ContractDeployer, @@ -1158,10 +1160,13 @@ func setupKeeperTest(t *testing.T, config *tc.TestConfig) ( clNodeConfig.Keeper.Registry.SyncInterval = &syncInterval clNodeConfig.Keeper.Registry.PerformGasOverhead = &performGasOverhead + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithCLNodes(5). WithCLNodeConfig(clNodeConfig). WithFunding(big.NewFloat(.5)). @@ -1174,10 +1179,14 @@ func setupKeeperTest(t *testing.T, config *tc.TestConfig) ( linkTokenContract, err := env.ContractDeployer.DeployLinkTokenContract() require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - err = env.EVMClient.WaitForEvents() + network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + + err = evmClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - return env.EVMClient, env.ClCluster.NodeAPIs(), env.ContractDeployer, linkTokenContract, env + return evmClient, env.ClCluster.NodeAPIs(), env.ContractDeployer, linkTokenContract, env } func TestKeeperJobReplacement(t *testing.T) { @@ -1189,7 +1198,7 @@ func TestKeeperJobReplacement(t *testing.T) { t.Fatal(err) } - chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t, &config) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(l, t, &config) registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( t, registryVersion, diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go index 593b4eb879..38ae2c5ba7 100644 --- a/integration-tests/smoke/log_poller_test.go +++ b/integration-tests/smoke/log_poller_test.go @@ -12,13 +12,13 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - lp_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/log_poller" logpoller "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -104,14 +104,18 @@ func executeBasicLogPollerTest(t *testing.T) { l.Info().Msg("No duplicate filters found. OK!") - err = testEnv.EVMClient.WaitForEvents() + network := networks.MustGetSelectedNetworkConfig(testConfig.GetNetworkConfig())[0] + evmClient, err := testEnv.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + + err = evmClient.WaitForEvents() require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") expectedFilters := logpoller.GetExpectedFilters(lpTestEnv.logEmitters, cfg) - waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l, coreLogger, t, testEnv, expectedFilters) + waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l, coreLogger, t, testEnv, &testConfig, expectedFilters) // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + sb, err := evmClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") startBlock := int64(sb) @@ -121,7 +125,7 @@ func executeBasicLogPollerTest(t *testing.T) { // Start chaos experimnents by randomly pausing random containers (Chainlink nodes or their DBs) chaosDoneCh := make(chan error, 1) go func() { - logpoller.ExecuteChaosExperiment(l, testEnv, cfg, chaosDoneCh) + logpoller.ExecuteChaosExperiment(l, testEnv, &testConfig, chaosDoneCh) }() totalLogsEmitted, err := logpoller.ExecuteGenerator(t, cfg, lpTestEnv.logEmitters) @@ -131,7 +135,7 @@ func executeBasicLogPollerTest(t *testing.T) { expectedLogsEmitted := logpoller.GetExpectedLogCount(cfg) duration := int(endTime.Sub(startTime).Seconds()) - eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + eb, err := evmClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") l.Info(). @@ -150,12 +154,10 @@ func executeBasicLogPollerTest(t *testing.T) { // as that's not trivial to do (i.e. just because chain was at block X when log emission ended it doesn't mean all events made it to that block) endBlock := int64(eb) + 10000 - // logCountWaitDuration, err := time.ParseDuration("5m") - // require.NoError(t, err, "Error parsing log count wait duration") - allNodesLogCountMatches, err := logpoller.FluentlyCheckIfAllNodesHaveLogCount("5m", startBlock, endBlock, totalLogsEmitted, expectedFilters, l, coreLogger, testEnv) + allNodesLogCountMatches, err := logpoller.FluentlyCheckIfAllNodesHaveLogCount("5m", startBlock, endBlock, totalLogsEmitted, expectedFilters, l, coreLogger, testEnv, evmClient.GetChainID().Int64()) require.NoError(t, err, "Error checking if CL nodes have expected log count") - conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l, coreLogger, t, allNodesLogCountMatches, lpTestEnv, cfg, startBlock, endBlock, "5m") + conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l, coreLogger, t, allNodesLogCountMatches, lpTestEnv, &testConfig, startBlock, endBlock, "5m") } func executeLogPollerReplay(t *testing.T, consistencyTimeout string) { @@ -176,8 +178,12 @@ func executeLogPollerReplay(t *testing.T, consistencyTimeout string) { lpTestEnv := prepareEnvironment(l, t, &testConfig) testEnv := lpTestEnv.testEnv + network := networks.MustGetSelectedNetworkConfig(testConfig.GetNetworkConfig())[0] + evmClient, err := testEnv.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + sb, err := evmClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") startBlock := int64(sb) @@ -190,17 +196,17 @@ func executeLogPollerReplay(t *testing.T, consistencyTimeout string) { duration := int(endTime.Sub(startTime).Seconds()) // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + eb, err := evmClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") - endBlock, err := logpoller.GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) + endBlock, err := logpoller.GetEndBlockToWaitFor(int64(eb), evmClient.GetChainID().Int64(), cfg) require.NoError(t, err, "Error getting end block to wait for") l.Info().Int64("Ending Block", endBlock).Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") // Lets make sure no logs are in DB yet expectedFilters := logpoller.GetExpectedFilters(lpTestEnv.logEmitters, cfg) - logCountMatches, err := logpoller.ClNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), 0, expectedFilters, l, coreLogger, testEnv.ClCluster) + logCountMatches, err := logpoller.ClNodesHaveExpectedLogCount(startBlock, endBlock, evmClient.GetChainID(), 0, expectedFilters, l, coreLogger, testEnv.ClCluster) require.NoError(t, err, "Error checking if CL nodes have expected log count") require.True(t, logCountMatches, "Some CL nodes already had logs in DB") l.Info().Msg("No logs were saved by CL nodes yet, as expected. Proceeding.") @@ -210,16 +216,16 @@ func executeLogPollerReplay(t *testing.T, consistencyTimeout string) { err = logpoller.RegisterFiltersAndAssertUniquness(l, lpTestEnv.registry, lpTestEnv.upkeepIDs, lpTestEnv.logEmitters, cfg, lpTestEnv.upKeepsNeeded) require.NoError(t, err, "Error registering filters") - err = testEnv.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") - waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l, coreLogger, t, testEnv, expectedFilters) + waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l, coreLogger, t, testEnv, &testConfig, expectedFilters) blockFinalisationWaitDuration := "5m" l.Warn().Str("Duration", blockFinalisationWaitDuration).Msg("Waiting for all CL nodes to have end block finalised") gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - hasFinalised, err := logpoller.LogPollerHasFinalisedEndBlock(endBlock, testEnv.EVMClient.GetChainID(), l, coreLogger, testEnv.ClCluster) + hasFinalised, err := logpoller.LogPollerHasFinalisedEndBlock(endBlock, evmClient.GetChainID(), l, coreLogger, testEnv.ClCluster) if err != nil { l.Warn().Err(err).Msg("Error checking if nodes have finalised end block. Retrying...") } @@ -230,7 +236,7 @@ func executeLogPollerReplay(t *testing.T, consistencyTimeout string) { l.Info().Msg("Triggering log poller's replay") for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { nodeName := testEnv.ClCluster.Nodes[i].ContainerName - response, _, err := testEnv.ClCluster.Nodes[i].API.ReplayLogPollerFromBlock(startBlock, testEnv.EVMClient.GetChainID().Int64()) + response, _, err := testEnv.ClCluster.Nodes[i].API.ReplayLogPollerFromBlock(startBlock, evmClient.GetChainID().Int64()) require.NoError(t, err, "Error triggering log poller's replay on node %s", nodeName) require.Equal(t, "Replay started", response.Data.Attributes.Message, "Unexpected response message from log poller's replay") } @@ -240,10 +246,10 @@ func executeLogPollerReplay(t *testing.T, consistencyTimeout string) { l.Warn().Str("Duration", consistencyTimeout).Msg("Waiting for replay logs to be processed by all nodes") // logCountWaitDuration, err := time.ParseDuration("5m") - allNodesLogCountMatches, err := logpoller.FluentlyCheckIfAllNodesHaveLogCount("5m", startBlock, endBlock, totalLogsEmitted, expectedFilters, l, coreLogger, testEnv) + allNodesLogCountMatches, err := logpoller.FluentlyCheckIfAllNodesHaveLogCount("5m", startBlock, endBlock, totalLogsEmitted, expectedFilters, l, coreLogger, testEnv, evmClient.GetChainID().Int64()) require.NoError(t, err, "Error checking if CL nodes have expected log count") - conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l, coreLogger, t, allNodesLogCountMatches, lpTestEnv, cfg, startBlock, endBlock, "5m") + conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l, coreLogger, t, allNodesLogCountMatches, lpTestEnv, &testConfig, startBlock, endBlock, "5m") } type logPollerEnvironment struct { @@ -277,7 +283,8 @@ func prepareEnvironment(l zerolog.Logger, t *testing.T, testConfig *tc.TestConfi ethereum.RegistryVersion_2_1, logpoller.DefaultOCRRegistryConfig, upKeepsNeeded, - time.Duration(500*time.Millisecond), + cfg.General.LogPollInterval.Duration, + *cfg.General.BackupLogPollerBlockDelay, *cfg.General.UseFinalityTag, testConfig, ) @@ -301,7 +308,7 @@ func prepareEnvironment(l zerolog.Logger, t *testing.T, testConfig *tc.TestConfi l.Info().Msg("No duplicate upkeep IDs found. OK!") // Deploy Log Emitter contracts - logEmitters := logpoller.UploadLogEmitterContractsAndWaitForFinalisation(l, t, testEnv, cfg) + logEmitters := logpoller.UploadLogEmitterContractsAndWaitForFinalisation(l, t, testEnv, testConfig) err = logpoller.AssertContractAddressUniquneness(logEmitters) require.NoError(t, err, "Error asserting contract addresses uniqueness") l.Info().Msg("No duplicate contract addresses found. OK!") @@ -316,8 +323,13 @@ func prepareEnvironment(l zerolog.Logger, t *testing.T, testConfig *tc.TestConfi } // waitForAllNodesToHaveExpectedFiltersRegisteredOrFail waits until all nodes have expected filters registered until timeout -func waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l zerolog.Logger, coreLogger core_logger.SugaredLogger, t *testing.T, testEnv *test_env.CLClusterTestEnv, expectedFilters []logpoller.ExpectedFilter) { +func waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l zerolog.Logger, coreLogger core_logger.SugaredLogger, t *testing.T, testEnv *test_env.CLClusterTestEnv, testConfig *tc.TestConfig, expectedFilters []logpoller.ExpectedFilter) { // Make sure that all nodes have expected filters registered before starting to emit events + + network := networks.MustGetSelectedNetworkConfig(testConfig.GetNetworkConfig())[0] + evmClient, err := testEnv.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { hasFilters := false @@ -329,7 +341,7 @@ func waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l zerolog.Logger, core var message string var err error - hasFilters, message, err = logpoller.NodeHasExpectedFilters(expectedFilters, coreLogger, testEnv.EVMClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) + hasFilters, message, err = logpoller.NodeHasExpectedFilters(expectedFilters, coreLogger, evmClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) if !hasFilters || err != nil { l.Warn(). Str("Details", message). @@ -349,16 +361,20 @@ func waitForAllNodesToHaveExpectedFiltersRegisteredOrFail(l zerolog.Logger, core // conditionallyWaitUntilNodesHaveTheSameLogsAsEvm checks whether all CL nodes have the same number of logs as EVM node // if not, then it prints missing logs and wait for some time and checks again -func conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l zerolog.Logger, coreLogger core_logger.SugaredLogger, t *testing.T, allNodesLogCountMatches bool, lpTestEnv logPollerEnvironment, cfg *lp_config.Config, startBlock, endBlock int64, waitDuration string) { +func conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l zerolog.Logger, coreLogger core_logger.SugaredLogger, t *testing.T, allNodesLogCountMatches bool, lpTestEnv logPollerEnvironment, testConfig *tc.TestConfig, startBlock, endBlock int64, waitDuration string) { logCountWaitDuration, err := time.ParseDuration(waitDuration) require.NoError(t, err, "Error parsing log count wait duration") + network := networks.MustGetSelectedNetworkConfig(testConfig.GetNetworkConfig())[0] + evmClient, err := lpTestEnv.testEnv.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + allNodesHaveAllExpectedLogs := false if !allNodesLogCountMatches { - missingLogs, err := logpoller.GetMissingLogs(startBlock, endBlock, lpTestEnv.logEmitters, lpTestEnv.testEnv.EVMClient, lpTestEnv.testEnv.ClCluster, l, coreLogger, cfg) + missingLogs, err := logpoller.GetMissingLogs(startBlock, endBlock, lpTestEnv.logEmitters, evmClient, lpTestEnv.testEnv.ClCluster, l, coreLogger, testConfig.LogPoller) if err == nil { if !missingLogs.IsEmpty() { - logpoller.PrintMissingLogsInfo(missingLogs, l, cfg) + logpoller.PrintMissingLogsInfo(missingLogs, l, testConfig.LogPoller) } else { allNodesHaveAllExpectedLogs = true l.Info().Msg("All CL nodes have all the logs that EVM node has") @@ -379,7 +395,7 @@ func conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l zerolog.Logger, coreLogge gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - missingLogs, err := logpoller.GetMissingLogs(startBlock, endBlock, lpTestEnv.logEmitters, lpTestEnv.testEnv.EVMClient, lpTestEnv.testEnv.ClCluster, l, coreLogger, cfg) + missingLogs, err := logpoller.GetMissingLogs(startBlock, endBlock, lpTestEnv.logEmitters, evmClient, lpTestEnv.testEnv.ClCluster, l, coreLogger, testConfig.LogPoller) if err != nil { l.Warn(). Err(err). @@ -387,7 +403,7 @@ func conditionallyWaitUntilNodesHaveTheSameLogsAsEvm(l zerolog.Logger, coreLogge } if !missingLogs.IsEmpty() { - logpoller.PrintMissingLogsInfo(missingLogs, l, cfg) + logpoller.PrintMissingLogsInfo(missingLogs, l, testConfig.LogPoller) } g.Expect(missingLogs.IsEmpty()).To(gomega.BeTrue(), "Some CL nodes were missing logs") }, logConsistencyWaitDuration, "10s").Should(gomega.Succeed()) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 6d3cf796ce..b70796c4ab 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" @@ -14,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" @@ -39,83 +41,16 @@ func TestOCRv2Basic(t *testing.T) { test := test t.Run(test.name, func(t *testing.T) { t.Parallel() - l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.OCR2) - if err != nil { - t.Fatal(err) - } - - network, err := actions.EthereumNetworkConfigFromConfig(l, &config) - require.NoError(t, err, "Error building ethereum network config") - - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&config). - WithPrivateEthereumNetwork(network). - WithMockAdapter(). - WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), - node.WithOCR2(), - node.WithP2Pv2(), - node.WithTracing(), - )). - WithCLNodeOptions(test_env.WithNodeEnvVars(test.env)). - WithCLNodes(6). - WithFunding(big.NewFloat(.1)). - WithStandardCleanup(). - Build() - require.NoError(t, err) - - env.ParallelTransactions(true) - - nodeClients := env.ClCluster.NodeAPIs() - bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) - require.NoError(t, err, "Error funding Chainlink nodes") - - // Gather transmitters - var transmitters []string - for _, node := range workerNodes { - addr, err := node.PrimaryEthAddress() - if err != nil { - require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) - } - transmitters = append(transmitters, addr) - } - - ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) - require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") + env, aggregatorContracts := prepareORCv2SmokeTestEnv(t, l, 5) - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, test.chainReaderAndCodec) - require.NoError(t, err, "Error creating OCRv2 jobs") - - ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) - require.NoError(t, err, "Error building OCRv2 config") - - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) - require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - - err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error watching for new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 5 but got %d", - roundData.Answer.Int64(), - ) - - err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) + err := env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) require.NoError(t, err) - err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions_seth.WatchNewRound(l, env.SethClient, 2, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) require.NoError(t, err) - roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", @@ -130,77 +65,14 @@ func TestOCRv2Request(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.ForwarderOcr) - if err != nil { - t.Fatal(err) - } - - network, err := actions.EthereumNetworkConfigFromConfig(l, &config) - require.NoError(t, err, "Error building ethereum network config") - - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&config). - WithPrivateEthereumNetwork(network). - WithMockAdapter(). - WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), - node.WithOCR2(), - node.WithP2Pv2(), - node.WithTracing(), - )). - WithCLNodes(6). - WithFunding(big.NewFloat(.1)). - WithStandardCleanup(). - Build() - require.NoError(t, err) - - env.ParallelTransactions(true) - - nodeClients := env.ClCluster.NodeAPIs() - bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) - require.NoError(t, err, "Error funding Chainlink nodes") - - // Gather transmitters - var transmitters []string - for _, node := range workerNodes { - addr, err := node.PrimaryEthAddress() - if err != nil { - require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) - } - transmitters = append(transmitters, addr) - } - - ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) - require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, false) - require.NoError(t, err, "Error creating OCRv2 jobs") - - ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) - require.NoError(t, err, "Error building OCRv2 config") - - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) - require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - - err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err, "Error watching for new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 5 but got %d", - roundData.Answer.Int64(), - ) + env, aggregatorContracts := prepareORCv2SmokeTestEnv(t, l, 5) // Keep the mockserver value the same and continually request new rounds for round := 2; round <= 4; round++ { - err = actions.StartNewOCR2Round(int64(round), aggregatorContracts, env.EVMClient, time.Minute*5, l) + err := actions_seth.StartNewRound(contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts)) require.NoError(t, err, "Error starting new OCR2 round") + err = actions_seth.WatchNewRound(l, env.SethClient, int64(round), contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) + require.NoError(t, err, "Error watching for new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(int64(round))) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), roundData.Answer.Int64(), @@ -215,15 +87,55 @@ func TestOCRv2JobReplacement(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + env, aggregatorContracts := prepareORCv2SmokeTestEnv(t, l, 5) + nodeClients := env.ClCluster.NodeAPIs() + bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] + + err := env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) + require.NoError(t, err) + err = actions_seth.WatchNewRound(l, env.SethClient, 2, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) + require.NoError(t, err, "Error watching for new OCR2 round") + + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 10 but got %d", + roundData.Answer.Int64(), + ) + + err = actions.DeleteJobs(nodeClients) + require.NoError(t, err) + + err = actions.DeleteBridges(nodeClients) + require.NoError(t, err) + + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, uint64(env.SethClient.ChainID), false, false) + require.NoError(t, err, "Error creating OCRv2 jobs") + + err = actions_seth.WatchNewRound(l, env.SethClient, 3, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*3) + require.NoError(t, err, "Error watching for new OCR2 round") + + roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(3)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(15), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 15 but got %d", + roundData.Answer.Int64(), + ) +} + +func prepareORCv2SmokeTestEnv(t *testing.T, l zerolog.Logger, firstRoundResult int) (*test_env.CLClusterTestEnv, []contracts.OffchainAggregatorV2) { config, err := tc.GetConfig("Smoke", tc.OCR2) if err != nil { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), node.WithOCR2(), @@ -233,18 +145,17 @@ func TestOCRv2JobReplacement(t *testing.T) { WithCLNodes(6). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). + WithSeth(). Build() require.NoError(t, err) - env.ParallelTransactions(true) - nodeClients := env.ClCluster.NodeAPIs() bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + linkDeploymentData, err := contracts.DeployLinkTokenContract(env.SethClient) + require.NoError(t, err, "Error deploying link token contract") - err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) + err = actions_seth.FundChainlinkNodesFromRootAddress(l, env.SethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes), big.NewFloat(.05)) require.NoError(t, err, "Error funding Chainlink nodes") // Gather transmitters @@ -258,54 +169,26 @@ func TestOCRv2JobReplacement(t *testing.T) { } ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) + aggregatorContracts, err := actions_seth.DeployOCRv2Contracts(l, env.SethClient, 1, linkDeploymentData.Address, transmitters, ocrOffchainOptions) require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false, false) + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, uint64(env.SethClient.ChainID), false, false) require.NoError(t, err, "Error creating OCRv2 jobs") ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) require.NoError(t, err, "Error building OCRv2 config") - err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) + err = actions_seth.ConfigureOCRv2AggregatorContracts(ocrv2Config, aggregatorContracts) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions_seth.WatchNewRound(l, env.SethClient, 1, contracts.V2OffChainAgrregatorToOffChainAggregatorWithRounds(aggregatorContracts), time.Minute*5) require.NoError(t, err, "Error watching for new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), roundData.Answer.Int64(), + require.Equal(t, int64(firstRoundResult), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", roundData.Answer.Int64(), ) - err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) - require.NoError(t, err) - err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) - require.NoError(t, err) - - roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) - require.NoError(t, err, "Error getting latest OCR answer") - require.Equal(t, int64(10), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 10 but got %d", - roundData.Answer.Int64(), - ) - - err = actions.DeleteJobs(nodeClients) - require.NoError(t, err) - - err = actions.DeleteBridges(nodeClients) - require.NoError(t, err) - - err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, env.EVMClient.GetChainID().Uint64(), false, false) - require.NoError(t, err, "Error creating OCRv2 jobs") - - err = actions.WatchNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) - require.NoError(t, err, "Error watching for new OCR2 round") - roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(3)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(15), roundData.Answer.Int64(), - "Expected latest answer from OCR contract to be 15 but got %d", - roundData.Answer.Int64(), - ) + return env, aggregatorContracts } diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index c01ac46fbb..3f9a7e3649 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -183,8 +183,8 @@ func setupOCR2VRFEnvironment(t *testing.T) (testEnvironment *environment.Environ } var overrideFn = func(_ interface{}, target interface{}) { - ctf_config.MustConfigOverrideChainlinkVersion(ocr2vrfSmokeConfig.ChainlinkImage, target) - ctf_config.MightConfigOverridePyroscopeKey(ocr2vrfSmokeConfig.Pyroscope, target) + ctf_config.MustConfigOverrideChainlinkVersion(ocr2vrfSmokeConfig.GetChainlinkImageConfig(), target) + ctf_config.MightConfigOverridePyroscopeKey(ocr2vrfSmokeConfig.GetPyroscopeConfig(), target) } cd := chainlink.NewWithOverride(0, map[string]interface{}{ diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 549d6b0a63..8047a19a50 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -3,141 +3,118 @@ package smoke import ( "math/big" "testing" + "time" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) +const ( + ErrWatchingNewOCRRound = "Error watching for new OCR round" +) + func TestOCRBasic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.OCR) - if err != nil { - t.Fatal(err) - } + env, ocrInstances := prepareORCv1SmokeTestEnv(t, l, 5) + nodeClients := env.ClCluster.NodeAPIs() + workerNodes := nodeClients[1:] - network, err := actions.EthereumNetworkConfigFromConfig(l, &config) - require.NoError(t, err, "Error building ethereum network config") + err := actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) + require.NoError(t, err, "Error setting all adapter responses to the same value") + err = actions_seth.WatchNewRound(l, env.SethClient, 2, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), time.Duration(3*time.Minute)) + require.NoError(t, err, ErrWatchingNewOCRRound) - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&config). - WithPrivateEthereumNetwork(network). - WithMockAdapter(). - WithCLNodes(6). - WithFunding(big.NewFloat(.5)). - WithStandardCleanup(). - Build() - require.NoError(t, err) + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) +} - env.ParallelTransactions(true) +func TestOCRJobReplacement(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + env, ocrInstances := prepareORCv1SmokeTestEnv(t, l, 5) nodeClients := env.ClCluster.NodeAPIs() bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - linkTokenContract, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + err := actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) + require.NoError(t, err, "Error setting all adapter responses to the same value") + err = actions_seth.WatchNewRound(l, env.SethClient, 2, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), time.Duration(3*time.Minute)) + require.NoError(t, err, ErrWatchingNewOCRRound) - ocrInstances, err := actions.DeployOCRContractsLocal(1, linkTokenContract, env.ContractDeployer, workerNodes, env.EVMClient) - require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, "Error waiting for events") + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) - require.NoError(t, err) + err = actions.DeleteJobs(nodeClients) + require.NoError(t, err, "Error deleting OCR jobs") - err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) - require.NoError(t, err) + err = actions.DeleteBridges(nodeClients) + require.NoError(t, err, "Error deleting OCR bridges") - answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) + //Recreate job + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, big.NewInt(env.SethClient.ChainID)) + require.NoError(t, err, "Error creating OCR jobs") - err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) - require.NoError(t, err) - err = actions.WatchNewRound(2, ocrInstances, env.EVMClient, l) - require.NoError(t, err) + err = actions_seth.WatchNewRound(l, env.SethClient, 1, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), time.Duration(3*time.Minute)) + require.NoError(t, err, ErrWatchingNewOCRRound) answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest OCR answer") + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } -func TestOCRJobReplacement(t *testing.T) { - t.Parallel() - l := logging.GetTestLogger(t) - +func prepareORCv1SmokeTestEnv(t *testing.T, l zerolog.Logger, firstRoundResult int64) (*test_env.CLClusterTestEnv, []contracts.OffchainAggregator) { config, err := tc.GetConfig("Smoke", tc.OCR) if err != nil { t.Fatal(err) } + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(network). WithMockAdapter(). WithCLNodes(6). - WithFunding(big.NewFloat(.1)). + WithFunding(big.NewFloat(.5)). WithStandardCleanup(). + WithSeth(). Build() require.NoError(t, err) - env.ParallelTransactions(true) - nodeClients := env.ClCluster.NodeAPIs() bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] - linkTokenContract, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + linkDeploymentData, err := contracts.DeployLinkTokenContract(env.SethClient) + require.NoError(t, err, "Error deploying link token contract") - ocrInstances, err := actions.DeployOCRContractsLocal(1, linkTokenContract, env.ContractDeployer, workerNodes, env.EVMClient) - require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, "Error waiting for events") + ocrInstances, err := actions_seth.DeployOCRv1Contracts(l, env.SethClient, 1, linkDeploymentData.Address, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(workerNodes)) + require.NoError(t, err, "Error deploying OCR contracts") - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) - require.NoError(t, err) + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, big.NewInt(env.SethClient.ChainID)) + require.NoError(t, err, "Error creating OCR jobs") - err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) - require.NoError(t, err) + err = actions_seth.WatchNewRound(l, env.SethClient, 1, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), time.Duration(3*time.Minute)) + require.NoError(t, err, "Error watching for new OCR round") answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) - - err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) - require.NoError(t, err) - err = actions.WatchNewRound(2, ocrInstances, env.EVMClient, l) - require.NoError(t, err) - - answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Error getting latest OCR answer") - require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) - - err = actions.DeleteJobs(nodeClients) - require.NoError(t, err) - - err = actions.DeleteBridges(nodeClients) - require.NoError(t, err) - - //Recreate job - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) - require.NoError(t, err) - - err = actions.WatchNewRound(1, ocrInstances, env.EVMClient, l) - require.NoError(t, err) - - answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) - require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") - require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) + require.Equal(t, firstRoundResult, answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) + return env, ocrInstances } diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index f7f5c54069..c7443b3a32 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -12,8 +12,10 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" @@ -29,10 +31,13 @@ func TestRunLogBasic(t *testing.T) { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithMockAdapter(). WithCLNodes(1). WithFunding(big.NewFloat(.1)). @@ -46,7 +51,11 @@ func TestRunLogBasic(t *testing.T) { require.NoError(t, err, "Deploying Oracle Contract shouldn't fail") consumer, err := env.ContractDeployer.DeployAPIConsumer(lt.Address()) require.NoError(t, err, "Deploying Consumer Contract shouldn't fail") - err = env.EVMClient.SetDefaultWallet(0) + + network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + err = evmClient.SetDefaultWallet(0) require.NoError(t, err, "Setting default wallet shouldn't fail") err = lt.Transfer(consumer.Address(), big.NewInt(2e18)) require.NoError(t, err, "Transferring %d to consumer contract shouldn't fail", big.NewInt(2e18)) @@ -74,7 +83,7 @@ func TestRunLogBasic(t *testing.T) { Name: fmt.Sprintf("direct-request-%s", uuid.NewString()), MinIncomingConfirmations: "1", ContractAddress: oracle.Address(), - EVMChainID: env.EVMClient.GetChainID().String(), + EVMChainID: evmClient.GetChainID().String(), ExternalJobID: jobUUID.String(), ObservationSource: ost, }) diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index f5aeb86173..b49c785477 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -11,7 +11,9 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv1" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -29,10 +31,13 @@ func TestVRFBasic(t *testing.T) { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithCLNodes(1). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). @@ -40,16 +45,20 @@ func TestVRFBasic(t *testing.T) { require.NoError(t, err) env.ParallelTransactions(true) + network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + lt, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - contracts, err := vrfv1.DeployVRFContracts(env.ContractDeployer, env.EVMClient, lt) + contracts, err := vrfv1.DeployVRFContracts(env.ContractDeployer, evmClient, lt) require.NoError(t, err, "Deploying VRF Contracts shouldn't fail") err = lt.Transfer(contracts.Consumer.Address(), big.NewInt(2e18)) require.NoError(t, err, "Funding consumer contract shouldn't fail") _, err = env.ContractDeployer.DeployVRFContract() require.NoError(t, err, "Deploying VRF contract shouldn't fail") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") for _, n := range env.ClCluster.Nodes { @@ -69,7 +78,7 @@ func TestVRFBasic(t *testing.T) { MinIncomingConfirmations: 1, PublicKey: pubKeyCompressed, ExternalJobID: jobUUID.String(), - EVMChainID: env.EVMClient.GetChainID().String(), + EVMChainID: evmClient.GetChainID().String(), ObservationSource: ost, }) require.NoError(t, err, "Creating VRF Job shouldn't fail") @@ -123,10 +132,13 @@ func TestVRFJobReplacement(t *testing.T) { t.Fatal(err) } + privateNetwork, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + env, err := test_env.NewCLTestEnvBuilder(). WithTestInstance(t). WithTestConfig(&config). - WithGeth(). + WithPrivateEthereumNetwork(privateNetwork). WithCLNodes(1). WithFunding(big.NewFloat(.1)). WithStandardCleanup(). @@ -134,16 +146,20 @@ func TestVRFJobReplacement(t *testing.T) { require.NoError(t, err) env.ParallelTransactions(true) + network := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + lt, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") - contracts, err := vrfv1.DeployVRFContracts(env.ContractDeployer, env.EVMClient, lt) + contracts, err := vrfv1.DeployVRFContracts(env.ContractDeployer, evmClient, lt) require.NoError(t, err, "Deploying VRF Contracts shouldn't fail") err = lt.Transfer(contracts.Consumer.Address(), big.NewInt(2e18)) require.NoError(t, err, "Funding consumer contract shouldn't fail") _, err = env.ContractDeployer.DeployVRFContract() require.NoError(t, err, "Deploying VRF contract shouldn't fail") - err = env.EVMClient.WaitForEvents() + err = evmClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") for _, n := range env.ClCluster.Nodes { @@ -163,7 +179,7 @@ func TestVRFJobReplacement(t *testing.T) { MinIncomingConfirmations: 1, PublicKey: pubKeyCompressed, ExternalJobID: jobUUID.String(), - EVMChainID: env.EVMClient.GetChainID().String(), + EVMChainID: evmClient.GetChainID().String(), ObservationSource: ost, }) require.NoError(t, err, "Creating VRF Job shouldn't fail") @@ -212,7 +228,7 @@ func TestVRFJobReplacement(t *testing.T) { MinIncomingConfirmations: 1, PublicKey: pubKeyCompressed, ExternalJobID: jobUUID.String(), - EVMChainID: env.EVMClient.GetChainID().String(), + EVMChainID: evmClient.GetChainID().String(), ObservationSource: ost, }) require.NoError(t, err, "Recreating VRF Job shouldn't fail") diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index e4ff160064..79f4a9975b 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -1,28 +1,32 @@ package smoke import ( - "context" + "fmt" "math/big" + "strings" + "sync" "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/onsi/gomega" "github.com/stretchr/testify/require" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - "github.com/smartcontractkit/chainlink/integration-tests/actions" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" + "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" ) func TestVRFv2Basic(t *testing.T) { @@ -54,12 +58,18 @@ func TestVRFv2Basic(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err) + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + // register proving key against oracle address (sending key) in order to test oracleWithdraw - defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + defaultWalletAddress := evmClient.GetDefaultWallet().Address() numberOfTxKeysToCreate := 1 - vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2.SetupVRFV2Environment( + vrfv2Contracts, subIDs, vrfv2KeyData, nodesMap, err := vrfv2.SetupVRFV2Environment( env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, &config, useVRFOwner, useTestCoordinator, @@ -75,25 +85,25 @@ func TestVRFv2Basic(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") - vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.CoordinatorV2) t.Run("Request Randomness", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) subBalanceBeforeRequest := subscription.Balance - jobRunsBeforeTest, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2Data.VRFJob.Data.ID) + jobRunsBeforeTest, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) require.NoError(t, err, "error reading job runs") // test and assert randomWordsFulfilledEvent, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, - vrfv2Contracts.LoadTestConsumers[0], - vrfv2Contracts.Coordinator, + vrfv2Contracts.VRFV2Consumer[0], + vrfv2Contracts.CoordinatorV2, subID, - vrfv2Data, + vrfv2KeyData, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -104,16 +114,16 @@ func TestVRFv2Basic(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := subscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) - jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2Data.VRFJob.Data.ID) + jobRuns, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) require.NoError(t, err, "error reading job runs") require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) - status, err := vrfv2Contracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + status, err := vrfv2Contracts.VRFV2Consumer[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -129,11 +139,12 @@ func TestVRFv2Basic(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) wrapperContracts, wrapperSubID, err := vrfv2.SetupVRFV2WrapperEnvironment( env, + networkConfig.ChainID, &configCopy, linkToken, mockETHLinkFeed, - vrfv2Contracts.Coordinator, - vrfv2Data.KeyHash, + vrfv2Contracts.CoordinatorV2, + vrfv2KeyData.KeyHash, 1, ) require.NoError(t, err) @@ -142,7 +153,7 @@ func TestVRFv2Basic(t *testing.T) { wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperConsumer.Address()) require.NoError(t, err, "Error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), *wrapperSubID) + wrapperSubscription, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), *wrapperSubID) require.NoError(t, err, "Error getting subscription information") subBalanceBeforeRequest := wrapperSubscription.Balance @@ -150,9 +161,9 @@ func TestVRFv2Basic(t *testing.T) { randomWordsFulfilledEvent, err := vrfv2.DirectFundingRequestRandomnessAndWaitForFulfillment( l, wrapperConsumer, - vrfv2Contracts.Coordinator, + vrfv2Contracts.CoordinatorV2, *wrapperSubID, - vrfv2Data, + vrfv2KeyData, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -164,7 +175,7 @@ func TestVRFv2Basic(t *testing.T) { // Check wrapper subscription balance expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), *wrapperSubID) + wrapperSubscription, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), *wrapperSubID) require.NoError(t, err, "Error getting subscription information") subBalanceAfterRequest := wrapperSubscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) @@ -205,10 +216,11 @@ func TestVRFv2Basic(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) subIDsForOracleWithDraw, err := vrfv2.CreateFundSubsAndAddConsumers( env, + networkConfig.ChainID, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, - vrfv2Contracts.Coordinator, - vrfv2Contracts.LoadTestConsumers, + vrfv2Contracts.CoordinatorV2, + vrfv2Contracts.VRFV2Consumer, 1, ) require.NoError(t, err) @@ -217,10 +229,10 @@ func TestVRFv2Basic(t *testing.T) { fulfilledEventLink, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, - vrfv2Contracts.LoadTestConsumers[0], - vrfv2Contracts.Coordinator, + vrfv2Contracts.VRFV2Consumer[0], + vrfv2Contracts.CoordinatorV2, subIDForOracleWithdraw, - vrfv2Data, + vrfv2KeyData, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -240,11 +252,11 @@ func TestVRFv2Basic(t *testing.T) { Str("Amount", amountToWithdrawLink.String()). Msg("Invoking Oracle Withdraw for LINK") - err = vrfv2Contracts.Coordinator.OracleWithdraw(common.HexToAddress(defaultWalletAddress), amountToWithdrawLink) + err = vrfv2Contracts.CoordinatorV2.OracleWithdraw(common.HexToAddress(defaultWalletAddress), amountToWithdrawLink) require.NoError(t, err, "Error withdrawing LINK from coordinator to default wallet") - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2.ErrWaitTXsComplete) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) @@ -261,10 +273,11 @@ func TestVRFv2Basic(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) subIDsForCancelling, err := vrfv2.CreateFundSubsAndAddConsumers( env, + networkConfig.ChainID, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, - vrfv2Contracts.Coordinator, - vrfv2Contracts.LoadTestConsumers, + vrfv2Contracts.CoordinatorV2, + vrfv2Contracts.VRFV2Consumer, 1, ) require.NoError(t, err) @@ -276,7 +289,7 @@ func TestVRFv2Basic(t *testing.T) { testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) - subscriptionForCancelling, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -287,13 +300,13 @@ func TestVRFv2Basic(t *testing.T) { Str("Returning funds to", testWalletAddress.String()). Msg("Canceling subscription and returning funds to subscription owner") - tx, err := vrfv2Contracts.Coordinator.CancelSubscription(subIDForCancelling, testWalletAddress) + tx, err := vrfv2Contracts.CoordinatorV2.CancelSubscription(subIDForCancelling, testWalletAddress) require.NoError(t, err, "Error canceling subscription") - subscriptionCanceledEvent, err := vrfv2Contracts.Coordinator.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30) + subscriptionCanceledEvent, err := vrfv2Contracts.CoordinatorV2.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30) require.NoError(t, err, "error waiting for subscription canceled event") - cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + cancellationTxReceipt, err := evmClient.GetTxReceipt(tx.Hash()) require.NoError(t, err, "error getting tx cancellation Tx Receipt") txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) @@ -317,7 +330,7 @@ func TestVRFv2Basic(t *testing.T) { require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + _, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") subFundsReturnedLinkActual := new(big.Int).Sub(testWalletBalanceLinkAfterSubCancelling, testWalletBalanceLinkBeforeSubCancelling) @@ -338,24 +351,25 @@ func TestVRFv2Basic(t *testing.T) { subIDsForCancelling, err := vrfv2.CreateFundSubsAndAddConsumers( env, + networkConfig.ChainID, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, - vrfv2Contracts.Coordinator, - vrfv2Contracts.LoadTestConsumers, + vrfv2Contracts.CoordinatorV2, + vrfv2Contracts.VRFV2Consumer, 1, ) require.NoError(t, err) subIDForCancelling := subIDsForCancelling[0] - subscriptionForCancelling, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "Error getting subscription information") - vrfv2.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2Contracts.CoordinatorV2) // No GetActiveSubscriptionIds function available - skipping check - pendingRequestsExist, err := vrfv2Contracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + pendingRequestsExist, err := vrfv2Contracts.CoordinatorV2.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) require.NoError(t, err) require.False(t, pendingRequestsExist, "Pending requests should not exist") @@ -363,10 +377,10 @@ func TestVRFv2Basic(t *testing.T) { randomWordsFulfilledEventTimeout := 5 * time.Second _, err = vrfv2.RequestRandomnessAndWaitForFulfillment( l, - vrfv2Contracts.LoadTestConsumers[0], - vrfv2Contracts.Coordinator, + vrfv2Contracts.VRFV2Consumer[0], + vrfv2Contracts.CoordinatorV2, subIDForCancelling, - vrfv2Data, + vrfv2KeyData, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -376,14 +390,14 @@ func TestVRFv2Basic(t *testing.T) { ) require.Error(t, err, "Error should occur while waiting for fulfilment due to low sub balance") - pendingRequestsExist, err = vrfv2Contracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + pendingRequestsExist, err = vrfv2Contracts.CoordinatorV2.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) require.NoError(t, err) require.True(t, pendingRequestsExist, "Pending requests should exist after unfilfulled requests due to low sub balance") walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) - subscriptionForCancelling, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "Error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -394,13 +408,13 @@ func TestVRFv2Basic(t *testing.T) { Msg("Canceling subscription and returning funds to subscription owner") // Call OwnerCancelSubscription - tx, err := vrfv2Contracts.Coordinator.OwnerCancelSubscription(subIDForCancelling) + tx, err := vrfv2Contracts.CoordinatorV2.OwnerCancelSubscription(subIDForCancelling) require.NoError(t, err, "Error canceling subscription") - subscriptionCanceledEvent, err := vrfv2Contracts.Coordinator.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30) + subscriptionCanceledEvent, err := vrfv2Contracts.CoordinatorV2.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30) require.NoError(t, err, "error waiting for subscription canceled event") - cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + cancellationTxReceipt, err := evmClient.GetTxReceipt(tx.Hash()) require.NoError(t, err, "error getting tx cancellation Tx Receipt") txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) @@ -424,7 +438,7 @@ func TestVRFv2Basic(t *testing.T) { require.NoError(t, err) // Verify that subscription was deleted from Coordinator contract - _, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + _, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) l.Info(). Str("Expected error message", err.Error()) require.Error(t, err, "Error did not occur when fetching deleted subscription from the Coordinator after owner cancelation") @@ -477,12 +491,18 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err) + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + // register proving key against oracle address (sending key) in order to test oracleWithdraw - defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + defaultWalletAddress := evmClient.GetDefaultWallet().Address() numberOfTxKeysToCreate := 2 - vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2.SetupVRFV2Environment( + vrfv2Contracts, subIDs, vrfv2KeyData, nodesMap, err := vrfv2.SetupVRFV2Environment( env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, &config, useVRFOwner, useTestCoordinator, @@ -498,13 +518,13 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") - vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.CoordinatorV2) t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { - txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") + txKeys, _, err := nodesMap[vrfcommon.VRF].CLNode.API.ReadTxKeys("evm") require.NoError(t, err, "error reading tx keys") require.Equal(t, numberOfTxKeysToCreate+1, len(txKeys.Data)) @@ -513,10 +533,10 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { for i := 0; i < numberOfTxKeysToCreate+1; i++ { randomWordsFulfilledEvent, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, - vrfv2Contracts.LoadTestConsumers[0], - vrfv2Contracts.Coordinator, + vrfv2Contracts.VRFV2Consumer[0], + vrfv2Contracts.CoordinatorV2, subID, - vrfv2Data, + vrfv2KeyData, *config.VRFv2.General.MinimumConfirmations, *config.VRFv2.General.CallbackGasLimit, *config.VRFv2.General.NumberOfWords, @@ -527,8 +547,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") //todo - move TransactionByHash to EVMClient in CTF - fulfillmentTx, _, err := env.EVMClient.(*blockchain.EthereumMultinodeClient).DefaultClient.(*blockchain.EthereumClient). - Client.TransactionByHash(context.Background(), randomWordsFulfilledEvent.Raw.TxHash) + fulfillmentTx, _, err := actions.GetTxByHash(testcontext.Get(t), evmClient, randomWordsFulfilledEvent.Raw.TxHash) require.NoError(t, err, "error getting tx from hash") fulfillmentTxFromAddress, err := actions.GetTxFromAddress(fulfillmentTx) require.NoError(t, err, "error getting tx from address") @@ -537,7 +556,7 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { require.Equal(t, numberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) var txKeyAddresses []string for _, txKey := range txKeys.Data { - txKeyAddresses = append(txKeyAddresses, txKey.ID) + txKeyAddresses = append(txKeyAddresses, txKey.Attributes.Address) } less := func(a, b string) bool { return a < b } equalIgnoreOrder := cmp.Diff(txKeyAddresses, fulfillmentTxFromAddresses, cmpopts.SortSlices(less)) == "" @@ -575,12 +594,18 @@ func TestVRFOwner(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err) + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + // register proving key against oracle address (sending key) in order to test oracleWithdraw - defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + defaultWalletAddress := evmClient.GetDefaultWallet().Address() numberOfTxKeysToCreate := 1 - vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2.SetupVRFV2Environment( + vrfv2Contracts, subIDs, vrfv2Data, _, err := vrfv2.SetupVRFV2Environment( env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, &config, useVRFOwner, useTestCoordinator, @@ -596,44 +621,44 @@ func TestVRFOwner(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") - vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.CoordinatorV2) t.Run("Request Randomness With Force-Fulfill", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - vrfCoordinatorOwner, err := vrfv2Contracts.Coordinator.GetOwner(testcontext.Get(t)) + vrfCoordinatorOwner, err := vrfv2Contracts.CoordinatorV2.GetOwner(testcontext.Get(t)) require.NoError(t, err) require.Equal(t, vrfv2Contracts.VRFOwner.Address(), vrfCoordinatorOwner.String()) err = linkToken.Transfer( - vrfv2Contracts.LoadTestConsumers[0].Address(), - conversions.EtherToWei(big.NewFloat(5)), + vrfv2Contracts.VRFV2Consumer[0].Address(), + conversions.EtherToWei(big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink)), ) require.NoError(t, err, "error transferring link to consumer contract") - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2.ErrWaitTXsComplete) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - consumerLinkBalance, err := linkToken.BalanceOf(testcontext.Get(t), vrfv2Contracts.LoadTestConsumers[0].Address()) + consumerLinkBalance, err := linkToken.BalanceOf(testcontext.Get(t), vrfv2Contracts.VRFV2Consumer[0].Address()) require.NoError(t, err, "error getting consumer link balance") l.Info(). Str("Balance", conversions.WeiToEther(consumerLinkBalance).String()). - Str("Consumer", vrfv2Contracts.LoadTestConsumers[0].Address()). + Str("Consumer", vrfv2Contracts.VRFV2Consumer[0].Address()). Msg("Consumer Link Balance") err = mockETHLinkFeed.SetBlockTimestampDeduction(big.NewInt(3)) require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2.ErrWaitTXsComplete) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) // test and assert _, randFulfilledEvent, _, err := vrfv2.RequestRandomnessWithForceFulfillAndWaitForFulfillment( l, - vrfv2Contracts.LoadTestConsumers[0], - vrfv2Contracts.Coordinator, + vrfv2Contracts.VRFV2Consumer[0], + vrfv2Contracts.CoordinatorV2, vrfv2Contracts.VRFOwner, vrfv2Data, *configCopy.VRFv2.General.MinimumConfirmations, @@ -648,7 +673,7 @@ func TestVRFOwner(t *testing.T) { require.NoError(t, err, "error requesting randomness with force-fulfillment and waiting for fulfilment") require.Equal(t, 0, randFulfilledEvent.Payment.Cmp(big.NewInt(0)), "Forced Fulfilled Randomness's Payment should be 0") - status, err := vrfv2Contracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randFulfilledEvent.RequestId) + status, err := vrfv2Contracts.VRFV2Consumer[0].GetRequestStatus(testcontext.Get(t), randFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -659,13 +684,13 @@ func TestVRFOwner(t *testing.T) { require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } - coordinatorConfig, err := vrfv2Contracts.Coordinator.GetConfig(testcontext.Get(t)) + coordinatorConfig, err := vrfv2Contracts.CoordinatorV2.GetConfig(testcontext.Get(t)) require.NoError(t, err, "error getting coordinator config") - coordinatorFeeConfig, err := vrfv2Contracts.Coordinator.GetFeeConfig(testcontext.Get(t)) + coordinatorFeeConfig, err := vrfv2Contracts.CoordinatorV2.GetFeeConfig(testcontext.Get(t)) require.NoError(t, err, "error getting coordinator fee config") - coordinatorFallbackWeiPerUnitLinkConfig, err := vrfv2Contracts.Coordinator.GetFallbackWeiPerUnitLink(testcontext.Get(t)) + coordinatorFallbackWeiPerUnitLinkConfig, err := vrfv2Contracts.CoordinatorV2.GetFallbackWeiPerUnitLink(testcontext.Get(t)) require.NoError(t, err, "error getting coordinator FallbackWeiPerUnitLink") require.Equal(t, *configCopy.VRFv2.General.StalenessSeconds, coordinatorConfig.StalenessSeconds) @@ -676,3 +701,191 @@ func TestVRFOwner(t *testing.T) { require.Equal(t, *configCopy.VRFv2.General.FallbackWeiPerUnitLink, coordinatorFallbackWeiPerUnitLinkConfig.Int64()) }) } + +func TestVRFV2WithBHS(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + config, err := tc.GetConfig("Smoke", tc.VRFv2) + require.NoError(t, err, "Error getting config") + + useVRFOwner := true + useTestCoordinator := true + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithTestConfig(&config). + WithPrivateEthereumNetwork(network). + WithCLNodes(2). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). + WithStandardCleanup(). + Build() + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := env.ContractDeployer.DeployVRFMockETHLINKFeed(big.NewInt(*config.VRFv2.General.LinkNativeFeedResponse)) + + require.NoError(t, err) + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err) + + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + + // register proving key against oracle address (sending key) in order to test oracleWithdraw + defaultWalletAddress := evmClient.GetDefaultWallet().Address() + + //Underfund Subscription + config.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0.000000000000000001)) // 1 Juel + + //decrease default span for checking blockhashes for unfulfilled requests + config.VRFv2.General.BHSJobWaitBlocks = ptr.Ptr(2) + config.VRFv2.General.BHSJobLookBackBlocks = ptr.Ptr(20) + + numberOfTxKeysToCreate := 0 + vrfv2Contracts, subIDs, vrfv2KeyData, nodesMap, err := vrfv2.SetupVRFV2Environment( + env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF, vrfcommon.BHS}, + &config, + useVRFOwner, + useTestCoordinator, + linkToken, + mockETHLinkFeed, + defaultWalletAddress, + numberOfTxKeysToCreate, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up VRF v2 env") + + subID := subIDs[0] + + subscription, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.CoordinatorV2) + + t.Run("BHS Job with complete E2E - wait 256 blocks to see if Rand Request is fulfilled", func(t *testing.T) { + t.Skip("Skipped since should be run on-demand on live testnet due to long execution time") + //BHS node should fill in blockhashes into BHS contract depending on the waitBlocks and lookBackBlocks settings + configCopy := config.MustCopy().(tc.TestConfig) + _, err := vrfv2Contracts.VRFV2Consumer[0].RequestRandomness( + vrfv2KeyData.KeyHash, + subID, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + ) + require.NoError(t, err, "error requesting randomness") + + randomWordsRequestedEvent, err := vrfv2Contracts.CoordinatorV2.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfv2KeyData.KeyHash}, + []uint64{subID}, + []common.Address{common.HexToAddress(vrfv2Contracts.VRFV2Consumer[0].Address())}, + time.Minute*1, + ) + require.NoError(t, err, "error waiting for randomness requested event") + vrfv2.LogRandomnessRequestedEvent(l, vrfv2Contracts.CoordinatorV2, randomWordsRequestedEvent) + randRequestBlockNumber := randomWordsRequestedEvent.Raw.BlockNumber + var wg sync.WaitGroup + wg.Add(1) + //Wait at least 256 blocks + _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(257), evmClient, &wg, time.Second*260, t) + wg.Wait() + require.NoError(t, err) + err = vrfv2.FundSubscriptions(env, networkConfig.ChainID, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, vrfv2Contracts.CoordinatorV2, subIDs) + require.NoError(t, err, "error funding subscriptions") + randomWordsFulfilledEvent, err := vrfv2Contracts.CoordinatorV2.WaitForRandomWordsFulfilledEvent( + []*big.Int{randomWordsRequestedEvent.RequestId}, + time.Second*30, + ) + require.NoError(t, err, "error waiting for randomness fulfilled event") + vrfv2.LogRandomWordsFulfilledEvent(l, vrfv2Contracts.CoordinatorV2, randomWordsFulfilledEvent) + status, err := vrfv2Contracts.VRFV2Consumer[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, status.Fulfilled) + l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") + }) + + t.Run("BHS Job should fill in blockhashes into BHS contract for unfulfilled requests", func(t *testing.T) { + //BHS node should fill in blockhashes into BHS contract depending on the waitBlocks and lookBackBlocks settings + configCopy := config.MustCopy().(tc.TestConfig) + _, err := vrfv2Contracts.VRFV2Consumer[0].RequestRandomness( + vrfv2KeyData.KeyHash, + subID, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + ) + require.NoError(t, err, "error requesting randomness") + + randomWordsRequestedEvent, err := vrfv2Contracts.CoordinatorV2.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfv2KeyData.KeyHash}, + []uint64{subID}, + []common.Address{common.HexToAddress(vrfv2Contracts.VRFV2Consumer[0].Address())}, + time.Minute*1, + ) + require.NoError(t, err, "error waiting for randomness requested event") + vrfv2.LogRandomnessRequestedEvent(l, vrfv2Contracts.CoordinatorV2, randomWordsRequestedEvent) + randRequestBlockNumber := randomWordsRequestedEvent.Raw.BlockNumber + + _, err = vrfv2Contracts.BHS.GetBlockHash(testcontext.Get(t), big.NewInt(int64(randRequestBlockNumber))) + require.Error(t, err, "error not occurred when getting blockhash for a blocknumber which was not stored in BHS contract") + + var wg sync.WaitGroup + wg.Add(1) + _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(*config.VRFv2.General.BHSJobWaitBlocks), evmClient, &wg, time.Minute*1, t) + wg.Wait() + require.NoError(t, err, "error waiting for blocknumber to be") + + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + metrics, err := vrfv2Contracts.VRFV2Consumer[0].GetLoadTestMetrics(testcontext.Get(t)) + require.Equal(t, 0, metrics.RequestCount.Cmp(big.NewInt(1))) + require.Equal(t, 0, metrics.FulfilmentCount.Cmp(big.NewInt(0))) + + var clNodeTxs *client.TransactionsData + var txHash string + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + clNodeTxs, _, err = nodesMap[vrfcommon.BHS].CLNode.API.ReadTransactions() + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "error getting CL Node transactions") + l.Debug().Int("Number of TXs", len(clNodeTxs.Data)).Msg("BHS Node txs") + g.Expect(len(clNodeTxs.Data)).Should(gomega.BeNumerically("==", 1), "Expected 1 tx posted by BHS Node, but found %d", len(clNodeTxs.Data)) + txHash = clNodeTxs.Data[0].Attributes.Hash + }, "2m", "1s").Should(gomega.Succeed()) + + require.Equal(t, strings.ToLower(vrfv2Contracts.BHS.Address()), strings.ToLower(clNodeTxs.Data[0].Attributes.To)) + + bhsStoreTx, _, err := actions.GetTxByHash(testcontext.Get(t), evmClient, common.HexToHash(txHash)) + require.NoError(t, err, "error getting tx from hash") + + bhsStoreTxInputData, err := actions.DecodeTxInputData(blockhash_store.BlockhashStoreABI, bhsStoreTx.Data()) + l.Info(). + Str("Block Number", bhsStoreTxInputData["n"].(*big.Int).String()). + Msg("BHS Node's Store Blockhash for Blocknumber Method TX") + require.Equal(t, randRequestBlockNumber, bhsStoreTxInputData["n"].(*big.Int).Uint64()) + + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + var randRequestBlockHash [32]byte + gom.Eventually(func(g gomega.Gomega) { + randRequestBlockHash, err = vrfv2Contracts.BHS.GetBlockHash(testcontext.Get(t), big.NewInt(int64(randRequestBlockNumber))) + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "error getting blockhash for a blocknumber which was stored in BHS contract") + }, "2m", "1s").Should(gomega.Succeed()) + l.Info(). + Str("Randomness Request's Blockhash", randomWordsRequestedEvent.Raw.BlockHash.String()). + Str("Block Hash stored by BHS contract", fmt.Sprintf("0x%x", randRequestBlockHash)). + Msg("BHS Contract's stored Blockhash for Randomness Request") + require.Equal(t, 0, randomWordsRequestedEvent.Raw.BlockHash.Cmp(randRequestBlockHash)) + }) +} diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 9309070f64..926efa588b 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -1,28 +1,31 @@ package smoke import ( - "context" "fmt" "math/big" + "strings" + "sync" "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/onsi/gomega" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2plus" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) @@ -56,12 +59,18 @@ func TestVRFv2Plus(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + // default wallet address is used to test Withdraw - defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + defaultWalletAddress := evmClient.GetDefaultWallet().Address() numberOfTxKeysToCreate := 2 - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( + vrfv2PlusContracts, subIDs, vrfv2PlusData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, &config, linkToken, mockETHLinkFeed, @@ -74,23 +83,23 @@ func TestVRFv2Plus(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) + subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) + vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) t.Run("Link Billing", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) var isNativeBilling = false subBalanceBeforeRequest := subscription.Balance - jobRunsBeforeTest, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2PlusData.VRFJob.Data.ID) + jobRunsBeforeTest, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) require.NoError(t, err, "error reading job runs") // test and assert randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.VRFV2PlusConsumer[0], + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, subID, isNativeBilling, @@ -104,17 +113,21 @@ func TestVRFv2Plus(t *testing.T) { ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + require.False(t, randomWordsFulfilledEvent.OnlyPremium, "RandomWordsFulfilled Event's `OnlyPremium` field should be false") + require.Equal(t, isNativeBilling, randomWordsFulfilledEvent.NativePayment, "RandomWordsFulfilled Event's `NativePayment` field should be false") + require.True(t, randomWordsFulfilledEvent.Success, "RandomWordsFulfilled Event's `Success` field should be true") + expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) + subscription, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := subscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) - jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2PlusData.VRFJob.Data.ID) + jobRuns, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) require.NoError(t, err, "error reading job runs") require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) - status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + status, err := vrfv2PlusContracts.VRFV2PlusConsumer[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -125,20 +138,19 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } }) - t.Run("Native Billing", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) testConfig := configCopy.VRFv2Plus.General var isNativeBilling = true subNativeTokenBalanceBeforeRequest := subscription.NativeBalance - jobRunsBeforeTest, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2PlusData.VRFJob.Data.ID) + jobRunsBeforeTest, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) require.NoError(t, err, "error reading job runs") // test and assert randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.VRFV2PlusConsumer[0], + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, subID, isNativeBilling, @@ -151,17 +163,20 @@ func TestVRFv2Plus(t *testing.T) { l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + require.False(t, randomWordsFulfilledEvent.OnlyPremium) + require.Equal(t, isNativeBilling, randomWordsFulfilledEvent.NativePayment) + require.True(t, randomWordsFulfilledEvent.Success) expectedSubBalanceWei := new(big.Int).Sub(subNativeTokenBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) + subscription, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err) subBalanceAfterRequest := subscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) - jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2PlusData.VRFJob.Data.ID) + jobRuns, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) require.NoError(t, err, "error reading job runs") require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) - status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + status, err := vrfv2PlusContracts.VRFV2PlusConsumer[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -176,10 +191,11 @@ func TestVRFv2Plus(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperEnvironment( env, + networkConfig.ChainID, &configCopy, linkToken, mockETHLinkFeed, - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData.KeyHash, 1, ) @@ -193,13 +209,13 @@ func TestVRFv2Plus(t *testing.T) { wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), wrapperSubID) + wrapperSubscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceBeforeRequest := wrapperSubscription.Balance randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( wrapperContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, wrapperSubID, isNativeBilling, @@ -214,7 +230,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), wrapperSubID) + wrapperSubscription, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := wrapperSubscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) @@ -244,16 +260,16 @@ func TestVRFv2Plus(t *testing.T) { testConfig := configCopy.VRFv2Plus.General var isNativeBilling = true - wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) + wrapperConsumerBalanceBeforeRequestWei, err := evmClient.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) require.NoError(t, err, "error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), wrapperSubID) + wrapperSubscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceBeforeRequest := wrapperSubscription.NativeBalance randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( wrapperContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, wrapperSubID, isNativeBilling, @@ -268,7 +284,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceWei := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), wrapperSubID) + wrapperSubscription, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := wrapperSubscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) @@ -279,7 +295,7 @@ func TestVRFv2Plus(t *testing.T) { expectedWrapperConsumerWeiBalance := new(big.Int).Sub(wrapperConsumerBalanceBeforeRequestWei, consumerStatus.Paid) - wrapperConsumerBalanceAfterRequestWei, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) + wrapperConsumerBalanceAfterRequestWei, err := evmClient.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) require.NoError(t, err, "error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerWeiBalance, wrapperConsumerBalanceAfterRequestWei) @@ -298,13 +314,13 @@ func TestVRFv2Plus(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) subIDsForCancelling, err := vrfv2plus.CreateFundSubsAndAddConsumers( env, + networkConfig.ChainID, big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, - vrfv2PlusContracts.Coordinator, - vrfv2PlusContracts.LoadTestConsumers, + vrfv2PlusContracts.CoordinatorV2Plus, + vrfv2PlusContracts.VRFV2PlusConsumer, 1, - vrfv2plus_config.BillingType(*configCopy.GetVRFv2PlusConfig().General.SubscriptionBillingType), ) require.NoError(t, err) subIDForCancelling := subIDsForCancelling[0] @@ -312,13 +328,13 @@ func TestVRFv2Plus(t *testing.T) { testWalletAddress, err := actions.GenerateWallet() require.NoError(t, err) - testWalletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), testWalletAddress) + testWalletBalanceNativeBeforeSubCancelling, err := evmClient.BalanceAt(testcontext.Get(t), testWalletAddress) require.NoError(t, err) testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) - subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -329,13 +345,13 @@ func TestVRFv2Plus(t *testing.T) { Str("Returning funds from SubID", subIDForCancelling.String()). Str("Returning funds to", testWalletAddress.String()). Msg("Canceling subscription and returning funds to subscription owner") - tx, err := vrfv2PlusContracts.Coordinator.CancelSubscription(subIDForCancelling, testWalletAddress) + tx, err := vrfv2PlusContracts.CoordinatorV2Plus.CancelSubscription(subIDForCancelling, testWalletAddress) require.NoError(t, err, "Error canceling subscription") - subscriptionCanceledEvent, err := vrfv2PlusContracts.Coordinator.WaitForSubscriptionCanceledEvent(subIDForCancelling, time.Second*30) + subscriptionCanceledEvent, err := vrfv2PlusContracts.CoordinatorV2Plus.WaitForSubscriptionCanceledEvent(subIDForCancelling, time.Second*30) require.NoError(t, err, "error waiting for subscription canceled event") - cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + cancellationTxReceipt, err := evmClient.GetTxReceipt(tx.Hash()) require.NoError(t, err, "error getting tx cancellation Tx Receipt") txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) @@ -357,14 +373,14 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, subBalanceNative, subscriptionCanceledEvent.AmountNative, "SubscriptionCanceled event native amount is not equal to sub amount while canceling subscription") require.Equal(t, subBalanceLink, subscriptionCanceledEvent.AmountLink, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") - testWalletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), testWalletAddress) + testWalletBalanceNativeAfterSubCancelling, err := evmClient.BalanceAt(testcontext.Get(t), testWalletAddress) require.NoError(t, err) testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") subFundsReturnedNativeActual := new(big.Int).Sub(testWalletBalanceNativeAfterSubCancelling, testWalletBalanceNativeBeforeSubCancelling) @@ -398,36 +414,36 @@ func TestVRFv2Plus(t *testing.T) { subIDsForCancelling, err := vrfv2plus.CreateFundSubsAndAddConsumers( env, + networkConfig.ChainID, big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, - vrfv2PlusContracts.Coordinator, - vrfv2PlusContracts.LoadTestConsumers, + vrfv2PlusContracts.CoordinatorV2Plus, + vrfv2PlusContracts.VRFV2PlusConsumer, 1, - vrfv2plus_config.BillingType(*configCopy.GetVRFv2PlusConfig().General.SubscriptionBillingType), ) require.NoError(t, err) subIDForCancelling := subIDsForCancelling[0] - subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") - vrfv2plus.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2PlusContracts.Coordinator) + vrfv2plus.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2PlusContracts.CoordinatorV2Plus) - activeSubscriptionIdsBeforeSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + activeSubscriptionIdsBeforeSubCancellation, err := vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err) require.True(t, it_utils.BigIntSliceContains(activeSubscriptionIdsBeforeSubCancellation, subIDForCancelling)) - pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + pendingRequestsExist, err := vrfv2PlusContracts.CoordinatorV2Plus.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) require.NoError(t, err) require.False(t, pendingRequestsExist, "Pending requests should not exist") randomWordsFulfilledEventTimeout := 5 * time.Second _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.VRFV2PlusConsumer[0], + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, subIDForCancelling, false, @@ -443,8 +459,8 @@ func TestVRFv2Plus(t *testing.T) { require.Error(t, err, "error should occur for waiting for fulfilment due to low sub balance") _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.VRFV2PlusConsumer[0], + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, subIDForCancelling, true, @@ -459,17 +475,17 @@ func TestVRFv2Plus(t *testing.T) { require.Error(t, err, "error should occur for waiting for fulfilment due to low sub balance") - pendingRequestsExist, err = vrfv2PlusContracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + pendingRequestsExist, err = vrfv2PlusContracts.CoordinatorV2Plus.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) require.NoError(t, err) require.True(t, pendingRequestsExist, "Pending requests should exist after unfulfilled rand requests due to low sub balance") - walletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) + walletBalanceNativeBeforeSubCancelling, err := evmClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) - subscriptionForCancelling, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -480,13 +496,13 @@ func TestVRFv2Plus(t *testing.T) { Str("Returning funds from SubID", subIDForCancelling.String()). Str("Returning funds to", defaultWalletAddress). Msg("Canceling subscription and returning funds to subscription owner") - tx, err := vrfv2PlusContracts.Coordinator.OwnerCancelSubscription(subIDForCancelling) + tx, err := vrfv2PlusContracts.CoordinatorV2Plus.OwnerCancelSubscription(subIDForCancelling) require.NoError(t, err, "Error canceling subscription") - subscriptionCanceledEvent, err := vrfv2PlusContracts.Coordinator.WaitForSubscriptionCanceledEvent(subIDForCancelling, time.Second*30) + subscriptionCanceledEvent, err := vrfv2PlusContracts.CoordinatorV2Plus.WaitForSubscriptionCanceledEvent(subIDForCancelling, time.Second*30) require.NoError(t, err, "error waiting for subscription canceled event") - cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + cancellationTxReceipt, err := evmClient.GetTxReceipt(tx.Hash()) require.NoError(t, err, "error getting tx cancellation Tx Receipt") txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) @@ -508,15 +524,14 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, subBalanceNative, subscriptionCanceledEvent.AmountNative, "SubscriptionCanceled event native amount is not equal to sub amount while canceling subscription") require.Equal(t, subBalanceLink, subscriptionCanceledEvent.AmountLink, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") - walletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) + walletBalanceNativeAfterSubCancelling, err := evmClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) - fmt.Println("err", err) + _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") subFundsReturnedNativeActual := new(big.Int).Sub(walletBalanceNativeAfterSubCancelling, walletBalanceNativeBeforeSubCancelling) @@ -544,7 +559,7 @@ func TestVRFv2Plus(t *testing.T) { //require.Equal(t, subFundsReturnedNativeExpected, subFundsReturnedNativeActual, "Returned funds are not equal to sub balance that was cancelled") require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled") - activeSubscriptionIdsAfterSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + activeSubscriptionIdsAfterSubCancellation, err := vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err, "error getting active subscription ids") require.False( @@ -553,25 +568,24 @@ func TestVRFv2Plus(t *testing.T) { "Active subscription ids should not contain sub id after sub cancellation", ) }) - t.Run("Owner Withdraw", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) subIDsForWithdraw, err := vrfv2plus.CreateFundSubsAndAddConsumers( env, + networkConfig.ChainID, big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), linkToken, - vrfv2PlusContracts.Coordinator, - vrfv2PlusContracts.LoadTestConsumers, + vrfv2PlusContracts.CoordinatorV2Plus, + vrfv2PlusContracts.VRFV2PlusConsumer, 1, - vrfv2plus_config.BillingType(*configCopy.GetVRFv2PlusConfig().General.SubscriptionBillingType), ) require.NoError(t, err) subIDForWithdraw := subIDsForWithdraw[0] fulfilledEventLink, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.VRFV2PlusConsumer[0], + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, subIDForWithdraw, false, @@ -586,8 +600,8 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err) fulfilledEventNative, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.VRFV2PlusConsumer[0], + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, subIDForWithdraw, true, @@ -602,7 +616,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err) amountToWithdrawLink := fulfilledEventLink.Payment - defaultWalletBalanceNativeBeforeWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) + defaultWalletBalanceNativeBeforeWithdraw, err := evmClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) defaultWalletBalanceLinkBeforeWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) @@ -613,7 +627,7 @@ func TestVRFv2Plus(t *testing.T) { Str("Amount", amountToWithdrawLink.String()). Msg("Invoking Oracle Withdraw for LINK") - err = vrfv2PlusContracts.Coordinator.Withdraw( + err = vrfv2PlusContracts.CoordinatorV2Plus.Withdraw( common.HexToAddress(defaultWalletAddress), ) require.NoError(t, err, "error withdrawing LINK from coordinator to default wallet") @@ -624,15 +638,15 @@ func TestVRFv2Plus(t *testing.T) { Str("Amount", amountToWithdrawNative.String()). Msg("Invoking Oracle Withdraw for Native") - err = vrfv2PlusContracts.Coordinator.WithdrawNative( + err = vrfv2PlusContracts.CoordinatorV2Plus.WithdrawNative( common.HexToAddress(defaultWalletAddress), ) require.NoError(t, err, "error withdrawing Native tokens from coordinator to default wallet") - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - defaultWalletBalanceNativeAfterWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) + defaultWalletBalanceNativeAfterWithdraw, err := evmClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) defaultWalletBalanceLinkAfterWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) @@ -668,6 +682,10 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { env.ParallelTransactions(true) + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*config.VRFv2Plus.General.LinkNativeFeedResponse)) require.NoError(t, err, "error deploying mock ETH/LINK feed") @@ -675,8 +693,10 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { require.NoError(t, err, "error deploying LINK contract") numberOfTxKeysToCreate := 2 - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( + vrfv2PlusContracts, subIDs, vrfv2PlusData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, &config, linkToken, mockETHLinkFeed, @@ -689,15 +709,15 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) + subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) + vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - var isNativeBilling = false - txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") + var isNativeBilling = true + txKeys, _, err := nodesMap[vrfcommon.VRF].CLNode.API.ReadTxKeys("evm") require.NoError(t, err, "error reading tx keys") require.Equal(t, numberOfTxKeysToCreate+1, len(txKeys.Data)) @@ -705,8 +725,8 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { var fulfillmentTxFromAddresses []string for i := 0; i < numberOfTxKeysToCreate+1; i++ { randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.LoadTestConsumers[0], - vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.VRFV2PlusConsumer[0], + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, subID, isNativeBilling, @@ -721,8 +741,7 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") //todo - move TransactionByHash to EVMClient in CTF - fulfillmentTx, _, err := env.EVMClient.(*blockchain.EthereumMultinodeClient).DefaultClient.(*blockchain.EthereumClient). - Client.TransactionByHash(context.Background(), randomWordsFulfilledEvent.Raw.TxHash) + fulfillmentTx, _, err := actions.GetTxByHash(testcontext.Get(t), evmClient, randomWordsFulfilledEvent.Raw.TxHash) require.NoError(t, err, "error getting tx from hash") fulfillmentTxFromAddress, err := actions.GetTxFromAddress(fulfillmentTx) require.NoError(t, err, "error getting tx from address") @@ -731,7 +750,7 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { require.Equal(t, numberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) var txKeyAddresses []string for _, txKey := range txKeys.Data { - txKeyAddresses = append(txKeyAddresses, txKey.ID) + txKeyAddresses = append(txKeyAddresses, txKey.Attributes.Address) } less := func(a, b string) bool { return a < b } equalIgnoreOrder := cmp.Diff(txKeyAddresses, fulfillmentTxFromAddresses, cmpopts.SortSlices(less)) == "" @@ -762,14 +781,20 @@ func TestVRFv2PlusMigration(t *testing.T) { require.NoError(t, err, "error creating test env") env.ParallelTransactions(true) + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + mockETHLinkFeedAddress, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*config.VRFv2Plus.General.LinkNativeFeedResponse)) require.NoError(t, err, "error deploying mock ETH/LINK feed") linkAddress, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( + vrfv2PlusContracts, subIDs, vrfv2PlusData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, &config, linkAddress, mockETHLinkFeedAddress, @@ -780,174 +805,679 @@ func TestVRFv2PlusMigration(t *testing.T) { ) require.NoError(t, err, "error setting up VRF v2_5 env") - subID := subIDs[0] + // Migrate subscription from old coordinator to new coordinator, verify if balances + // are moved correctly and requests can be made successfully in the subscription in + // new coordinator + t.Run("Test migration of Subscription Billing subID", func(t *testing.T) { + subID := subIDs[0] - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) - require.NoError(t, err, "error getting subscription information") + subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) + vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) - activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) - require.NoError(t, err, "error occurred getting active sub ids") - require.Len(t, activeSubIdsOldCoordinatorBeforeMigration, 1, "Active Sub Ids length is not equal to 1") - require.Equal(t, subID, activeSubIdsOldCoordinatorBeforeMigration[0]) + activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + require.NoError(t, err, "error occurred getting active sub ids") + require.Len(t, activeSubIdsOldCoordinatorBeforeMigration, 1, "Active Sub Ids length is not equal to 1") + require.Equal(t, subID, activeSubIdsOldCoordinatorBeforeMigration[0]) - oldSubscriptionBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) - require.NoError(t, err, "error getting subscription information") + oldSubscriptionBeforeMigration, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") - //Migration Process - newCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2PlusUpgradedVersion(vrfv2PlusContracts.BHS.Address()) - require.NoError(t, err, "error deploying VRF CoordinatorV2PlusUpgradedVersion") - - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) - - _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, newCoordinator) - require.NoError(t, err, fmt.Errorf("%s, err: %w", vrfv2plus.ErrRegisteringProvingKey, err)) - - vrfv2PlusConfig := config.VRFv2Plus.General - err = newCoordinator.SetConfig( - *vrfv2PlusConfig.MinimumConfirmations, - *vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, - *vrfv2PlusConfig.StalenessSeconds, - *vrfv2PlusConfig.GasAfterPaymentCalculation, - big.NewInt(*vrfv2PlusConfig.LinkNativeFeedResponse), - vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionFeeConfig{ - FulfillmentFlatFeeLinkPPM: *vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, - FulfillmentFlatFeeNativePPM: *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, - }, - ) - require.NoError(t, err) + //Migration Process + newCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2PlusUpgradedVersion(vrfv2PlusContracts.BHS.Address()) + require.NoError(t, err, "error deploying VRF CoordinatorV2PlusUpgradedVersion") + + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, newCoordinator) + require.NoError(t, err, fmt.Errorf("%s, err: %w", vrfcommon.ErrRegisteringProvingKey, err)) + + vrfv2PlusConfig := config.VRFv2Plus.General + err = newCoordinator.SetConfig( + *vrfv2PlusConfig.MinimumConfirmations, + *vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, + *vrfv2PlusConfig.StalenessSeconds, + *vrfv2PlusConfig.GasAfterPaymentCalculation, + big.NewInt(*vrfv2PlusConfig.LinkNativeFeedResponse), + *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, + *vrfv2PlusConfig.FulfillmentFlatFeeLinkDiscountPPM, + *vrfv2PlusConfig.NativePremiumPercentage, + *vrfv2PlusConfig.LinkPremiumPercentage, + ) + require.NoError(t, err) - err = newCoordinator.SetLINKAndLINKNativeFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address()) - require.NoError(t, err, vrfv2plus.ErrSetLinkNativeLinkFeed) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) - - vrfJobSpecConfig := vrfv2plus.VRFJobSpecConfig{ - ForwardingAllowed: false, - CoordinatorAddress: newCoordinator.Address(), - FromAddresses: []string{vrfv2PlusData.PrimaryEthAddress}, - EVMChainID: vrfv2PlusData.ChainID.String(), - MinIncomingConfirmations: int(*vrfv2PlusConfig.MinimumConfirmations), - PublicKey: vrfv2PlusData.VRFKey.Data.ID, - EstimateGasMultiplier: 1, - BatchFulfillmentEnabled: false, - BatchFulfillmentGasMultiplier: 1.15, - PollPeriod: time.Second * 1, - RequestTimeout: time.Hour * 24, - } + err = newCoordinator.SetLINKAndLINKNativeFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address()) + require.NoError(t, err, vrfv2plus.ErrSetLinkNativeLinkFeed) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ + ForwardingAllowed: *vrfv2PlusConfig.VRFJobForwardingAllowed, + CoordinatorAddress: newCoordinator.Address(), + FromAddresses: nodesMap[vrfcommon.VRF].TXKeyAddressStrings, + EVMChainID: evmClient.GetChainID().String(), + MinIncomingConfirmations: int(*vrfv2PlusConfig.MinimumConfirmations), + PublicKey: vrfv2PlusData.VRFKey.Data.ID, + EstimateGasMultiplier: *vrfv2PlusConfig.VRFJobEstimateGasMultiplier, + BatchFulfillmentEnabled: *vrfv2PlusConfig.VRFJobBatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *vrfv2PlusConfig.VRFJobBatchFulfillmentGasMultiplier, + PollPeriod: vrfv2PlusConfig.VRFJobPollPeriod.Duration, + RequestTimeout: vrfv2PlusConfig.VRFJobRequestTimeout.Duration, + } - _, err = vrfv2plus.CreateVRFV2PlusJob( - env.ClCluster.NodeAPIs()[0], - vrfJobSpecConfig, - ) - require.NoError(t, err, vrfv2plus.ErrCreateVRFV2PlusJobs) + _, err = vrfv2plus.CreateVRFV2PlusJob( + nodesMap[vrfcommon.VRF].CLNode.API, + vrfJobSpecConfig, + ) + require.NoError(t, err, vrfv2plus.ErrCreateVRFV2PlusJobs) - err = vrfv2PlusContracts.Coordinator.RegisterMigratableCoordinator(newCoordinator.Address()) - require.NoError(t, err, "error registering migratable coordinator") + err = vrfv2PlusContracts.CoordinatorV2Plus.RegisterMigratableCoordinator(newCoordinator.Address()) + require.NoError(t, err, "error registering migratable coordinator") - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - oldCoordinatorLinkTotalBalanceBeforeMigration, oldCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.Coordinator) - require.NoError(t, err) + oldCoordinatorLinkTotalBalanceBeforeMigration, oldCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.CoordinatorV2Plus) + require.NoError(t, err) - migratedCoordinatorLinkTotalBalanceBeforeMigration, migratedCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) - require.NoError(t, err) + migratedCoordinatorLinkTotalBalanceBeforeMigration, migratedCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) + require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - err = vrfv2PlusContracts.Coordinator.Migrate(subID, newCoordinator.Address()) - require.NoError(t, err, "error migrating sub id ", subID.String(), " from ", vrfv2PlusContracts.Coordinator.Address(), " to new Coordinator address ", newCoordinator.Address()) - migrationCompletedEvent, err := vrfv2PlusContracts.Coordinator.WaitForMigrationCompletedEvent(time.Minute * 1) - require.NoError(t, err, "error waiting for MigrationCompleted event") - err = env.EVMClient.WaitForEvents() - require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) + err = vrfv2PlusContracts.CoordinatorV2Plus.Migrate(subID, newCoordinator.Address()) - vrfv2plus.LogMigrationCompletedEvent(l, migrationCompletedEvent, vrfv2PlusContracts) + require.NoError(t, err, "error migrating sub id ", subID.String(), " from ", vrfv2PlusContracts.CoordinatorV2Plus.Address(), " to new Coordinator address ", newCoordinator.Address()) + migrationCompletedEvent, err := vrfv2PlusContracts.CoordinatorV2Plus.WaitForMigrationCompletedEvent(time.Minute * 1) + require.NoError(t, err, "error waiting for MigrationCompleted event") + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - oldCoordinatorLinkTotalBalanceAfterMigration, oldCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.Coordinator) - require.NoError(t, err) + vrfv2plus.LogMigrationCompletedEvent(l, migrationCompletedEvent, vrfv2PlusContracts) - migratedCoordinatorLinkTotalBalanceAfterMigration, migratedCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) - require.NoError(t, err) + oldCoordinatorLinkTotalBalanceAfterMigration, oldCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.CoordinatorV2Plus) + require.NoError(t, err) - migratedSubscription, err := newCoordinator.GetSubscription(testcontext.Get(t), subID) - require.NoError(t, err, "error getting subscription information") + migratedCoordinatorLinkTotalBalanceAfterMigration, migratedCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) + require.NoError(t, err) + + migratedSubscription, err := newCoordinator.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2plus.LogSubDetailsAfterMigration(l, newCoordinator, subID, migratedSubscription) + + //Verify that Coordinators were updated in Consumers + for _, consumer := range vrfv2PlusContracts.VRFV2PlusConsumer { + coordinatorAddressInConsumerAfterMigration, err := consumer.GetCoordinator(testcontext.Get(t)) + require.NoError(t, err, "error getting Coordinator from Consumer contract") + require.Equal(t, newCoordinator.Address(), coordinatorAddressInConsumerAfterMigration.String()) + l.Debug(). + Str("Consumer", consumer.Address()). + Str("Coordinator", coordinatorAddressInConsumerAfterMigration.String()). + Msg("Coordinator Address in Consumer After Migration") + } + + //Verify old and migrated subs + require.Equal(t, oldSubscriptionBeforeMigration.NativeBalance, migratedSubscription.NativeBalance) + require.Equal(t, oldSubscriptionBeforeMigration.Balance, migratedSubscription.Balance) + require.Equal(t, oldSubscriptionBeforeMigration.Owner, migratedSubscription.Owner) + require.Equal(t, oldSubscriptionBeforeMigration.Consumers, migratedSubscription.Consumers) + + //Verify that old sub was deleted from old Coordinator + _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") + + _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + // If (subscription billing), numActiveSub should be 0 after migration in oldCoordinator + require.Error(t, err, "error not occurred getting active sub ids. Should occur since it should revert when sub id array is empty") + + activeSubIdsMigratedCoordinator, err := newCoordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + require.NoError(t, err, "error occurred getting active sub ids") + require.Len(t, activeSubIdsMigratedCoordinator, 1, "Active Sub Ids length is not equal to 1 for Migrated Coordinator after migration") + require.Equal(t, subID, activeSubIdsMigratedCoordinator[0]) + + //Verify that total balances changed for Link and Eth for new and old coordinator + expectedLinkTotalBalanceForMigratedCoordinator := new(big.Int).Add(oldSubscriptionBeforeMigration.Balance, migratedCoordinatorLinkTotalBalanceBeforeMigration) + expectedEthTotalBalanceForMigratedCoordinator := new(big.Int).Add(oldSubscriptionBeforeMigration.NativeBalance, migratedCoordinatorEthTotalBalanceBeforeMigration) + + expectedLinkTotalBalanceForOldCoordinator := new(big.Int).Sub(oldCoordinatorLinkTotalBalanceBeforeMigration, oldSubscriptionBeforeMigration.Balance) + expectedEthTotalBalanceForOldCoordinator := new(big.Int).Sub(oldCoordinatorEthTotalBalanceBeforeMigration, oldSubscriptionBeforeMigration.NativeBalance) + require.Equal(t, 0, expectedLinkTotalBalanceForMigratedCoordinator.Cmp(migratedCoordinatorLinkTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedEthTotalBalanceForMigratedCoordinator.Cmp(migratedCoordinatorEthTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedLinkTotalBalanceForOldCoordinator.Cmp(oldCoordinatorLinkTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedEthTotalBalanceForOldCoordinator.Cmp(oldCoordinatorEthTotalBalanceAfterMigration)) + + //Verify rand requests fulfills with Link Token billing + _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillmentUpgraded( + vrfv2PlusContracts.VRFV2PlusConsumer[0], + newCoordinator, + vrfv2PlusData, + subID, + false, + *config.VRFv2Plus.General.MinimumConfirmations, + *config.VRFv2Plus.General.CallbackGasLimit, + *config.VRFv2Plus.General.NumberOfWords, + *config.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *config.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + config.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + + //Verify rand requests fulfills with Native Token billing + _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillmentUpgraded( + vrfv2PlusContracts.VRFV2PlusConsumer[1], + newCoordinator, + vrfv2PlusData, + subID, + true, + *config.VRFv2Plus.General.MinimumConfirmations, + *config.VRFv2Plus.General.CallbackGasLimit, + *config.VRFv2Plus.General.NumberOfWords, + *config.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *config.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + config.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + }) + + // Migrate wrapper subscription from old coordinator to new coordinator, verify if balances + // are moved correctly and requests can be made successfully in the subscription in + // new coordinator + t.Run("Test migration of direct billing using VRFV2PlusWrapper subID", func(t *testing.T) { + configCopy := config.MustCopy().(tc.TestConfig) + wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperEnvironment( + env, + networkConfig.ChainID, + &configCopy, + linkAddress, + mockETHLinkFeedAddress, + vrfv2PlusContracts.CoordinatorV2Plus, + vrfv2PlusData.KeyHash, + 1, + ) + require.NoError(t, err) + subID := wrapperSubID + + subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) + + activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + require.NoError(t, err, "error occurred getting active sub ids") + require.Len(t, activeSubIdsOldCoordinatorBeforeMigration, 1, "Active Sub Ids length is not equal to 1") + activeSubID := activeSubIdsOldCoordinatorBeforeMigration[0] + require.Equal(t, subID, activeSubID) + + oldSubscriptionBeforeMigration, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + //Migration Process + newCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2PlusUpgradedVersion(vrfv2PlusContracts.BHS.Address()) + require.NoError(t, err, "error deploying VRF CoordinatorV2PlusUpgradedVersion") + + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, newCoordinator) + require.NoError(t, err, fmt.Errorf("%s, err: %w", vrfcommon.ErrRegisteringProvingKey, err)) + + vrfv2PlusConfig := config.VRFv2Plus.General + err = newCoordinator.SetConfig( + *vrfv2PlusConfig.MinimumConfirmations, + *vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, + *vrfv2PlusConfig.StalenessSeconds, + *vrfv2PlusConfig.GasAfterPaymentCalculation, + big.NewInt(*vrfv2PlusConfig.LinkNativeFeedResponse), + *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, + *vrfv2PlusConfig.FulfillmentFlatFeeLinkDiscountPPM, + *vrfv2PlusConfig.NativePremiumPercentage, + *vrfv2PlusConfig.LinkPremiumPercentage, + ) + require.NoError(t, err) + + err = newCoordinator.SetLINKAndLINKNativeFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address()) + require.NoError(t, err, vrfv2plus.ErrSetLinkNativeLinkFeed) + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ + ForwardingAllowed: *vrfv2PlusConfig.VRFJobForwardingAllowed, + CoordinatorAddress: newCoordinator.Address(), + FromAddresses: nodesMap[vrfcommon.VRF].TXKeyAddressStrings, + EVMChainID: evmClient.GetChainID().String(), + MinIncomingConfirmations: int(*vrfv2PlusConfig.MinimumConfirmations), + PublicKey: vrfv2PlusData.VRFKey.Data.ID, + EstimateGasMultiplier: *vrfv2PlusConfig.VRFJobEstimateGasMultiplier, + BatchFulfillmentEnabled: *vrfv2PlusConfig.VRFJobBatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *vrfv2PlusConfig.VRFJobBatchFulfillmentGasMultiplier, + PollPeriod: vrfv2PlusConfig.VRFJobPollPeriod.Duration, + RequestTimeout: vrfv2PlusConfig.VRFJobRequestTimeout.Duration, + } + + _, err = vrfv2plus.CreateVRFV2PlusJob( + nodesMap[vrfcommon.VRF].CLNode.API, + vrfJobSpecConfig, + ) + require.NoError(t, err, vrfv2plus.ErrCreateVRFV2PlusJobs) + + err = vrfv2PlusContracts.CoordinatorV2Plus.RegisterMigratableCoordinator(newCoordinator.Address()) + require.NoError(t, err, "error registering migratable coordinator") + + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + oldCoordinatorLinkTotalBalanceBeforeMigration, oldCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.CoordinatorV2Plus) + require.NoError(t, err) + + migratedCoordinatorLinkTotalBalanceBeforeMigration, migratedCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) + require.NoError(t, err) + + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + // Migrate sub using VRFV2PlusWrapper's migrate method + err = wrapperContracts.VRFV2PlusWrapper.Migrate(common.HexToAddress(newCoordinator.Address())) + + require.NoError(t, err, "error migrating sub id ", subID.String(), " from ", vrfv2PlusContracts.CoordinatorV2Plus.Address(), " to new Coordinator address ", newCoordinator.Address()) + migrationCompletedEvent, err := vrfv2PlusContracts.CoordinatorV2Plus.WaitForMigrationCompletedEvent(time.Minute * 1) + require.NoError(t, err, "error waiting for MigrationCompleted event") + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - vrfv2plus.LogSubDetailsAfterMigration(l, newCoordinator, subID, migratedSubscription) + vrfv2plus.LogMigrationCompletedEvent(l, migrationCompletedEvent, vrfv2PlusContracts) - //Verify that Coordinators were updated in Consumers - for _, consumer := range vrfv2PlusContracts.LoadTestConsumers { - coordinatorAddressInConsumerAfterMigration, err := consumer.GetCoordinator(testcontext.Get(t)) - require.NoError(t, err, "error getting Coordinator from Consumer contract") + oldCoordinatorLinkTotalBalanceAfterMigration, oldCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.CoordinatorV2Plus) + require.NoError(t, err) + + migratedCoordinatorLinkTotalBalanceAfterMigration, migratedCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) + require.NoError(t, err) + + migratedSubscription, err := newCoordinator.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2plus.LogSubDetailsAfterMigration(l, newCoordinator, subID, migratedSubscription) + + // Verify that Coordinators were updated in Consumers- Consumer in this case is the VRFV2PlusWrapper + coordinatorAddressInConsumerAfterMigration, err := wrapperContracts.VRFV2PlusWrapper.Coordinator(testcontext.Get(t)) + require.NoError(t, err, "error getting Coordinator from Consumer contract- VRFV2PlusWrapper") require.Equal(t, newCoordinator.Address(), coordinatorAddressInConsumerAfterMigration.String()) l.Debug(). - Str("Consumer", consumer.Address()). + Str("Consumer-VRFV2PlusWrapper", wrapperContracts.VRFV2PlusWrapper.Address()). Str("Coordinator", coordinatorAddressInConsumerAfterMigration.String()). - Msg("Coordinator Address in Consumer After Migration") + Msg("Coordinator Address in VRFV2PlusWrapper After Migration") + + //Verify old and migrated subs + require.Equal(t, oldSubscriptionBeforeMigration.NativeBalance, migratedSubscription.NativeBalance) + require.Equal(t, oldSubscriptionBeforeMigration.Balance, migratedSubscription.Balance) + require.Equal(t, oldSubscriptionBeforeMigration.Owner, migratedSubscription.Owner) + require.Equal(t, oldSubscriptionBeforeMigration.Consumers, migratedSubscription.Consumers) + + //Verify that old sub was deleted from old Coordinator + _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") + + _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + // If (subscription billing) or (direct billing and numActiveSubs is 0 before this test) -> numActiveSub should be 0 after migration in oldCoordinator + require.Error(t, err, "error not occurred getting active sub ids. Should occur since it should revert when sub id array is empty") + + activeSubIdsMigratedCoordinator, err := newCoordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + require.NoError(t, err, "error occurred getting active sub ids") + require.Len(t, activeSubIdsMigratedCoordinator, 1, "Active Sub Ids length is not equal to 1 for Migrated Coordinator after migration") + require.Equal(t, subID, activeSubIdsMigratedCoordinator[0]) + + //Verify that total balances changed for Link and Eth for new and old coordinator + expectedLinkTotalBalanceForMigratedCoordinator := new(big.Int).Add(oldSubscriptionBeforeMigration.Balance, migratedCoordinatorLinkTotalBalanceBeforeMigration) + expectedEthTotalBalanceForMigratedCoordinator := new(big.Int).Add(oldSubscriptionBeforeMigration.NativeBalance, migratedCoordinatorEthTotalBalanceBeforeMigration) + + expectedLinkTotalBalanceForOldCoordinator := new(big.Int).Sub(oldCoordinatorLinkTotalBalanceBeforeMigration, oldSubscriptionBeforeMigration.Balance) + expectedEthTotalBalanceForOldCoordinator := new(big.Int).Sub(oldCoordinatorEthTotalBalanceBeforeMigration, oldSubscriptionBeforeMigration.NativeBalance) + require.Equal(t, 0, expectedLinkTotalBalanceForMigratedCoordinator.Cmp(migratedCoordinatorLinkTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedEthTotalBalanceForMigratedCoordinator.Cmp(migratedCoordinatorEthTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedLinkTotalBalanceForOldCoordinator.Cmp(oldCoordinatorLinkTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedEthTotalBalanceForOldCoordinator.Cmp(oldCoordinatorEthTotalBalanceAfterMigration)) + + // Verify rand requests fulfills with Link Token billing + isNativeBilling := false + randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillmentUpgraded( + wrapperContracts.LoadTestConsumers[0], + newCoordinator, + vrfv2PlusData, + subID, + isNativeBilling, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, consumerStatus.Fulfilled) + + // Verify rand requests fulfills with Native Token billing + isNativeBilling = true + randomWordsFulfilledEvent, err = vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillmentUpgraded( + wrapperContracts.LoadTestConsumers[0], + newCoordinator, + vrfv2PlusData, + subID, + isNativeBilling, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + consumerStatus, err = wrapperContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, consumerStatus.Fulfilled) + }) +} + +func TestVRFV2PlusWithBHS(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + require.NoError(t, err, "Error getting config") + + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithTestConfig(&config). + WithPrivateEthereumNetwork(network). + WithCLNodes(2). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). + WithStandardCleanup(). + Build() + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + evmClient, err := env.GetEVMClient(networkConfig.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + + mockETHLinkFeed, err := env.ContractDeployer.DeployVRFMockETHLINKFeed(big.NewInt(*config.VRFv2Plus.General.LinkNativeFeedResponse)) + + require.NoError(t, err) + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err) + + //Underfund Subscription + config.VRFv2Plus.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0.000000000000000001)) // 1 Juel + + //decrease default span for checking blockhashes for unfulfilled requests + config.VRFv2Plus.General.BHSJobWaitBlocks = ptr.Ptr(2) + config.VRFv2Plus.General.BHSJobLookBackBlocks = ptr.Ptr(20) + + numberOfTxKeysToCreate := 0 + vrfContracts, subIDs, vrfKeyData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( + env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF, vrfcommon.BHS}, + &config, + linkToken, + mockETHLinkFeed, + numberOfTxKeysToCreate, + 1, + 2, + l, + ) + require.NoError(t, err, "error setting up VRF v2_5 env") + + var isNativeBilling = true + t.Run("BHS Job with complete E2E - wait 256 blocks to see if Rand Request is fulfilled", func(t *testing.T) { + t.Skip("Skipped since should be run on-demand on live testnet due to long execution time") + + subID := subIDs[0] + + subscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2plus.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2Plus) + + //BHS node should fill in blockhashes into BHS contract depending on the waitBlocks and lookBackBlocks settings + configCopy := config.MustCopy().(tc.TestConfig) + _, err = vrfContracts.VRFV2PlusConsumer[0].RequestRandomness( + vrfKeyData.KeyHash, + subID, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + isNativeBilling, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + ) + require.NoError(t, err, "error requesting randomness") + + randomWordsRequestedEvent, err := vrfContracts.CoordinatorV2Plus.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfKeyData.KeyHash}, + []*big.Int{subID}, + []common.Address{common.HexToAddress(vrfContracts.VRFV2PlusConsumer[0].Address())}, + time.Minute*1, + ) + require.NoError(t, err, "error waiting for randomness requested event") + vrfv2plus.LogRandomnessRequestedEvent(l, vrfContracts.CoordinatorV2Plus, randomWordsRequestedEvent, isNativeBilling) + randRequestBlockNumber := randomWordsRequestedEvent.Raw.BlockNumber + var wg sync.WaitGroup + wg.Add(1) + //Wait at least 256 blocks + _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(257), evmClient, &wg, time.Second*260, t) + wg.Wait() + require.NoError(t, err) + err = vrfv2plus.FundSubscriptions( + env, + networkConfig.ChainID, + big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionFundingAmountNative), + big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionFundingAmountLink), + linkToken, + vrfContracts.CoordinatorV2Plus, + subIDs, + ) + require.NoError(t, err, "error funding subscriptions") + randomWordsFulfilledEvent, err := vrfContracts.CoordinatorV2Plus.WaitForRandomWordsFulfilledEvent( + []*big.Int{subID}, + []*big.Int{randomWordsRequestedEvent.RequestId}, + time.Second*30, + ) + require.NoError(t, err, "error waiting for randomness fulfilled event") + vrfv2plus.LogRandomWordsFulfilledEvent(l, vrfContracts.CoordinatorV2Plus, randomWordsFulfilledEvent, isNativeBilling) + status, err := vrfContracts.VRFV2PlusConsumer[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, status.Fulfilled) + l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") + + randRequestBlockHash, err := vrfContracts.BHS.GetBlockHash(testcontext.Get(t), big.NewInt(int64(randRequestBlockNumber))) + require.NoError(t, err, "error getting blockhash for a blocknumber which was stored in BHS contract") + + l.Info(). + Str("Randomness Request's Blockhash", randomWordsRequestedEvent.Raw.BlockHash.String()). + Str("Block Hash stored by BHS contract", fmt.Sprintf("0x%x", randRequestBlockHash)). + Msg("BHS Contract's stored Blockhash for Randomness Request") + require.Equal(t, 0, randomWordsRequestedEvent.Raw.BlockHash.Cmp(randRequestBlockHash)) + }) + + t.Run("BHS Job should fill in blockhashes into BHS contract for unfulfilled requests", func(t *testing.T) { + subID := subIDs[1] + + subscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2plus.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2Plus) + + //BHS node should fill in blockhashes into BHS contract depending on the waitBlocks and lookBackBlocks settings + configCopy := config.MustCopy().(tc.TestConfig) + _, err = vrfContracts.VRFV2PlusConsumer[0].RequestRandomness( + vrfKeyData.KeyHash, + subID, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.CallbackGasLimit, + isNativeBilling, + *configCopy.VRFv2Plus.General.NumberOfWords, + *configCopy.VRFv2Plus.General.RandomnessRequestCountPerRequest, + ) + require.NoError(t, err, "error requesting randomness") + + randomWordsRequestedEvent, err := vrfContracts.CoordinatorV2Plus.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfKeyData.KeyHash}, + []*big.Int{subID}, + []common.Address{common.HexToAddress(vrfContracts.VRFV2PlusConsumer[0].Address())}, + time.Minute*1, + ) + require.NoError(t, err, "error waiting for randomness requested event") + vrfv2plus.LogRandomnessRequestedEvent(l, vrfContracts.CoordinatorV2Plus, randomWordsRequestedEvent, isNativeBilling) + randRequestBlockNumber := randomWordsRequestedEvent.Raw.BlockNumber + _, err = vrfContracts.BHS.GetBlockHash(testcontext.Get(t), big.NewInt(int64(randRequestBlockNumber))) + require.Error(t, err, "error not occurred when getting blockhash for a blocknumber which was not stored in BHS contract") + + var wg sync.WaitGroup + wg.Add(1) + _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(*config.VRFv2Plus.General.BHSJobWaitBlocks+10), evmClient, &wg, time.Minute*1, t) + wg.Wait() + require.NoError(t, err, "error waiting for blocknumber to be") + + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + var clNodeTxs *client.TransactionsData + var txHash string + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + clNodeTxs, _, err = nodesMap[vrfcommon.BHS].CLNode.API.ReadTransactions() + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "error getting CL Node transactions") + l.Debug().Int("Number of TXs", len(clNodeTxs.Data)).Msg("BHS Node txs") + g.Expect(len(clNodeTxs.Data)).Should(gomega.BeNumerically("==", 1), "Expected 1 tx posted by BHS Node, but found %d", len(clNodeTxs.Data)) + txHash = clNodeTxs.Data[0].Attributes.Hash + }, "2m", "1s").Should(gomega.Succeed()) + + require.Equal(t, strings.ToLower(vrfContracts.BHS.Address()), strings.ToLower(clNodeTxs.Data[0].Attributes.To)) + + bhsStoreTx, _, err := actions.GetTxByHash(testcontext.Get(t), evmClient, common.HexToHash(txHash)) + require.NoError(t, err, "error getting tx from hash") + + bhsStoreTxInputData, err := actions.DecodeTxInputData(blockhash_store.BlockhashStoreABI, bhsStoreTx.Data()) + l.Info(). + Str("Block Number", bhsStoreTxInputData["n"].(*big.Int).String()). + Msg("BHS Node's Store Blockhash for Blocknumber Method TX") + require.Equal(t, randRequestBlockNumber, bhsStoreTxInputData["n"].(*big.Int).Uint64()) + + err = evmClient.WaitForEvents() + require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) + + var randRequestBlockHash [32]byte + gom.Eventually(func(g gomega.Gomega) { + randRequestBlockHash, err = vrfContracts.BHS.GetBlockHash(testcontext.Get(t), big.NewInt(int64(randRequestBlockNumber))) + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "error getting blockhash for a blocknumber which was stored in BHS contract") + }, "2m", "1s").Should(gomega.Succeed()) + l.Info(). + Str("Randomness Request's Blockhash", randomWordsRequestedEvent.Raw.BlockHash.String()). + Str("Block Hash stored by BHS contract", fmt.Sprintf("0x%x", randRequestBlockHash)). + Msg("BHS Contract's stored Blockhash for Randomness Request") + require.Equal(t, 0, randomWordsRequestedEvent.Raw.BlockHash.Cmp(randRequestBlockHash)) + }) +} + +func TestVRFv2PlusPendingBlockSimulationAndZeroConfirmationDelays(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) + if err != nil { + t.Fatal(err) } - //Verify old and migrated subs - require.Equal(t, oldSubscriptionBeforeMigration.NativeBalance, migratedSubscription.NativeBalance) - require.Equal(t, oldSubscriptionBeforeMigration.Balance, migratedSubscription.Balance) - require.Equal(t, oldSubscriptionBeforeMigration.Owner, migratedSubscription.Owner) - require.Equal(t, oldSubscriptionBeforeMigration.Consumers, migratedSubscription.Consumers) - - //Verify that old sub was deleted from old Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) - require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") - - _, err = vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) - require.Error(t, err, "error not occurred getting active sub ids. Should occur since it should revert when sub id array is empty") - - activeSubIdsMigratedCoordinator, err := newCoordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) - require.NoError(t, err, "error occurred getting active sub ids") - require.Len(t, activeSubIdsMigratedCoordinator, 1, "Active Sub Ids length is not equal to 1 for Migrated Coordinator after migration") - require.Equal(t, subID, activeSubIdsMigratedCoordinator[0]) - - //Verify that total balances changed for Link and Eth for new and old coordinator - expectedLinkTotalBalanceForMigratedCoordinator := new(big.Int).Add(oldSubscriptionBeforeMigration.Balance, migratedCoordinatorLinkTotalBalanceBeforeMigration) - expectedEthTotalBalanceForMigratedCoordinator := new(big.Int).Add(oldSubscriptionBeforeMigration.NativeBalance, migratedCoordinatorEthTotalBalanceBeforeMigration) - - expectedLinkTotalBalanceForOldCoordinator := new(big.Int).Sub(oldCoordinatorLinkTotalBalanceBeforeMigration, oldSubscriptionBeforeMigration.Balance) - expectedEthTotalBalanceForOldCoordinator := new(big.Int).Sub(oldCoordinatorEthTotalBalanceBeforeMigration, oldSubscriptionBeforeMigration.NativeBalance) - require.Equal(t, 0, expectedLinkTotalBalanceForMigratedCoordinator.Cmp(migratedCoordinatorLinkTotalBalanceAfterMigration)) - require.Equal(t, 0, expectedEthTotalBalanceForMigratedCoordinator.Cmp(migratedCoordinatorEthTotalBalanceAfterMigration)) - require.Equal(t, 0, expectedLinkTotalBalanceForOldCoordinator.Cmp(oldCoordinatorLinkTotalBalanceAfterMigration)) - require.Equal(t, 0, expectedEthTotalBalanceForOldCoordinator.Cmp(oldCoordinatorEthTotalBalanceAfterMigration)) - - //Verify rand requests fulfills with Link Token billing - _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillmentUpgraded( - vrfv2PlusContracts.LoadTestConsumers[0], - newCoordinator, - vrfv2PlusData, - subID, - false, - *config.VRFv2Plus.General.MinimumConfirmations, - *config.VRFv2Plus.General.CallbackGasLimit, - *config.VRFv2Plus.General.NumberOfWords, - *config.VRFv2Plus.General.RandomnessRequestCountPerRequest, - *config.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + // override config with minConf = 0 and use pending block for simulation + config.VRFv2Plus.General.MinimumConfirmations = ptr.Ptr[uint16](0) + config.VRFv2Plus.General.VRFJobSimulationBlock = ptr.Ptr[string]("pending") + + network, err := actions.EthereumNetworkConfigFromConfig(l, &config) + require.NoError(t, err, "Error building ethereum network config") + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithTestConfig(&config). + WithPrivateEthereumNetwork(network). + WithCLNodes(1). + WithFunding(big.NewFloat(*config.Common.ChainlinkNodeFunding)). + WithStandardCleanup(). + Build() + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*config.VRFv2Plus.General.LinkNativeFeedResponse)) + require.NoError(t, err, "error deploying mock ETH/LINK feed") + + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err, "error deploying LINK contract") + + networkConfig := networks.MustGetSelectedNetworkConfig(config.GetNetworkConfig())[0] + numberOfTxKeysToCreate := 2 + vrfv2PlusContracts, subIDs, vrfv2PlusData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( + env, + networkConfig.ChainID, + []vrfcommon.VRFNodeType{vrfcommon.VRF}, + &config, + linkToken, + mockETHLinkFeed, + numberOfTxKeysToCreate, + 1, + 1, l, ) - require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + require.NoError(t, err, "error setting up VRF v2_5 env") + + subID := subIDs[0] - //Verify rand requests fulfills with Native Token billing - _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillmentUpgraded( - vrfv2PlusContracts.LoadTestConsumers[1], - newCoordinator, + subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) + + var isNativeBilling = true + + jobRunsBeforeTest, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) + require.NoError(t, err, "error reading job runs") + + l.Info().Uint16("minimumConfirmationDelay", *config.VRFv2Plus.General.MinimumConfirmations).Msg("Minimum Confirmation Delay") + + // test and assert + randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( + vrfv2PlusContracts.VRFV2PlusConsumer[0], + vrfv2PlusContracts.CoordinatorV2Plus, vrfv2PlusData, subID, - true, + isNativeBilling, *config.VRFv2Plus.General.MinimumConfirmations, *config.VRFv2Plus.General.CallbackGasLimit, *config.VRFv2Plus.General.NumberOfWords, *config.VRFv2Plus.General.RandomnessRequestCountPerRequest, *config.VRFv2Plus.General.RandomnessRequestCountPerRequestDeviation, + config.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + + jobRuns, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) + require.NoError(t, err, "error reading job runs") + require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) + + status, err := vrfv2PlusContracts.VRFV2PlusConsumer[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, status.Fulfilled) + l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") } diff --git a/integration-tests/soak/forwarder_ocr_test.go b/integration-tests/soak/forwarder_ocr_test.go index b5355a6c3f..401100748d 100644 --- a/integration-tests/soak/forwarder_ocr_test.go +++ b/integration-tests/soak/forwarder_ocr_test.go @@ -7,7 +7,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" ) @@ -32,7 +32,7 @@ ForwardersEnabled = true` return } t.Cleanup(func() { - if err := actions.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { + if err := actions_seth.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { l.Error().Err(err).Msg("Error tearing down environment") } }) diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index e25391e845..e99ecdf072 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -7,7 +7,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" ) @@ -34,7 +34,7 @@ func TestOCRSoak(t *testing.T) { return } t.Cleanup(func() { - if err := actions.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { + if err := actions_seth.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { l.Error().Err(err).Msg("Error tearing down environment") } }) diff --git a/integration-tests/testconfig/README.md b/integration-tests/testconfig/README.md new file mode 100644 index 0000000000..a86531551f --- /dev/null +++ b/integration-tests/testconfig/README.md @@ -0,0 +1,168 @@ +# TOML is the Ultimate Choice! + +## Introduction + +Final implementation has undergone minor adjustments in comparison to the approach by Adam Hamric, Anindita Ghosh, and Sergey Kudasov stated in the ADR. The primary changes are as follows: +* `TEST_LOG_LEVEL` remains an environment variable, pending the release of version 2. +* `TEST_TYPE` is also kept as an environment variable to facilitate dynamic configuration selection by some tests. +* TOML configuration of Chainlink nodes themselves has not been added, awaiting version 2. +* The hierarchy of configuration overrides has been streamlined for simplicity. + +By design, all test configurations are intended to reside within the `testconfig` package, organized into application-specific folders. However, the system can locate these configurations in any folder within the `integration-tests` directory, selecting the first one found. To identify the configurations in use, execute tests with the `debug` log level. + +The `testconfig` package serves as a centralized resource for accessing configurations across all products, including shared settings like logging and network preferences, as well as initial funding for Chainlink nodes. Product configurations, if present, are subjected to validation based on logical assumptions and observed code values. The `TestConfig` structure includes a `Save()` method, allowing for the preservation of test configurations after all adjustments have been applied. + +## Configuration and Overrides + +The order of precedence for overrides is as follows: +* Environment variable `BASE64_CONFIG_OVERRIDE` +* File `overrides.toml` +* Product-specific file, e.g., `[product_name].toml` +* The `default.toml` file + +The `BASE64_CONFIG_OVERRIDE` environment variable is primarily intended for use in continuous integration environments, enabling the substitution of default settings with confidential or user-specific parameters. For instance: + +```bash +cat << EOF > config.toml +[Network] +selected_networks=["$SELECTED_NETWORKS"] + +[ChainlinkImage] +image="$CHAINLINK_IMAGE" +version="$CHAINLINK_VERSION" +postgres_version="$CHAINLINK_POSTGRES_VERSION" + +[Pyroscope] +enabled=$pyroscope_enabled +server_url="$PYROSCOPE_SERVER" +environment="$PYROSCOPE_ENVIRONMENT" +key_secret="$PYROSCOPE_KEY" + +[Logging] +test_log_collect=false +run_id="$RUN_ID" + +[Logging.LogStream] +log_targets=["$LOG_TARGETS"] + +[Logging.Loki] +tenant_id="$LOKI_TENANT_ID" +endpoint="$LOKI_ENDPOINT" +basic_auth_secret="$LOKI_BASIC_AUTH" + +[Logging.Grafana] +base_url="$GRAFANA_URL" +dashboard_url="$GRAFANA_DASHBOARD_URL" +EOF + +BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) +echo ::add-mask::$BASE64_CONFIG_OVERRIDE +echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV +``` + +**It is highly recommended to use reusable GHA actions present in [.actions](../../../.github/.actions) to generate and apply the base64-encoded configuration.** Own implementation of `BASE64_CONFIG_OVERRIDE` generation is discouraged and should be used only if existing actions do not cover the use case. But even in that case it might be a better idea to extend existing actions. + +This variable is automatically relayed to Kubernetes-based tests, eliminating the need for manual intervention in test scripts. + +The `overrides.toml` file is recommended for local use to adjust dynamic variables or modify predefined settings. At the very minimum it should contain the Chainlink image and version, as shown in the example below: + +```toml +[ChainlinkImage] +image = "your image name" +version = "your tag" +``` + +Product-specific configurations, such as those in `[product_name].toml`, house the bulk of default and variant settings, supporting default configurations like the following in `log_poller.toml`: + +```toml +# product defaults +[LogPoller] +[LogPoller.General] +generator = "looped" +contracts = 2 +events_per_tx = 4 +use_finality_tag = true +log_poll_interval = "500ms" +# 0 disables backup poller +backup_log_poller_block_delay = 0 + +[LogPoller.Looped] +execution_count = 100 +min_emit_wait_time_ms = 200 +max_emit_wait_time_ms = 500 +``` + +Named configurations allow for the customization of settings through unique identifiers, such as a test name or type, acting as specific overrides. Here's how you can define and use these configurations: + +For instance, to tailor configurations for a particular test, you might define it as follows: + +```toml +# Here the configuration name is "TestLogManyFiltersPollerFinalityTag" +[TestLogManyFiltersPollerFinalityTag.LogPoller.General] +contracts = 300 +``` + +Alternatively, for a configuration that applies to a certain type of test, as seen in `vrfv2.toml`, you could specify: + +```toml +# Here the configuration name is "Soak" +[Soak.VRFv2.Common] +cancel_subs_after_test_run = true +``` + +When processing TOML files, the system initially searches for a general (unnamed) configuration. If a named configuration is found, it can specifically override the general (unnamed) settings, providing a targeted approach to configuration management based on distinct identifiers like test names or types. + +Finally `default.toml` file is envisioned to contain fundamental and universally applicable settings, such as logging configurations. + +## Local/Kubernetes Usage + +GitHub workflows in this repository have been updated to dynamically generate and utilize base64-encoded TOML configurations derived from user inputs or environment variables. For local execution or remote Kubernetes runners, users must manually supply certain variables, which cannot be embedded in configuration files due to their sensitive or dynamic nature. + +Essential variables might include: +* Chainlink image and version +* Test duration for specific tests (e.g., load, soak) +* Configuration specific to Loki (mandatory for certain tests) +* Grafana dashboard URLs + +For local testing, it is advisable to place these variables in the `overrides.toml` file. For Kubernetes or remote runners, the process involves creating a TOML file with the necessary values, encoding it in base64, and setting the result as the `BASE64_CONFIG_OVERRIDE` environment variable. + +## Embeded config +Because Go automatically excludes TOML files during the compilation of binaries, we must take deliberate steps to include our configuration files in the compiled binary. This can be accomplished by using a custom build tag `-o embed`. Implementing this tag will incorporate all the default configurations located in the `./testconfig` folder directly into the binary. Therefore, when executing tests from the binary, you'll only need to supply the `overrides.toml` file. This file should list only the settings you wish to modify; all other configurations will be sourced from the embedded configurations. You can access these embedded configurations [here](.integration-tests/testconfig/configs_embed.go). + +## To bear in mind +### Validation failures +When the system encounters even a single setting related to a specific product or configuration within the configurations, it triggers a comprehensive validation of the entire configuration for that product. This approach is based on the assumption that if any configuration for a given product is specified, the entire set of configurations for that product must be complete and valid. This is particularly crucial when dealing with the `overrides.toml` file, where it's easy to overlook the need to comment out or adjust values when switching between configurations for different products. Essentially, the presence of any specific configuration detail necessitates that all relevant configurations for that product be fully defined and correct to prevent validation errors. + +## Possible nil pointers +If no configuration values are set for a product or its logging parameters, the system won't perform validation checks. This can lead to a 'nil pointer exception' error if you attempt to access a configuration property later on. This situation arises because we use pointers to facilitate optional overrides; accessing an unset (nil) pointer will cause an error. To avoid such issues, especially when general validations might not cover every scenario, it's crucial for users to ensure that all necessary configuration options are explicitly set. Additionally, it's highly recommended to implement test-specific validations to confirm that all required values for a particular test are indeed established. This proactive approach helps prevent runtime errors and ensures smooth test execution. + +## Contributing +It's crucial to incorporate all new test configuration settings directly into the TOML configuration files, steering clear of using environment variables for this purpose. Our goal is to centralize all configuration details, including examples, within the same package. This approach simplifies the process of understanding the available configuration options and identifying the appropriate values to use for each setting. + +## Reusing TestConfig in other projects +To ensure the cleanliness and simplicity of your project's configuration, it's advised against using the `testconfig` code as a direct library in other projects. The reason is that much of this code is tailored specifically to its current application, which might not align with the requirements of your project. Your project might not necessitate any overrides or could perhaps benefit from a simpler configuration approach. + +However, if you find a need to utilize some methods from this project, the recommended practice is to implement the required interfaces within your project's configuration package, rather than directly copying and pasting code. For instance, if you aim to incorporate a setup action similar to the `SetupVRFV2Environment` for VRFv2, like the one shown below: + +```go +func SetupVRFV2Environment( + env *test_env.CLClusterTestEnv, + nodesToCreate []vrfcommon.VRFNodeType, + vrfv2TestConfig types.VRFv2TestConfig, + useVRFOwner bool, + useTestCoordinator bool, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.MockETHLINKFeed, + registerProvingKeyAgainstAddress string, + numberOfTxKeysToCreate int, + numberOfConsumers int, + numberOfSubToCreate int, + l zerolog.Logger, +) (*vrfcommon.VRFContracts, []uint64, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { +``` + +You should not replicate the entire `TestConfig` structure. Instead, create an implementation of the `types.VRFv2TestConfig` interface in your project and use that as the parameter. This approach allows you to maintain a streamlined and focused configuration package in your project. + +## Known Issues/Limitations +* Duplicate file names in different locations may lead to unpredictable configurations being selected. +* The use of pointer fields for optional configuration elements necessitates careful handling, especially for programmatic modifications, to avoid unintended consequences. The `MustCopy()` function is recommended for creating deep copies of configurations for isolated modifications. Unfortunately some of the custom types are not copied at all, you need to set them manually. It's true for example for `blockchain.StrDuration` type. diff --git a/integration-tests/testconfig/common/vrf/common.go b/integration-tests/testconfig/common/vrf/common.go new file mode 100644 index 0000000000..ca6f44f27c --- /dev/null +++ b/integration-tests/testconfig/common/vrf/common.go @@ -0,0 +1,277 @@ +package vrf + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" +) + +type Config struct { + General *General `toml:"General"` + ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnv"` + Performance *PerformanceConfig `toml:"Performance"` +} + +const ( + ErrDeviationShouldBeLessThanOriginal = "`RandomnessRequestCountPerRequestDeviation` should be less than `RandomnessRequestCountPerRequest`" +) + +func (c *Config) Validate() error { + if c.General != nil { + if err := c.General.Validate(); err != nil { + return err + } + } + if c.ExistingEnvConfig != nil { + if err := c.ExistingEnvConfig.Validate(); err != nil { + return err + } + } + if c.Performance != nil { + if err := c.Performance.Validate(); err != nil { + return err + } + } + return nil +} + +type PerformanceConfig struct { + TestDuration *blockchain.StrDuration `toml:"test_duration"` + RPS *int64 `toml:"rps"` + RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` +} + +func (c *PerformanceConfig) Validate() error { + if c.TestDuration == nil || c.TestDuration.Duration == 0 { + return errors.New("test_duration must be set to a positive value") + } + if c.RPS == nil || *c.RPS == 0 { + return errors.New("rps must be set to a positive value") + } + if c.RateLimitUnitDuration == nil { + return errors.New("rate_limit_unit_duration must be set ") + } + + return nil +} + +type ExistingEnvConfig struct { + CoordinatorAddress *string `toml:"coordinator_address"` + ConsumerAddress *string `toml:"consumer_address"` + LinkAddress *string `toml:"link_address"` + KeyHash *string `toml:"key_hash"` + CreateFundSubsAndAddConsumers *bool `toml:"create_fund_subs_and_add_consumers"` + NodeSendingKeys []string `toml:"node_sending_keys"` + Funding +} + +func (c *ExistingEnvConfig) Validate() error { + if c.CreateFundSubsAndAddConsumers == nil { + return errors.New("create_fund_subs_and_add_consumers must be set ") + } + if c.CoordinatorAddress == nil { + return errors.New("coordinator_address must be set when using existing environment") + } + if !common.IsHexAddress(*c.CoordinatorAddress) { + return errors.New("coordinator_address must be a valid hex address") + } + if c.KeyHash == nil { + return errors.New("key_hash must be set when using existing environment") + } + if *c.KeyHash == "" { + return errors.New("key_hash must be a non-empty string") + } + if *c.CreateFundSubsAndAddConsumers { + if err := c.Funding.Validate(); err != nil { + return err + } + } else { + if c.ConsumerAddress == nil || *c.ConsumerAddress == "" { + return errors.New("consumer_address must be set when using existing environment") + } + if !common.IsHexAddress(*c.ConsumerAddress) { + return errors.New("consumer_address must be a valid hex address") + } + } + + if c.NodeSendingKeys != nil { + for _, key := range c.NodeSendingKeys { + if !common.IsHexAddress(key) { + return errors.New("node_sending_keys must be a valid hex address") + } + } + } + + return nil +} + +type Funding struct { + NodeSendingKeyFundingMin *float64 `toml:"node_sending_key_funding_min"` +} + +func (c *Funding) Validate() error { + if c.NodeSendingKeyFundingMin != nil && *c.NodeSendingKeyFundingMin <= 0 { + return errors.New("when set node_sending_key_funding_min must be a positive value") + } + + return nil +} + +type General struct { + UseExistingEnv *bool `toml:"use_existing_env"` + CancelSubsAfterTestRun *bool `toml:"cancel_subs_after_test_run"` + CLNodeMaxGasPriceGWei *int64 `toml:"cl_node_max_gas_price_gwei"` // Max gas price in GWei for the chainlink node + LinkNativeFeedResponse *int64 `toml:"link_native_feed_response"` // Response of the LINK/ETH feed + MinimumConfirmations *uint16 `toml:"minimum_confirmations"` // Minimum number of confirmations for the VRF Coordinator + SubscriptionFundingAmountLink *float64 `toml:"subscription_funding_amount_link"` // Amount of LINK to fund the subscription with + NumberOfWords *uint32 `toml:"number_of_words"` // Number of words to request + CallbackGasLimit *uint32 `toml:"callback_gas_limit"` // Gas limit for the callback + MaxGasLimitCoordinatorConfig *uint32 `toml:"max_gas_limit_coordinator_config"` // Max gas limit for the VRF Coordinator config + FallbackWeiPerUnitLink *int64 `toml:"fallback_wei_per_unit_link"` // Fallback wei per unit LINK for the VRF Coordinator config + StalenessSeconds *uint32 `toml:"staleness_seconds"` // Staleness in seconds for the VRF Coordinator config + GasAfterPaymentCalculation *uint32 `toml:"gas_after_payment_calculation"` // Gas after payment calculation for the VRF Coordinator + + NumberOfSubToCreate *int `toml:"number_of_sub_to_create"` // Number of subscriptions to create + + RandomnessRequestCountPerRequest *uint16 `toml:"randomness_request_count_per_request"` // How many randomness requests to send per request + RandomnessRequestCountPerRequestDeviation *uint16 `toml:"randomness_request_count_per_request_deviation"` // How many randomness requests to send per request + + RandomWordsFulfilledEventTimeout *blockchain.StrDuration `toml:"random_words_fulfilled_event_timeout"` // How long to wait for the RandomWordsFulfilled event to be emitted + + // Wrapper Config + WrapperGasOverhead *uint32 `toml:"wrapped_gas_overhead"` + CoordinatorGasOverhead *uint32 `toml:"coordinator_gas_overhead"` + WrapperPremiumPercentage *uint8 `toml:"wrapper_premium_percentage"` + WrapperMaxNumberOfWords *uint8 `toml:"wrapper_max_number_of_words"` + WrapperConsumerFundingAmountNativeToken *float64 `toml:"wrapper_consumer_funding_amount_native_token"` + WrapperConsumerFundingAmountLink *int64 `toml:"wrapper_consumer_funding_amount_link"` + + //VRF Job Config + VRFJobForwardingAllowed *bool `toml:"vrf_job_forwarding_allowed"` + VRFJobEstimateGasMultiplier *float64 `toml:"vrf_job_estimate_gas_multiplier"` + VRFJobBatchFulfillmentEnabled *bool `toml:"vrf_job_batch_fulfillment_enabled"` + VRFJobBatchFulfillmentGasMultiplier *float64 `toml:"vrf_job_batch_fulfillment_gas_multiplier"` + VRFJobPollPeriod *blockchain.StrDuration `toml:"vrf_job_poll_period"` + VRFJobRequestTimeout *blockchain.StrDuration `toml:"vrf_job_request_timeout"` + VRFJobSimulationBlock *string `toml:"vrf_job_simulation_block"` + + //BHS Job Config + BHSJobWaitBlocks *int `toml:"bhs_job_wait_blocks"` + BHSJobLookBackBlocks *int `toml:"bhs_job_lookback_blocks"` + BHSJobPollPeriod *blockchain.StrDuration `toml:"bhs_job_poll_period"` + BHSJobRunTimeout *blockchain.StrDuration `toml:"bhs_job_run_timeout"` +} + +func (c *General) Validate() error { + if c.UseExistingEnv == nil { + return errors.New("use_existing_env must not be nil") + } + if c.CLNodeMaxGasPriceGWei == nil || *c.CLNodeMaxGasPriceGWei == 0 { + return errors.New("max_gas_price_gwei must be set to a positive value") + } + if c.LinkNativeFeedResponse == nil || *c.LinkNativeFeedResponse == 0 { + return errors.New("link_native_feed_response must be set to a positive value") + } + if c.MinimumConfirmations == nil { + return errors.New("minimum_confirmations must be set to a non-negative value") + } + if c.SubscriptionFundingAmountLink == nil || *c.SubscriptionFundingAmountLink < 0 { + return errors.New("subscription_funding_amount_link must be set to non-negative value") + } + if c.NumberOfWords == nil || *c.NumberOfWords == 0 { + return errors.New("number_of_words must be set to a positive value") + } + if c.CallbackGasLimit == nil || *c.CallbackGasLimit == 0 { + return errors.New("callback_gas_limit must be set to a positive value") + } + if c.MaxGasLimitCoordinatorConfig == nil || *c.MaxGasLimitCoordinatorConfig == 0 { + return errors.New("max_gas_limit_coordinator_config must be set to a positive value") + } + if c.FallbackWeiPerUnitLink == nil || *c.FallbackWeiPerUnitLink == 0 { + return errors.New("fallback_wei_per_unit_link must be set to a positive value") + } + if c.StalenessSeconds == nil || *c.StalenessSeconds == 0 { + return errors.New("staleness_seconds must be set to a positive value") + } + if c.GasAfterPaymentCalculation == nil || *c.GasAfterPaymentCalculation == 0 { + return errors.New("gas_after_payment_calculation must be set to a positive value") + } + if c.NumberOfSubToCreate == nil || *c.NumberOfSubToCreate == 0 { + return errors.New("number_of_sub_to_create must be set to a positive value") + } + if c.RandomnessRequestCountPerRequest == nil || *c.RandomnessRequestCountPerRequest == 0 { + return errors.New("randomness_request_count_per_request must be set to a positive value") + } + if c.RandomnessRequestCountPerRequestDeviation == nil { + return errors.New("randomness_request_count_per_request_deviation must be set to a non-negative value") + } + if c.RandomWordsFulfilledEventTimeout == nil || c.RandomWordsFulfilledEventTimeout.Duration == 0 { + return errors.New("random_words_fulfilled_event_timeout must be set to a positive value") + } + if c.WrapperGasOverhead == nil { + return errors.New("wrapped_gas_overhead must be set to a non-negative value") + } + if c.CoordinatorGasOverhead == nil || *c.CoordinatorGasOverhead == 0 { + return errors.New("coordinator_gas_overhead must be set to a non-negative value") + } + if c.WrapperPremiumPercentage == nil || *c.WrapperPremiumPercentage == 0 { + return errors.New("wrapper_premium_percentage must be set to a positive value") + } + if c.WrapperMaxNumberOfWords == nil || *c.WrapperMaxNumberOfWords == 0 { + return errors.New("wrapper_max_number_of_words must be set to a positive value") + } + if c.WrapperConsumerFundingAmountNativeToken == nil || *c.WrapperConsumerFundingAmountNativeToken < 0 { + return errors.New("wrapper_consumer_funding_amount_native_token must be set to a non-negative value") + } + if c.WrapperConsumerFundingAmountLink == nil || *c.WrapperConsumerFundingAmountLink < 0 { + return errors.New("wrapper_consumer_funding_amount_link must be set to a non-negative value") + } + if *c.RandomnessRequestCountPerRequest <= *c.RandomnessRequestCountPerRequestDeviation { + return errors.New(ErrDeviationShouldBeLessThanOriginal) + } + + if c.VRFJobForwardingAllowed == nil { + return errors.New("vrf_job_forwarding_allowed must be set") + } + + if c.VRFJobBatchFulfillmentEnabled == nil { + return errors.New("vrf_job_batch_fulfillment_enabled must be set") + } + if c.VRFJobEstimateGasMultiplier == nil || *c.VRFJobEstimateGasMultiplier < 0 { + return errors.New("vrf_job_estimate_gas_multiplier must be set to a non-negative value") + } + if c.VRFJobBatchFulfillmentGasMultiplier == nil || *c.VRFJobBatchFulfillmentGasMultiplier < 0 { + return errors.New("vrf_job_batch_fulfillment_gas_multiplier must be set to a non-negative value") + } + + if c.VRFJobPollPeriod == nil || c.VRFJobPollPeriod.Duration == 0 { + return errors.New("vrf_job_poll_period must be set to a non-negative value") + } + + if c.VRFJobRequestTimeout == nil || c.VRFJobRequestTimeout.Duration == 0 { + return errors.New("vrf_job_request_timeout must be set to a non-negative value") + } + + if c.VRFJobSimulationBlock != nil && (*c.VRFJobSimulationBlock != "latest" && *c.VRFJobSimulationBlock != "pending") { + return errors.New("simulation_block must be nil or \"latest\" or \"pending\"") + } + + if c.BHSJobLookBackBlocks == nil || *c.BHSJobLookBackBlocks < 0 { + return errors.New("bhs_job_lookback_blocks must be set to a non-negative value") + } + + if c.BHSJobPollPeriod == nil || c.BHSJobPollPeriod.Duration == 0 { + return errors.New("bhs_job_poll_period must be set to a non-negative value") + } + + if c.BHSJobRunTimeout == nil || c.BHSJobRunTimeout.Duration == 0 { + return errors.New("bhs_job_run_timeout must be set to a non-negative value") + } + + if c.BHSJobWaitBlocks == nil || *c.BHSJobWaitBlocks < 0 { + return errors.New("bhs_job_wait_blocks must be set to a non-negative value") + } + + return nil +} diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index a65c23d70d..34051aff5e 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -6,6 +6,9 @@ log_targets=["file"] log_producer_timeout="10s" log_producer_retry_limit=10 +[ChainlinkImage] +postgres_version="15.6" + [Network] selected_networks=["simulated"] @@ -19,4 +22,84 @@ slots_per_epoch=2 genesis_delay=15 validator_count=4 chain_id=1337 -addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] \ No newline at end of file +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +[Seth] +# enables automatic tracing of all transactions that are decoded via Decode() method +tracing_enabled = false +# saves each tracing result to json file in ./traces/.json +trace_to_json = false +# number of addresses to be generated and runtime, if set to 0, no addresses will be generated +# each generated address will receive a proportion of native tokens from root private key's balance +# with the value equal to (root_balance / ephemeral_addresses_number) - transfer_fee * ephemeral_addresses_number +ephemeral_addresses_number = 0 + +[Seth.nonce_manager] +key_sync_rate_limit_per_sec = 10 +key_sync_timeout = "2s" +key_sync_retry_delay = "1s" +key_sync_retries = 10 + +[[Seth.networks]] +name = "Geth" +chain_id = "1337" +transaction_timeout = "30s" +urls = ["ws://localhost:8546"] +transfer_gas_fee = 21_000 +gas_limit = 8_000_000 +# legacy transactions +gas_price = 1_000_000_000 +# EIP-1559 transactions +#eip_1559_dynamic_fees = true +gas_fee_cap = 10_000_000_000 +gas_tip_cap = 3_000_000_000 + +[[Seth.networks]] +name = "Fuji" +chain_id = "43113" +transaction_timeout = "3m" +transfer_gas_fee = 21_000 +# legacy transactions +gas_price = 30_000_000_000 +# EIP-1559 transactions +eip_1559_dynamic_fees = true +gas_fee_cap = 30_000_000_000 +gas_tip_cap = 1_800_000_000 + +[[Seth.networks]] +name = "Sepolia" +chain_id = "11155111" +transaction_timeout = "3m" +transfer_gas_fee = 40_000 +gas_limit = 30_000_000 +# legacy transactions +gas_price = 20_000_000_000 +# EIP-1559 transactions +# eip_1559_dynamic_fees = true2 +gas_fee_cap = 45_000_000_000 +gas_tip_cap = 10_000_000_000 + +[[Seth.networks]] +name = "Mumbai" +chain_id = "80001" +transaction_timeout = "3m" +transfer_gas_fee = 21_000 +# legacy transactions +#gas_price = 1_800_000_000 +# EIP-1559 transactions +eip_1559_dynamic_fees = true +gas_fee_cap = 1_800_000_000 +gas_tip_cap = 1_800_000_000 + +[[Seth.networks]] +name = "zkEVM" +chain_id = "1442" +transaction_timeout = "3m" +transfer_gas_fee = 21_000 +gas_limit = 3_000_000 +# legacy transactions +gas_price = 50_000_000 +# EIP-1559 transactions +#eip_1559_dynamic_fees = true +gas_fee_cap = 1_800_000_000 +gas_tip_cap = 1_800_000_000 \ No newline at end of file diff --git a/integration-tests/testconfig/log_poller/config.go b/integration-tests/testconfig/log_poller/config.go index 96c3b55c27..890c33f26c 100644 --- a/integration-tests/testconfig/log_poller/config.go +++ b/integration-tests/testconfig/log_poller/config.go @@ -90,11 +90,13 @@ func (l *LoopedConfig) Validate() error { } type General struct { - Generator *string `toml:"generator"` - EventsToEmit []abi.Event `toml:"-"` - Contracts *int `toml:"contracts"` - EventsPerTx *int `toml:"events_per_tx"` - UseFinalityTag *bool `toml:"use_finality_tag"` + Generator *string `toml:"generator"` + EventsToEmit []abi.Event `toml:"-"` + Contracts *int `toml:"contracts"` + EventsPerTx *int `toml:"events_per_tx"` + UseFinalityTag *bool `toml:"use_finality_tag"` + BackupLogPollerBlockDelay *uint64 `toml:"backup_log_poller_block_delay"` + LogPollInterval *blockchain.StrDuration `toml:"log_poll_interval"` } func (g *General) Validate() error { diff --git a/integration-tests/testconfig/log_poller/log_poller.toml b/integration-tests/testconfig/log_poller/log_poller.toml index 2f46ebf11c..89d2f07b4e 100644 --- a/integration-tests/testconfig/log_poller/log_poller.toml +++ b/integration-tests/testconfig/log_poller/log_poller.toml @@ -5,6 +5,9 @@ generator = "looped" contracts = 2 events_per_tx = 4 use_finality_tag = true +log_poll_interval = "500ms" +# 0 disables backup poller +backup_log_poller_block_delay = 0 [LogPoller.Looped] execution_count = 100 diff --git a/integration-tests/testconfig/ocr/ocr.toml b/integration-tests/testconfig/ocr/ocr.toml index 8d3c73ca76..67d7d4588a 100644 --- a/integration-tests/testconfig/ocr/ocr.toml +++ b/integration-tests/testconfig/ocr/ocr.toml @@ -40,4 +40,4 @@ 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/ocr2/example.toml b/integration-tests/testconfig/ocr2/example.toml new file mode 100644 index 0000000000..b8eb891ba3 --- /dev/null +++ b/integration-tests/testconfig/ocr2/example.toml @@ -0,0 +1,96 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth_secret="loki-basic-auth" +# only needed for cloud grafana +bearer_token_secret="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +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" +bearer_token_secret="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# load test specific configuration +[Load.OCR] +[Load.OCR.Common] +eth_funds = 3 + +[Load.OCR.Load] +test_duration = "3m" +rate_limit_unit_duration = "1m" +rate = 3 +verification_interval = "5s" +verification_timeout = "3m" +ea_change_interval = "5s" + +# soak test specific configuration +[Soak.Common] +chainlink_node_funding = 100 + +[Soak.OCR] +[Soak.OCR.Common] +test_duration="15m" + +[Soak.OCR.Soak] +ocr_version="1" +number_of_contracts=2 +time_between_rounds="1m" \ No newline at end of file diff --git a/integration-tests/testconfig/ocr2/ocr2.go b/integration-tests/testconfig/ocr2/ocr2.go new file mode 100644 index 0000000000..c039de0ff6 --- /dev/null +++ b/integration-tests/testconfig/ocr2/ocr2.go @@ -0,0 +1,57 @@ +package ocr + +import ( + "errors" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" +) + +type Config struct { + Soak *SoakConfig `toml:"Soak"` + Common *Common `toml:"Common"` +} + +func (o *Config) Validate() error { + if o.Common != nil { + if err := o.Common.Validate(); err != nil { + return err + } + } + if o.Soak != nil { + if err := o.Soak.Validate(); err != nil { + 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 { + OCRVersion *string `toml:"ocr_version"` + NumberOfContracts *int `toml:"number_of_contracts"` + TimeBetweenRounds *blockchain.StrDuration `toml:"time_between_rounds"` +} + +func (o *SoakConfig) Validate() error { + if o.OCRVersion == nil || *o.OCRVersion == "" { + return errors.New("ocr_version must be set to either 1 or 2") + } + 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 +} diff --git a/integration-tests/testconfig/ocr2/ocr2.toml b/integration-tests/testconfig/ocr2/ocr2.toml new file mode 100644 index 0000000000..8d3c73ca76 --- /dev/null +++ b/integration-tests/testconfig/ocr2/ocr2.toml @@ -0,0 +1,43 @@ +# product defaults +[Common] +chainlink_node_funding = 0.5 + +# load test specific configuration +[Load.OCR] +[Load.OCR.Common] +eth_funds = 3 + +[Load.OCR.Load] +test_duration = "3m" +rate_limit_unit_duration = "1m" +rate = 3 +verification_interval = "5s" +verification_timeout = "3m" +ea_change_interval = "5s" + +# volume test specific configuration +[Volume.OCR] +[Volume.OCR.Common] +eth_funds = 3 + +[Volume.OCR.Volume] +test_duration = "3m" +rate_limit_unit_duration = "1m" +vu_requests_per_unit = 10 +rate = 1 +verification_interval = "5s" +verification_timeout = "3m" +ea_change_interval = "5s" + +# soak test specific configuration +[Soak.Common] +chainlink_node_funding = 100 + +[Soak.OCR] +[Soak.OCR.Common] +test_duration="15m" + +[Soak.OCR.Soak] +ocr_version="1" +number_of_contracts=2 +time_between_rounds="1m" \ No newline at end of file diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 28eddb24e6..c78210853f 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -16,6 +16,8 @@ import ( "golang.org/x/text/cases" "golang.org/x/text/language" + "github.com/smartcontractkit/seth" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" @@ -28,6 +30,7 @@ import ( keeper_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/keeper" lp_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/log_poller" ocr_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/ocr" + ocr2_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/ocr2" vrf_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrf" vrfv2_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" @@ -39,6 +42,7 @@ type GlobalTestConfig interface { GetNetworkConfig() *ctf_config.NetworkConfig GetPrivateEthereumNetworkConfig() *test_env.EthereumNetwork GetPyroscopeConfig() *ctf_config.PyroscopeConfig + SethConfig } type UpgradeableChainlinkTestConfig interface { @@ -69,10 +73,18 @@ type OcrTestConfig interface { GetOCRConfig() *ocr_config.Config } +type Ocr2TestConfig interface { + GetOCR2Config() *ocr2_config.Config +} + type NamedConfiguration interface { GetConfigurationName() string } +type SethConfig interface { + GetSethConfig() *seth.Config +} + type TestConfig struct { ChainlinkImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkImage"` ChainlinkUpgradeImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkUpgradeImage"` @@ -80,6 +92,9 @@ type TestConfig struct { Network *ctf_config.NetworkConfig `toml:"Network"` Pyroscope *ctf_config.PyroscopeConfig `toml:"Pyroscope"` PrivateEthereumNetwork *ctf_test_env.EthereumNetwork `toml:"PrivateEthereumNetwork"` + WaspConfig *ctf_config.WaspAutoBuildConfig `toml:"WaspAutoBuild"` + + Seth *seth.Config `toml:"Seth"` Common *Common `toml:"Common"` Automation *a_config.Config `toml:"Automation"` @@ -87,6 +102,7 @@ type TestConfig struct { Keeper *keeper_config.Config `toml:"Keeper"` LogPoller *lp_config.Config `toml:"LogPoller"` OCR *ocr_config.Config `toml:"OCR"` + OCR2 *ocr2_config.Config `toml:"OCR2"` VRF *vrf_config.Config `toml:"VRF"` VRFv2 *vrfv2_config.Config `toml:"VRFv2"` VRFv2Plus *vrfv2plus_config.Config `toml:"VRFv2Plus"` @@ -203,6 +219,19 @@ func (c TestConfig) GetConfigurationName() string { return c.ConfigurationName } +func (c TestConfig) GetSethConfig() *seth.Config { + return c.Seth +} + +func (c *TestConfig) AsBase64() (string, error) { + content, err := toml.Marshal(*c) + if err != nil { + return "", errors.Wrapf(err, "error marshaling test config") + } + + return base64.StdEncoding.EncodeToString(content), nil +} + type Common struct { ChainlinkNodeFunding *float64 `toml:"chainlink_node_funding"` } @@ -356,6 +385,8 @@ func GetConfig(configurationName string, product Product) (TestConfig, error) { logger.Debug().Msg("Validating test config") err = testConfig.Validate() if err != nil { + logger.Error(). + Msg("Error validating test config. You might want refer to integration-tests/testconfig/README.md for more information.") return TestConfig{}, errors.Wrapf(err, "error validating test config") } @@ -369,7 +400,7 @@ func GetConfig(configurationName string, product Product) (TestConfig, error) { func (c *TestConfig) readNetworkConfiguration() error { // currently we need to read that kind of secrets only for network configuration - if c == nil { + if c.Network == nil { c.Network = &ctf_config.NetworkConfig{} } @@ -390,22 +421,25 @@ func (c *TestConfig) readNetworkConfiguration() error { func (c *TestConfig) Validate() error { defer func() { if r := recover(); r != nil { - panic(fmt.Errorf("Panic during test config validation: '%v'. Most probably due to presence of partial product config", r)) + panic(fmt.Errorf("panic during test config validation: '%v'. Most probably due to presence of partial product config", r)) } }() + if c.ChainlinkImage == nil { - return fmt.Errorf("chainlink image config must be set") + return MissingImageInfoAsError("chainlink image config must be set") } - if err := c.ChainlinkImage.Validate(); err != nil { - return errors.Wrapf(err, "chainlink image config validation failed") + if c.ChainlinkImage != nil { + if err := c.ChainlinkImage.Validate(); err != nil { + return MissingImageInfoAsError(fmt.Sprintf("chainlink image config validation failed: %s", err.Error())) + } } if c.ChainlinkUpgradeImage != nil { if err := c.ChainlinkUpgradeImage.Validate(); err != nil { - return errors.Wrapf(err, "chainlink upgrade image config validation failed") + return MissingImageInfoAsError(fmt.Sprintf("chainlink upgrade image config validation failed: %s", err.Error())) } } if err := c.Network.Validate(); err != nil { - return errors.Wrapf(err, "network config validation failed") + return NoSelectedNetworkInfoAsError(fmt.Sprintf("network config validation failed: %s", err.Error())) } if c.Logging == nil { @@ -416,14 +450,7 @@ func (c *TestConfig) Validate() error { return errors.Wrapf(err, "logging config validation failed") } - // require Loki config only if these tests run locally - _, willUseRemoteRunner := os.LookupEnv(k8s_config.EnvVarJobImage) - _, isInsideK8s := os.LookupEnv(k8s_config.EnvVarInsideK8s) - if (!willUseRemoteRunner && !isInsideK8s) && slices.Contains(TestTypesWithLoki, c.ConfigurationName) { - if c.Logging.Loki == nil { - return fmt.Errorf("for local execution you must set Loki config in logging config") - } - + if c.Logging.Loki != nil { if err := c.Logging.Loki.Validate(); err != nil { return errors.Wrapf(err, "loki config validation failed") } @@ -505,6 +532,11 @@ func (c *TestConfig) Validate() error { } } + if c.WaspConfig != nil { + if err := c.WaspConfig.Validate(); err != nil { + return errors.Wrapf(err, "WaspAutoBuildConfig validation failed") + } + } return nil } diff --git a/integration-tests/testconfig/testconfig_utils.go b/integration-tests/testconfig/testconfig_utils.go new file mode 100644 index 0000000000..d1803c2265 --- /dev/null +++ b/integration-tests/testconfig/testconfig_utils.go @@ -0,0 +1,70 @@ +package testconfig + +import ( + "fmt" + "os" + "strings" +) + +// MissingImageInfoAsError return a helfpul error message when the no Chainlink image info is found in TOML config. +// If legacy env vars are found it prints ready to use TOML configuration +func MissingImageInfoAsError(errStr string) error { + intro := ` +Old configuration approach detected. Please use TOML instead of env vars. +Please refer to integration-tests/testconfig/README.md for more information. +` + + var imgStr, versionStr string + + if img := os.Getenv("CHAINLINK_IMAGE"); img != "" { + imgStr = fmt.Sprintf("image = \"%s\"\n", img) + } + + if version := os.Getenv("CHAINLINK_VERSION"); version != "" { + versionStr = fmt.Sprintf("version = \"%s\"\n", version) + } + + finalErrStr := fmt.Sprintf("%s\n%s", errStr, intro) + + if imgStr != "" && versionStr != "" { + extraInfo := ` +Or if you want to run your tests right now add following content to integration-tests/testconfig/overrides.toml: +[ChainlinkImage] +` + finalErrStr = fmt.Sprintf("%s\n%s%s%s%s", errStr, intro, extraInfo, imgStr, versionStr) + } + + return fmt.Errorf(finalErrStr) +} + +// NoSelectedNetworkInfoAsError return a helfpul error message when the no selected network info is found in TOML config. +// If legacy env var is found it prints ready to use TOML configuration. +func NoSelectedNetworkInfoAsError(errStr string) error { + intro := ` +Old configuration approach detected. Please use TOML instead of env vars. +Please refer to integration-tests/testconfig/README.md for more information. +` + + finalErrStr := fmt.Sprintf("%s\n%s", errStr, intro) + + if net := os.Getenv("SELECTED_NETWORKS"); net != "" { + parts := strings.Split(net, ",") + selectedNetworkStr := "[" + for i, network := range parts { + selectedNetworkStr += fmt.Sprintf("\"%s\"", network) + + if i < len(parts)-1 { + selectedNetworkStr += ", " + } + } + selectedNetworkStr += "]" + + extraInfo := ` +Or if you want to run your tests right now add following content to integration-tests/testconfig/overrides.toml: +[Network] +selected_networks=` + finalErrStr = fmt.Sprintf("%s\n%s%s%s", errStr, intro, extraInfo, selectedNetworkStr) + } + + return fmt.Errorf(finalErrStr) +} diff --git a/integration-tests/testconfig/vrfv2/config.go b/integration-tests/testconfig/vrfv2/config.go index f539d91799..dcfd959880 100644 --- a/integration-tests/testconfig/vrfv2/config.go +++ b/integration-tests/testconfig/vrfv2/config.go @@ -5,27 +5,16 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" -) - -const ( - ErrDeviationShouldBeLessThanOriginal = "`RandomnessRequestCountPerRequestDeviation` should be less than `RandomnessRequestCountPerRequest`" + vrf_common_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/common/vrf" ) type Config struct { - Common *Common `toml:"Common"` - General *General `toml:"General"` - ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnv"` - NewEnvConfig *NewEnvConfig `toml:"NewEnv"` - Performance *PerformanceConfig `toml:"Performance"` + General *General `toml:"General"` + ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnv"` + Performance *vrf_common_config.PerformanceConfig `toml:"Performance"` } func (c *Config) Validate() error { - if c.Common != nil { - if err := c.Common.Validate(); err != nil { - return err - } - } if c.General != nil { if err := c.General.Validate(); err != nil { return err @@ -35,235 +24,61 @@ func (c *Config) Validate() error { if err := c.Performance.Validate(); err != nil { return err } - if *c.Performance.UseExistingEnv { - if c.ExistingEnvConfig != nil { - if err := c.ExistingEnvConfig.Validate(); err != nil { - return err - } - } - } else { - if c.NewEnvConfig != nil { - if err := c.NewEnvConfig.Validate(); err != nil { - return err - } - } + } + if c.ExistingEnvConfig != nil && *c.General.UseExistingEnv { + if err := c.ExistingEnvConfig.Validate(); err != nil { + return err } } - - return nil -} - -type Common struct { - CancelSubsAfterTestRun *bool `toml:"cancel_subs_after_test_run"` -} - -func (c *Common) Validate() error { - return nil -} - -type PerformanceConfig struct { - TestDuration *blockchain.StrDuration `toml:"test_duration"` - RPS *int64 `toml:"rps"` - RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` - - // Using existing environment and contracts - UseExistingEnv *bool `toml:"use_existing_env"` - CoordinatorAddress *string - ConsumerAddress *string - LinkAddress *string - SubID *uint64 - KeyHash *string -} - -func (c *PerformanceConfig) Validate() error { - if c.TestDuration == nil || c.TestDuration.Duration == 0 { - return errors.New("test_duration must be set to a positive value") - } - if c.RPS == nil || *c.RPS == 0 { - return errors.New("rps must be set to a positive value") - } - if c.RateLimitUnitDuration == nil { - return errors.New("rate_limit_unit_duration must be set ") - } - if c.UseExistingEnv == nil { - return errors.New("use_existing_env must be set ") - } - return nil } type ExistingEnvConfig struct { - CoordinatorAddress *string `toml:"coordinator_address"` - ConsumerAddress *string `toml:"consumer_address"` - LinkAddress *string `toml:"link_address"` - SubID *uint64 `toml:"sub_id"` - KeyHash *string `toml:"key_hash"` - CreateFundSubsAndAddConsumers *bool `toml:"create_fund_subs_and_add_consumers"` - NodeSendingKeys []string `toml:"node_sending_keys"` - Funding + *vrf_common_config.ExistingEnvConfig + SubID *uint64 `toml:"sub_id"` } func (c *ExistingEnvConfig) Validate() error { - if c.CreateFundSubsAndAddConsumers == nil { - return errors.New("create_fund_subs_and_add_consumers must be set ") - } - if c.CoordinatorAddress == nil { - return errors.New("coordinator_address must be set when using existing environment") - } - if !common.IsHexAddress(*c.CoordinatorAddress) { - return errors.New("coordinator_address must be a valid hex address") - } - if c.KeyHash == nil { - return errors.New("key_hash must be set when using existing environment") - } - if *c.KeyHash == "" { - return errors.New("key_hash must be a non-empty string") - } - if c.LinkAddress != nil && !common.IsHexAddress(*c.LinkAddress) { - return errors.New("link_address must be a valid hex address") - } - - if *c.CreateFundSubsAndAddConsumers { - if err := c.Funding.Validate(); err != nil { + if c.ExistingEnvConfig != nil { + if err := c.ExistingEnvConfig.Validate(); err != nil { return err } - if err := c.Funding.SubFunding.Validate(); err != nil { - return err - } - } else { - if c.ConsumerAddress == nil || *c.ConsumerAddress == "" { - return errors.New("consumer_address must be set when using existing environment") - } - if !common.IsHexAddress(*c.ConsumerAddress) { - return errors.New("consumer_address must be a valid hex address") - } + } + if !*c.CreateFundSubsAndAddConsumers { if c.SubID == nil { return errors.New("sub_id must be set when using existing environment") } + if *c.SubID == 0 { - return errors.New("sub_id must be a positive value") + return errors.New("sub_id must be positive value") } - } - if c.NodeSendingKeys != nil { - for _, key := range c.NodeSendingKeys { - if !common.IsHexAddress(key) { - return errors.New("node_sending_keys must be a valid hex address") - } + if c.LinkAddress != nil && !common.IsHexAddress(*c.LinkAddress) { + return errors.New("link_address must be a valid hex address") } } - return nil -} - -type NewEnvConfig struct { - *Funding -} - -func (c *NewEnvConfig) Validate() error { - if c.Funding != nil { - return c.Funding.Validate() - } - - return nil -} - -type Funding struct { - SubFunding - NodeSendingKeyFunding *float64 `toml:"node_sending_key_funding"` - NodeSendingKeyFundingMin *float64 `toml:"node_sending_key_funding_min"` -} - -func (c *Funding) Validate() error { - if c.NodeSendingKeyFunding != nil && *c.NodeSendingKeyFunding <= 0 { - return errors.New("when set node_sending_key_funding must be a positive value") - } - if c.NodeSendingKeyFundingMin != nil && *c.NodeSendingKeyFundingMin <= 0 { - return errors.New("when set node_sending_key_funding_min must be a positive value") - } - - return nil -} - -type SubFunding struct { - SubFundsLink *float64 `toml:"sub_funds_link"` -} - -func (c *SubFunding) Validate() error { - if c.SubFundsLink != nil && *c.SubFundsLink < 0 { - return errors.New("when set sub_funds_link must be a non-negative value") - } - - return nil + return c.Funding.Validate() } type General struct { - CLNodeMaxGasPriceGWei *int64 `toml:"max_gas_price_gwei"` // Max gas price in GWei for the chainlink node - LinkNativeFeedResponse *int64 `toml:"link_native_feed_response"` // Response of the LINK/ETH feed - MinimumConfirmations *uint16 `toml:"minimum_confirmations" ` // Minimum number of confirmations for the VRF Coordinator - SubscriptionFundingAmountLink *float64 `toml:"subscription_funding_amount_link"` // Amount of LINK to fund the subscription with - NumberOfWords *uint32 `toml:"number_of_words" ` // Number of words to request - CallbackGasLimit *uint32 `toml:"callback_gas_limit" ` // Gas limit for the callback - MaxGasLimitCoordinatorConfig *uint32 `toml:"max_gas_limit_coordinator_config"` // Max gas limit for the VRF Coordinator config - FallbackWeiPerUnitLink *int64 `toml:"fallback_wei_per_unit_link"` // Fallback wei per unit LINK for the VRF Coordinator config - StalenessSeconds *uint32 `toml:"staleness_seconds" ` // Staleness in seconds for the VRF Coordinator config - GasAfterPaymentCalculation *uint32 `toml:"gas_after_payment_calculation" ` // Gas after payment calculation for the VRF Coordinator - FulfillmentFlatFeeLinkPPMTier1 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_1"` - FulfillmentFlatFeeLinkPPMTier2 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_2"` - FulfillmentFlatFeeLinkPPMTier3 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_3"` - FulfillmentFlatFeeLinkPPMTier4 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_4"` - FulfillmentFlatFeeLinkPPMTier5 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_5"` - ReqsForTier2 *int64 `toml:"reqs_for_tier_2"` - ReqsForTier3 *int64 `toml:"reqs_for_tier_3"` - ReqsForTier4 *int64 `toml:"reqs_for_tier_4"` - ReqsForTier5 *int64 `toml:"reqs_for_tier_5"` - - NumberOfSubToCreate *int `toml:"number_of_sub_to_create"` // Number of subscriptions to create - - RandomnessRequestCountPerRequest *uint16 `toml:"randomness_request_count_per_request"` // How many randomness requests to send per request - RandomnessRequestCountPerRequestDeviation *uint16 `toml:"randomness_request_count_per_request_deviation"` // How many randomness requests to send per request - - RandomWordsFulfilledEventTimeout *blockchain.StrDuration `toml:"random_words_fulfilled_event_timeout"` // How long to wait for the RandomWordsFulfilled event to be emitted - - // Wrapper Config - WrapperGasOverhead *uint32 `toml:"wrapped_gas_overhead"` - CoordinatorGasOverhead *uint32 `toml:"coordinator_gas_overhead"` - WrapperPremiumPercentage *uint8 `toml:"wrapper_premium_percentage"` - WrapperMaxNumberOfWords *uint8 `toml:"wrapper_max_number_of_words"` - WrapperConsumerFundingAmountNativeToken *float64 `toml:"wrapper_consumer_funding_amount_native_token"` - WrapperConsumerFundingAmountLink *int64 `toml:"wrapper_consumer_funding_amount_link"` + *vrf_common_config.General + FulfillmentFlatFeeLinkPPMTier1 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_1"` + FulfillmentFlatFeeLinkPPMTier2 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_2"` + FulfillmentFlatFeeLinkPPMTier3 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_3"` + FulfillmentFlatFeeLinkPPMTier4 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_4"` + FulfillmentFlatFeeLinkPPMTier5 *uint32 `toml:"fulfilment_flat_fee_link_ppm_tier_5"` + ReqsForTier2 *int64 `toml:"reqs_for_tier_2"` + ReqsForTier3 *int64 `toml:"reqs_for_tier_3"` + ReqsForTier4 *int64 `toml:"reqs_for_tier_4"` + ReqsForTier5 *int64 `toml:"reqs_for_tier_5"` } func (c *General) Validate() error { - if c.CLNodeMaxGasPriceGWei == nil || *c.CLNodeMaxGasPriceGWei == 0 { - return errors.New("max_gas_price_gwei must be set to a positive value") - } - if c.LinkNativeFeedResponse == nil || *c.LinkNativeFeedResponse == 0 { - return errors.New("link_native_feed_response must be set to a positive value") - } - if c.MinimumConfirmations == nil { - return errors.New("minimum_confirmations must be set to a non-negative value") - } - if c.SubscriptionFundingAmountLink == nil || *c.SubscriptionFundingAmountLink == 0 { - return errors.New("subscription_funding_amount_link must be set to a positive value") - } - if c.NumberOfWords == nil || *c.NumberOfWords == 0 { - return errors.New("number_of_words must be set to a positive value") - } - if c.CallbackGasLimit == nil || *c.CallbackGasLimit == 0 { - return errors.New("callback_gas_limit must be set to a positive value") - } - if c.MaxGasLimitCoordinatorConfig == nil || *c.MaxGasLimitCoordinatorConfig == 0 { - return errors.New("max_gas_limit_coordinator_config must be set to a positive value") - } - if c.FallbackWeiPerUnitLink == nil || *c.FallbackWeiPerUnitLink == 0 { - return errors.New("fallback_wei_per_unit_link must be set to a positive value") - } - if c.StalenessSeconds == nil || *c.StalenessSeconds == 0 { - return errors.New("staleness_seconds must be set to a positive value") - } - if c.GasAfterPaymentCalculation == nil || *c.GasAfterPaymentCalculation == 0 { - return errors.New("gas_after_payment_calculation must be set to a positive value") + if c.General != nil { + if err := c.General.Validate(); err != nil { + return err + } } if c.FulfillmentFlatFeeLinkPPMTier1 == nil || *c.FulfillmentFlatFeeLinkPPMTier1 == 0 { return errors.New("fulfilment_flat_fee_link_ppm_tier_1 must be set to a positive value") @@ -292,39 +107,6 @@ func (c *General) Validate() error { if c.ReqsForTier5 == nil || *c.ReqsForTier5 < 0 { return errors.New("reqs_for_tier_5 must be set to a non-negative value") } - if c.NumberOfSubToCreate == nil || *c.NumberOfSubToCreate == 0 { - return errors.New("number_of_sub_to_create must be set to a positive value") - } - if c.RandomnessRequestCountPerRequest == nil || *c.RandomnessRequestCountPerRequest == 0 { - return errors.New("randomness_request_count_per_request must be set to a positive value") - } - if c.RandomnessRequestCountPerRequestDeviation == nil { - return errors.New("randomness_request_count_per_request_deviation must be set to a non-negative value") - } - if c.RandomWordsFulfilledEventTimeout == nil || c.RandomWordsFulfilledEventTimeout.Duration == 0 { - return errors.New("random_words_fulfilled_event_timeout must be set to a positive value") - } - if c.WrapperGasOverhead == nil { - return errors.New("wrapped_gas_overhead must be set to a non-negative value") - } - if c.CoordinatorGasOverhead == nil || *c.CoordinatorGasOverhead == 0 { - return errors.New("coordinator_gas_overhead must be set to a non-negative value") - } - if c.WrapperPremiumPercentage == nil || *c.WrapperPremiumPercentage == 0 { - return errors.New("wrapper_premium_percentage must be set to a positive value") - } - if c.WrapperMaxNumberOfWords == nil || *c.WrapperMaxNumberOfWords == 0 { - return errors.New("wrapper_max_number_of_words must be set to a positive value") - } - if c.WrapperConsumerFundingAmountNativeToken == nil || *c.WrapperConsumerFundingAmountNativeToken < 0 { - return errors.New("wrapper_consumer_funding_amount_native_token must be set to a non-negative value") - } - if c.WrapperConsumerFundingAmountLink == nil || *c.WrapperConsumerFundingAmountLink < 0 { - return errors.New("wrapper_consumer_funding_amount_link must be set to a non-negative value") - } - if *c.RandomnessRequestCountPerRequest <= *c.RandomnessRequestCountPerRequestDeviation { - return errors.New(ErrDeviationShouldBeLessThanOriginal) - } return nil } diff --git a/integration-tests/testconfig/vrfv2/example.toml b/integration-tests/testconfig/vrfv2/example.toml index e62e56791d..53d0888f6d 100644 --- a/integration-tests/testconfig/vrfv2/example.toml +++ b/integration-tests/testconfig/vrfv2/example.toml @@ -75,10 +75,8 @@ chainlink_node_funding = 0.5 # Product part [VRFv2] -[VRFv2.Common] -cancel_subs_after_test_run = true - [VRFv2.General] +cancel_subs_after_test_run = true max_gas_price_gwei = 1000 link_native_feed_response = 1000000000000000000 minimum_confirmations = 3 diff --git a/integration-tests/testconfig/vrfv2/vrfv2.toml b/integration-tests/testconfig/vrfv2/vrfv2.toml index 64e628c4af..3ce3135b3a 100644 --- a/integration-tests/testconfig/vrfv2/vrfv2.toml +++ b/integration-tests/testconfig/vrfv2/vrfv2.toml @@ -4,10 +4,14 @@ chainlink_node_funding = 0.1 [VRFv2] [VRFv2.General] -max_gas_price_gwei = 10 +cancel_subs_after_test_run = true +use_existing_env = false +subscription_funding_amount_link = 5.0 + +cl_node_max_gas_price_gwei = 10 link_native_feed_response = 1000000000000000000 minimum_confirmations = 3 -subscription_funding_amount_link = 5.0 + number_of_words = 3 callback_gas_limit = 1000000 max_gas_limit_coordinator_config = 2500000 @@ -34,110 +38,79 @@ wrapper_max_number_of_words = 10 wrapper_consumer_funding_amount_native_token = 1.0 wrapper_consumer_funding_amount_link = 10 -# load test specific config -[Load.VRFv2] -[Load.VRFv2.Common] -cancel_subs_after_test_run = true +# VRF Job config +vrf_job_forwarding_allowed = false +vrf_job_estimate_gas_multiplier = 1.0 +vrf_job_batch_fulfillment_enabled = false +vrf_job_batch_fulfillment_gas_multiplier = 1.15 +vrf_job_poll_period = "1s" +vrf_job_request_timeout = "24h" -[Load.VRFv2.General] -minimum_confirmations = 3 -randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 +# BHS Job config +bhs_job_wait_blocks = 30 +bhs_job_lookback_blocks = 250 +bhs_job_poll_period = "1s" +bhs_job_run_timeout = "24h" -[Load.VRFv2.Performance] -# approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds -rate_limit_unit_duration = "3s" -rps = 1 -[Load.VRFv2.NewEnv] -sub_funds_link = 1000 -node_sending_key_funding = 1000 +# PERFORMANCE test specific config -[Load.VRFv2.ExistingEnv] +[VRFv2.ExistingEnv] +coordinator_address = "" +consumer_address = "" sub_id = 1 +key_hash = "" create_fund_subs_and_add_consumers = true -sub_funds_link = 10 -node_sending_key_funding_min = 1 -node_sending_keys = [] - -# soak test specific config -[Soak.VRFv2] -[VRFv2.Common] -cancel_subs_after_test_run = true +link_address = "" +node_sending_key_funding_min = 10 +node_sending_keys = [ + "", + "", + "" +] + +#SOAK TEST CONFIG +[Soak.Common] +chainlink_node_funding = 0.1 [Soak.VRFv2.General] -minimum_confirmations = 3 randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 [Soak.VRFv2.Performance] -# 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds -rate_limit_unit_duration = "6s" +test_duration = "1m" +rate_limit_unit_duration = "3s" rps = 1 -[Soak.VRFv2.NewEnv] -sub_funds_link = 1000 -node_sending_key_funding = 1000 - -[Soak.VRFv2.ExistingEnv] -sub_id = 1 -create_fund_subs_and_add_consumers = true -sub_funds_link = 10 -node_sending_key_funding_min = 1 -node_sending_keys = [] - -# spike test specific config -[Spike.VRFv2] -[Spike.VRFv2.Common] -cancel_subs_after_test_run = true +# LOAD TEST CONFIG +[Load.Common] +chainlink_node_funding = 0.1 -[Spike.VRFv2.General] -minimum_confirmations = 3 -randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +[Load.VRFv2.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 -[Spike.VRFv2.Performance] -# approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds -rate_limit_unit_duration = "1m" +[Load.VRFv2.Performance] +test_duration = "2m" +rate_limit_unit_duration = "3s" rps = 1 -[Spike.VRFv2.NewEnv] -sub_funds_link = 1000 -node_sending_key_funding = 1000 - -[Spike.VRFv2.ExistingEnv] -sub_id = 1 -create_fund_subs_and_add_consumers = true -sub_funds_link = 10 -node_sending_key_funding_min = 1 -node_sending_keys = [] -# stress test specific config -[Stress.VRFv2] -[Stress.VRFv2.Common] -cancel_subs_after_test_run = true +# STRESS TEST CONFIG +[Stress.Common] +chainlink_node_funding = 0.1 [Stress.VRFv2.General] -minimum_confirmations = 3 -randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 [Stress.VRFv2.Performance] -# approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx -rate_limit_unit_duration = "1s" -rps = 3 - -[Stress.VRFv2.NewEnv] -sub_funds_link = 1000 -node_sending_key_funding = 1000 - -[Stress.VRFv2.ExistingEnv] -sub_id = 1 -create_fund_subs_and_add_consumers = true -sub_funds_link = 10 -node_sending_key_funding_min = 1 -node_sending_keys = [] +test_duration = "2m" +rate_limit_unit_duration = "3s" +rps = 1 diff --git a/integration-tests/testconfig/vrfv2plus/config.go b/integration-tests/testconfig/vrfv2plus/config.go index 667803e06b..fe05dbd9d1 100644 --- a/integration-tests/testconfig/vrfv2plus/config.go +++ b/integration-tests/testconfig/vrfv2plus/config.go @@ -3,7 +3,7 @@ package testconfig import ( "errors" - vrfv2 "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" + vrf_common_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/common/vrf" ) type BillingType string @@ -15,19 +15,12 @@ const ( ) type Config struct { - Common *Common `toml:"Common"` - General *General `toml:"General"` - ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnv"` - NewEnvConfig *NewEnvConfig `toml:"NewEnv"` - Performance *vrfv2.PerformanceConfig `toml:"Performance"` + General *General `toml:"General"` + ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnv"` + Performance *vrf_common_config.PerformanceConfig `toml:"Performance"` } func (c *Config) Validate() error { - if c.Common != nil { - if err := c.Common.Validate(); err != nil { - return err - } - } if c.General != nil { if err := c.General.Validate(); err != nil { return err @@ -37,41 +30,24 @@ func (c *Config) Validate() error { if err := c.Performance.Validate(); err != nil { return err } - if *c.Performance.UseExistingEnv { - if c.ExistingEnvConfig != nil { - if err := c.ExistingEnvConfig.Validate(); err != nil { - return err - } - } - } else { - if c.NewEnvConfig != nil { - if err := c.NewEnvConfig.Validate(); err != nil { - return err - } - } + } + if c.ExistingEnvConfig != nil && *c.General.UseExistingEnv { + if err := c.ExistingEnvConfig.Validate(); err != nil { + return err } } - return nil } -type Common struct { - *vrfv2.Common -} - -func (c *Common) Validate() error { - if c.Common == nil { - return nil - } - return c.Common.Validate() -} - type General struct { - *vrfv2.General - SubscriptionBillingType *string `toml:"subscription_billing_type"` // Billing type for the subscription - SubscriptionFundingAmountNative *float64 `toml:"subscription_funding_amount_native"` // Amount of LINK to fund the subscription with - FulfillmentFlatFeeLinkPPM *uint32 `toml:"fulfillment_flat_fee_link_ppm"` // Flat fee in ppm for LINK for the VRF Coordinator config - FulfillmentFlatFeeNativePPM *uint32 `toml:"fulfillment_flat_fee_native_ppm"` // Flat fee in ppm for native currency for the VRF Coordinator config + *vrf_common_config.General + SubscriptionBillingType *string `toml:"subscription_billing_type"` // Billing type for the subscription + SubscriptionFundingAmountNative *float64 `toml:"subscription_funding_amount_native"` // Amount of LINK to fund the subscription with + FulfillmentFlatFeeNativePPM *uint32 `toml:"fulfillment_flat_fee_native_ppm"` // Flat fee in ppm for native currency for the VRF Coordinator config + FulfillmentFlatFeeLinkPPM *uint32 `toml:"fulfillment_flat_fee_link_ppm"` // Flat fee in ppm for LINK for the VRF Coordinator config + FulfillmentFlatFeeLinkDiscountPPM *uint32 `toml:"fulfillment_flat_fee_link_discount_ppm"` // Flat fee discount in ppm for LINK for the VRF Coordinator config + NativePremiumPercentage *uint8 `toml:"native_premium_percentage"` // Native Premium Percentage + LinkPremiumPercentage *uint8 `toml:"link_premium_percentage"` // LINK Premium Percentage } func (c *General) Validate() error { @@ -84,31 +60,27 @@ func (c *General) Validate() error { if c.SubscriptionFundingAmountNative == nil || *c.SubscriptionFundingAmountNative <= 0 { return errors.New("subscription_funding_amount_native must be greater than 0") } - if c.FulfillmentFlatFeeLinkPPM == nil || *c.FulfillmentFlatFeeLinkPPM <= 0 { - return errors.New("fulfillment_flat_fee_link_ppm must be greater than 0") + if c.FulfillmentFlatFeeNativePPM == nil { + return errors.New("fulfillment_flat_fee_native_ppm must not be nil") } - if c.FulfillmentFlatFeeNativePPM == nil || *c.FulfillmentFlatFeeNativePPM <= 0 { - return errors.New("fulfillment_flat_fee_native_ppm must be greater than 0") + if c.FulfillmentFlatFeeLinkPPM == nil { + return errors.New("fulfillment_flat_fee_link_ppm must not be nil") } - - return nil -} - -type NewEnvConfig struct { - *Funding -} - -func (c *NewEnvConfig) Validate() error { - if c.Funding == nil { - return nil + if c.FulfillmentFlatFeeLinkDiscountPPM == nil { + return errors.New("fulfillment_flat_fee_link_discount_ppm must not be nil") } - - return c.Funding.Validate() + if c.NativePremiumPercentage == nil { + return errors.New("native_premium_percentage must not be nil") + } + if c.LinkPremiumPercentage == nil { + return errors.New("link_premium_percentage must not be nil") + } + return nil } type ExistingEnvConfig struct { - *vrfv2.ExistingEnvConfig - Funding + *vrf_common_config.ExistingEnvConfig + SubID *string `toml:"sub_id"` } func (c *ExistingEnvConfig) Validate() error { @@ -117,42 +89,10 @@ func (c *ExistingEnvConfig) Validate() error { return err } } - - return c.Funding.Validate() -} - -type Funding struct { - SubFunding - NodeSendingKeyFunding *float64 `toml:"node_sending_key_funding"` - NodeSendingKeyFundingMin *float64 `toml:"node_sending_key_funding_min"` -} - -func (c *Funding) Validate() error { - if c.NodeSendingKeyFunding != nil && *c.NodeSendingKeyFunding <= 0 { - return errors.New("when set node_sending_key_funding must be a positive value") - } - if c.NodeSendingKeyFundingMin != nil && *c.NodeSendingKeyFundingMin <= 0 { - return errors.New("when set node_sending_key_funding_min must be a positive value") - } - - return c.SubFunding.Validate() -} - -type SubFunding struct { - SubFundsLink *float64 `toml:"sub_funds_link"` - SubFundsNative *float64 `toml:"sub_funds_native"` -} - -func (c *SubFunding) Validate() error { - if c.SubFundsLink == nil || c.SubFundsNative == nil { - return errors.New("both sub_funds_link and sub_funds_native must be set") - } - if c.SubFundsLink != nil && *c.SubFundsLink < 0 { - return errors.New("sub_funds_link must be a non-negative number") - } - if c.SubFundsNative != nil && *c.SubFundsNative < 0 { - return errors.New("sub_funds_native must be a non-negative number") + if !*c.CreateFundSubsAndAddConsumers { + if c.SubID == nil && *c.SubID == "" { + return errors.New("sub_id must be set when using existing environment") + } } - - return nil + return c.Funding.Validate() } diff --git a/integration-tests/testconfig/vrfv2plus/example.toml b/integration-tests/testconfig/vrfv2plus/example.toml index 4e1b7d3785..2ef1c27d39 100644 --- a/integration-tests/testconfig/vrfv2plus/example.toml +++ b/integration-tests/testconfig/vrfv2plus/example.toml @@ -75,10 +75,8 @@ chainlink_node_funding = 0.5 # Product part [VRFv2Plus] -[VRFv2Plus.Common] -cancel_subs_after_test_run = true - [VRFv2Plus.General] +cancel_subs_after_test_run = true max_gas_price_gwei = 1000 link_native_feed_response = 1000000000000000000 minimum_confirmations = 3 @@ -112,6 +110,9 @@ wrapper_consumer_funding_amount_link = 10 subscription_funding_amount_native=1 fulfillment_flat_fee_link_ppm=500 fulfillment_flat_fee_native_ppm=500 +fulfillment_flat_fee_link_discount_ppm=100 +native_premium_percentage=1 +link_premium_percentage=1 [VRFv2Plus.Performance] test_duration = "2m" diff --git a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml index 5e187d9de3..96b1a3f722 100644 --- a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml +++ b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml @@ -4,26 +4,22 @@ chainlink_node_funding = 0.1 [VRFv2Plus] [VRFv2Plus.General] -max_gas_price_gwei = 10 +cancel_subs_after_test_run = true +use_existing_env = false +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native=1 + +cl_node_max_gas_price_gwei = 10 link_native_feed_response = 1000000000000000000 minimum_confirmations = 3 subscription_billing_type = "LINK_AND_NATIVE" -subscription_funding_amount_link = 5.0 + number_of_words = 3 callback_gas_limit = 1000000 max_gas_limit_coordinator_config = 2500000 fallback_wei_per_unit_link = 60000000000000000 staleness_seconds = 86400 gas_after_payment_calculation = 33825 -fulfilment_flat_fee_link_ppm_tier_1 = 500 -fulfilment_flat_fee_link_ppm_tier_2 = 500 -fulfilment_flat_fee_link_ppm_tier_3 = 500 -fulfilment_flat_fee_link_ppm_tier_4 = 500 -fulfilment_flat_fee_link_ppm_tier_5 = 500 -reqs_for_tier_2 = 0 -reqs_for_tier_3 = 0 -reqs_for_tier_4 = 0 -reqs_for_tier_5 = 0 number_of_sub_to_create = 1 randomness_request_count_per_request = 1 randomness_request_count_per_request_deviation = 0 @@ -34,128 +30,83 @@ wrapper_premium_percentage = 25 wrapper_max_number_of_words = 10 wrapper_consumer_funding_amount_native_token = 1.0 wrapper_consumer_funding_amount_link = 10 -subscription_funding_amount_native=1 fulfillment_flat_fee_link_ppm=500 fulfillment_flat_fee_native_ppm=500 - -# load test specific config -[Load.VRFv2Plus] -[Load.VRFv2Plus.Common] -cancel_subs_after_test_run = true - -[Load.VRFv2Plus.General] -minimum_confirmations = 3 -randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 - -[Load.VRFv2Plus.Performance] -test_duration = "2m" -# approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds -rate_limit_unit_duration = "3s" -rps = 1 - -[Load.VRFv2Plus.NewEnv] -sub_funds_link = 1 -sub_funds_native = 1 -node_funds = 10 -node_sending_key_funding = 1000 - -[Load.VRFv2Plus.ExistingEnv] -sub_id = 1 +fulfillment_flat_fee_link_discount_ppm=100 +native_premium_percentage=1 +link_premium_percentage=1 + +# VRF Job config +vrf_job_forwarding_allowed = false +vrf_job_estimate_gas_multiplier = 1.1 +vrf_job_batch_fulfillment_enabled = false +vrf_job_batch_fulfillment_gas_multiplier = 1.15 +vrf_job_poll_period = "1s" +vrf_job_request_timeout = "24h" + +# BHS Job config +bhs_job_wait_blocks = 30 +bhs_job_lookback_blocks = 250 +bhs_job_poll_period = "1s" +bhs_job_run_timeout = "24h" + +# PERFORMANCE test specific config + +[VRFv2Plus.ExistingEnv] +coordinator_address = "" +consumer_address = "" +sub_id = "" +key_hash = "" create_fund_subs_and_add_consumers = true link_address = "" -sub_funds_link = 10 node_sending_key_funding_min = 1 node_sending_keys = [] -# soak test specific config -[Soak.VRFv2Plus] -[Soak.VRFv2Plus.Common] -cancel_subs_after_test_run = true +#SOAK TEST CONFIG +[Soak.Common] +chainlink_node_funding = 0.1 [Soak.VRFv2Plus.General] -minimum_confirmations = 3 -randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native=1 [Soak.VRFv2Plus.Performance] test_duration = "2m" -# 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds -rate_limit_unit_duration = "6s" +rate_limit_unit_duration = "3s" rps = 1 -use_existing_env = false - -[Soak.VRFv2Plus.NewEnv] -sub_funds_link = 1 -sub_funds_native = 1 -node_funds = 10 -node_sending_key_funding = 1000 - -[Soak.VRFv2Plus.ExistingEnv] -sub_id = 1 -create_fund_subs_and_add_consumers = true -sub_funds_link = 10 -node_sending_key_funding_min = 1 -node_sending_keys = [] -# spike test specific config -[Spike.VRFv2Plus] -[Spike.VRFv2Plus.Common] -cancel_subs_after_test_run = true +# LOAD TEST CONFIG +[Load.Common] +chainlink_node_funding = 0.1 -[Spike.VRFv2Plus.General] -minimum_confirmations = 3 -randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +[Load.VRFv2Plus.General] +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native=1 -[Spike.VRFv2Plus.Performance] +[Load.VRFv2Plus.Performance] test_duration = "2m" -# approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds -rate_limit_unit_duration = "1m" +rate_limit_unit_duration = "3s" rps = 1 -[Spike.VRFv2Plus.NewEnv] -sub_funds_link = 1 -sub_funds_native = 1 -node_funds = 10 -node_sending_key_funding = 1000 -[Spike.VRFv2Plus.ExistingEnv] -sub_id = 1 -create_fund_subs_and_add_consumers = true -sub_funds_link = 10 -node_sending_key_funding_min = 1 -node_sending_keys = [] - -# stress test specific config -[Stress.VRFv2Plus] -[Stress.VRFv2Plus.Common] -cancel_subs_after_test_run = true +# STRESS TEST CONFIG +[Stress.Common] +chainlink_node_funding = 0.1 [Stress.VRFv2Plus.General] -minimum_confirmations = 3 -randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request -randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 +subscription_funding_amount_link = 5.0 +subscription_funding_amount_native=1 [Stress.VRFv2Plus.Performance] test_duration = "2m" -# approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx -rate_limit_unit_duration = "1s" -rps = 3 - -[Stress.VRFv2Plus.NewEnv] -sub_funds_link = 1 -sub_funds_native = 1 -node_funds = 10 -node_sending_key_funding = 1000 - -[Stress.VRFv2Plus.ExistingEnv] -sub_id = 1 -create_fund_subs_and_add_consumers = true -sub_funds_link = 10 -node_sending_key_funding_min = 1 -node_sending_keys = [] \ No newline at end of file +rate_limit_unit_duration = "3s" +rps = 1 diff --git a/integration-tests/testreporters/vrfv2.go b/integration-tests/testreporters/vrfv2.go index 4f4c7dc8fd..d94c66abc5 100644 --- a/integration-tests/testreporters/vrfv2.go +++ b/integration-tests/testreporters/vrfv2.go @@ -77,7 +77,7 @@ func (o *VRFV2TestReporter) SendSlackNotification(t *testing.T, slackClient *sla "RandomnessRequestCountPerRequestDeviation: %d\n", o.TestType, perfCfg.TestDuration.Duration.Truncate(time.Second).String(), - *perfCfg.UseExistingEnv, + *o.VRFv2TestConfig.GetVRFv2Config().General.UseExistingEnv, o.RequestCount.String(), o.FulfilmentCount.String(), o.AverageFulfillmentInMillions.String(), diff --git a/integration-tests/testreporters/vrfv2plus.go b/integration-tests/testreporters/vrfv2plus.go index 8d384b0786..ddbf1f35e2 100644 --- a/integration-tests/testreporters/vrfv2plus.go +++ b/integration-tests/testreporters/vrfv2plus.go @@ -15,31 +15,30 @@ import ( ) type VRFV2PlusTestReporter struct { - TestType string - RequestCount *big.Int - FulfilmentCount *big.Int - AverageFulfillmentInMillions *big.Int - SlowestFulfillment *big.Int - FastestFulfillment *big.Int - VRFv2PlusTestConfig types.VRFv2PlusTestConfig + TestType string + LoadTestMetrics VRFLoadTestMetrics + VRFv2PlusTestConfig types.VRFv2PlusTestConfig +} + +type VRFLoadTestMetrics struct { + RequestCount *big.Int + FulfilmentCount *big.Int + AverageFulfillmentInMillions *big.Int + SlowestFulfillment *big.Int + FastestFulfillment *big.Int + AverageResponseTimeInSecondsMillions *big.Int + SlowestResponseTimeInSeconds *big.Int + FastestResponseTimeInSeconds *big.Int } func (o *VRFV2PlusTestReporter) SetReportData( testType string, - RequestCount *big.Int, - FulfilmentCount *big.Int, - AverageFulfillmentInMillions *big.Int, - SlowestFulfillment *big.Int, - FastestFulfillment *big.Int, - vtfv2PlusTestConfig types.VRFv2PlusTestConfig, + metrics VRFLoadTestMetrics, + testConfig types.VRFv2PlusTestConfig, ) { o.TestType = testType - o.RequestCount = RequestCount - o.FulfilmentCount = FulfilmentCount - o.AverageFulfillmentInMillions = AverageFulfillmentInMillions - o.SlowestFulfillment = SlowestFulfillment - o.FastestFulfillment = FastestFulfillment - o.VRFv2PlusTestConfig = vtfv2PlusTestConfig + o.LoadTestMetrics = metrics + o.VRFv2PlusTestConfig = testConfig } // SendSlackNotification sends a slack message to a slack webhook @@ -63,21 +62,27 @@ func (o *VRFV2PlusTestReporter) SendSlackNotification(t *testing.T, slackClient "Use Existing Env: %t\n"+ "Request Count: %s\n"+ "Fulfilment Count: %s\n"+ - "AverageFulfillmentInMillions: %s\n"+ - "Slowest Fulfillment: %s\n"+ - "Fastest Fulfillment: %s \n"+ + "AverageFulfillmentInMillions (blocks): %s\n"+ + "Slowest Fulfillment (blocks): %s\n"+ + "Fastest Fulfillment (blocks): %s \n"+ + "AverageFulfillmentInMillions (seconds): %s\n"+ + "Slowest Fulfillment (seconds): %s\n"+ + "Fastest Fulfillment (seconds): %s \n"+ "RPS: %d\n"+ "RateLimitUnitDuration: %s\n"+ "RandomnessRequestCountPerRequest: %d\n"+ "RandomnessRequestCountPerRequestDeviation: %d\n", o.TestType, vrfv2lusConfig.TestDuration.Duration.Truncate(time.Second).String(), - *vrfv2lusConfig.UseExistingEnv, - o.RequestCount.String(), - o.FulfilmentCount.String(), - o.AverageFulfillmentInMillions.String(), - o.SlowestFulfillment.String(), - o.FastestFulfillment.String(), + *o.VRFv2PlusTestConfig.GetVRFv2PlusConfig().General.UseExistingEnv, + o.LoadTestMetrics.RequestCount.String(), + o.LoadTestMetrics.FulfilmentCount.String(), + o.LoadTestMetrics.AverageFulfillmentInMillions.String(), + o.LoadTestMetrics.SlowestFulfillment.String(), + o.LoadTestMetrics.FastestFulfillment.String(), + o.LoadTestMetrics.AverageResponseTimeInSecondsMillions.String(), + o.LoadTestMetrics.SlowestResponseTimeInSeconds.String(), + o.LoadTestMetrics.FastestResponseTimeInSeconds.String(), *vrfv2lusConfig.RPS, vrfv2lusConfig.RateLimitUnitDuration.String(), *o.VRFv2PlusTestConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequest, diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index a2e2fe42ba..f3a7a77e8c 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" "github.com/stretchr/testify/require" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" @@ -39,11 +40,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" + actions_seth "github.com/smartcontractkit/chainlink/integration-tests/actions/seth" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" tt "github.com/smartcontractkit/chainlink/integration-tests/types" + "github.com/smartcontractkit/chainlink/integration-tests/utils" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) @@ -58,6 +61,7 @@ type OCRSoakTest struct { Config *tc.TestConfig TestReporter testreporters.OCRSoakTestReporter OperatorForwarderFlow bool + seth *seth.Client t *testing.T startTime time.Time @@ -68,7 +72,6 @@ type OCRSoakTest struct { log zerolog.Logger bootstrapNode *client.ChainlinkK8sClient workerNodes []*client.ChainlinkK8sClient - chainClient blockchain.EVMClient mockServer *ctfClient.MockserverClient filterQuery geth.FilterQuery @@ -152,21 +155,6 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string, ocrTe o.namespace = testEnvironment.Cfg.Namespace } -// LoadEnvironment loads an existing test environment using the provided URLs -func (o *OCRSoakTest) LoadEnvironment(chainlinkURLs []string, mockServerURL string, ocrTestConfig tt.OcrTestConfig) { - var ( - network = networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] - err error - ) - o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) - require.NoError(o.t, err, "Error connecting to EVM client") - chainlinkNodes, err := client.ConnectChainlinkNodeURLs(chainlinkURLs) - require.NoError(o.t, err, "Error connecting to chainlink nodes") - o.bootstrapNode, o.workerNodes = chainlinkNodes[0], chainlinkNodes[1:] - o.mockServer, err = ctfClient.ConnectMockServerURL(mockServerURL) - require.NoError(o.t, err, "Error connecting to mockserver") -} - // Environment returns the full K8s test environment func (o *OCRSoakTest) Environment() *environment.Environment { return o.testEnvironment @@ -178,89 +166,102 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { network = networks.MustGetSelectedNetworkConfig(ocrTestConfig.GetNetworkConfig())[0] ) - // Environment currently being used to soak test on - // Make connections to soak test resources - o.chainClient, err = blockchain.NewEVMClient(network, o.testEnvironment, o.log) - require.NoError(o.t, err, "Error creating EVM client") - contractDeployer, err := contracts.NewContractDeployer(o.chainClient, o.log) - require.NoError(o.t, err, "Unable to create contract deployer") - require.NotNil(o.t, contractDeployer, "Contract deployer shouldn't be nil") + network = utils.MustReplaceSimulatedNetworkUrlWithK8(o.log, network, *o.testEnvironment) + readSethCfg := ocrTestConfig.GetSethConfig() + require.NotNil(o.t, readSethCfg, "Seth config shouldn't be nil") + + sethCfg := utils.MergeSethAndEvmNetworkConfigs(o.log, network, *readSethCfg) + + seth, err := seth.NewClientWithConfig(&sethCfg) + require.NoError(o.t, err, "Error creating seth client") + + o.seth = seth + nodes, err := client.ConnectChainlinkNodes(o.testEnvironment) require.NoError(o.t, err, "Connecting to chainlink nodes shouldn't fail") o.bootstrapNode, o.workerNodes = nodes[0], nodes[1:] o.mockServer, err = ctfClient.ConnectMockServer(o.testEnvironment) require.NoError(o.t, err, "Creating mockserver clients shouldn't fail") - o.chainClient.ParallelTransactions(true) + // Deploy LINK - linkTokenContract, err := contractDeployer.DeployLinkTokenContract() - require.NoError(o.t, err, "Deploying Link Token Contract shouldn't fail") + linkDeploymentData, err := contracts.DeployLinkTokenContract(seth) + require.NoError(o.t, err, "Error deploying LINK contract") // Fund Chainlink nodes, excluding the bootstrap node o.log.Info().Float64("ETH amount per node", *o.Config.Common.ChainlinkNodeFunding).Msg("Funding Chainlink nodes") - err = actions.FundChainlinkNodes(o.workerNodes, o.chainClient, big.NewFloat(*o.Config.Common.ChainlinkNodeFunding)) + err = actions_seth.FundChainlinkNodesFromRootAddress(o.log, seth, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(o.workerNodes), big.NewFloat(*o.Config.Common.ChainlinkNodeFunding)) require.NoError(o.t, err, "Error funding Chainlink nodes") - if o.OperatorForwarderFlow { - contractLoader, err := contracts.NewContractLoader(o.chainClient, o.log) - require.NoError(o.t, err, "Loading contracts shouldn't fail") + var forwarders []common.Address - operators, authorizedForwarders, _ := actions.DeployForwarderContracts( - o.t, contractDeployer, linkTokenContract, o.chainClient, len(o.workerNodes), + if o.OperatorForwarderFlow { + var operators []common.Address + operators, forwarders, _ = actions_seth.DeployForwarderContracts( + o.t, o.seth, linkDeploymentData, len(o.workerNodes), ) + require.Equal(o.t, len(o.workerNodes), len(operators), "Number of operators should match number of nodes") + require.Equal(o.t, len(o.workerNodes), len(forwarders), "Number of authorized forwarders should match number of nodes") forwarderNodesAddresses, err := actions.ChainlinkNodeAddresses(o.workerNodes) require.NoError(o.t, err, "Retrieving on-chain wallet addresses for chainlink nodes shouldn't fail") for i := range o.workerNodes { - actions.AcceptAuthorizedReceiversOperator( - o.t, operators[i], authorizedForwarders[i], []common.Address{forwarderNodesAddresses[i]}, o.chainClient, contractLoader, - ) + actions_seth.AcceptAuthorizedReceiversOperator( + o.t, o.log, o.seth, operators[i], forwarders[i], []common.Address{forwarderNodesAddresses[i]}) require.NoError(o.t, err, "Accepting Authorize Receivers on Operator shouldn't fail") - actions.TrackForwarder(o.t, o.chainClient, authorizedForwarders[i], o.workerNodes[i]) - err = o.chainClient.WaitForEvents() - } - o.ocrV1Instances = actions.DeployOCRContractsForwarderFlow( - o.t, - *o.Config.OCR.Soak.NumberOfContracts, - linkTokenContract, - contractDeployer, - o.workerNodes, - authorizedForwarders, - o.chainClient, - ) + actions_seth.TrackForwarder(o.t, o.seth, forwarders[i], o.workerNodes[i]) + } } else if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "1" { - o.ocrV1Instances, err = actions.DeployOCRContracts( - *o.Config.OCR.Soak.NumberOfContracts, - linkTokenContract, - contractDeployer, - o.workerNodes, - o.chainClient, - ) - require.NoError(o.t, err) + if o.OperatorForwarderFlow { + o.ocrV1Instances, err = actions_seth.DeployOCRContractsForwarderFlow( + o.log, + o.seth, + *o.Config.OCR.Soak.NumberOfContracts, + linkDeploymentData.Address, + contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(o.workerNodes), + forwarders, + ) + require.NoError(o.t, err, "Error deploying OCR Forwarder contracts") + } else { + o.ocrV1Instances, err = actions_seth.DeployOCRv1Contracts( + o.log, + seth, + *o.Config.OCR.Soak.NumberOfContracts, + linkDeploymentData.Address, + contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(o.workerNodes), + ) + require.NoError(o.t, err) + } } else if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "2" { var transmitters []string - for _, node := range o.workerNodes { - nodeAddress, err := node.PrimaryEthAddress() - require.NoError(o.t, err, "Error getting node's primary ETH address") - transmitters = append(transmitters, nodeAddress) + + if o.OperatorForwarderFlow { + for _, forwarder := range forwarders { + transmitters = append(transmitters, forwarder.Hex()) + } + } else { + for _, node := range o.workerNodes { + nodeAddress, err := node.PrimaryEthAddress() + require.NoError(o.t, err, "Error getting node's primary ETH address") + transmitters = append(transmitters, nodeAddress) + } } + ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() - o.ocrV2Instances, err = actions.DeployOCRv2Contracts( + o.ocrV2Instances, err = actions_seth.DeployOCRv2Contracts( + o.log, + o.seth, *ocrTestConfig.GetOCRConfig().Soak.NumberOfContracts, - linkTokenContract, - contractDeployer, + linkDeploymentData.Address, transmitters, - o.chainClient, 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(o.chainClient, contractConfig, o.ocrV2Instances) + err = actions_seth.ConfigureOCRv2AggregatorContracts(contractConfig, o.ocrV2Instances) require.NoError(o.t, err, "Error configuring OCRv2 aggregator contracts") } - err = o.chainClient.WaitForEvents() - require.NoError(o.t, err, "Error waiting for OCR contracts to be deployed") if *ocrTestConfig.GetOCRConfig().Soak.OCRVersion == "1" { for _, ocrInstance := range o.ocrV1Instances { o.ocrV1InstanceMap[ocrInstance.Address()] = ocrInstance @@ -280,19 +281,23 @@ func (o *OCRSoakTest) Run() { require.NoError(o.t, err, "Error getting config") ctx, cancel := context.WithTimeout(testcontext.Get(o.t), time.Second*5) - latestBlockNum, err := o.chainClient.LatestBlockNumber(ctx) + latestBlockNum, err := o.seth.Client.BlockNumber(ctx) cancel() require.NoError(o.t, err, "Error getting current block number") o.startingBlockNum = latestBlockNum startingValue := 5 if o.OperatorForwarderFlow { - actions.CreateOCRJobsWithForwarder(o.t, o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) + actions.CreateOCRJobsWithForwarder(o.t, o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.seth.ChainID) } else if *config.OCR.Soak.OCRVersion == "1" { - err := actions.CreateOCRJobs(o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, o.chainClient.GetChainID().String()) + ctx, cancel := context.WithTimeout(testcontext.Get(o.t), time.Second*5) + chainId, err := o.seth.Client.ChainID(ctx) + cancel() + require.NoError(o.t, err, "Error getting chain ID") + err = actions.CreateOCRJobs(o.ocrV1Instances, o.bootstrapNode, o.workerNodes, startingValue, o.mockServer, chainId.String()) require.NoError(o.t, err, "Error creating OCR jobs") } else if *config.OCR.Soak.OCRVersion == "2" { - err := actions.CreateOCRv2Jobs(o.ocrV2Instances, o.bootstrapNode, o.workerNodes, o.mockServer, startingValue, o.chainClient.GetChainID().Uint64(), o.OperatorForwarderFlow) + err := actions.CreateOCRv2Jobs(o.ocrV2Instances, o.bootstrapNode, o.workerNodes, o.mockServer, startingValue, o.seth.ChainID, o.OperatorForwarderFlow) require.NoError(o.t, err, "Error creating OCR jobs") } @@ -309,13 +314,13 @@ func (o *OCRSoakTest) Run() { // Networks returns the networks that the test is running on func (o *OCRSoakTest) TearDownVals(t *testing.T) ( *testing.T, + *seth.Client, string, []*client.ChainlinkK8sClient, reportModel.TestReporter, reportModel.GrafanaURLProvider, - blockchain.EVMClient, ) { - return t, o.namespace, append(o.workerNodes, o.bootstrapNode), &o.TestReporter, o.Config, o.chainClient + return t, o.seth, o.namespace, append(o.workerNodes, o.bootstrapNode), &o.TestReporter, o.Config } // ********************* @@ -359,7 +364,6 @@ func (o *OCRSoakTest) SaveState() error { OCRContractAddresses: ocrAddresses, OCRVersion: *o.Config.OCR.Soak.OCRVersion, - ChainURL: o.chainClient.GetNetworkConfig().URL, MockServerURL: "http://mockserver:1080", // TODO: Make this dynamic BootStrapNodeURL: o.bootstrapNode.URL(), WorkerNodeURLs: workerNodeURLs, @@ -415,15 +419,6 @@ func (o *OCRSoakTest) LoadState() error { o.startingBlockNum = testState.StartingBlockNum o.Config.OCR.Soak.OCRVersion = &testState.OCRVersion - network := networks.MustGetSelectedNetworkConfig(o.Config.Network)[0] - o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) - if err != nil { - return err - } - contractDeployer, err := contracts.NewContractDeployer(o.chainClient, o.log) - if err != nil { - return err - } o.bootstrapNode, err = client.ConnectChainlinkNodeURL(testState.BootStrapNodeURL) if err != nil { return err @@ -436,22 +431,20 @@ func (o *OCRSoakTest) LoadState() error { if testState.OCRVersion == "1" { o.ocrV1Instances = make([]contracts.OffchainAggregator, len(testState.OCRContractAddresses)) for i, addr := range testState.OCRContractAddresses { - address := common.HexToAddress(addr) - instance, err := contractDeployer.LoadOffChainAggregator(&address) + instance, err := contracts.LoadOffchainAggregator(o.log, o.seth, common.HexToAddress(addr)) if err != nil { - return err + return fmt.Errorf("failed to instantiate OCR instance: %w", err) } - o.ocrV1Instances[i] = instance + o.ocrV1Instances[i] = &instance } } else if testState.OCRVersion == "2" { o.ocrV2Instances = make([]contracts.OffchainAggregatorV2, len(testState.OCRContractAddresses)) for i, addr := range testState.OCRContractAddresses { - address := common.HexToAddress(addr) - instance, err := contractDeployer.LoadOffChainAggregatorV2(&address) + instance, err := contracts.LoadOffChainAggregatorV2(o.log, o.seth, common.HexToAddress(addr)) if err != nil { return err } - o.ocrV2Instances[i] = instance + o.ocrV2Instances[i] = &instance } } @@ -565,16 +558,6 @@ func (o *OCRSoakTest) testLoop(testDuration time.Duration, newValue int) { newValue = rand.Intn(256) + 1 // #nosec G404 - kudos to you if you actually find a way to exploit this } lastValue = newValue - case t := <-o.chainClient.ConnectionIssue(): - o.testIssues = append(o.testIssues, &testreporters.TestIssue{ - StartTime: t, - Message: "RPC Connection Lost", - }) - case t := <-o.chainClient.ConnectionRestored(): - o.testIssues = append(o.testIssues, &testreporters.TestIssue{ - StartTime: t, - Message: "RPC Connection Restored", - }) } } } @@ -612,7 +595,7 @@ func (o *OCRSoakTest) setFilterQuery() { func (o *OCRSoakTest) observeOCREvents() error { eventLogs := make(chan types.Log) ctx, cancel := context.WithTimeout(testcontext.Get(o.t), 5*time.Second) - eventSub, err := o.chainClient.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) + eventSub, err := o.seth.Client.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) cancel() if err != nil { return err @@ -664,7 +647,7 @@ func (o *OCRSoakTest) observeOCREvents() error { Interface("Query", o.filterQuery). Msg("Error while subscribed to OCR Logs. Resubscribing") ctx, cancel = context.WithTimeout(testcontext.Get(o.t), backoff) - eventSub, err = o.chainClient.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) + eventSub, err = o.seth.Client.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) cancel() if err != nil { time.Sleep(backoff) @@ -729,12 +712,12 @@ func (o *OCRSoakTest) collectEvents() error { o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") ctx, cancel := context.WithTimeout(testcontext.Get(o.t), timeout) - contractEvents, err := o.chainClient.FilterLogs(ctx, o.filterQuery) + contractEvents, err := o.seth.Client.FilterLogs(ctx, o.filterQuery) cancel() for err != nil { o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") ctx, cancel := context.WithTimeout(testcontext.Get(o.t), timeout) - contractEvents, err = o.chainClient.FilterLogs(ctx, o.filterQuery) + contractEvents, err = o.seth.Client.FilterLogs(ctx, o.filterQuery) cancel() if err != nil { o.log.Warn().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Error collecting on-chain events, trying again") diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 3508f77555..024a05f63e 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -188,7 +188,7 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { HistoryDepth: ptr.Ptr(uint32(100)), }, GasEstimator: evmcfg.GasEstimator{ - LimitDefault: ptr.Ptr(uint32(6000000)), + LimitDefault: ptr.Ptr(uint64(6000000)), PriceMax: assets.GWei(200), FeeCapDefault: assets.GWei(200), }, @@ -223,7 +223,7 @@ func WithVRFv2EVMEstimator(addresses []string, maxGasPriceGWei int64) NodeConfig return func(c *chainlink.Config) { c.EVM[0].KeySpecific = keySpecicifArr c.EVM[0].Chain.GasEstimator = evmcfg.GasEstimator{ - LimitDefault: ptr.Ptr[uint32](3500000), + LimitDefault: ptr.Ptr[uint64](3500000), } c.EVM[0].Chain.Transactions = evmcfg.Transactions{ MaxQueued: ptr.Ptr[uint32](10000), diff --git a/integration-tests/types/testconfigs.go b/integration-tests/types/testconfigs.go index 0c704f0cd7..cb36a1d3e8 100644 --- a/integration-tests/types/testconfigs.go +++ b/integration-tests/types/testconfigs.go @@ -41,4 +41,11 @@ type OcrTestConfig interface { tc.GlobalTestConfig tc.CommonTestConfig tc.OcrTestConfig + tc.SethConfig +} + +type Ocr2TestConfig interface { + tc.GlobalTestConfig + tc.CommonTestConfig + tc.Ocr2TestConfig } diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index db7eaee625..28441ccf45 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -31,6 +31,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -894,7 +895,7 @@ type PauseData struct { var ChaosPauses = []PauseData{} // chaosPauseSyncFn pauses ranom container of the provided type for a random amount of time between 5 and 20 seconds -func chaosPauseSyncFn(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, targetComponent string) ChaosPauseData { +func chaosPauseSyncFn(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, testConfig *tc.TestConfig, targetComponent string) ChaosPauseData { rand.New(rand.NewSource(time.Now().UnixNano())) randomNode := testEnv.ClCluster.Nodes[rand.Intn(len(testEnv.ClCluster.Nodes)-1)+1] @@ -909,8 +910,15 @@ func chaosPauseSyncFn(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, targ return ChaosPauseData{Err: fmt.Errorf("unknown component %s", targetComponent)} } - ctx := context.Background() - pauseStartBlock, err := testEnv.EVMClient.LatestBlockNumber(ctx) + network := networks.MustGetSelectedNetworkConfig(testConfig.GetNetworkConfig())[0] + evmClient, err := testEnv.GetEVMClient(network.ChainID) + if err != nil { + return ChaosPauseData{Err: err} + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + pauseStartBlock, err := evmClient.LatestBlockNumber(ctx) if err != nil { return ChaosPauseData{Err: err} } @@ -923,7 +931,10 @@ func chaosPauseSyncFn(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, targ } l.Info().Str("Container", component.ContainerName).Msg("Component unpaused") - pauseEndBlock, err := testEnv.EVMClient.LatestBlockNumber(ctx) + ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + pauseEndBlock, err := evmClient.LatestBlockNumber(ctx) if err != nil { return ChaosPauseData{Err: err} } @@ -942,20 +953,20 @@ type ChaosPauseData struct { } // ExecuteChaosExperiment executes the configured chaos experiment, which consist of pausing CL node or Postgres containers -func ExecuteChaosExperiment(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, cfg *lp_config.Config, errorCh chan error) { - if cfg.ChaosConfig == nil || *cfg.ChaosConfig.ExperimentCount == 0 { +func ExecuteChaosExperiment(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, testConfig *tc.TestConfig, errorCh chan error) { + if testConfig == nil || testConfig.LogPoller.ChaosConfig == nil || *testConfig.LogPoller.ChaosConfig.ExperimentCount == 0 { errorCh <- nil return } - chaosChan := make(chan ChaosPauseData, *cfg.ChaosConfig.ExperimentCount) + chaosChan := make(chan ChaosPauseData, *testConfig.LogPoller.ChaosConfig.ExperimentCount) wg := &sync.WaitGroup{} go func() { // if we wanted to have more than 1 container paused, we'd need to make sure we aren't trying to pause an already paused one guardChan := make(chan struct{}, 1) - for i := 0; i < *cfg.ChaosConfig.ExperimentCount; i++ { + for i := 0; i < *testConfig.LogPoller.ChaosConfig.ExperimentCount; i++ { i := i wg.Add(1) guardChan <- struct{}{} @@ -964,9 +975,9 @@ func ExecuteChaosExperiment(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv <-guardChan wg.Done() current := i + 1 - l.Info().Str("Current/Total", fmt.Sprintf("%d/%d", current, cfg.ChaosConfig.ExperimentCount)).Msg("Done with experiment") + l.Info().Str("Current/Total", fmt.Sprintf("%d/%d", current, testConfig.LogPoller.ChaosConfig.ExperimentCount)).Msg("Done with experiment") }() - chaosChan <- chaosPauseSyncFn(l, testEnv, *cfg.ChaosConfig.TargetComponent) + chaosChan <- chaosPauseSyncFn(l, testEnv, testConfig, *testConfig.LogPoller.ChaosConfig.TargetComponent) time.Sleep(10 * time.Second) }() } @@ -1078,6 +1089,7 @@ func SetupLogPollerTestDocker( registryConfig contracts.KeeperRegistrySettings, upkeepsNeeded int, lpPollingInterval time.Duration, + backupPollingInterval uint64, finalityTagEnabled bool, testConfig *tc.TestConfig, ) ( @@ -1120,6 +1132,7 @@ func SetupLogPollerTestDocker( chain.LogPollInterval = commonconfig.MustNewDuration(lpPollingInterval) chain.FinalityDepth = ptr.Ptr[uint32](uint32(finalityDepth)) chain.FinalityTagEnabled = ptr.Ptr[bool](finalityTagEnabled) + chain.BackupLogPollerBlockDelay = ptr.Ptr[uint64](backupPollingInterval) return chain } @@ -1131,7 +1144,7 @@ func SetupLogPollerTestDocker( ethBuilder := ctf_test_env.NewEthereumNetworkBuilder() cfg, err := ethBuilder. - WithConsensusType(ctf_test_env.ConsensusType_PoS). + WithEthereumVersion(ctf_test_env.EthereumVersion_Eth2). WithConsensusLayer(ctf_test_env.ConsensusLayer_Prysm). WithExecutionLayer(ctf_test_env.ExecutionLayer_Geth). WithEthereumChainConfig(ctf_test_env.EthereumChainConfig{ @@ -1175,7 +1188,10 @@ func SetupLogPollerTestDocker( } require.NoError(t, err, "Error loading/deploying LINK token") - linkBalance, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(linkToken.Address())) + evmClient, err := env.GetEVMClient(network.ChainID) + require.NoError(t, err, "Getting EVM client shouldn't fail") + + linkBalance, err := evmClient.BalanceAt(context.Background(), common.HexToAddress(linkToken.Address())) require.NoError(t, err, "Error getting LINK balance") l.Info().Str("Balance", big.NewInt(0).Div(linkBalance, big.NewInt(1e18)).String()).Msg("LINK balance") @@ -1191,7 +1207,7 @@ func SetupLogPollerTestDocker( registryConfig, linkToken, env.ContractDeployer, - env.EVMClient, + evmClient, ) // Fund the registry with LINK @@ -1200,32 +1216,37 @@ func SetupLogPollerTestDocker( err = actions.CreateOCRKeeperJobsLocal(l, nodeClients, registry.Address(), network.ChainID, 0, registryVersion) require.NoError(t, err, "Error creating OCR Keeper Jobs") - ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, workerNodes, registryConfig, registrar.Address(), 30*time.Second, registry.RegistryOwnerAddress()) + ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, workerNodes, registryConfig, registrar.Address(), 30*time.Second, registry.RegistryOwnerAddress(), registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) require.NoError(t, err, "Error building OCR config vars") err = registry.SetConfig(automationDefaultRegistryConfig, ocrConfig) require.NoError(t, err, "Registry config should be set successfully") - require.NoError(t, env.EVMClient.WaitForEvents(), "Waiting for config to be set") + require.NoError(t, evmClient.WaitForEvents(), "Waiting for config to be set") - return env.EVMClient, nodeClients, env.ContractDeployer, linkToken, registry, registrar, env + return evmClient, nodeClients, env.ContractDeployer, linkToken, registry, registrar, env } // UploadLogEmitterContractsAndWaitForFinalisation uploads the configured number of log emitter contracts and waits for the upload blocks to be finalised -func UploadLogEmitterContractsAndWaitForFinalisation(l zerolog.Logger, t *testing.T, testEnv *test_env.CLClusterTestEnv, cfg *lp_config.Config) []*contracts.LogEmitter { +func UploadLogEmitterContractsAndWaitForFinalisation(l zerolog.Logger, t *testing.T, testEnv *test_env.CLClusterTestEnv, testConfig *tc.TestConfig) []*contracts.LogEmitter { logEmitters := make([]*contracts.LogEmitter, 0) - for i := 0; i < *cfg.General.Contracts; i++ { + for i := 0; i < *testConfig.LogPoller.General.Contracts; i++ { logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() logEmitters = append(logEmitters, &logEmitter) require.NoError(t, err, "Error deploying log emitter contract") l.Info().Str("Contract address", logEmitter.Address().Hex()).Msg("Log emitter contract deployed") time.Sleep(200 * time.Millisecond) } - afterUploadBlock, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + + network := networks.MustGetSelectedNetworkConfig(testConfig.GetNetworkConfig())[0] + evmClient, err := testEnv.GetEVMClient(network.ChainID) + require.NoError(t, err, "Error getting EVM client") + + afterUploadBlock, err := evmClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { targetBlockNumber := int64(afterUploadBlock + 1) - finalized, err := testEnv.EVMClient.GetLatestFinalizedBlockHeader(testcontext.Get(t)) + finalized, err := evmClient.GetLatestFinalizedBlockHeader(testcontext.Get(t)) if err != nil { l.Warn().Err(err).Msg("Error checking if contract were uploaded. Retrying...") return @@ -1310,7 +1331,7 @@ func RegisterFiltersAndAssertUniquness(l zerolog.Logger, registry contracts.Keep // FluentlyCheckIfAllNodesHaveLogCount checks if all CL nodes have the expected log count for the provided block range and expected filters // It will retry until the provided duration is reached or until all nodes have the expected log count -func FluentlyCheckIfAllNodesHaveLogCount(duration string, startBlock, endBlock int64, expectedLogCount int, expectedFilters []ExpectedFilter, l zerolog.Logger, coreLogger core_logger.SugaredLogger, testEnv *test_env.CLClusterTestEnv) (bool, error) { +func FluentlyCheckIfAllNodesHaveLogCount(duration string, startBlock, endBlock int64, expectedLogCount int, expectedFilters []ExpectedFilter, l zerolog.Logger, coreLogger core_logger.SugaredLogger, testEnv *test_env.CLClusterTestEnv, chainId int64) (bool, error) { logCountWaitDuration, err := time.ParseDuration(duration) if err != nil { return false, err @@ -1320,7 +1341,7 @@ func FluentlyCheckIfAllNodesHaveLogCount(duration string, startBlock, endBlock i // not using gomega here, because I want to see which logs were missing allNodesLogCountMatches := false for time.Now().Before(endTime) { - logCountMatches, clErr := ClNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), expectedLogCount, expectedFilters, l, coreLogger, testEnv.ClCluster) + logCountMatches, clErr := ClNodesHaveExpectedLogCount(startBlock, endBlock, big.NewInt(chainId), expectedLogCount, expectedFilters, l, coreLogger, testEnv.ClCluster) if clErr != nil { l.Warn(). Err(clErr). diff --git a/integration-tests/utils/seth.go b/integration-tests/utils/seth.go new file mode 100644 index 0000000000..c2d4f743a6 --- /dev/null +++ b/integration-tests/utils/seth.go @@ -0,0 +1,131 @@ +package utils + +import ( + "fmt" + "strconv" + + "github.com/rs/zerolog" + "github.com/smartcontractkit/seth" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" +) + +// MergeSethAndEvmNetworkConfigs merges EVMNetwork to Seth config. If Seth config already has Network settings, +// it will return unchanged Seth config that was passed to it. If the network is simulated, it will +// use Geth-specific settings. Otherwise it will use the chain ID to find the correct network settings. +// If no match is found it will use default settings (currently based on Sepolia network settings). +func MergeSethAndEvmNetworkConfigs(l zerolog.Logger, evmNetwork blockchain.EVMNetwork, sethConfig seth.Config) seth.Config { + if sethConfig.Network != nil { + return sethConfig + } + + var sethNetwork *seth.Network + + for _, conf := range sethConfig.Networks { + if evmNetwork.Simulated { + if conf.Name == seth.GETH { + conf.PrivateKeys = evmNetwork.PrivateKeys + conf.URLs = evmNetwork.URLs + // important since Besu doesn't support EIP-1559, but other EVM clients do + conf.EIP1559DynamicFees = evmNetwork.SupportsEIP1559 + + sethNetwork = conf + break + } + } else if conf.ChainID == fmt.Sprint(evmNetwork.ChainID) { + conf.PrivateKeys = evmNetwork.PrivateKeys + conf.URLs = evmNetwork.URLs + + sethNetwork = conf + break + } + } + + if sethNetwork == nil { + //TODO in the future we could run gas estimator here + l.Warn(). + Int64("chainID", evmNetwork.ChainID). + Msg("Could not find any Seth network settings for chain ID. Using default network settings") + sethNetwork = &seth.Network{} + sethNetwork.PrivateKeys = evmNetwork.PrivateKeys + sethNetwork.URLs = evmNetwork.URLs + sethNetwork.EIP1559DynamicFees = evmNetwork.SupportsEIP1559 + sethNetwork.ChainID = fmt.Sprint(evmNetwork.ChainID) + // Sepolia settings + sethNetwork.GasLimit = 14_000_000 + sethNetwork.GasPrice = 1_000_000_000 + sethNetwork.GasFeeCap = 25_000_000_000 + sethNetwork.GasTipCap = 5_000_000_000 + sethNetwork.TransferGasFee = 21_000 + sethNetwork.TxnTimeout = seth.MustMakeDuration(evmNetwork.Timeout.Duration) + } + + sethConfig.Network = sethNetwork + + return sethConfig +} + +// MustReplaceSimulatedNetworkUrlWithK8 replaces the simulated network URL with the K8 URL and returns the network. +// If the network is not simulated, it will return the network unchanged. +func MustReplaceSimulatedNetworkUrlWithK8(l zerolog.Logger, network blockchain.EVMNetwork, testEnvironment environment.Environment) blockchain.EVMNetwork { + if !network.Simulated { + return network + } + + if _, ok := testEnvironment.URLs["Simulated Geth"]; !ok { + for k := range testEnvironment.URLs { + l.Info().Str("Network", k).Msg("Available networks") + } + panic("no network settings for Simulated Geth") + } + network.URLs = testEnvironment.URLs["Simulated Geth"] + + return network +} + +// ValidateSethNetworkConfig validates the Seth network config +func ValidateSethNetworkConfig(cfg *seth.Network) error { + if cfg == nil { + return fmt.Errorf("Network cannot be nil") + } + if cfg.ChainID == "" { + return fmt.Errorf("ChainID is required") + } + _, err := strconv.Atoi(cfg.ChainID) + if err != nil { + return fmt.Errorf("ChainID needs to be a number") + } + if cfg.URLs == nil || len(cfg.URLs) == 0 { + return fmt.Errorf("URLs are required") + } + if cfg.PrivateKeys == nil || len(cfg.PrivateKeys) == 0 { + return fmt.Errorf("PrivateKeys are required") + } + if cfg.TransferGasFee == 0 { + return fmt.Errorf("TransferGasFee needs to be above 0. It's the gas fee for a simple transfer transaction") + } + if cfg.TxnTimeout.Duration() == 0 { + return fmt.Errorf("TxnTimeout needs to be above 0. It's the timeout for a transaction") + } + if cfg.GasLimit == 0 { + return fmt.Errorf("GasLimit needs to be above 0. It's the gas limit for a transaction") + } + if cfg.EIP1559DynamicFees { + if cfg.GasFeeCap == 0 { + return fmt.Errorf("GasFeeCap needs to be above 0. It's the maximum fee per gas for a transaction (including tip)") + } + if cfg.GasTipCap == 0 { + return fmt.Errorf("GasTipCap needs to be above 0. It's the maximum tip per gas for a transaction") + } + if cfg.GasFeeCap <= cfg.GasTipCap { + return fmt.Errorf("GasFeeCap needs to be above GasTipCap (as it is base fee + tip cap)") + } + } else { + if cfg.GasPrice == 0 { + return fmt.Errorf("GasPrice needs to be above 0. It's the price of gas for a transaction") + } + } + + return nil +} diff --git a/integration-tests/wrappers/contract_caller.go b/integration-tests/wrappers/contract_caller.go new file mode 100644 index 0000000000..4be76ee74a --- /dev/null +++ b/integration-tests/wrappers/contract_caller.go @@ -0,0 +1,130 @@ +package wrappers + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/smartcontractkit/seth" + + evmClient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" +) + +// WrappedContractBackend is a wrapper around the go-ethereum ContractBackend interface. It's a thin wrapper +// around the go-ethereum/ethclient.Client, which replaces only CallContract and PendingCallContract calls with +// methods that send data both in "input" and "data" field for backwards compatibility with older clients. Other methods +// are passed through to the underlying client. +type WrappedContractBackend struct { + evmClient blockchain.EVMClient + sethClient *seth.Client +} + +// MustNewWrappedContractBackend creates a new WrappedContractBackend with the given clients +func MustNewWrappedContractBackend(evmClient blockchain.EVMClient, sethClient *seth.Client) *WrappedContractBackend { + if evmClient == nil && sethClient == nil { + panic("Must provide at least one client") + } + + return &WrappedContractBackend{ + evmClient: evmClient, + sethClient: sethClient, + } +} + +func (w *WrappedContractBackend) getGethClient() *ethclient.Client { + if w.sethClient != nil { + return w.sethClient.Client + } + + if w.evmClient != nil { + return w.evmClient.GetEthClient() + } + + panic("No client found") +} + +func (w *WrappedContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + client := w.getGethClient() + return client.CodeAt(ctx, contract, blockNumber) +} + +func (w *WrappedContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + client := w.getGethClient() + return client.PendingCodeAt(ctx, contract) +} + +func (w *WrappedContractBackend) CodeAtHash(ctx context.Context, contract common.Address, blockHash common.Hash) ([]byte, error) { + client := w.getGethClient() + return client.CodeAtHash(ctx, contract, blockHash) +} + +func (w *WrappedContractBackend) CallContractAtHash(ctx context.Context, call ethereum.CallMsg, blockHash common.Hash) ([]byte, error) { + client := w.getGethClient() + return client.CallContractAtHash(ctx, call, blockHash) +} + +func (w *WrappedContractBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + client := w.getGethClient() + return client.HeaderByNumber(ctx, number) +} + +func (w *WrappedContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + client := w.getGethClient() + return client.PendingNonceAt(ctx, account) +} + +func (w *WrappedContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + client := w.getGethClient() + return client.SuggestGasPrice(ctx) +} + +func (w *WrappedContractBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + client := w.getGethClient() + return client.SuggestGasTipCap(ctx) +} + +func (w *WrappedContractBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { + client := w.getGethClient() + return client.EstimateGas(ctx, call) +} + +func (w *WrappedContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + client := w.getGethClient() + return client.SendTransaction(ctx, tx) +} + +func (w *WrappedContractBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { + client := w.getGethClient() + return client.FilterLogs(ctx, query) +} + +func (w *WrappedContractBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { + client := w.getGethClient() + return client.SubscribeFilterLogs(ctx, query, ch) +} + +func (w *WrappedContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + var hex hexutil.Bytes + client := w.getGethClient() + err := client.Client().CallContext(ctx, &hex, "eth_call", evmClient.ToBackwardCompatibleCallArg(msg), evmClient.ToBackwardCompatibleBlockNumArg(blockNumber)) + if err != nil { + return nil, err + } + return hex, nil +} + +func (w *WrappedContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { + var hex hexutil.Bytes + client := w.getGethClient() + err := client.Client().CallContext(ctx, &hex, "eth_call", evmClient.ToBackwardCompatibleCallArg(msg), "pending") + if err != nil { + return nil, err + } + return hex, nil +}