From 9686592f6876890545841f6e740a875eec8d1338 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Mon, 27 Nov 2023 15:58:36 +0200 Subject: [PATCH] =?UTF-8?q?VRF-543:=20add=20test=20for=20verifying=20round?= =?UTF-8?q?=20robin=20for=20multiple=20sending=20keys=E2=80=A6=20(#11382)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * VRF-543: add test for verifying round robin for multiple sending keys in VRF v2 and VRF V2 Plus * VRF-543: fixing lint issue --- integration-tests/actions/actions.go | 23 ++++ .../actions/vrfv2_actions/vrfv2_steps.go | 45 +++++-- .../actions/vrfv2plus/vrfv2plus_steps.go | 42 +++++-- .../contracts/contract_vrf_models.go | 2 +- .../contracts/ethereum_vrfv2_contracts.go | 15 ++- integration-tests/go.mod | 2 +- integration-tests/load/vrfv2/vrfv2_test.go | 10 +- .../load/vrfv2plus/vrfv2plus_test.go | 1 + integration-tests/smoke/vrfv2_test.go | 93 +++++++++++++++ integration-tests/smoke/vrfv2plus_test.go | 111 +++++++++++++++++- integration-tests/types/config/node/core.go | 22 ++-- 11 files changed, 318 insertions(+), 48 deletions(-) diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index 438c36a9f0e..624df39c479 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum" @@ -458,3 +459,25 @@ func GenerateWallet() (common.Address, error) { } return crypto.PubkeyToAddress(*publicKeyECDSA), nil } + +// todo - move to CTF +func FundAddress(client blockchain.EVMClient, sendingKey string, fundingToSendEth *big.Float) error { + address := common.HexToAddress(sendingKey) + gasEstimates, err := client.EstimateGas(ethereum.CallMsg{ + To: &address, + }) + if err != nil { + return err + } + err = client.Fund(sendingKey, fundingToSendEth, gasEstimates) + if err != nil { + return err + } + return nil +} + +// todo - move to CTF +func GetTxFromAddress(tx *types.Transaction) (string, error) { + from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx) + return from.String(), err +} diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go index f565af26d93..4ea4a4a8534 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go @@ -29,6 +29,7 @@ import ( 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" @@ -105,7 +106,7 @@ func DeployVRFV2Consumers(contractDeployer contracts.ContractDeployer, coordinat func CreateVRFV2Job( chainlinkNode *client.ChainlinkClient, coordinatorAddress string, - nativeTokenPrimaryKeyAddress string, + nativeTokenKeyAddresses []string, pubKeyCompressed string, chainID string, minIncomingConfirmations uint16, @@ -122,7 +123,7 @@ func CreateVRFV2Job( job, err := chainlinkNode.MustCreateJob(&client.VRFV2JobSpec{ Name: fmt.Sprintf("vrf-v2-%s", jobUUID), CoordinatorAddress: coordinatorAddress, - FromAddresses: []string{nativeTokenPrimaryKeyAddress}, + FromAddresses: nativeTokenKeyAddresses, EVMChainID: chainID, MinIncomingConfirmations: int(minIncomingConfirmations), PublicKey: pubKeyCompressed, @@ -181,6 +182,7 @@ func SetupVRFV2Environment( linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, registerProvingKeyAgainstAddress string, + numberOfTxKeysToCreate int, numberOfConsumers int, numberOfSubToCreate int, l zerolog.Logger, @@ -254,17 +256,21 @@ func SetupVRFV2Environment( } chainID := env.EVMClient.GetChainID() - + newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2Config, numberOfTxKeysToCreate, chainID) + if err != nil { + return nil, nil, nil, err + } nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) } + allNativeTokenKeyAddresses := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) l.Info().Msg("Creating VRFV2 Job") vrfV2job, err := CreateVRFV2Job( env.ClCluster.NodeAPIs()[0], vrfv2Contracts.Coordinator.Address(), - nativeTokenPrimaryKeyAddress, + allNativeTokenKeyAddresses, pubKeyCompressed, chainID.String(), vrfv2Config.MinimumConfirmations, @@ -276,12 +282,8 @@ func SetupVRFV2Environment( // this part is here because VRFv2 can work with only a specific key // [[EVM.KeySpecific]] // Key = '...' - addr, err := env.ClCluster.Nodes[0].API.PrimaryEthAddress() - if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrGetPrimaryKey, err) - } nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(addr, vrfv2Config.CLNodeMaxGasPriceGWei), + node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, vrfv2Config.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) @@ -306,6 +308,25 @@ func SetupVRFV2Environment( return vrfv2Contracts, subIDs, &data, nil } +func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, 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(vrfv2Config.ChainlinkNodeFunding)) + if err != nil { + return nil, err + } + } + return newNativeTokenKeyAddresses, nil +} + func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, @@ -448,7 +469,7 @@ func RequestRandomnessAndWaitForFulfillment( l zerolog.Logger, ) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { logRandRequest(consumer.Address(), coordinator.Address(), subID, vrfv2Config, l) - err := consumer.RequestRandomness( + _, err := consumer.RequestRandomness( vrfv2Data.KeyHash, subID, vrfv2Config.MinimumConfirmations, @@ -460,7 +481,7 @@ func RequestRandomnessAndWaitForFulfillment( return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) } - return WaitForRequestAndFulfillmentEvents( + fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents( consumer.Address(), coordinator, vrfv2Data, @@ -468,6 +489,7 @@ func RequestRandomnessAndWaitForFulfillment( randomWordsFulfilledEventTimeout, l, ) + return fulfillmentEvents, err } func WaitForRequestAndFulfillmentEvents( @@ -489,7 +511,6 @@ func WaitForRequestAndFulfillmentEvents( } LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent) - randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent( []*big.Int{randomWordsRequestedEvent.RequestId}, randomWordsFulfilledEventTimeout, diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index d0a33948e31..0e3c8096f0c 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -7,9 +7,8 @@ import ( "sync" "time" - "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" - commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" @@ -31,6 +30,7 @@ import ( 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" @@ -112,7 +112,7 @@ func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coord func CreateVRFV2PlusJob( chainlinkNode *client.ChainlinkClient, coordinatorAddress string, - nativeTokenPrimaryKeyAddress string, + nativeTokenKeyAddresses []string, pubKeyCompressed string, chainID string, minIncomingConfirmations uint16, @@ -129,7 +129,7 @@ func CreateVRFV2PlusJob( job, err := chainlinkNode.MustCreateJob(&client.VRFV2PlusJobSpec{ Name: fmt.Sprintf("vrf-v2-plus-%s", jobUUID), CoordinatorAddress: coordinatorAddress, - FromAddresses: []string{nativeTokenPrimaryKeyAddress}, + FromAddresses: nativeTokenKeyAddresses, EVMChainID: chainID, MinIncomingConfirmations: int(minIncomingConfirmations), PublicKey: pubKeyCompressed, @@ -207,6 +207,7 @@ func SetupVRFV2_5Environment( linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, registerProvingKeyAgainstAddress string, + numberOfTxKeysToCreate int, numberOfConsumers int, numberOfSubToCreate int, l zerolog.Logger, @@ -273,17 +274,21 @@ func SetupVRFV2_5Environment( } chainID := env.EVMClient.GetChainID() - + newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2PlusConfig, numberOfTxKeysToCreate, chainID) + if err != nil { + return nil, nil, nil, err + } nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) } + allNativeTokenKeyAddresses := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) l.Info().Msg("Creating VRFV2 Plus Job") job, err := CreateVRFV2PlusJob( env.ClCluster.NodeAPIs()[0], vrfv2_5Contracts.Coordinator.Address(), - nativeTokenPrimaryKeyAddress, + allNativeTokenKeyAddresses, pubKeyCompressed, chainID.String(), vrfv2PlusConfig.MinimumConfirmations, @@ -295,12 +300,8 @@ func SetupVRFV2_5Environment( // this part is here because VRFv2 can work with only a specific key // [[EVM.KeySpecific]] // Key = '...' - addr, err := env.ClCluster.Nodes[0].API.PrimaryEthAddress() - if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrGetPrimaryKey, err) - } nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(addr, vrfv2PlusConfig.CLNodeMaxGasPriceGWei), + node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, vrfv2PlusConfig.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) @@ -325,6 +326,25 @@ func SetupVRFV2_5Environment( return vrfv2_5Contracts, subIDs, &data, nil } +func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, 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(vrfv2PlusConfig.ChainlinkNodeFunding)) + if err != nil { + return nil, err + } + } + return newNativeTokenKeyAddresses, nil +} + func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 5f850fce1a7..548cac252b1 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -170,7 +170,7 @@ type VRFv2Consumer interface { type VRFv2LoadTestConsumer interface { Address() string - RequestRandomness(hash [32]byte, subID uint64, confs uint16, gasLimit uint32, numWords uint32, requestCount uint16) error + RequestRandomness(hash [32]byte, subID uint64, confs uint16, gasLimit uint32, numWords uint32, requestCount uint16) (*types.Transaction, error) GetRequestStatus(ctx context.Context, requestID *big.Int) (vrf_load_test_with_metrics.GetRequestStatus, error) GetLastRequestId(ctx context.Context) (*big.Int, error) GetLoadTestMetrics(ctx context.Context) (*VRFLoadTestMetrics, error) diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index ac3926fa746..d1637c93c87 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -441,17 +441,24 @@ func (v *EthereumVRFv2LoadTestConsumer) Address() string { return v.address.Hex() } -func (v *EthereumVRFv2LoadTestConsumer) RequestRandomness(keyHash [32]byte, subID uint64, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32, requestCount uint16) error { +func (v *EthereumVRFv2LoadTestConsumer) RequestRandomness( + keyHash [32]byte, + subID uint64, + requestConfirmations uint16, + callbackGasLimit uint32, + numWords uint32, + requestCount uint16, +) (*types.Transaction, error) { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { - return err + return nil, err } tx, err := v.consumer.RequestRandomWords(opts, subID, requestConfirmations, keyHash, callbackGasLimit, numWords, requestCount) if err != nil { - return err + return nil, err } - return v.client.ProcessTransaction(tx) + return tx, v.client.ProcessTransaction(tx) } func (v *EthereumVRFv2Consumer) GetRequestStatus(ctx context.Context, requestID *big.Int) (vrf_v2_consumer_wrapper.GetRequestStatus, error) { diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 2338c0caa58..d0ca2346636 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -11,6 +11,7 @@ require ( github.com/cli/go-gh/v2 v2.0.0 github.com/ethereum/go-ethereum v1.12.0 github.com/go-resty/resty/v2 v2.7.0 + github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.1 github.com/jmoiron/sqlx v1.3.5 github.com/kelseyhightower/envconfig v1.4.0 @@ -191,7 +192,6 @@ require ( 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.5.9 // 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 diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 8e74e3aa901..ae109f75e28 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -8,7 +8,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/kelseyhightower/envconfig" "github.com/rs/zerolog" @@ -199,6 +198,7 @@ func TestVRFV2Performance(t *testing.T) { mockETHLinkFeed, //register proving key against EOA address in order to return funds to this address env.EVMClient.GetDefaultWallet().Address(), + 0, 1, vrfv2Config.NumberOfSubToCreate, l, @@ -302,13 +302,7 @@ func FundNodesIfNeeded(cfg *PerformanceConfig, client blockchain.EVMClient, l ze Str("Should have at least", fundingAtLeast.String()). Str("Funding Amount in ETH", fundingToSendEth.String()). Msg("Funding Node's Sending Key") - gasEstimates, err := client.EstimateGas(ethereum.CallMsg{ - To: &address, - }) - if err != nil { - return err - } - err = client.Fund(sendingKey, fundingToSendEth, gasEstimates) + err := actions.FundAddress(client, sendingKey, fundingToSendEth) if err != nil { return err } diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index ac22b5256f6..4b6728440b3 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -204,6 +204,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { mockETHLinkFeed, //register proving key against EOA address in order to return funds to this address env.EVMClient.GetDefaultWallet().Address(), + 0, 1, vrfv2PlusConfig.NumberOfSubToCreate, l, diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index c7b48fc3a35..4edb92e5df6 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -5,9 +5,12 @@ import ( "math/big" "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" @@ -44,12 +47,14 @@ func TestVRFv2Basic(t *testing.T) { // register proving key against oracle address (sending key) in order to test oracleWithdraw defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + numberOfTxKeysToCreate := 1 vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( env, vrfv2Config, linkToken, mockETHLinkFeed, defaultWalletAddress, + numberOfTxKeysToCreate, 1, 1, l, @@ -105,3 +110,91 @@ func TestVRFv2Basic(t *testing.T) { } }) } + +func TestVRFv2MultipleSendingKeys(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + var vrfv2Config vrfv2_config.VRFV2Config + err := envconfig.Process("VRFV2", &vrfv2Config) + require.NoError(t, err) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithCLNodes(1). + WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). + WithStandardCleanup(). + Build() + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2Config.LinkNativeFeedResponse)) + require.NoError(t, err) + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err) + + // register proving key against oracle address (sending key) in order to test oracleWithdraw + defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + + numberOfTxKeysToCreate := 2 + vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( + env, + vrfv2Config, + linkToken, + mockETHLinkFeed, + defaultWalletAddress, + numberOfTxKeysToCreate, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up VRF v2 env") + + subID := subIDs[0] + + subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + + t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { + testConfig := vrfv2Config + txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") + require.NoError(t, err, "error reading tx keys") + + require.Equal(t, numberOfTxKeysToCreate+1, len(txKeys.Data)) + + var fulfillmentTxFromAddresses []string + for i := 0; i < numberOfTxKeysToCreate+1; i++ { + randomWordsFulfilledEvent, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + vrfv2Contracts.LoadTestConsumers[0], + vrfv2Contracts.Coordinator, + vrfv2Data, + subID, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + 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) + require.NoError(t, err, "error getting tx from hash") + fulfillmentTxFromAddress, err := actions.GetTxFromAddress(fulfillmentTx) + require.NoError(t, err, "error getting tx from address") + fulfillmentTxFromAddresses = append(fulfillmentTxFromAddresses, fulfillmentTxFromAddress) + } + require.Equal(t, numberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) + var txKeyAddresses []string + for _, txKey := range txKeys.Data { + txKeyAddresses = append(txKeyAddresses, txKey.ID) + } + less := func(a, b string) bool { return a < b } + equalIgnoreOrder := cmp.Diff(txKeyAddresses, fulfillmentTxFromAddresses, cmpopts.SortSlices(less)) == "" + require.True(t, equalIgnoreOrder) + }) +} diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 9ce9f216995..f4f52b6ee01 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -1,15 +1,19 @@ package smoke import ( + "context" "fmt" "math/big" "testing" "time" "github.com/ethereum/go-ethereum/common" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/kelseyhightower/envconfig" "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/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" @@ -49,12 +53,14 @@ func TestVRFv2Plus(t *testing.T) { // register proving key against oracle address (sending key) in order to test oracleWithdraw defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + numberOfTxKeysToCreate := 2 vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( env, vrfv2PlusConfig, linkToken, mockETHLinkFeed, defaultWalletAddress, + numberOfTxKeysToCreate, 1, 1, l, @@ -596,6 +602,97 @@ func TestVRFv2Plus(t *testing.T) { }) } +func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig + err := envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) + require.NoError(t, err) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithCLNodes(1). + WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). + WithStandardCleanup(). + Build() + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2PlusConfig.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") + + // register proving key against oracle address (sending key) in order to test oracleWithdraw + defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + + numberOfTxKeysToCreate := 2 + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( + env, + vrfv2PlusConfig, + linkToken, + mockETHLinkFeed, + defaultWalletAddress, + numberOfTxKeysToCreate, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up VRF v2_5 env") + + subID := subIDs[0] + + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) + + t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { + testConfig := vrfv2PlusConfig + var isNativeBilling = false + txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") + require.NoError(t, err, "error reading tx keys") + + require.Equal(t, numberOfTxKeysToCreate+1, len(txKeys.Data)) + + var fulfillmentTxFromAddresses []string + for i := 0; i < numberOfTxKeysToCreate+1; i++ { + randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( + vrfv2PlusContracts.LoadTestConsumers[0], + vrfv2PlusContracts.Coordinator, + vrfv2PlusData, + subID, + isNativeBilling, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + 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) + require.NoError(t, err, "error getting tx from hash") + fulfillmentTxFromAddress, err := actions.GetTxFromAddress(fulfillmentTx) + require.NoError(t, err, "error getting tx from address") + fulfillmentTxFromAddresses = append(fulfillmentTxFromAddresses, fulfillmentTxFromAddress) + } + require.Equal(t, numberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) + var txKeyAddresses []string + for _, txKey := range txKeys.Data { + txKeyAddresses = append(txKeyAddresses, txKey.ID) + } + less := func(a, b string) bool { return a < b } + equalIgnoreOrder := cmp.Diff(txKeyAddresses, fulfillmentTxFromAddresses, cmpopts.SortSlices(less)) == "" + require.True(t, equalIgnoreOrder) + }) +} + func TestVRFv2PlusMigration(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -622,7 +719,17 @@ func TestVRFv2PlusMigration(t *testing.T) { nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() require.NoError(t, err, "error getting primary eth address") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, vrfv2PlusConfig, linkAddress, mockETHLinkFeedAddress, nativeTokenPrimaryKeyAddress, 2, 1, l) + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( + env, + vrfv2PlusConfig, + linkAddress, + mockETHLinkFeedAddress, + nativeTokenPrimaryKeyAddress, + 0, + 2, + 1, + l, + ) require.NoError(t, err, "error setting up VRF v2_5 env") subID := subIDs[0] @@ -671,7 +778,7 @@ func TestVRFv2PlusMigration(t *testing.T) { _, err = vrfv2plus.CreateVRFV2PlusJob( env.ClCluster.NodeAPIs()[0], newCoordinator.Address(), - vrfv2PlusData.PrimaryEthAddress, + []string{vrfv2PlusData.PrimaryEthAddress}, vrfv2PlusData.VRFKey.Data.ID, vrfv2PlusData.ChainID.String(), vrfv2PlusConfig.MinimumConfirmations, diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 47bc8e8cc1e..5934809bace 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -223,22 +223,26 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { } } -func WithVRFv2EVMEstimator(addr string, maxGasPriceGWei int64) NodeConfigOpt { +func WithVRFv2EVMEstimator(addresses []string, maxGasPriceGWei int64) NodeConfigOpt { est := assets.GWei(maxGasPriceGWei) - return func(c *chainlink.Config) { - c.EVM[0].KeySpecific = evmcfg.KeySpecificConfig{ - { - Key: ptr.Ptr(ethkey.EIP55Address(addr)), - GasEstimator: evmcfg.KeySpecificGasEstimator{ - PriceMax: est, - }, + + var keySpecicifArr []evmcfg.KeySpecific + for _, addr := range addresses { + keySpecicifArr = append(keySpecicifArr, evmcfg.KeySpecific{ + Key: ptr.Ptr(ethkey.EIP55Address(addr)), + GasEstimator: evmcfg.KeySpecificGasEstimator{ + PriceMax: est, }, - } + }) + } + return func(c *chainlink.Config) { + c.EVM[0].KeySpecific = keySpecicifArr c.EVM[0].Chain.GasEstimator = evmcfg.GasEstimator{ LimitDefault: ptr.Ptr[uint32](3500000), } c.EVM[0].Chain.Transactions = evmcfg.Transactions{ MaxQueued: ptr.Ptr[uint32](10000), } + } }