diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 6a0c80abc97..bb75e6b568b 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -517,7 +517,7 @@ jobs: os: ubuntu-latest pyroscope_env: ci-smoke-vrf2-evm-simulated - name: vrfv2plus - nodes: 5 + nodes: 6 os: ubuntu-latest pyroscope_env: ci-smoke-vrf2plus-evm-simulated - name: forwarder_ocr diff --git a/.github/workflows/on-demand-vrfv2-performance-test.yml b/.github/workflows/on-demand-vrfv2-performance-test.yml index 47f86b23ad1..7ed01a554f7 100644 --- a/.github/workflows/on-demand-vrfv2-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2-performance-test.yml @@ -70,7 +70,7 @@ jobs: - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@5bee84d30d90295010bda68b0cd46be3a1eea917 # v2.3.9 with: - test_command_to_run: cd ./integration-tests/load && go test -v -count=1 -timeout 24h -run TestVRFV2Performance/vrfv2_performance_test ./vrfv2 + test_command_to_run: cd ./integration-tests/load && go test -v -count=1 -timeout 24h -run TestVRFV2Performance ./vrfv2 test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ env.CHAINLINK_VERSION }} diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index 009b8303a42..2e07f4e1194 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -71,7 +71,7 @@ jobs: - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@5bee84d30d90295010bda68b0cd46be3a1eea917 # v2.3.9 with: - test_command_to_run: cd ./integration-tests/load && go test -v -count=1 -timeout 24h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./vrfv2plus + test_command_to_run: cd ./integration-tests/load && go test -v -count=1 -timeout 24h -run TestVRFV2PlusPerformance ./vrfv2plus test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ env.CHAINLINK_VERSION }} diff --git a/integration-tests/actions/vrf/common/actions.go b/integration-tests/actions/vrf/common/actions.go index 20d44e60de2..e912c61927e 100644 --- a/integration-tests/actions/vrf/common/actions.go +++ b/integration-tests/actions/vrf/common/actions.go @@ -12,6 +12,7 @@ import ( "github.com/rs/zerolog" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -191,14 +192,17 @@ func retrieveLoadTestMetrics( metricsChannel <- metrics } -func CreateNodeTypeToNodeMap(cluster *test_env.ClCluster, nodesToCreate []VRFNodeType) map[VRFNodeType]*VRFNode { +func CreateNodeTypeToNodeMap(cluster *test_env.ClCluster, nodesToCreate []VRFNodeType) (map[VRFNodeType]*VRFNode, error) { var nodesMap = make(map[VRFNodeType]*VRFNode) + if len(cluster.Nodes) < len(nodesToCreate) { + return nil, fmt.Errorf("not enough nodes in the cluster (cluster size is %d nodes) to create %d nodes", len(cluster.Nodes), len(nodesToCreate)) + } for i, nodeType := range nodesToCreate { nodesMap[nodeType] = &VRFNode{ CLNode: cluster.Nodes[i], } } - return nodesMap + return nodesMap, nil } func CreateVRFKeyOnVRFNode(vrfNode *VRFNode, l zerolog.Logger) (*client.VRFKey, string, error) { @@ -216,3 +220,35 @@ func CreateVRFKeyOnVRFNode(vrfNode *VRFNode, l zerolog.Logger) (*client.VRFKey, Msg("VRF Key created on the Node") return vrfKey, pubKeyCompressed, nil } + +func FundNodesIfNeeded(ctx context.Context, existingEnvConfig *vrf_common_config.ExistingEnvConfig, client blockchain.EVMClient, l zerolog.Logger) error { + if *existingEnvConfig.NodeSendingKeyFundingMin > 0 { + for _, sendingKey := range existingEnvConfig.NodeSendingKeys { + address := common.HexToAddress(sendingKey) + sendingKeyBalance, err := client.BalanceAt(ctx, address) + if err != nil { + return err + } + fundingAtLeast := conversions.EtherToWei(big.NewFloat(*existingEnvConfig.NodeSendingKeyFundingMin)) + fundingToSendWei := new(big.Int).Sub(fundingAtLeast, sendingKeyBalance) + fundingToSendEth := conversions.WeiToEther(fundingToSendWei) + log := l.Info(). + Str("Sending Key", sendingKey). + Str("Sending Key Current Balance", sendingKeyBalance.String()). + Str("Should have at least", fundingAtLeast.String()) + if fundingToSendWei.Cmp(big.NewInt(0)) == 1 { + log. + Str("Funding Amount in ETH", fundingToSendEth.String()). + Msg("Funding Node's Sending Key") + err := actions.FundAddress(client, sendingKey, fundingToSendEth) + if err != nil { + return err + } + } else { + log. + Msg("Skipping Node's Sending Key funding as it has enough funds") + } + } + } + return nil +} diff --git a/integration-tests/actions/vrf/common/models.go b/integration-tests/actions/vrf/common/models.go index 177410d9606..479b7680330 100644 --- a/integration-tests/actions/vrf/common/models.go +++ b/integration-tests/actions/vrf/common/models.go @@ -46,8 +46,10 @@ type VRFContracts struct { CoordinatorV2Plus contracts.VRFCoordinatorV2_5 VRFOwner contracts.VRFOwner BHS contracts.BlockHashStore - VRFV2Consumer []contracts.VRFv2LoadTestConsumer + VRFV2Consumers []contracts.VRFv2LoadTestConsumer VRFV2PlusConsumer []contracts.VRFv2PlusLoadTestConsumer + LinkToken contracts.LinkToken + MockETHLINKFeed contracts.VRFMockETHLINKFeed } type VRFOwnerConfig struct { @@ -74,3 +76,10 @@ type VRFJobSpecConfig struct { type VRFLoadTestConsumer interface { GetLoadTestMetrics(ctx context.Context) (*contracts.VRFLoadTestMetrics, error) } + +type NewEnvConfig struct { + NodesToCreate []VRFNodeType + NumberOfTxKeysToCreate int + UseVRFOwner bool + UseTestCoordinator bool +} diff --git a/integration-tests/actions/vrf/vrfv2/vrfv2_steps.go b/integration-tests/actions/vrf/vrfv2/contract_steps.go similarity index 59% rename from integration-tests/actions/vrf/vrfv2/vrfv2_steps.go rename to integration-tests/actions/vrf/vrfv2/contract_steps.go index 121c259278a..65073e1f848 100644 --- a/integration-tests/actions/vrf/vrfv2/vrfv2_steps.go +++ b/integration-tests/actions/vrf/vrfv2/contract_steps.go @@ -8,35 +8,25 @@ import ( "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" - - "github.com/google/uuid" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "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" - + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - "github.com/smartcontractkit/chainlink/integration-tests/types" + testconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" + chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner" ) func DeployVRFV2Contracts( env *test_env.CLClusterTestEnv, linkTokenContract contracts.LinkToken, - linkEthFeedContract contracts.MockETHLINKFeed, - consumerContractsAmount int, + linkEthFeedContract contracts.VRFMockETHLINKFeed, useVRFOwner bool, useTestCoordinator bool, ) (*vrfcommon.VRFContracts, error) { @@ -71,14 +61,6 @@ func DeployVRFV2Contracts( } coordinatorAddress = coordinator.Address() } - consumers, err := DeployVRFV2Consumers(env.ContractDeployer, coordinatorAddress, consumerContractsAmount) - if err != nil { - return nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(coordinatorAddress) if err != nil { @@ -94,17 +76,21 @@ func DeployVRFV2Contracts( return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } return &vrfcommon.VRFContracts{ - CoordinatorV2: coordinator, - VRFOwner: vrfOwner, - BHS: bhs, - VRFV2Consumer: consumers, + CoordinatorV2: coordinator, + VRFOwner: vrfOwner, + BHS: bhs, + VRFV2Consumers: nil, + LinkToken: linkTokenContract, + MockETHLINKFeed: linkEthFeedContract, }, nil } return &vrfcommon.VRFContracts{ - CoordinatorV2: coordinator, - VRFOwner: nil, - BHS: bhs, - VRFV2Consumer: consumers, + CoordinatorV2: coordinator, + VRFOwner: nil, + BHS: bhs, + VRFV2Consumers: nil, + LinkToken: linkTokenContract, + MockETHLINKFeed: linkEthFeedContract, }, nil } @@ -160,53 +146,6 @@ func DeployVRFV2DirectFundingContracts( return &VRFV2WrapperContracts{vrfv2Wrapper, consumers}, nil } -func CreateVRFV2Job( - chainlinkNode *client.ChainlinkClient, - 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", vrfcommon.ErrParseJob, err) - } - - spec := &client.VRFV2JobSpec{ - Name: fmt.Sprintf("vrf-v2-%s", jobUUID), - ForwardingAllowed: vrfJobSpecConfig.ForwardingAllowed, - CoordinatorAddress: vrfJobSpecConfig.CoordinatorAddress, - FromAddresses: vrfJobSpecConfig.FromAddresses, - EVMChainID: vrfJobSpecConfig.EVMChainID, - MinIncomingConfirmations: vrfJobSpecConfig.MinIncomingConfirmations, - PublicKey: vrfJobSpecConfig.PublicKey, - ExternalJobID: jobUUID.String(), - ObservationSource: ost, - BatchFulfillmentEnabled: vrfJobSpecConfig.BatchFulfillmentEnabled, - BatchFulfillmentGasMultiplier: vrfJobSpecConfig.BatchFulfillmentGasMultiplier, - PollPeriod: vrfJobSpecConfig.PollPeriod, - RequestTimeout: vrfJobSpecConfig.RequestTimeout, - } - if vrfJobSpecConfig.VRFOwnerConfig.UseVRFOwner { - spec.VRFOwner = vrfJobSpecConfig.VRFOwnerConfig.OwnerAddress - spec.UseVRFOwner = true - } - - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrParseJob, err) - - } - job, err := chainlinkNode.MustCreateJob(spec) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Job, err) - } - return job, nil -} - func VRFV2RegisterProvingKey( vrfKey *client.VRFKey, oracleAddress string, @@ -226,216 +165,25 @@ func VRFV2RegisterProvingKey( return provingKey, nil } -func FundVRFCoordinatorV2Subscription( - linkToken contracts.LinkToken, - coordinator contracts.VRFCoordinatorV2, - chainClient blockchain.EVMClient, - subscriptionID uint64, - linkFundingAmountJuels *big.Int, -) error { - encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint64"}]`, subscriptionID) - if err != nil { - 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", vrfcommon.ErrSendingLinkToken, err) - } - return chainClient.WaitForEvents() -} - -// SetupVRFV2Environment will create specified number of subscriptions and add the same conumer/s to each of them -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) { - l.Info().Msg("Starting VRFV2 environment setup") - configGeneral := vrfv2TestConfig.GetVRFv2Config().General - vrfContracts, subIDs, err := SetupVRFV2Contracts( - env, - linkToken, - mockNativeLINKFeed, - numberOfConsumers, - useVRFOwner, - useTestCoordinator, - configGeneral, - numberOfSubToCreate, - l, - ) - if err != nil { - return nil, nil, nil, nil, err - } - - nodeTypeToNodeMap := vrfcommon.CreateNodeTypeToNodeMap(env.ClCluster, nodesToCreate) - vrfKey, pubKeyCompressed, err := vrfcommon.CreateVRFKeyOnVRFNode(nodeTypeToNodeMap[vrfcommon.VRF], l) - if err != nil { - return nil, nil, nil, nil, err - } - - 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, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisteringProvingKey, err) - } - keyHash, err := vrfContracts.CoordinatorV2.HashOfKey(context.Background(), provingKey) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreatingProvingKeyHash, err) - } - - chainID := env.EVMClient.GetChainID() - vrfTXKeyAddressStrings, vrfTXKeyAddresses, err := vrfcommon.CreateFundAndGetSendingKeys( - env.EVMClient, - nodeTypeToNodeMap[vrfcommon.VRF], - *vrfv2TestConfig.GetCommonConfig().ChainlinkNodeFunding, - numberOfTxKeysToCreate, - chainID, - ) - if err != nil { - return nil, nil, nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - nodeTypeToNodeMap[vrfcommon.VRF].TXKeyAddressStrings = vrfTXKeyAddressStrings - - var vrfOwnerConfig *vrfcommon.VRFOwnerConfig - if useVRFOwner { - err := setupVRFOwnerContract(env, vrfContracts, vrfTXKeyAddressStrings, vrfTXKeyAddresses, l) - if err != nil { - return nil, nil, nil, nil, err - } - vrfOwnerConfig = &vrfcommon.VRFOwnerConfig{ - OwnerAddress: vrfContracts.VRFOwner.Address(), - UseVRFOwner: useVRFOwner, - } - } else { - vrfOwnerConfig = &vrfcommon.VRFOwnerConfig{ - OwnerAddress: "", - UseVRFOwner: useVRFOwner, - } - } - - g := errgroup.Group{} - if vrfNode, exists := nodeTypeToNodeMap[vrfcommon.VRF]; exists { - g.Go(func() error { - err := setupVRFNode(vrfContracts, 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, - 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: *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( - vrfNode.CLNode.API, - vrfJobSpecConfig, - ) - if err != nil { - 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(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 = vrfNode.CLNode.Restart(nodeConfig) - if err != nil { - return fmt.Errorf("%s, err %w", vrfcommon.ErrRestartCLNode, err) - } - return nil -} - func SetupVRFV2Contracts( env *test_env.CLClusterTestEnv, linkToken contracts.LinkToken, - mockNativeLINKFeed contracts.MockETHLINKFeed, - numberOfConsumers int, + mockNativeLINKFeed contracts.VRFMockETHLINKFeed, useVRFOwner bool, useTestCoordinator bool, vrfv2Config *testconfig.General, - numberOfSubToCreate int, l zerolog.Logger, -) (*vrfcommon.VRFContracts, []uint64, error) { +) (*vrfcommon.VRFContracts, error) { l.Info().Msg("Deploying VRFV2 contracts") vrfContracts, err := DeployVRFV2Contracts( env, linkToken, mockNativeLINKFeed, - numberOfConsumers, useVRFOwner, useTestCoordinator, ) if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2Contracts, err) + return nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2Contracts, err) } vrfCoordinatorV2FeeConfig := vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ @@ -459,25 +207,13 @@ func SetupVRFV2Contracts( vrfCoordinatorV2FeeConfig, ) if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrSetVRFCoordinatorConfig, err) + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrSetVRFCoordinatorConfig, err) } err = env.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, - big.NewFloat(*vrfv2Config.SubscriptionFundingAmountLink), - linkToken, - vrfContracts.CoordinatorV2, vrfContracts.VRFV2Consumer, numberOfSubToCreate) - if err != nil { - return nil, nil, err + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) } - return vrfContracts, subIDs, nil + return vrfContracts, nil } func setupVRFOwnerContract(env *test_env.CLClusterTestEnv, contracts *vrfcommon.VRFContracts, allNativeTokenKeyAddressStrings []string, allNativeTokenKeyAddresses []common.Address, l zerolog.Logger) error { @@ -520,82 +256,6 @@ func setupVRFOwnerContract(env *test_env.CLClusterTestEnv, contracts *vrfcommon. return err } -func SetupVRFV2WrapperEnvironment( - env *test_env.CLClusterTestEnv, - vrfv2TestConfig tc.VRFv2TestConfig, - linkToken contracts.LinkToken, - mockNativeLINKFeed contracts.MockETHLINKFeed, - coordinator contracts.VRFCoordinatorV2, - keyHash [32]byte, - wrapperConsumerContractsAmount int, -) (*VRFV2WrapperContracts, *uint64, error) { - // Deploy VRF v2 direct funding contracts - wrapperContracts, err := DeployVRFV2DirectFundingContracts( - env.ContractDeployer, - env.EVMClient, - linkToken.Address(), - mockNativeLINKFeed.Address(), - coordinator, - wrapperConsumerContractsAmount, - ) - if err != nil { - return nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - vrfv2Config := vrfv2TestConfig.GetVRFv2Config() - - // Configure VRF v2 wrapper contract - err = wrapperContracts.VRFV2Wrapper.SetConfig( - *vrfv2Config.General.WrapperGasOverhead, - *vrfv2Config.General.CoordinatorGasOverhead, - *vrfv2Config.General.WrapperPremiumPercentage, - keyHash, - *vrfv2Config.General.WrapperMaxNumberOfWords, - ) - if err != nil { - return nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - // Fetch wrapper subscription ID - wrapperSubID, err := wrapperContracts.VRFV2Wrapper.GetSubID(context.Background()) - if err != nil { - return nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - 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}) - if err != nil { - return nil, nil, err - } - - // Fund consumer with LINK - err = linkToken.Transfer( - wrapperContracts.LoadTestConsumers[0].Address(), - big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(*vrfv2Config.General.WrapperConsumerFundingAmountLink)), - ) - if err != nil { - return nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - return wrapperContracts, &wrapperSubID, nil -} - func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, subscriptionFundingAmountLink *big.Float, @@ -727,7 +387,26 @@ func FundSubscriptions( return nil } +func FundVRFCoordinatorV2Subscription( + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2, + chainClient blockchain.EVMClient, + subscriptionID uint64, + linkFundingAmountJuels *big.Int, +) error { + encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint64"}]`, subscriptionID) + if err != nil { + 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", vrfcommon.ErrSendingLinkToken, err) + } + return chainClient.WaitForEvents() +} + func DirectFundingRequestRandomnessAndWaitForFulfillment( + ctx context.Context, l zerolog.Logger, consumer contracts.VRFv2WrapperLoadTestConsumer, coordinator contracts.VRFCoordinatorV2, @@ -740,7 +419,18 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( randomnessRequestCountPerRequestDeviation uint16, randomWordsFulfilledEventTimeout time.Duration, ) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { - logRandRequest(l, consumer.Address(), coordinator.Address(), subID, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) + logRandRequest( + l, + consumer.Address(), + coordinator.Address(), + subID, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + randomnessRequestCountPerRequest, + randomnessRequestCountPerRequestDeviation, + vrfv2KeyData.KeyHash, + ) _, err := consumer.RequestRandomness( minimumConfirmations, callbackGasLimit, @@ -750,7 +440,7 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( if err != nil { return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) } - wrapperAddress, err := consumer.GetWrapper(context.Background()) + wrapperAddress, err := consumer.GetWrapper(ctx) if err != nil { return nil, fmt.Errorf("error getting wrapper address, err: %w", err) } @@ -778,28 +468,130 @@ func RequestRandomnessAndWaitForFulfillment( randomnessRequestCountPerRequestDeviation uint16, randomWordsFulfilledEventTimeout time.Duration, ) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { - logRandRequest(l, consumer.Address(), coordinator.Address(), subID, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) - _, err := consumer.RequestRandomness( - vrfKeyData.KeyHash, + logRandRequest( + l, + consumer.Address(), + coordinator.Address(), subID, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, + randomnessRequestCountPerRequestDeviation, + vrfKeyData.KeyHash, ) - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) + ch := make(chan *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled) + errorChannel := make(chan error) + go func() { + _, err := consumer.RequestRandomness( + vrfKeyData.KeyHash, + subID, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + randomnessRequestCountPerRequest, + ) + if err != nil { + l.Error().Err(err).Msg(err.Error()) + errorChannel <- err + } + }() + go func() { + fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents( + consumer.Address(), + coordinator, + vrfKeyData, + subID, + randomWordsFulfilledEventTimeout, + l, + ) + if err != nil { + l.Error().Err(err).Msg("error waiting for RandomnessRequested and RandomWordsFulfilled events") + errorChannel <- err + } + ch <- fulfillmentEvents + }() + for { + select { + case err := <-errorChannel: + return nil, err + case fulfillmentEvent := <-ch: + return fulfillmentEvent, nil + case <-time.After(randomWordsFulfilledEventTimeout): + return nil, fmt.Errorf("timeout waiting for RandomnessRequested and RandomWordsFulfilled events") + } } +} - fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents( +func RequestRandomnessAndWaitForRequestedEvent( + l zerolog.Logger, + consumer contracts.VRFv2LoadTestConsumer, + coordinator contracts.VRFCoordinatorV2, + subID uint64, + vrfKeyData *vrfcommon.VRFKeyData, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, + randomnessRequestCountPerRequest uint16, + randomnessRequestCountPerRequestDeviation uint16, + randomWordsRequestedEventTimeout time.Duration, +) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error) { + logRandRequest( + l, consumer.Address(), - coordinator, - vrfKeyData, + coordinator.Address(), subID, - randomWordsFulfilledEventTimeout, - l, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + randomnessRequestCountPerRequest, + randomnessRequestCountPerRequestDeviation, + vrfKeyData.KeyHash, ) - return fulfillmentEvents, err + ch := make(chan *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested) + errorChannel := make(chan error) + go func() { + _, err := consumer.RequestRandomness( + vrfKeyData.KeyHash, + subID, + minimumConfirmations, + callbackGasLimit, + numberOfWords, + randomnessRequestCountPerRequest, + ) + if err != nil { + l.Error().Err(err).Msg(err.Error()) + errorChannel <- err + } + }() + go func() { + randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfKeyData.KeyHash}, + []uint64{subID}, + []common.Address{common.HexToAddress(consumer.Address())}, + time.Minute*1, + ) + if err != nil { + l.Error().Err(err).Msg(err.Error()) + errorChannel <- err + } + LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent) + if err != nil { + l.Error().Err(err).Msg("error waiting for RandomnessRequested events") + errorChannel <- err + } + ch <- randomWordsRequestedEvent + }() + for { + select { + case err := <-errorChannel: + return nil, err + case randRequestedEvent := <-ch: + return randRequestedEvent, nil + case <-time.After(randomWordsRequestedEventTimeout): + return nil, fmt.Errorf("timeout waiting for RandomnessRequested events") + } + } } func RequestRandomnessWithForceFulfillAndWaitForFulfillment( @@ -817,7 +609,7 @@ func RequestRandomnessWithForceFulfillAndWaitForFulfillment( linkAddress common.Address, randomWordsFulfilledEventTimeout time.Duration, ) (*vrf_coordinator_v2.VRFCoordinatorV2ConfigSet, *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, *vrf_owner.VRFOwnerRandomWordsForced, error) { - logRandRequest(l, consumer.Address(), coordinator.Address(), 0, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation) + logRandRequest(l, consumer.Address(), coordinator.Address(), 0, minimumConfirmations, callbackGasLimit, numberOfWords, randomnessRequestCountPerRequest, randomnessRequestCountPerRequestDeviation, vrfv2KeyData.KeyHash) _, err := consumer.RequestRandomWordsWithForceFulfill( vrfv2KeyData.KeyHash, minimumConfirmations, @@ -933,85 +725,78 @@ func WaitForRequestAndFulfillmentEvents( return randomWordsFulfilledEvent, err } -func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2.GetSubscription, subID uint64, coordinator contracts.VRFCoordinatorV2) { - l.Debug(). - Str("Coordinator", coordinator.Address()). - Str("Link Balance", (*commonassets.Link)(subscription.Balance).Link()). - Uint64("Subscription ID", subID). - Str("Subscription Owner", subscription.Owner.String()). - Interface("Subscription Consumers", subscription.Consumers). - Msg("Subscription Data") +func SetupVRFOwnerContractIfNeeded(useVRFOwner bool, env *test_env.CLClusterTestEnv, vrfContracts *vrfcommon.VRFContracts, vrfTXKeyAddressStrings []string, vrfTXKeyAddresses []common.Address, l zerolog.Logger) (*vrfcommon.VRFOwnerConfig, error) { + var vrfOwnerConfig *vrfcommon.VRFOwnerConfig + if useVRFOwner { + err := setupVRFOwnerContract(env, vrfContracts, vrfTXKeyAddressStrings, vrfTXKeyAddresses, l) + if err != nil { + return nil, err + } + vrfOwnerConfig = &vrfcommon.VRFOwnerConfig{ + OwnerAddress: vrfContracts.VRFOwner.Address(), + UseVRFOwner: useVRFOwner, + } + } else { + vrfOwnerConfig = &vrfcommon.VRFOwnerConfig{ + OwnerAddress: "", + UseVRFOwner: useVRFOwner, + } + } + return vrfOwnerConfig, nil } -func LogRandomnessRequestedEvent( - l zerolog.Logger, +func SetupNewConsumersAndSubs( + env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2, - randomWordsRequestedEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, -) { - l.Info(). - Str("Coordinator", coordinator.Address()). - Str("Request ID", randomWordsRequestedEvent.RequestId.String()). - Uint64("Subscription ID", randomWordsRequestedEvent.SubId). - Str("Sender Address", randomWordsRequestedEvent.Sender.String()). - 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") -} - -func LogRandomWordsFulfilledEvent( + testConfig tc.TestConfig, + linkToken contracts.LinkToken, + numberOfConsumerContractsToDeployAndAddToSub int, + numberOfSubToCreate int, l zerolog.Logger, - coordinator contracts.VRFCoordinatorV2, - randomWordsFulfilledEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, -) { +) ([]contracts.VRFv2LoadTestConsumer, []uint64, error) { + consumers, err := DeployVRFV2Consumers(env.ContractDeployer, coordinator.Address(), numberOfConsumerContractsToDeployAndAddToSub) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err: %w", vrfcommon.ErrWaitTXsComplete, err) + } 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)") -} - -func LogRandomWordsForcedEvent( - l zerolog.Logger, - vrfOwner contracts.VRFOwner, - randomWordsForcedEvent *vrf_owner.VRFOwnerRandomWordsForced, -) { - l.Debug(). - Str("VRFOwner", vrfOwner.Address()). - Uint64("Sub ID", randomWordsForcedEvent.SubId). - Str("TX Hash", randomWordsForcedEvent.Raw.TxHash.String()). - Str("Request ID", randomWordsForcedEvent.RequestId.String()). - Str("Sender", randomWordsForcedEvent.Sender.String()). - Msg("RandomWordsForced Event (TX metadata)") + Str("Coordinator", *testConfig.VRFv2.ExistingEnvConfig.ExistingEnvConfig.CoordinatorAddress). + Int("Number of Subs to create", numberOfSubToCreate). + Msg("Creating and funding subscriptions, deploying and adding consumers to subs") + subIDs, err := CreateFundSubsAndAddConsumers( + env, + big.NewFloat(*testConfig.VRFv2.General.SubscriptionFundingAmountLink), + linkToken, + coordinator, + consumers, + numberOfSubToCreate, + ) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + return consumers, subIDs, nil } -func logRandRequest( - l zerolog.Logger, - consumer string, - coordinator string, - subID uint64, - minimumConfirmations uint16, - callbackGasLimit uint32, - numberOfWords uint32, - randomnessRequestCountPerRequest uint16, - randomnessRequestCountPerRequestDeviation uint16, -) { - l.Info(). - Str("Consumer", consumer). - Str("Coordinator", coordinator). - Uint64("SubID", subID). - Uint16("MinimumConfirmations", minimumConfirmations). - Uint32("CallbackGasLimit", callbackGasLimit). - Uint32("NumberOfWords", numberOfWords). - Uint16("RandomnessRequestCountPerRequest", randomnessRequestCountPerRequest). - Uint16("RandomnessRequestCountPerRequestDeviation", randomnessRequestCountPerRequestDeviation). - Msg("Requesting randomness") +func CancelSubsAndReturnFunds(ctx context.Context, vrfContracts *vrfcommon.VRFContracts, eoaWalletAddress string, 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 := vrfContracts.CoordinatorV2.PendingRequestsExist(ctx, subID) + if err != nil { + l.Error().Err(err).Msg("Error checking if pending requests exist") + } + if !pendingRequestsExist { + _, err := vrfContracts.CoordinatorV2.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + if err != nil { + l.Error().Err(err).Msg("Error canceling subscription") + } + } else { + l.Error().Uint64("Sub ID", subID).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") + } + } } diff --git a/integration-tests/actions/vrf/vrfv2/logging_helpers.go b/integration-tests/actions/vrf/vrfv2/logging_helpers.go new file mode 100644 index 00000000000..82c45267aaf --- /dev/null +++ b/integration-tests/actions/vrf/vrfv2/logging_helpers.go @@ -0,0 +1,97 @@ +package vrfv2 + +import ( + "fmt" + + "github.com/rs/zerolog" + + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner" +) + +func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2.GetSubscription, subID uint64, coordinator contracts.VRFCoordinatorV2) { + l.Debug(). + Str("Coordinator", coordinator.Address()). + Str("Link Balance", (*commonassets.Link)(subscription.Balance).Link()). + Uint64("Subscription ID", subID). + Str("Subscription Owner", subscription.Owner.String()). + Interface("Subscription Consumers", subscription.Consumers). + Msg("Subscription Data") +} + +func LogRandomnessRequestedEvent( + l zerolog.Logger, + coordinator contracts.VRFCoordinatorV2, + randomWordsRequestedEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, +) { + l.Info(). + Str("Coordinator", coordinator.Address()). + Str("Request ID", randomWordsRequestedEvent.RequestId.String()). + Uint64("Subscription ID", randomWordsRequestedEvent.SubId). + Str("Sender Address", randomWordsRequestedEvent.Sender.String()). + 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") +} + +func LogRandomWordsFulfilledEvent( + l zerolog.Logger, + coordinator contracts.VRFCoordinatorV2, + randomWordsFulfilledEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, +) { + 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)") +} + +func LogRandomWordsForcedEvent( + l zerolog.Logger, + vrfOwner contracts.VRFOwner, + randomWordsForcedEvent *vrf_owner.VRFOwnerRandomWordsForced, +) { + l.Debug(). + Str("VRFOwner", vrfOwner.Address()). + Uint64("Sub ID", randomWordsForcedEvent.SubId). + Str("TX Hash", randomWordsForcedEvent.Raw.TxHash.String()). + Str("Request ID", randomWordsForcedEvent.RequestId.String()). + Str("Sender", randomWordsForcedEvent.Sender.String()). + Msg("RandomWordsForced Event (TX metadata)") +} + +func logRandRequest( + l zerolog.Logger, + consumer string, + coordinator string, + subID uint64, + minimumConfirmations uint16, + callbackGasLimit uint32, + numberOfWords uint32, + randomnessRequestCountPerRequest uint16, + randomnessRequestCountPerRequestDeviation uint16, + keyhash [32]byte, +) { + l.Info(). + Str("Consumer", consumer). + Str("Coordinator", coordinator). + Uint64("SubID", subID). + Uint16("MinimumConfirmations", minimumConfirmations). + Uint32("CallbackGasLimit", callbackGasLimit). + Uint32("NumberOfWords", numberOfWords). + Uint16("RandomnessRequestCountPerRequest", randomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", randomnessRequestCountPerRequestDeviation). + Str("Keyhash", fmt.Sprintf("0x%x", keyhash)). + Msg("Requesting randomness") +} diff --git a/integration-tests/actions/vrf/vrfv2/vrfv2_models.go b/integration-tests/actions/vrf/vrfv2/models.go similarity index 100% rename from integration-tests/actions/vrf/vrfv2/vrfv2_models.go rename to integration-tests/actions/vrf/vrfv2/models.go diff --git a/integration-tests/actions/vrf/vrfv2/setup_steps.go b/integration-tests/actions/vrf/vrfv2/setup_steps.go new file mode 100644 index 00000000000..1f7757be071 --- /dev/null +++ b/integration-tests/actions/vrf/vrfv2/setup_steps.go @@ -0,0 +1,479 @@ +package vrfv2 + +import ( + "context" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/rs/zerolog" + "golang.org/x/sync/errgroup" + + "github.com/google/uuid" + + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "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" + + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/types" +) + +func CreateVRFV2Job( + chainlinkNode *client.ChainlinkClient, + 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", vrfcommon.ErrParseJob, err) + } + + spec := &client.VRFV2JobSpec{ + Name: fmt.Sprintf("vrf-v2-%s", jobUUID), + ForwardingAllowed: vrfJobSpecConfig.ForwardingAllowed, + CoordinatorAddress: vrfJobSpecConfig.CoordinatorAddress, + FromAddresses: vrfJobSpecConfig.FromAddresses, + EVMChainID: vrfJobSpecConfig.EVMChainID, + MinIncomingConfirmations: vrfJobSpecConfig.MinIncomingConfirmations, + PublicKey: vrfJobSpecConfig.PublicKey, + ExternalJobID: jobUUID.String(), + ObservationSource: ost, + BatchFulfillmentEnabled: vrfJobSpecConfig.BatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: vrfJobSpecConfig.BatchFulfillmentGasMultiplier, + PollPeriod: vrfJobSpecConfig.PollPeriod, + RequestTimeout: vrfJobSpecConfig.RequestTimeout, + } + if vrfJobSpecConfig.VRFOwnerConfig.UseVRFOwner { + spec.VRFOwner = vrfJobSpecConfig.VRFOwnerConfig.OwnerAddress + spec.UseVRFOwner = true + } + + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrParseJob, err) + + } + job, err := chainlinkNode.MustCreateJob(spec) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Job, err) + } + return job, nil +} + +// SetupVRFV2Environment will create specified number of subscriptions and add the same conumer/s to each of them +func SetupVRFV2Environment( + ctx context.Context, + env *test_env.CLClusterTestEnv, + nodesToCreate []vrfcommon.VRFNodeType, + vrfv2TestConfig types.VRFv2TestConfig, + useVRFOwner bool, + useTestCoordinator bool, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.VRFMockETHLINKFeed, + registerProvingKeyAgainstAddress string, + numberOfTxKeysToCreate int, + l zerolog.Logger, +) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { + l.Info().Msg("Starting VRFV2 environment setup") + configGeneral := vrfv2TestConfig.GetVRFv2Config().General + vrfContracts, err := SetupVRFV2Contracts( + env, + linkToken, + mockNativeLINKFeed, + useVRFOwner, + useTestCoordinator, + configGeneral, + l, + ) + if err != nil { + return nil, nil, nil, err + } + + nodeTypeToNodeMap, err := vrfcommon.CreateNodeTypeToNodeMap(env.ClCluster, nodesToCreate) + if err != nil { + return nil, nil, nil, err + } + vrfKey, pubKeyCompressed, err := vrfcommon.CreateVRFKeyOnVRFNode(nodeTypeToNodeMap[vrfcommon.VRF], l) + if err != nil { + return nil, nil, nil, err + } + + 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, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisteringProvingKey, err) + } + keyHash, err := vrfContracts.CoordinatorV2.HashOfKey(ctx, provingKey) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreatingProvingKeyHash, err) + } + + chainID := env.EVMClient.GetChainID() + vrfTXKeyAddressStrings, vrfTXKeyAddresses, err := vrfcommon.CreateFundAndGetSendingKeys( + env.EVMClient, + nodeTypeToNodeMap[vrfcommon.VRF], + *vrfv2TestConfig.GetCommonConfig().ChainlinkNodeFunding, + numberOfTxKeysToCreate, + chainID, + ) + if err != nil { + return nil, nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + nodeTypeToNodeMap[vrfcommon.VRF].TXKeyAddressStrings = vrfTXKeyAddressStrings + + vrfOwnerConfig, err := SetupVRFOwnerContractIfNeeded(useVRFOwner, env, vrfContracts, vrfTXKeyAddressStrings, vrfTXKeyAddresses, l) + if err != nil { + return nil, nil, nil, err + } + + g := errgroup.Group{} + if vrfNode, exists := nodeTypeToNodeMap[vrfcommon.VRF]; exists { + g.Go(func() error { + err := setupVRFNode(vrfContracts, 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, + 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, fmt.Errorf("VRF node setup ended up with an error: %w", err) + } + + vrfKeyData := vrfcommon.VRFKeyData{ + VRFKey: vrfKey, + EncodedProvingKey: provingKey, + KeyHash: keyHash, + PubKeyCompressed: pubKeyCompressed, + } + + l.Info().Msg("VRFV2 environment setup is finished") + return vrfContracts, &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: *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( + vrfNode.CLNode.API, + vrfJobSpecConfig, + ) + if err != nil { + 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(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 = vrfNode.CLNode.Restart(nodeConfig) + if err != nil { + return fmt.Errorf("%s, err %w", vrfcommon.ErrRestartCLNode, err) + } + return nil +} + +func SetupVRFV2WrapperEnvironment( + ctx context.Context, + env *test_env.CLClusterTestEnv, + vrfv2TestConfig tc.VRFv2TestConfig, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.VRFMockETHLINKFeed, + coordinator contracts.VRFCoordinatorV2, + keyHash [32]byte, + wrapperConsumerContractsAmount int, +) (*VRFV2WrapperContracts, *uint64, error) { + // Deploy VRF v2 direct funding contracts + wrapperContracts, err := DeployVRFV2DirectFundingContracts( + env.ContractDeployer, + env.EVMClient, + linkToken.Address(), + mockNativeLINKFeed.Address(), + coordinator, + wrapperConsumerContractsAmount, + ) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + vrfv2Config := vrfv2TestConfig.GetVRFv2Config() + + // Configure VRF v2 wrapper contract + err = wrapperContracts.VRFV2Wrapper.SetConfig( + *vrfv2Config.General.WrapperGasOverhead, + *vrfv2Config.General.CoordinatorGasOverhead, + *vrfv2Config.General.WrapperPremiumPercentage, + keyHash, + *vrfv2Config.General.WrapperMaxNumberOfWords, + ) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + // Fetch wrapper subscription ID + wrapperSubID, err := wrapperContracts.VRFV2Wrapper.GetSubID(ctx) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + 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}) + if err != nil { + return nil, nil, err + } + + // Fund consumer with LINK + err = linkToken.Transfer( + wrapperContracts.LoadTestConsumers[0].Address(), + big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(*vrfv2Config.General.WrapperConsumerFundingAmountLink)), + ) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + return wrapperContracts, &wrapperSubID, nil +} + +func SetupVRFV2Universe(ctx context.Context, t *testing.T, testConfig tc.TestConfig, cleanupFn func(), newEnvConfig vrfcommon.NewEnvConfig, l zerolog.Logger) (*test_env.CLClusterTestEnv, *vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { + var ( + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + err error + ) + if *testConfig.VRFv2.General.UseExistingEnv { + vrfContracts, vrfKey, env, err = SetupVRFV2ForExistingEnv(ctx, t, testConfig, cleanupFn, l) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "Error setting up VRF V2 for Existing env", err) + } + } else { + vrfContracts, vrfKey, env, nodeTypeToNodeMap, err = SetupVRFV2ForNewEnv(ctx, t, testConfig, cleanupFn, newEnvConfig, l) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "Error setting up VRF V2 for New env", err) + } + } + return env, vrfContracts, vrfKey, nodeTypeToNodeMap, nil +} + +func SetupVRFV2ForNewEnv( + ctx context.Context, + t *testing.T, + testConfig tc.TestConfig, + cleanupFn func(), + newEnvConfig vrfcommon.NewEnvConfig, + l zerolog.Logger, +) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, *test_env.CLClusterTestEnv, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { + network, err := actions.EthereumNetworkConfigFromConfig(l, &testConfig) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "Error building ethereum network config", err) + } + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithTestConfig(&testConfig). + WithPrivateEthereumNetwork(network). + WithCLNodes(len(newEnvConfig.NodesToCreate)). + WithFunding(big.NewFloat(*testConfig.Common.ChainlinkNodeFunding)). + WithCustomCleanup(cleanupFn). + Build() + + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error creating test env", err) + } + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := env.ContractDeployer.DeployVRFMockETHLINKFeed(big.NewInt(*testConfig.VRFv2.General.LinkNativeFeedResponse)) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error deploying mock ETH/LINK feed", err) + } + + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error deploying LINK contract", err) + } + + vrfContracts, vrfKey, nodeTypeToNode, err := SetupVRFV2Environment( + ctx, + env, + newEnvConfig.NodesToCreate, + &testConfig, + newEnvConfig.UseVRFOwner, + newEnvConfig.UseTestCoordinator, + linkToken, + mockETHLinkFeed, + //register proving key against EOA address in order to return funds to this address + env.EVMClient.GetDefaultWallet().Address(), + newEnvConfig.NumberOfTxKeysToCreate, + l, + ) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error setting up VRF v2 env", err) + } + return vrfContracts, vrfKey, env, nodeTypeToNode, nil +} + +func SetupVRFV2ForExistingEnv(ctx context.Context, t *testing.T, testConfig tc.TestConfig, cleanupFn func(), l zerolog.Logger) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, *test_env.CLClusterTestEnv, error) { + commonExistingEnvConfig := testConfig.VRFv2.ExistingEnvConfig.ExistingEnvConfig + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithTestConfig(&testConfig). + WithCustomCleanup(cleanupFn). + Build() + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err: %w", "error creating test env", err) + } + coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(*commonExistingEnvConfig.CoordinatorAddress) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading VRFCoordinator2", err) + } + linkToken, err := env.ContractLoader.LoadLINKToken(*commonExistingEnvConfig.LinkAddress) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading LinkToken", err) + } + err = vrfcommon.FundNodesIfNeeded(ctx, commonExistingEnvConfig, env.EVMClient, l) + if err != nil { + return nil, nil, nil, fmt.Errorf("err: %w", err) + } + vrfContracts := &vrfcommon.VRFContracts{ + CoordinatorV2: coordinator, + VRFV2Consumers: nil, + LinkToken: linkToken, + BHS: nil, + } + vrfKey := &vrfcommon.VRFKeyData{ + VRFKey: nil, + EncodedProvingKey: [2]*big.Int{}, + KeyHash: common.HexToHash(*commonExistingEnvConfig.KeyHash), + } + return vrfContracts, vrfKey, env, nil +} + +func SetupSubsAndConsumersForExistingEnv( + env *test_env.CLClusterTestEnv, + coordinator contracts.VRFCoordinatorV2, + linkToken contracts.LinkToken, + numberOfConsumerContractsToDeployAndAddToSub int, + numberOfSubToCreate int, + testConfig tc.TestConfig, + l zerolog.Logger, +) ([]uint64, []contracts.VRFv2LoadTestConsumer, error) { + var ( + subIDs []uint64 + consumers []contracts.VRFv2LoadTestConsumer + err error + ) + if *testConfig.VRFv2.General.UseExistingEnv { + commonExistingEnvConfig := testConfig.VRFv2.ExistingEnvConfig.ExistingEnvConfig + if *commonExistingEnvConfig.CreateFundSubsAndAddConsumers { + consumers, subIDs, err = SetupNewConsumersAndSubs( + env, + coordinator, + testConfig, + linkToken, + numberOfConsumerContractsToDeployAndAddToSub, + numberOfSubToCreate, + l, + ) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + } else { + consumer, err := env.ContractLoader.LoadVRFv2LoadTestConsumer(*commonExistingEnvConfig.ConsumerAddress) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + consumers = append(consumers, consumer) + subIDs = append(subIDs, *testConfig.VRFv2.ExistingEnvConfig.SubID) + } + } else { + consumers, subIDs, err = SetupNewConsumersAndSubs( + env, + coordinator, + testConfig, + linkToken, + numberOfConsumerContractsToDeployAndAddToSub, + numberOfSubToCreate, + l, + ) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + } + return subIDs, consumers, nil +} diff --git a/integration-tests/actions/vrf/vrfv2plus/contract_steps.go b/integration-tests/actions/vrf/vrfv2plus/contract_steps.go new file mode 100644 index 00000000000..347e775d80f --- /dev/null +++ b/integration-tests/actions/vrf/vrfv2plus/contract_steps.go @@ -0,0 +1,770 @@ +package vrfv2plus + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/rs/zerolog" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + "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" + "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" + 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" +) + +func DeployVRFV2_5Contracts( + contractDeployer contracts.ContractDeployer, + chainClient blockchain.EVMClient, +) (*vrfcommon.VRFContracts, error) { + bhs, err := contractDeployer.DeployBlockhashStore() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrDeployBlockHashStore, err) + } + err = chainClient.WaitForEvents() + if err != nil { + 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", vrfcommon.ErrDeployCoordinator, err) + } + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + return &vrfcommon.VRFContracts{ + CoordinatorV2Plus: coordinator, + BHS: bhs, + VRFV2PlusConsumer: nil, + }, nil +} + +func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coordinator contracts.VRFCoordinatorV2_5, consumerContractsAmount int) ([]contracts.VRFv2PlusLoadTestConsumer, error) { + var consumers []contracts.VRFv2PlusLoadTestConsumer + for i := 1; i <= consumerContractsAmount; i++ { + loadTestConsumer, err := contractDeployer.DeployVRFv2PlusLoadTestConsumer(coordinator.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) + } + consumers = append(consumers, loadTestConsumer) + } + return consumers, nil +} + +func VRFV2_5RegisterProvingKey( + vrfKey *client.VRFKey, + coordinator contracts.VRFCoordinatorV2_5, + gasLaneMaxGas uint64, +) (vrfcommon.VRFEncodedProvingKey, error) { + provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) + if err != nil { + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrEncodingProvingKey, err) + } + err = coordinator.RegisterProvingKey( + provingKey, + gasLaneMaxGas, + ) + if err != nil { + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisterProvingKey, err) + } + return provingKey, nil +} + +func VRFV2PlusUpgradedVersionRegisterProvingKey( + vrfKey *client.VRFKey, + coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, +) (vrfcommon.VRFEncodedProvingKey, error) { + provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) + if err != nil { + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrEncodingProvingKey, err) + } + err = coordinator.RegisterProvingKey( + provingKey, + ) + if err != nil { + return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisterProvingKey, err) + } + return provingKey, nil +} + +func FundVRFCoordinatorV2_5Subscription( + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2_5, + chainClient blockchain.EVMClient, + subscriptionID *big.Int, + linkFundingAmountJuels *big.Int, +) error { + encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint256"}]`, subscriptionID) + if err != nil { + 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", vrfcommon.ErrSendingLinkToken, err) + } + return chainClient.WaitForEvents() +} + +func CreateFundSubsAndAddConsumers( + env *test_env.CLClusterTestEnv, + subscriptionFundingAmountNative *big.Float, + subscriptionFundingAmountLink *big.Float, + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2_5, + consumers []contracts.VRFv2PlusLoadTestConsumer, + numberOfSubToCreate int, +) ([]*big.Int, error) { + subIDs, err := CreateSubsAndFund( + env, + subscriptionFundingAmountNative, + subscriptionFundingAmountLink, + linkToken, + coordinator, + numberOfSubToCreate, + ) + if err != nil { + return nil, err + } + subToConsumersMap := map[*big.Int][]contracts.VRFv2PlusLoadTestConsumer{} + + //each subscription will have the same consumers + for _, subID := range subIDs { + subToConsumersMap[subID] = consumers + } + + err = AddConsumersToSubs( + subToConsumersMap, + coordinator, + ) + if err != nil { + return nil, err + } + + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + return subIDs, nil +} + +func CreateSubsAndFund( + env *test_env.CLClusterTestEnv, + subscriptionFundingAmountNative *big.Float, + subscriptionFundingAmountLink *big.Float, + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2_5, + subAmountToCreate int, +) ([]*big.Int, error) { + subs, err := CreateSubs(env, coordinator, subAmountToCreate) + if err != nil { + return nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + err = FundSubscriptions( + env, + subscriptionFundingAmountNative, + subscriptionFundingAmountLink, + linkToken, + coordinator, + subs, + ) + if err != nil { + return nil, err + } + return subs, nil +} + +func CreateSubs( + env *test_env.CLClusterTestEnv, + coordinator contracts.VRFCoordinatorV2_5, + subAmountToCreate int, +) ([]*big.Int, error) { + var subIDArr []*big.Int + + for i := 0; i < subAmountToCreate; i++ { + subID, err := CreateSubAndFindSubID(env, coordinator) + if err != nil { + return nil, err + } + subIDArr = append(subIDArr, subID) + } + return subIDArr, nil +} + +func AddConsumersToSubs( + subToConsumerMap map[*big.Int][]contracts.VRFv2PlusLoadTestConsumer, + coordinator contracts.VRFCoordinatorV2_5, +) error { + for subID, consumers := range subToConsumerMap { + for _, consumer := range consumers { + err := coordinator.AddConsumer(subID, consumer.Address()) + if err != nil { + return fmt.Errorf("%s, err %w", ErrAddConsumerToSub, err) + } + } + } + return nil +} + +func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2_5) (*big.Int, error) { + tx, err := coordinator.CreateSubscription() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreateVRFSubscription, err) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + receipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + //SubscriptionsCreated Log should be emitted with the subscription ID + subID := receipt.Logs[0].Topics[1].Big() + + return subID, nil +} + +func FundSubscriptions( + env *test_env.CLClusterTestEnv, + subscriptionFundingAmountNative *big.Float, + subscriptionFundingAmountLink *big.Float, + linkAddress contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2_5, + subIDs []*big.Int, +) error { + for _, subID := range subIDs { + //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", vrfcommon.ErrFundSubWithLinkToken, err) + } + } + err := env.EVMClient.WaitForEvents() + if err != nil { + return fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + return nil +} + +func GetUpgradedCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { + linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) + } + nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) + } + return +} + +func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { + linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) + } + nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) + } + return +} + +func RequestRandomnessAndWaitForRequestedEvent( + consumer contracts.VRFv2PlusLoadTestConsumer, + coordinator contracts.VRFCoordinatorV2_5, + vrfKeyData *vrfcommon.VRFKeyData, + subID *big.Int, + isNativeBilling bool, + config *vrfv2plus_config.General, + l zerolog.Logger, +) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested, error) { + LogRandRequest( + l, + consumer.Address(), + coordinator.Address(), + subID, + isNativeBilling, + vrfKeyData.KeyHash, + config, + ) + ch := make(chan *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested) + errorChannel := make(chan error) + go func() { + _, err := consumer.RequestRandomness( + vrfKeyData.KeyHash, + subID, + *config.MinimumConfirmations, + *config.CallbackGasLimit, + isNativeBilling, + *config.NumberOfWords, + *config.RandomnessRequestCountPerRequest, + ) + if err != nil { + l.Error().Err(err).Msg(err.Error()) + errorChannel <- err + } + }() + go func() { + randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfKeyData.KeyHash}, + []*big.Int{subID}, + []common.Address{common.HexToAddress(consumer.Address())}, + time.Minute*1, + ) + if err != nil { + l.Error().Err(err).Msg("error waiting for RandomnessRequested events") + errorChannel <- err + } + LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent, isNativeBilling) + ch <- randomWordsRequestedEvent + }() + for { + select { + case err := <-errorChannel: + return nil, err + case event := <-ch: + return event, nil + case <-time.After(config.RandomWordsFulfilledEventTimeout.Duration): + return nil, fmt.Errorf("timeout waiting for RandomnessRequested events") + } + } +} + +func RequestRandomnessAndWaitForFulfillment( + consumer contracts.VRFv2PlusLoadTestConsumer, + coordinator contracts.VRFCoordinatorV2_5, + vrfKeyData *vrfcommon.VRFKeyData, + subID *big.Int, + isNativeBilling bool, + config *vrfv2plus_config.General, + l zerolog.Logger, +) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { + LogRandRequest( + l, + consumer.Address(), + coordinator.Address(), + subID, + isNativeBilling, + vrfKeyData.KeyHash, + config, + ) + ch := make(chan *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled) + errorChannel := make(chan error) + go func() { + _, err := consumer.RequestRandomness( + vrfKeyData.KeyHash, + subID, + *config.MinimumConfirmations, + *config.CallbackGasLimit, + isNativeBilling, + *config.NumberOfWords, + *config.RandomnessRequestCountPerRequest, + ) + if err != nil { + l.Error().Err(err).Msg(err.Error()) + errorChannel <- err + } + }() + go func() { + fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents( + consumer.Address(), + coordinator, + vrfKeyData, + subID, + isNativeBilling, + config.RandomWordsFulfilledEventTimeout.Duration, + l, + ) + if err != nil { + l.Error().Err(err).Msg("error waiting for RandomnessRequested and RandomWordsFulfilled events") + errorChannel <- err + } + ch <- fulfillmentEvents + }() + for { + select { + case err := <-errorChannel: + return nil, err + case fulfillmentEvent := <-ch: + return fulfillmentEvent, nil + case <-time.After(config.RandomWordsFulfilledEventTimeout.Duration): + return nil, fmt.Errorf("timeout waiting for RandomnessRequested and RandomWordsFulfilled events") + } + } +} + +func RequestRandomnessAndWaitForFulfillmentUpgraded( + consumer contracts.VRFv2PlusLoadTestConsumer, + coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, + vrfKeyData *vrfcommon.VRFKeyData, + subID *big.Int, + isNativeBilling bool, + config *vrfv2plus_config.General, + l zerolog.Logger, +) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { + LogRandRequest( + l, + consumer.Address(), + coordinator.Address(), + subID, + isNativeBilling, + vrfKeyData.KeyHash, + config, + ) + _, err := consumer.RequestRandomness( + vrfKeyData.KeyHash, + subID, + *config.MinimumConfirmations, + *config.CallbackGasLimit, + isNativeBilling, + *config.NumberOfWords, + *config.RandomnessRequestCountPerRequest, + ) + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) + } + + return WaitForRequestAndFulfillmentEventsUpgraded( + consumer.Address(), + coordinator, + vrfKeyData, + subID, + isNativeBilling, + config.RandomWordsFulfilledEventTimeout.Duration, + l, + ) +} + +func DeployVRFV2PlusDirectFundingContracts( + contractDeployer contracts.ContractDeployer, + chainClient blockchain.EVMClient, + linkTokenAddress string, + linkEthFeedAddress string, + coordinator contracts.VRFCoordinatorV2_5, + consumerContractsAmount int, +) (*VRFV2PlusWrapperContracts, error) { + + vrfv2PlusWrapper, err := contractDeployer.DeployVRFV2PlusWrapper(linkTokenAddress, linkEthFeedAddress, coordinator.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrDeployWrapper, err) + } + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + consumers, err := DeployVRFV2PlusWrapperConsumers(contractDeployer, vrfv2PlusWrapper, consumerContractsAmount) + if err != nil { + return nil, err + } + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + return &VRFV2PlusWrapperContracts{vrfv2PlusWrapper, consumers}, nil +} + +func WrapperRequestRandomness( + consumer contracts.VRFv2PlusWrapperLoadTestConsumer, + coordinatorAddress string, + vrfKeyData *vrfcommon.VRFKeyData, + subID *big.Int, + isNativeBilling bool, + config *vrfv2plus_config.General, + l zerolog.Logger) (string, error) { + LogRandRequest( + l, + consumer.Address(), + coordinatorAddress, + subID, + isNativeBilling, + vrfKeyData.KeyHash, + config, + ) + if isNativeBilling { + _, err := consumer.RequestRandomnessNative( + *config.MinimumConfirmations, + *config.CallbackGasLimit, + *config.NumberOfWords, + *config.RandomnessRequestCountPerRequest, + ) + if err != nil { + return "", fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingNativePayment, err) + } + } else { + _, err := consumer.RequestRandomness( + *config.MinimumConfirmations, + *config.CallbackGasLimit, + *config.NumberOfWords, + *config.RandomnessRequestCountPerRequest, + ) + if err != nil { + 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, + config *vrfv2plus_config.General, + l zerolog.Logger, +) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { + wrapperAddress, err := WrapperRequestRandomness(consumer, coordinator.Address(), vrfKeyData, subID, + isNativeBilling, config, l) + if err != nil { + return nil, fmt.Errorf("error getting wrapper address, err: %w", err) + } + return WaitForRequestAndFulfillmentEvents( + wrapperAddress, + coordinator, + vrfKeyData, + subID, + isNativeBilling, + config.RandomWordsFulfilledEventTimeout.Duration, + l, + ) +} + +func DirectFundingRequestRandomnessAndWaitForFulfillmentUpgraded( + consumer contracts.VRFv2PlusWrapperLoadTestConsumer, + coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, + vrfKeyData *vrfcommon.VRFKeyData, + subID *big.Int, + isNativeBilling bool, + config *vrfv2plus_config.General, + l zerolog.Logger, +) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { + wrapperAddress, err := WrapperRequestRandomness(consumer, coordinator.Address(), vrfKeyData, subID, + isNativeBilling, config, l) + if err != nil { + return nil, fmt.Errorf("error getting wrapper address, err: %w", err) + } + return WaitForRequestAndFulfillmentEventsUpgraded( + wrapperAddress, + coordinator, + vrfKeyData, + subID, + isNativeBilling, + config.RandomWordsFulfilledEventTimeout.Duration, + l, + ) +} + +func WaitForRequestAndFulfillmentEvents( + consumerAddress string, + coordinator contracts.VRFCoordinatorV2_5, + 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{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) + } + + LogRandomnessRequestedEvent(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) + } + + LogRandomWordsFulfilledEvent(l, coordinator, randomWordsFulfilledEvent, isNativeBilling) + return randomWordsFulfilledEvent, 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 DeployVRFV2PlusWrapperConsumers(contractDeployer contracts.ContractDeployer, vrfV2PlusWrapper contracts.VRFV2PlusWrapper, consumerContractsAmount int) ([]contracts.VRFv2PlusWrapperLoadTestConsumer, error) { + var consumers []contracts.VRFv2PlusWrapperLoadTestConsumer + for i := 1; i <= consumerContractsAmount; i++ { + loadTestConsumer, err := contractDeployer.DeployVRFV2PlusWrapperLoadTestConsumer(vrfV2PlusWrapper.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) + } + consumers = append(consumers, loadTestConsumer) + } + return consumers, nil +} + +func SetupVRFV2PlusContracts( + env *test_env.CLClusterTestEnv, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.VRFMockETHLINKFeed, + configGeneral *vrfv2plus_config.General, + l zerolog.Logger, +) (*vrfcommon.VRFContracts, error) { + l.Info().Msg("Deploying VRFV2 Plus contracts") + vrfContracts, err := DeployVRFV2_5Contracts(env.ContractDeployer, env.EVMClient) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2_5Contracts, err) + } + vrfContracts.LinkToken = linkToken + vrfContracts.MockETHLINKFeed = mockNativeLINKFeed + + 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, fmt.Errorf("%s, err %w", vrfcommon.ErrSetVRFCoordinatorConfig, err) + } + + 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, fmt.Errorf("%s, err %w", ErrSetLinkNativeLinkFeed, err) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + return vrfContracts, nil +} + +func SetupNewConsumersAndSubs( + env *test_env.CLClusterTestEnv, + coordinator contracts.VRFCoordinatorV2_5, + testConfig tc.TestConfig, + linkToken contracts.LinkToken, + consumerContractsAmount int, + numberOfSubToCreate int, + l zerolog.Logger, +) ([]contracts.VRFv2PlusLoadTestConsumer, []*big.Int, error) { + consumers, err := DeployVRFV2PlusConsumers(env.ContractDeployer, coordinator, consumerContractsAmount) + if err != nil { + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err: %w", vrfcommon.ErrWaitTXsComplete, err) + } + l.Info(). + Str("Coordinator", *testConfig.VRFv2Plus.ExistingEnvConfig.ExistingEnvConfig.CoordinatorAddress). + Int("Number of Subs to create", numberOfSubToCreate). + Msg("Creating and funding subscriptions, deploying and adding consumers to subs") + subIDs, err := CreateFundSubsAndAddConsumers( + env, + big.NewFloat(*testConfig.VRFv2Plus.General.SubscriptionFundingAmountNative), + big.NewFloat(*testConfig.VRFv2Plus.General.SubscriptionFundingAmountLink), + linkToken, + coordinator, + consumers, + *testConfig.VRFv2Plus.General.NumberOfSubToCreate, + ) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + return consumers, subIDs, nil +} + +func CancelSubsAndReturnFunds(ctx context.Context, vrfContracts *vrfcommon.VRFContracts, eoaWalletAddress string, 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 := vrfContracts.CoordinatorV2Plus.PendingRequestsExist(ctx, subID) + if err != nil { + l.Error().Err(err).Msg("Error checking if pending requests exist") + } + if !pendingRequestsExist { + _, err := vrfContracts.CoordinatorV2Plus.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + if err != nil { + l.Error().Err(err).Msg("Error canceling subscription") + } + } else { + l.Error().Str("Sub ID", subID.String()).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") + } + } +} diff --git a/integration-tests/actions/vrf/vrfv2plus/logging_helpers.go b/integration-tests/actions/vrf/vrfv2plus/logging_helpers.go new file mode 100644 index 00000000000..995af9ee76c --- /dev/null +++ b/integration-tests/actions/vrf/vrfv2plus/logging_helpers.go @@ -0,0 +1,188 @@ +package vrfv2plus + +import ( + "fmt" + "math/big" + + "github.com/rs/zerolog" + + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" +) + +func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2_5.GetSubscription, subID *big.Int, coordinator contracts.VRFCoordinatorV2_5) { + l.Debug(). + Str("Coordinator", coordinator.Address()). + Str("Link Balance", (*commonassets.Link)(subscription.Balance).Link()). + Str("Native Token Balance", assets.FormatWei(subscription.NativeBalance)). + Str("Subscription ID", subID.String()). + Str("Subscription Owner", subscription.SubOwner.String()). + Interface("Subscription Consumers", subscription.Consumers). + Msg("Subscription Data") +} + +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()). + 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). + Msg("RandomnessRequested Event") +} + +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()). + Str("Request ID", randomWordsFulfilledEvent.RequestId.String()). + Bool("Success", randomWordsFulfilledEvent.Success). + Msg("RandomWordsFulfilled Event (TX metadata)") +} + +func LogRandomnessRequestedEvent( + l zerolog.Logger, + coordinator contracts.VRFCoordinatorV2_5, + randomWordsRequestedEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested, + isNativeBilling bool, +) { + 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()). + 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") +} + +func LogRandomWordsFulfilledEvent( + l zerolog.Logger, + coordinator contracts.VRFCoordinatorV2_5, + randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, + isNativeBilling bool, +) { + l.Info(). + Bool("Native Billing", isNativeBilling). + Str("Coordinator", coordinator.Address()). + Str("Total Payment", randomWordsFulfilledEvent.Payment.String()). + Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). + 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 *vrfcommon.VRFContracts) { + l.Info(). + Str("Subscription ID", migrationCompletedEvent.SubId.String()). + 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.Info(). + Str("New Coordinator", newCoordinator.Address()). + Str("Subscription ID", subID.String()). + Str("Juels Balance", migratedSubscription.Balance.String()). + Str("Native Token Balance", migratedSubscription.NativeBalance.String()). + Str("Subscription Owner", migratedSubscription.SubOwner.String()). + Interface("Subscription Consumers", migratedSubscription.Consumers). + Msg("Subscription Data After Migration to New Coordinator") +} + +func LogFulfillmentDetailsLinkBilling( + l zerolog.Logger, + wrapperConsumerJuelsBalanceBeforeRequest *big.Int, + wrapperConsumerJuelsBalanceAfterRequest *big.Int, + consumerStatus vrfv2plus_wrapper_load_test_consumer.GetRequestStatus, + randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, +) { + 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). + Str("Paid by Consumer Contract (Link)", (*commonassets.Link)(consumerStatus.Paid).Link()). + Str("Paid by Coordinator Sub (Link)", (*commonassets.Link)(randomWordsFulfilledEvent.Payment).Link()). + Str("RequestTimestamp", consumerStatus.RequestTimestamp.String()). + Str("FulfilmentTimestamp", consumerStatus.FulfilmentTimestamp.String()). + Str("RequestBlockNumber", consumerStatus.RequestBlockNumber.String()). + Str("FulfilmentBlockNumber", consumerStatus.FulfilmentBlockNumber.String()). + Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). + Msg("Random Words Fulfilment Details For Link Billing") +} + +func LogFulfillmentDetailsNativeBilling( + l zerolog.Logger, + wrapperConsumerBalanceBeforeRequestWei *big.Int, + wrapperConsumerBalanceAfterRequestWei *big.Int, + consumerStatus vrfv2plus_wrapper_load_test_consumer.GetRequestStatus, + randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, +) { + l.Info(). + Str("Consumer Balance Before Request", assets.FormatWei(wrapperConsumerBalanceBeforeRequestWei)). + Str("Consumer Balance After Request", assets.FormatWei(wrapperConsumerBalanceAfterRequestWei)). + Bool("Fulfilment Status", consumerStatus.Fulfilled). + Str("Paid by Consumer Contract", assets.FormatWei(consumerStatus.Paid)). + Str("Paid by Coordinator Sub", assets.FormatWei(randomWordsFulfilledEvent.Payment)). + Str("RequestTimestamp", consumerStatus.RequestTimestamp.String()). + Str("FulfilmentTimestamp", consumerStatus.FulfilmentTimestamp.String()). + Str("RequestBlockNumber", consumerStatus.RequestBlockNumber.String()). + Str("FulfilmentBlockNumber", consumerStatus.FulfilmentBlockNumber.String()). + Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). + Msg("Random Words Request Fulfilment Details For Native Billing") +} + +func LogRandRequest( + l zerolog.Logger, + consumer string, + coordinator string, + subID *big.Int, + isNativeBilling bool, + keyHash [32]byte, + config *vrfv2plus_config.General) { + l.Info(). + Str("Consumer", consumer). + Str("Coordinator", coordinator). + Str("SubID", subID.String()). + Bool("IsNativePayment", isNativeBilling). + Uint16("MinimumConfirmations", *config.MinimumConfirmations). + Uint32("CallbackGasLimit", *config.CallbackGasLimit). + Uint32("NumberOfWords", *config.NumberOfWords). + Str("KeyHash", fmt.Sprintf("0x%x", keyHash)). + Uint16("RandomnessRequestCountPerRequest", *config.RandomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", *config.RandomnessRequestCountPerRequestDeviation). + Msg("Requesting randomness") +} diff --git a/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_models.go b/integration-tests/actions/vrf/vrfv2plus/models.go similarity index 100% rename from integration-tests/actions/vrf/vrfv2plus/vrfv2plus_models.go rename to integration-tests/actions/vrf/vrfv2plus/models.go diff --git a/integration-tests/actions/vrf/vrfv2plus/setup_steps.go b/integration-tests/actions/vrf/vrfv2plus/setup_steps.go new file mode 100644 index 00000000000..cfaa49b418b --- /dev/null +++ b/integration-tests/actions/vrf/vrfv2plus/setup_steps.go @@ -0,0 +1,480 @@ +package vrfv2plus + +import ( + "context" + "fmt" + "math/big" + "testing" + "time" + + "golang.org/x/sync/errgroup" + + "github.com/ethereum/go-ethereum/common" + "github.com/google/uuid" + "github.com/rs/zerolog" + + vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + + "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" + vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" + "github.com/smartcontractkit/chainlink/integration-tests/types" +) + +func CreateVRFV2PlusJob( + chainlinkNode *client.ChainlinkClient, + 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", vrfcommon.ErrParseJob, err) + } + + job, err := chainlinkNode.MustCreateJob(&client.VRFV2PlusJobSpec{ + Name: fmt.Sprintf("vrf-v2-plus-%s", jobUUID), + CoordinatorAddress: vrfJobSpecConfig.CoordinatorAddress, + FromAddresses: vrfJobSpecConfig.FromAddresses, + EVMChainID: vrfJobSpecConfig.EVMChainID, + MinIncomingConfirmations: vrfJobSpecConfig.MinIncomingConfirmations, + PublicKey: vrfJobSpecConfig.PublicKey, + ExternalJobID: jobUUID.String(), + ObservationSource: ost, + BatchFulfillmentEnabled: vrfJobSpecConfig.BatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: vrfJobSpecConfig.BatchFulfillmentGasMultiplier, + PollPeriod: vrfJobSpecConfig.PollPeriod, + RequestTimeout: vrfJobSpecConfig.RequestTimeout, + }) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2PlusJob, err) + } + + return job, nil +} + +// SetupVRFV2_5Environment will create specified number of subscriptions and add the same conumer/s to each of them +func SetupVRFV2_5Environment( + ctx context.Context, + env *test_env.CLClusterTestEnv, + nodesToCreate []vrfcommon.VRFNodeType, + vrfv2PlusTestConfig types.VRFv2PlusTestConfig, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.VRFMockETHLINKFeed, + numberOfTxKeysToCreate int, + l zerolog.Logger, +) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { + l.Info().Msg("Starting VRFV2 Plus environment setup") + configGeneral := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General + vrfContracts, err := SetupVRFV2PlusContracts( + env, + linkToken, + mockNativeLINKFeed, + configGeneral, + l, + ) + if err != nil { + return nil, nil, nil, err + } + + nodeTypeToNodeMap, err := vrfcommon.CreateNodeTypeToNodeMap(env.ClCluster, nodesToCreate) + if err != nil { + return nil, nil, nil, err + } + vrfKey, pubKeyCompressed, err := vrfcommon.CreateVRFKeyOnVRFNode(nodeTypeToNodeMap[vrfcommon.VRF], l) + if err != nil { + return nil, nil, nil, err + } + 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", vrfcommon.ErrRegisteringProvingKey, err) + } + keyHash, err := vrfContracts.CoordinatorV2Plus.HashOfKey(ctx, provingKey) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreatingProvingKeyHash, err) + } + + chainID := env.EVMClient.GetChainID() + vrfTXKeyAddressStrings, _, err := vrfcommon.CreateFundAndGetSendingKeys( + env.EVMClient, + nodeTypeToNodeMap[vrfcommon.VRF], + *vrfv2PlusTestConfig.GetCommonConfig().ChainlinkNodeFunding, + numberOfTxKeysToCreate, + chainID, + ) + if err != nil { + return nil, nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return 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, 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, + 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, fmt.Errorf("VRF node setup ended up with an error: %w", err) + } + + vrfKeyData := vrfcommon.VRFKeyData{ + VRFKey: vrfKey, + EncodedProvingKey: provingKey, + KeyHash: keyHash, + PubKeyCompressed: pubKeyCompressed, + } + + l.Info().Msg("VRFV2 Plus environment setup is finished") + return vrfContracts, &vrfKeyData, nodeTypeToNodeMap, nil +} + +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(*config.MinimumConfirmations), + PublicKey: pubKeyCompressed, + 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( + vrfNode.CLNode.API, + vrfJobSpecConfig, + ) + if err != nil { + 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(vrfNode.CLNode.NodeConfig, + node.WithLogPollInterval(1*time.Second), + node.WithVRFv2EVMEstimator(vrfNode.TXKeyAddressStrings, *config.CLNodeMaxGasPriceGWei), + ) + l.Info().Msg("Restarting Node with new sending key PriceMax configuration") + err = vrfNode.CLNode.Restart(nodeConfig) + if err != nil { + return fmt.Errorf("%s, err %w", vrfcommon.ErrRestartCLNode, err) + } + return nil +} + +func SetupVRFV2PlusWrapperEnvironment( + ctx context.Context, + env *test_env.CLClusterTestEnv, + vrfv2PlusTestConfig types.VRFv2PlusTestConfig, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.MockETHLINKFeed, + coordinator contracts.VRFCoordinatorV2_5, + keyHash [32]byte, + wrapperConsumerContractsAmount int, +) (*VRFV2PlusWrapperContracts, *big.Int, error) { + vrfv2PlusConfig := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General + wrapperContracts, err := DeployVRFV2PlusDirectFundingContracts( + env.ContractDeployer, + env.EVMClient, + linkToken.Address(), + mockNativeLINKFeed.Address(), + coordinator, + wrapperConsumerContractsAmount, + ) + if err != nil { + return nil, nil, err + } + + err = env.EVMClient.WaitForEvents() + + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + err = wrapperContracts.VRFV2PlusWrapper.SetConfig( + *vrfv2PlusConfig.WrapperGasOverhead, + *vrfv2PlusConfig.CoordinatorGasOverhead, + //todo - introduce separate config for Wrapper Premium Percentage + *vrfv2PlusConfig.NativePremiumPercentage, + *vrfv2PlusConfig.LinkPremiumPercentage, + keyHash, + *vrfv2PlusConfig.WrapperMaxNumberOfWords, + *vrfv2PlusConfig.StalenessSeconds, + big.NewInt(*vrfv2PlusConfig.FallbackWeiPerUnitLink), + *vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, + *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, + ) + if err != nil { + return nil, nil, err + } + + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + //fund sub + wrapperSubID, err := wrapperContracts.VRFV2PlusWrapper.GetSubID(ctx) + if err != nil { + return nil, nil, err + } + + err = env.EVMClient.WaitForEvents() + if err != nil { + 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}, + ) + if err != nil { + return nil, nil, err + } + + //fund consumer with Link + err = linkToken.Transfer( + wrapperContracts.LoadTestConsumers[0].Address(), + big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(*vrfv2PlusConfig.WrapperConsumerFundingAmountLink)), + ) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + + //fund consumer with Eth + err = wrapperContracts.LoadTestConsumers[0].Fund(big.NewFloat(*vrfv2PlusConfig.WrapperConsumerFundingAmountNativeToken)) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) + } + return wrapperContracts, wrapperSubID, nil +} + +func SetupVRFV2PlusUniverse(ctx context.Context, t *testing.T, testConfig tc.TestConfig, cleanupFn func(), newEnvConfig vrfcommon.NewEnvConfig, l zerolog.Logger) (*test_env.CLClusterTestEnv, *vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { + var ( + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNode map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + err error + ) + if *testConfig.VRFv2Plus.General.UseExistingEnv { + vrfContracts, vrfKey, env, err = SetupVRFV2PlusForExistingEnv(ctx, t, testConfig, cleanupFn, l) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "Error setting up VRF V2 Plus for Existing env", err) + } + } else { + vrfContracts, vrfKey, env, nodeTypeToNode, err = SetupVRFV2PlusForNewEnv(ctx, t, testConfig, cleanupFn, newEnvConfig, l) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "Error setting up VRF V2 Plus for New env", err) + } + } + return env, vrfContracts, vrfKey, nodeTypeToNode, nil +} + +func SetupVRFV2PlusForNewEnv( + ctx context.Context, + t *testing.T, + testConfig tc.TestConfig, + cleanupFn func(), + newEnvConfig vrfcommon.NewEnvConfig, + l zerolog.Logger, +) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, *test_env.CLClusterTestEnv, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { + network, err := actions.EthereumNetworkConfigFromConfig(l, &testConfig) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "Error building ethereum network config", err) + } + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithTestConfig(&testConfig). + WithPrivateEthereumNetwork(network). + WithCLNodes(len(newEnvConfig.NodesToCreate)). + WithFunding(big.NewFloat(*testConfig.Common.ChainlinkNodeFunding)). + WithCustomCleanup(cleanupFn). + Build() + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error creating test env", err) + } + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := env.ContractDeployer.DeployVRFMockETHLINKFeed(big.NewInt(*testConfig.VRFv2Plus.General.LinkNativeFeedResponse)) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error deploying mock ETH/LINK feed", err) + } + + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error deploying LINK contract", err) + } + + vrfContracts, vrfKey, nodeTypeToNode, err := SetupVRFV2_5Environment( + ctx, + env, + newEnvConfig.NodesToCreate, + &testConfig, + linkToken, + mockETHLinkFeed, + newEnvConfig.NumberOfTxKeysToCreate, + l, + ) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error setting up VRF v2_5 env", err) + } + return vrfContracts, vrfKey, env, nodeTypeToNode, nil +} + +func SetupVRFV2PlusForExistingEnv(ctx context.Context, t *testing.T, testConfig tc.TestConfig, cleanupFn func(), l zerolog.Logger) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, *test_env.CLClusterTestEnv, error) { + commonExistingEnvConfig := testConfig.VRFv2Plus.ExistingEnvConfig.ExistingEnvConfig + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithTestConfig(&testConfig). + WithCustomCleanup(cleanupFn). + Build() + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err: %w", "error creating test env", err) + } + coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2_5(*commonExistingEnvConfig.CoordinatorAddress) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading VRFCoordinator2_5", err) + } + linkToken, err := env.ContractLoader.LoadLINKToken(*commonExistingEnvConfig.LinkAddress) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading LinkToken", err) + } + err = vrfcommon.FundNodesIfNeeded(ctx, commonExistingEnvConfig, env.EVMClient, l) + if err != nil { + return nil, nil, nil, fmt.Errorf("err: %w", err) + } + vrfContracts := &vrfcommon.VRFContracts{ + CoordinatorV2Plus: coordinator, + VRFV2PlusConsumer: nil, + LinkToken: linkToken, + BHS: nil, + } + + vrfKey := &vrfcommon.VRFKeyData{ + VRFKey: nil, + EncodedProvingKey: [2]*big.Int{}, + KeyHash: common.HexToHash(*commonExistingEnvConfig.KeyHash), + } + return vrfContracts, vrfKey, env, nil +} + +func SetupSubsAndConsumersForExistingEnv( + env *test_env.CLClusterTestEnv, + coordinator contracts.VRFCoordinatorV2_5, + linkToken contracts.LinkToken, + numberOfConsumerContractsToDeployAndAddToSub int, + numberOfSubToCreate int, + testConfig tc.TestConfig, + l zerolog.Logger, +) ([]*big.Int, []contracts.VRFv2PlusLoadTestConsumer, error) { + var ( + subIDs []*big.Int + consumers []contracts.VRFv2PlusLoadTestConsumer + err error + ) + if *testConfig.VRFv2Plus.General.UseExistingEnv { + commonExistingEnvConfig := testConfig.VRFv2Plus.ExistingEnvConfig.ExistingEnvConfig + if *commonExistingEnvConfig.CreateFundSubsAndAddConsumers { + consumers, subIDs, err = SetupNewConsumersAndSubs( + env, + coordinator, + testConfig, + linkToken, + numberOfConsumerContractsToDeployAndAddToSub, + numberOfSubToCreate, + l, + ) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + } else { + consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(*commonExistingEnvConfig.ConsumerAddress) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + consumers = append(consumers, consumer) + var ok bool + subID, ok := new(big.Int).SetString(*testConfig.VRFv2Plus.ExistingEnvConfig.SubID, 10) + if !ok { + return nil, nil, fmt.Errorf("unable to parse subID: %s %w", *testConfig.VRFv2Plus.ExistingEnvConfig.SubID, err) + } + subIDs = append(subIDs, subID) + } + } else { + consumers, subIDs, err = SetupNewConsumersAndSubs( + env, + coordinator, + testConfig, + linkToken, + numberOfConsumerContractsToDeployAndAddToSub, + numberOfSubToCreate, + l, + ) + if err != nil { + return nil, nil, fmt.Errorf("err: %w", err) + } + } + return subIDs, consumers, nil +} diff --git a/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go deleted file mode 100644 index c9a19c7268c..00000000000 --- a/integration-tests/actions/vrf/vrfv2plus/vrfv2plus_steps.go +++ /dev/null @@ -1,1146 +0,0 @@ -package vrfv2plus - -import ( - "context" - "fmt" - "math/big" - "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" - - "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" - vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" - "github.com/smartcontractkit/chainlink/integration-tests/types" - 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" -) - -func DeployVRFV2_5Contracts( - contractDeployer contracts.ContractDeployer, - chainClient blockchain.EVMClient, - consumerContractsAmount int, -) (*vrfcommon.VRFContracts, error) { - bhs, err := contractDeployer.DeployBlockhashStore() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrDeployBlockHashStore, err) - } - err = chainClient.WaitForEvents() - if err != nil { - 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", vrfcommon.ErrDeployCoordinator, err) - } - err = chainClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - consumers, err := DeployVRFV2PlusConsumers(contractDeployer, coordinator, consumerContractsAmount) - if err != nil { - return nil, err - } - err = chainClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - return &vrfcommon.VRFContracts{ - CoordinatorV2Plus: coordinator, - BHS: bhs, - VRFV2PlusConsumer: consumers, - }, nil -} - -func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coordinator contracts.VRFCoordinatorV2_5, consumerContractsAmount int) ([]contracts.VRFv2PlusLoadTestConsumer, error) { - var consumers []contracts.VRFv2PlusLoadTestConsumer - for i := 1; i <= consumerContractsAmount; i++ { - loadTestConsumer, err := contractDeployer.DeployVRFv2PlusLoadTestConsumer(coordinator.Address()) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) - } - consumers = append(consumers, loadTestConsumer) - } - return consumers, nil -} - -func CreateVRFV2PlusJob( - chainlinkNode *client.ChainlinkClient, - 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", vrfcommon.ErrParseJob, err) - } - - job, err := chainlinkNode.MustCreateJob(&client.VRFV2PlusJobSpec{ - Name: fmt.Sprintf("vrf-v2-plus-%s", jobUUID), - CoordinatorAddress: vrfJobSpecConfig.CoordinatorAddress, - FromAddresses: vrfJobSpecConfig.FromAddresses, - EVMChainID: vrfJobSpecConfig.EVMChainID, - MinIncomingConfirmations: vrfJobSpecConfig.MinIncomingConfirmations, - PublicKey: vrfJobSpecConfig.PublicKey, - ExternalJobID: jobUUID.String(), - ObservationSource: ost, - BatchFulfillmentEnabled: vrfJobSpecConfig.BatchFulfillmentEnabled, - BatchFulfillmentGasMultiplier: vrfJobSpecConfig.BatchFulfillmentGasMultiplier, - PollPeriod: vrfJobSpecConfig.PollPeriod, - RequestTimeout: vrfJobSpecConfig.RequestTimeout, - }) - 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, - gasLaneMaxGas uint64, -) (vrfcommon.VRFEncodedProvingKey, error) { - provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) - if err != nil { - return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrEncodingProvingKey, err) - } - err = coordinator.RegisterProvingKey( - provingKey, - gasLaneMaxGas, - ) - if err != nil { - return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisterProvingKey, err) - } - return provingKey, nil -} - -func VRFV2PlusUpgradedVersionRegisterProvingKey( - vrfKey *client.VRFKey, - coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, -) (vrfcommon.VRFEncodedProvingKey, error) { - provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) - if err != nil { - return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrEncodingProvingKey, err) - } - err = coordinator.RegisterProvingKey( - provingKey, - ) - if err != nil { - return vrfcommon.VRFEncodedProvingKey{}, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisterProvingKey, err) - } - return provingKey, nil -} - -func FundVRFCoordinatorV2_5Subscription( - linkToken contracts.LinkToken, - coordinator contracts.VRFCoordinatorV2_5, - chainClient blockchain.EVMClient, - subscriptionID *big.Int, - linkFundingAmountJuels *big.Int, -) error { - encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint256"}]`, subscriptionID) - if err != nil { - 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", vrfcommon.ErrSendingLinkToken, err) - } - return chainClient.WaitForEvents() -} - -// 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, - nodesToCreate []vrfcommon.VRFNodeType, - vrfv2PlusTestConfig types.VRFv2PlusTestConfig, - linkToken contracts.LinkToken, - mockNativeLINKFeed contracts.MockETHLINKFeed, - numberOfTxKeysToCreate int, - numberOfConsumers int, - numberOfSubToCreate int, - l zerolog.Logger, -) (*vrfcommon.VRFContracts, []*big.Int, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { - l.Info().Msg("Starting VRFV2 Plus environment setup") - configGeneral := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General - vrfContracts, subIDs, err := SetupVRFV2PlusContracts( - env, - linkToken, - mockNativeLINKFeed, - configGeneral, - numberOfSubToCreate, - numberOfConsumers, - l, - ) - if err != nil { - return nil, nil, nil, nil, err - } - - nodeTypeToNodeMap := vrfcommon.CreateNodeTypeToNodeMap(env.ClCluster, nodesToCreate) - vrfKey, pubKeyCompressed, err := vrfcommon.CreateVRFKeyOnVRFNode(nodeTypeToNodeMap[vrfcommon.VRF], l) - if err != nil { - return nil, nil, nil, nil, err - } - - 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, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisteringProvingKey, err) - } - keyHash, err := vrfContracts.CoordinatorV2Plus.HashOfKey(context.Background(), provingKey) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreatingProvingKeyHash, err) - } - - chainID := env.EVMClient.GetChainID() - vrfTXKeyAddressStrings, _, err := vrfcommon.CreateFundAndGetSendingKeys( - env.EVMClient, - nodeTypeToNodeMap[vrfcommon.VRF], - *vrfv2PlusTestConfig.GetCommonConfig().ChainlinkNodeFunding, - numberOfTxKeysToCreate, - chainID, - ) - if err != nil { - return nil, nil, nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - 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, 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, - 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: pubKeyCompressed, - } - - l.Info().Msg("VRFV2 Plus environment setup is finished") - return vrfContracts, subIDs, &vrfKeyData, nodeTypeToNodeMap, nil -} - -func SetupVRFV2PlusContracts( - env *test_env.CLClusterTestEnv, - 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") - vrfContracts, err := DeployVRFV2_5Contracts(env.ContractDeployer, env.EVMClient, numberOfConsumers) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2_5Contracts, err) - } - - 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, fmt.Errorf("%s, err %w", vrfcommon.ErrSetVRFCoordinatorConfig, err) - } - - 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 = env.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, - big.NewFloat(*configGeneral.SubscriptionFundingAmountNative), - big.NewFloat(*configGeneral.SubscriptionFundingAmountLink), - linkToken, - vrfContracts.CoordinatorV2Plus, vrfContracts.VRFV2PlusConsumer, - numberOfSubToCreate, - ) - if err != nil { - return nil, nil, err - } - return vrfContracts, subIDs, nil -} - -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(*config.MinimumConfirmations), - PublicKey: pubKeyCompressed, - 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( - vrfNode.CLNode.API, - vrfJobSpecConfig, - ) - if err != nil { - 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(vrfNode.CLNode.NodeConfig, - node.WithLogPollInterval(1*time.Second), - node.WithVRFv2EVMEstimator(vrfNode.TXKeyAddressStrings, *config.CLNodeMaxGasPriceGWei), - ) - l.Info().Msg("Restarting Node with new sending key PriceMax configuration") - err = vrfNode.CLNode.Restart(nodeConfig) - if err != nil { - return fmt.Errorf("%s, err %w", vrfcommon.ErrRestartCLNode, err) - } - return nil -} - -func CreateFundSubsAndAddConsumers( - env *test_env.CLClusterTestEnv, - subscriptionFundingAmountNative *big.Float, - subscriptionFundingAmountLink *big.Float, - linkToken contracts.LinkToken, - coordinator contracts.VRFCoordinatorV2_5, - consumers []contracts.VRFv2PlusLoadTestConsumer, - numberOfSubToCreate int, -) ([]*big.Int, error) { - subIDs, err := CreateSubsAndFund( - env, - subscriptionFundingAmountNative, - subscriptionFundingAmountLink, - linkToken, - coordinator, - numberOfSubToCreate, - ) - if err != nil { - return nil, err - } - subToConsumersMap := map[*big.Int][]contracts.VRFv2PlusLoadTestConsumer{} - - //each subscription will have the same consumers - for _, subID := range subIDs { - subToConsumersMap[subID] = consumers - } - - err = AddConsumersToSubs( - subToConsumersMap, - coordinator, - ) - if err != nil { - return nil, err - } - - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - return subIDs, nil -} - -func CreateSubsAndFund( - env *test_env.CLClusterTestEnv, - subscriptionFundingAmountNative *big.Float, - subscriptionFundingAmountLink *big.Float, - linkToken contracts.LinkToken, - coordinator contracts.VRFCoordinatorV2_5, - subAmountToCreate int, -) ([]*big.Int, error) { - subs, err := CreateSubs(env, coordinator, subAmountToCreate) - if err != nil { - return nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - err = FundSubscriptions( - env, - subscriptionFundingAmountNative, - subscriptionFundingAmountLink, - linkToken, - coordinator, - subs, - ) - if err != nil { - return nil, err - } - return subs, nil -} - -func CreateSubs( - env *test_env.CLClusterTestEnv, - coordinator contracts.VRFCoordinatorV2_5, - subAmountToCreate int, -) ([]*big.Int, error) { - var subIDArr []*big.Int - - for i := 0; i < subAmountToCreate; i++ { - subID, err := CreateSubAndFindSubID(env, coordinator) - if err != nil { - return nil, err - } - subIDArr = append(subIDArr, subID) - } - return subIDArr, nil -} - -func AddConsumersToSubs( - subToConsumerMap map[*big.Int][]contracts.VRFv2PlusLoadTestConsumer, - coordinator contracts.VRFCoordinatorV2_5, -) error { - for subID, consumers := range subToConsumerMap { - for _, consumer := range consumers { - err := coordinator.AddConsumer(subID, consumer.Address()) - if err != nil { - return fmt.Errorf("%s, err %w", ErrAddConsumerToSub, err) - } - } - } - return nil -} - -func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2_5) (*big.Int, error) { - tx, err := coordinator.CreateSubscription() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreateVRFSubscription, err) - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - receipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - //SubscriptionsCreated Log should be emitted with the subscription ID - subID := receipt.Logs[0].Topics[1].Big() - - return subID, nil -} - -func FundSubscriptions( - env *test_env.CLClusterTestEnv, - subscriptionFundingAmountNative *big.Float, - subscriptionFundingAmountLink *big.Float, - linkAddress contracts.LinkToken, - coordinator contracts.VRFCoordinatorV2_5, - subIDs []*big.Int, -) error { - for _, subID := range subIDs { - //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", vrfcommon.ErrFundSubWithLinkToken, err) - } - } - err := env.EVMClient.WaitForEvents() - if err != nil { - return fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - return nil -} - -func GetUpgradedCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { - linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) - } - nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) - } - return -} - -func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { - linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) - } - nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) - } - return -} - -func RequestRandomnessAndWaitForFulfillment( - consumer contracts.VRFv2PlusLoadTestConsumer, - coordinator contracts.VRFCoordinatorV2_5, - vrfKeyData *vrfcommon.VRFKeyData, - subID *big.Int, - isNativeBilling bool, - config *vrfv2plus_config.General, - l zerolog.Logger, -) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { - LogRandRequest( - l, - consumer.Address(), - coordinator.Address(), - subID, - isNativeBilling, - vrfKeyData.KeyHash, - config, - ) - _, err := consumer.RequestRandomness( - vrfKeyData.KeyHash, - subID, - *config.MinimumConfirmations, - *config.CallbackGasLimit, - isNativeBilling, - *config.NumberOfWords, - *config.RandomnessRequestCountPerRequest, - ) - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) - } - - return WaitForRequestAndFulfillmentEvents( - consumer.Address(), - coordinator, - vrfKeyData, - subID, - isNativeBilling, - config.RandomWordsFulfilledEventTimeout.Duration, - l, - ) -} - -func RequestRandomnessAndWaitForFulfillmentUpgraded( - consumer contracts.VRFv2PlusLoadTestConsumer, - coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, - vrfKeyData *vrfcommon.VRFKeyData, - subID *big.Int, - isNativeBilling bool, - config *vrfv2plus_config.General, - l zerolog.Logger, -) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { - LogRandRequest( - l, - consumer.Address(), - coordinator.Address(), - subID, - isNativeBilling, - vrfKeyData.KeyHash, - config, - ) - _, err := consumer.RequestRandomness( - vrfKeyData.KeyHash, - subID, - *config.MinimumConfirmations, - *config.CallbackGasLimit, - isNativeBilling, - *config.NumberOfWords, - *config.RandomnessRequestCountPerRequest, - ) - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRequestRandomness, err) - } - - return WaitForRequestAndFulfillmentEventsUpgraded( - consumer.Address(), - coordinator, - vrfKeyData, - subID, - isNativeBilling, - config.RandomWordsFulfilledEventTimeout.Duration, - l, - ) -} - -func SetupVRFV2PlusWrapperEnvironment( - env *test_env.CLClusterTestEnv, - vrfv2PlusTestConfig types.VRFv2PlusTestConfig, - linkToken contracts.LinkToken, - mockNativeLINKFeed contracts.MockETHLINKFeed, - coordinator contracts.VRFCoordinatorV2_5, - keyHash [32]byte, - wrapperConsumerContractsAmount int, -) (*VRFV2PlusWrapperContracts, *big.Int, error) { - vrfv2PlusConfig := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General - wrapperContracts, err := DeployVRFV2PlusDirectFundingContracts( - env.ContractDeployer, - env.EVMClient, - linkToken.Address(), - mockNativeLINKFeed.Address(), - coordinator, - wrapperConsumerContractsAmount, - ) - if err != nil { - return nil, nil, err - } - - err = env.EVMClient.WaitForEvents() - - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - err = wrapperContracts.VRFV2PlusWrapper.SetConfig( - *vrfv2PlusConfig.WrapperGasOverhead, - *vrfv2PlusConfig.CoordinatorGasOverhead, - *vrfv2PlusConfig.NativePremiumPercentage, - *vrfv2PlusConfig.LinkPremiumPercentage, - keyHash, - *vrfv2PlusConfig.WrapperMaxNumberOfWords, - *vrfv2PlusConfig.StalenessSeconds, - big.NewInt(*vrfv2PlusConfig.FallbackWeiPerUnitLink), - *vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, - *vrfv2PlusConfig.FulfillmentFlatFeeLinkDiscountPPM, - ) - if err != nil { - return nil, nil, err - } - - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - //fund sub - wrapperSubID, err := wrapperContracts.VRFV2PlusWrapper.GetSubID(context.Background()) - if err != nil { - return nil, nil, err - } - - err = env.EVMClient.WaitForEvents() - if err != nil { - 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}, - ) - if err != nil { - return nil, nil, err - } - - //fund consumer with Link - err = linkToken.Transfer( - wrapperContracts.LoadTestConsumers[0].Address(), - big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(*vrfv2PlusConfig.WrapperConsumerFundingAmountLink)), - ) - if err != nil { - return nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - //fund consumer with Eth - err = wrapperContracts.LoadTestConsumers[0].Fund(big.NewFloat(*vrfv2PlusConfig.WrapperConsumerFundingAmountNativeToken)) - if err != nil { - return nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - return wrapperContracts, wrapperSubID, nil -} - -func DeployVRFV2PlusWrapperConsumers(contractDeployer contracts.ContractDeployer, vrfV2PlusWrapper contracts.VRFV2PlusWrapper, consumerContractsAmount int) ([]contracts.VRFv2PlusWrapperLoadTestConsumer, error) { - var consumers []contracts.VRFv2PlusWrapperLoadTestConsumer - for i := 1; i <= consumerContractsAmount; i++ { - loadTestConsumer, err := contractDeployer.DeployVRFV2PlusWrapperLoadTestConsumer(vrfV2PlusWrapper.Address()) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) - } - consumers = append(consumers, loadTestConsumer) - } - return consumers, nil -} - -func DeployVRFV2PlusDirectFundingContracts( - contractDeployer contracts.ContractDeployer, - chainClient blockchain.EVMClient, - linkTokenAddress string, - linkEthFeedAddress string, - coordinator contracts.VRFCoordinatorV2_5, - consumerContractsAmount int, -) (*VRFV2PlusWrapperContracts, error) { - - vrfv2PlusWrapper, err := contractDeployer.DeployVRFV2PlusWrapper(linkTokenAddress, linkEthFeedAddress, coordinator.Address()) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployWrapper, err) - } - err = chainClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - - consumers, err := DeployVRFV2PlusWrapperConsumers(contractDeployer, vrfv2PlusWrapper, consumerContractsAmount) - if err != nil { - return nil, err - } - err = chainClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrWaitTXsComplete, err) - } - return &VRFV2PlusWrapperContracts{vrfv2PlusWrapper, consumers}, nil -} - -func WrapperRequestRandomness( - consumer contracts.VRFv2PlusWrapperLoadTestConsumer, - coordinatorAddress string, - vrfKeyData *vrfcommon.VRFKeyData, - subID *big.Int, - isNativeBilling bool, - config *vrfv2plus_config.General, - l zerolog.Logger) (string, error) { - LogRandRequest( - l, - consumer.Address(), - coordinatorAddress, - subID, - isNativeBilling, - vrfKeyData.KeyHash, - config, - ) - if isNativeBilling { - _, err := consumer.RequestRandomnessNative( - *config.MinimumConfirmations, - *config.CallbackGasLimit, - *config.NumberOfWords, - *config.RandomnessRequestCountPerRequest, - ) - if err != nil { - return "", fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingNativePayment, err) - } - } else { - _, err := consumer.RequestRandomness( - *config.MinimumConfirmations, - *config.CallbackGasLimit, - *config.NumberOfWords, - *config.RandomnessRequestCountPerRequest, - ) - if err != nil { - 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, - config *vrfv2plus_config.General, - l zerolog.Logger, -) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { - wrapperAddress, err := WrapperRequestRandomness(consumer, coordinator.Address(), vrfKeyData, subID, - isNativeBilling, config, l) - if err != nil { - return nil, fmt.Errorf("error getting wrapper address, err: %w", err) - } - return WaitForRequestAndFulfillmentEvents( - wrapperAddress, - coordinator, - vrfKeyData, - subID, - isNativeBilling, - config.RandomWordsFulfilledEventTimeout.Duration, - l, - ) -} - -func DirectFundingRequestRandomnessAndWaitForFulfillmentUpgraded( - consumer contracts.VRFv2PlusWrapperLoadTestConsumer, - coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion, - vrfKeyData *vrfcommon.VRFKeyData, - subID *big.Int, - isNativeBilling bool, - config *vrfv2plus_config.General, - l zerolog.Logger, -) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { - wrapperAddress, err := WrapperRequestRandomness(consumer, coordinator.Address(), vrfKeyData, subID, - isNativeBilling, config, l) - if err != nil { - return nil, fmt.Errorf("error getting wrapper address, err: %w", err) - } - return WaitForRequestAndFulfillmentEventsUpgraded( - wrapperAddress, - coordinator, - vrfKeyData, - subID, - isNativeBilling, - config.RandomWordsFulfilledEventTimeout.Duration, - l, - ) -} - -func WaitForRequestAndFulfillmentEvents( - consumerAddress string, - coordinator contracts.VRFCoordinatorV2_5, - 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{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) - } - - LogRandomnessRequestedEvent(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) - } - - LogRandomWordsFulfilledEvent(l, coordinator, randomWordsFulfilledEvent, isNativeBilling) - return randomWordsFulfilledEvent, 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 { - linkTotalBalance, err := coordinator.GetLinkTotalBalance(context.Background()) - if err != nil { - return fmt.Errorf("Error getting LINK total balance, err: %w", err) - } - defaultWallet := client.GetDefaultWallet().Address() - l.Info(). - Str("LINK amount", linkTotalBalance.String()). - Str("Returning to", defaultWallet). - Msg("Returning LINK for fulfilled requests") - err = coordinator.Withdraw( - common.HexToAddress(defaultWallet), - ) - if err != nil { - return fmt.Errorf("Error withdrawing LINK from coordinator to default wallet, err: %w", err) - } - nativeTotalBalance, err := coordinator.GetNativeTokenTotalBalance(context.Background()) - if err != nil { - return fmt.Errorf("Error getting NATIVE total balance, err: %w", err) - } - l.Info(). - Str("Native Token amount", nativeTotalBalance.String()). - Str("Returning to", defaultWallet). - Msg("Returning Native Token for fulfilled requests") - err = coordinator.WithdrawNative( - common.HexToAddress(defaultWallet), - ) - if err != nil { - return fmt.Errorf("Error withdrawing NATIVE from coordinator to default wallet, err: %w", err) - } - return nil -} - -func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2_5.GetSubscription, subID *big.Int, coordinator contracts.VRFCoordinatorV2_5) { - l.Debug(). - Str("Coordinator", coordinator.Address()). - Str("Link Balance", (*commonassets.Link)(subscription.Balance).Link()). - Str("Native Token Balance", assets.FormatWei(subscription.NativeBalance)). - Str("Subscription ID", subID.String()). - Str("Subscription Owner", subscription.SubOwner.String()). - Interface("Subscription Consumers", subscription.Consumers). - Msg("Subscription Data") -} - -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()). - 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). - Msg("RandomnessRequested Event") -} - -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()). - Str("Request ID", randomWordsFulfilledEvent.RequestId.String()). - Bool("Success", randomWordsFulfilledEvent.Success). - Msg("RandomWordsFulfilled Event (TX metadata)") -} - -func LogRandomnessRequestedEvent( - l zerolog.Logger, - coordinator contracts.VRFCoordinatorV2_5, - randomWordsRequestedEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested, - isNativeBilling bool, -) { - 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()). - 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") -} - -func LogRandomWordsFulfilledEvent( - l zerolog.Logger, - coordinator contracts.VRFCoordinatorV2_5, - randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, - isNativeBilling bool, -) { - l.Info(). - Bool("Native Billing", isNativeBilling). - Str("Coordinator", coordinator.Address()). - Str("Total Payment", randomWordsFulfilledEvent.Payment.String()). - Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). - 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 *vrfcommon.VRFContracts) { - l.Info(). - Str("Subscription ID", migrationCompletedEvent.SubId.String()). - 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.Info(). - Str("New Coordinator", newCoordinator.Address()). - Str("Subscription ID", subID.String()). - Str("Juels Balance", migratedSubscription.Balance.String()). - Str("Native Token Balance", migratedSubscription.NativeBalance.String()). - Str("Subscription Owner", migratedSubscription.SubOwner.String()). - Interface("Subscription Consumers", migratedSubscription.Consumers). - Msg("Subscription Data After Migration to New Coordinator") -} - -func LogFulfillmentDetailsLinkBilling( - l zerolog.Logger, - wrapperConsumerJuelsBalanceBeforeRequest *big.Int, - wrapperConsumerJuelsBalanceAfterRequest *big.Int, - consumerStatus vrfv2plus_wrapper_load_test_consumer.GetRequestStatus, - randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, -) { - 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). - Str("Paid by Consumer Contract (Link)", (*commonassets.Link)(consumerStatus.Paid).Link()). - Str("Paid by Coordinator Sub (Link)", (*commonassets.Link)(randomWordsFulfilledEvent.Payment).Link()). - Str("RequestTimestamp", consumerStatus.RequestTimestamp.String()). - Str("FulfilmentTimestamp", consumerStatus.FulfilmentTimestamp.String()). - Str("RequestBlockNumber", consumerStatus.RequestBlockNumber.String()). - Str("FulfilmentBlockNumber", consumerStatus.FulfilmentBlockNumber.String()). - Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). - Msg("Random Words Fulfilment Details For Link Billing") -} - -func LogFulfillmentDetailsNativeBilling( - l zerolog.Logger, - wrapperConsumerBalanceBeforeRequestWei *big.Int, - wrapperConsumerBalanceAfterRequestWei *big.Int, - consumerStatus vrfv2plus_wrapper_load_test_consumer.GetRequestStatus, - randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, -) { - l.Info(). - Str("Consumer Balance Before Request", assets.FormatWei(wrapperConsumerBalanceBeforeRequestWei)). - Str("Consumer Balance After Request", assets.FormatWei(wrapperConsumerBalanceAfterRequestWei)). - Bool("Fulfilment Status", consumerStatus.Fulfilled). - Str("Paid by Consumer Contract", assets.FormatWei(consumerStatus.Paid)). - Str("Paid by Coordinator Sub", assets.FormatWei(randomWordsFulfilledEvent.Payment)). - Str("RequestTimestamp", consumerStatus.RequestTimestamp.String()). - Str("FulfilmentTimestamp", consumerStatus.FulfilmentTimestamp.String()). - Str("RequestBlockNumber", consumerStatus.RequestBlockNumber.String()). - Str("FulfilmentBlockNumber", consumerStatus.FulfilmentBlockNumber.String()). - Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). - Msg("Random Words Request Fulfilment Details For Native Billing") -} - -func LogRandRequest( - l zerolog.Logger, - consumer string, - coordinator string, - subID *big.Int, - isNativeBilling bool, - keyHash [32]byte, - config *vrfv2plus_config.General) { - l.Info(). - Str("Consumer", consumer). - Str("Coordinator", coordinator). - Str("SubID", subID.String()). - Bool("IsNativePayment", isNativeBilling). - Uint16("MinimumConfirmations", *config.MinimumConfirmations). - Uint32("CallbackGasLimit", *config.CallbackGasLimit). - Uint32("NumberOfWords", *config.NumberOfWords). - Str("KeyHash", fmt.Sprintf("0x%x", keyHash)). - Uint16("RandomnessRequestCountPerRequest", *config.RandomnessRequestCountPerRequest). - Uint16("RandomnessRequestCountPerRequestDeviation", *config.RandomnessRequestCountPerRequestDeviation). - Msg("Requesting randomness") -} diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index fc4863c935c..6014ec7363a 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_load_test_with_metrics" @@ -63,6 +64,8 @@ type VRFCoordinatorV2 interface { GetOwner(ctx context.Context) (common.Address, error) PendingRequestsExist(ctx context.Context, subID uint64) (bool, error) OwnerCancelSubscription(subID uint64) (*types.Transaction, error) + ParseSubscriptionCanceled(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) + ParseLog(log types.Log) (generated.AbigenLog, error) CancelSubscription(subID uint64, to common.Address) (*types.Transaction, error) FindSubscriptionID(subID uint64) (uint64, error) WaitForRandomWordsFulfilledEvent(requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) @@ -175,6 +178,7 @@ type VRFOwner interface { SetAuthorizedSenders(senders []common.Address) error AcceptVRFOwnership() error WaitForRandomWordsForcedEvent(requestIDs []*big.Int, subIds []uint64, senders []common.Address, timeout time.Duration) (*vrf_owner.VRFOwnerRandomWordsForced, error) + OwnerCancelSubscription(subID uint64) (*types.Transaction, error) } type VRFConsumer interface { diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index 5ff12e81d57..111440a251c 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -15,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/wrappers" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_test_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_mock_ethlink_aggregator" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner" @@ -443,6 +444,14 @@ func (v *EthereumVRFCoordinatorV2) OwnerCancelSubscription(subID uint64) (*types return tx, v.client.ProcessTransaction(tx) } +func (v *EthereumVRFCoordinatorV2) ParseSubscriptionCanceled(log types.Log) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) { + return v.coordinator.ParseSubscriptionCanceled(log) +} + +func (v *EthereumVRFCoordinatorV2) ParseLog(log types.Log) (generated.AbigenLog, error) { + return v.coordinator.ParseLog(log) +} + // CancelSubscription cancels subscription by Sub owner, // return funds to specified address, // checks if pending requests for a sub exist @@ -500,8 +509,8 @@ func (v *EthereumVRFCoordinatorV2) WaitForRandomWordsFulfilledEvent(requestID [] } func (v *EthereumVRFCoordinatorV2) WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []uint64, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error) { - randomWordsFulfilledEventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested) - subscription, err := v.coordinator.WatchRandomWordsRequested(nil, randomWordsFulfilledEventsChannel, keyHash, subID, sender) + eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested) + subscription, err := v.coordinator.WatchRandomWordsRequested(nil, eventsChannel, keyHash, subID, sender) if err != nil { return nil, err } @@ -513,8 +522,8 @@ func (v *EthereumVRFCoordinatorV2) WaitForRandomWordsRequestedEvent(keyHash [][3 return nil, err case <-time.After(timeout): return nil, fmt.Errorf("timeout waiting for RandomWordsRequested event") - case randomWordsFulfilledEvent := <-randomWordsFulfilledEventsChannel: - return randomWordsFulfilledEvent, nil + case event := <-eventsChannel: + return event, nil } } } @@ -1097,6 +1106,21 @@ func (v *EthereumVRFOwner) WaitForRandomWordsForcedEvent(requestIDs []*big.Int, } } +func (v *EthereumVRFOwner) OwnerCancelSubscription(subID uint64) (*types.Transaction, error) { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := v.vrfOwner.OwnerCancelSubscription( + opts, + subID, + ) + if err != nil { + return nil, err + } + return tx, v.client.ProcessTransaction(tx) +} + func (v *EthereumVRFCoordinatorTestV2) Address() string { return v.address.Hex() } diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index 9bf34f70b92..6f95752ae94 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -8,16 +8,60 @@ import ( 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" + vrfv2_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" ) -/* SingleHashGun is a gun that constantly requests randomness for one feed */ +type BHSTestGun struct { + contracts *vrfcommon.VRFContracts + subIDs []uint64 + keyHash [32]byte + testConfig *vrfv2_config.Config + logger zerolog.Logger +} + +func NewBHSTestGun( + contracts *vrfcommon.VRFContracts, + keyHash [32]byte, + subIDs []uint64, + testConfig *vrfv2_config.Config, + logger zerolog.Logger, +) *BHSTestGun { + return &BHSTestGun{ + contracts: contracts, + subIDs: subIDs, + keyHash: keyHash, + testConfig: testConfig, + logger: logger, + } +} + +// Call implements example gun call, assertions on response bodies should be done here +func (m *BHSTestGun) Call(_ *wasp.Generator) *wasp.Response { + _, err := vrfv2.RequestRandomnessAndWaitForRequestedEvent( + m.logger, + m.contracts.VRFV2Consumers[0], + m.contracts.CoordinatorV2, + m.subIDs[0], + &vrfcommon.VRFKeyData{KeyHash: m.keyHash}, + *m.testConfig.General.MinimumConfirmations, + *m.testConfig.General.CallbackGasLimit, + *m.testConfig.General.NumberOfWords, + *m.testConfig.General.RandomnessRequestCountPerRequest, + *m.testConfig.General.RandomnessRequestCountPerRequestDeviation, + m.testConfig.General.RandomWordsFulfilledEventTimeout.Duration, + ) + //todo - might need to store randRequestBlockNumber and blockhash to verify that it was stored in BHS contract at the end of the test + if err != nil { + return &wasp.Response{Error: err.Error(), Failed: true} + } + return &wasp.Response{} +} type SingleHashGun struct { contracts *vrfcommon.VRFContracts keyHash [32]byte subIDs []uint64 - testConfig types.VRFv2TestConfig + testConfig *vrfv2_config.Config logger zerolog.Logger } @@ -25,7 +69,7 @@ func NewSingleHashGun( contracts *vrfcommon.VRFContracts, keyHash [32]byte, subIDs []uint64, - testConfig types.VRFv2TestConfig, + testConfig *vrfv2_config.Config, logger zerolog.Logger, ) *SingleHashGun { return &SingleHashGun{ @@ -41,13 +85,13 @@ func NewSingleHashGun( func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { //todo - should work with multiple consumers and consumers having different keyhashes and wallets - vrfv2Config := m.testConfig.GetVRFv2Config().General + vrfv2Config := m.testConfig.General //randomly increase/decrease randomness request count per TX randomnessRequestCountPerRequest := deviateValue(*vrfv2Config.RandomnessRequestCountPerRequest, *vrfv2Config.RandomnessRequestCountPerRequestDeviation) _, err := vrfv2.RequestRandomnessAndWaitForFulfillment( m.logger, //the same consumer is used for all requests and in all subs - m.contracts.VRFV2Consumer[0], + m.contracts.VRFV2Consumers[0], m.contracts.CoordinatorV2, //randomly pick a subID from pool of subIDs m.subIDs[randInRange(0, len(m.subIDs)-1)], diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index e99f353d149..1a81be58c08 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -1,25 +1,20 @@ package loadvrfv2 import ( - "context" "math/big" "sync" "testing" "time" - "github.com/ethereum/go-ethereum/common" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "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/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" @@ -30,9 +25,9 @@ import ( ) var ( - env *test_env.CLClusterTestEnv + testEnv *test_env.CLClusterTestEnv vrfContracts *vrfcommon.VRFContracts - vrfKeyData *vrfcommon.VRFKeyData + vrfKey *vrfcommon.VRFKeyData subIDs []uint64 eoaWalletAddress string @@ -43,6 +38,7 @@ var ( ) func TestVRFV2Performance(t *testing.T) { + l := logging.GetTestLogger(t) testType, err := tc.GetConfigurationNameFromEnv() @@ -64,7 +60,7 @@ func TestVRFV2Performance(t *testing.T) { updatedLabels := UpdateLabels(labels, t) l.Info(). - Str("Test Type", string(testType)). + Str("Test Type", testType). Str("Test Duration", vrfv2Config.Performance.TestDuration.Duration.Truncate(time.Second).String()). Int64("RPS", *vrfv2Config.Performance.RPS). Str("RateLimitUnitDuration", vrfv2Config.Performance.RateLimitUnitDuration.String()). @@ -73,134 +69,49 @@ func TestVRFV2Performance(t *testing.T) { Bool("UseExistingEnv", *vrfv2Config.General.UseExistingEnv). Msg("Performance Test Configuration") - if *vrfv2Config.General.UseExistingEnv { - env, err = test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&testConfig). - WithCustomCleanup( - func() { - teardown(t, vrfContracts.VRFV2Consumer[0], lc, updatedLabels, testReporter, string(testType), &testConfig) - if env.EVMClient.NetworkSimulated() { - l.Info(). - Str("Network Name", env.EVMClient.GetNetworkName()). - Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") - } else { - if *vrfv2Config.General.CancelSubsAfterTestRun { - //cancel subs and return funds to sub owner - cancelSubsAndReturnFunds(testcontext.Get(t), subIDs, l) - } - } - }). - Build() - - require.NoError(t, err, "error creating test env") - - coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(*vrfv2Config.ExistingEnvConfig.CoordinatorAddress) - require.NoError(t, err) + cleanupFn := func() { + teardown(t, vrfContracts.VRFV2Consumers[0], lc, updatedLabels, testReporter, testType, &testConfig) - var consumers []contracts.VRFv2LoadTestConsumer - 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, vrfcommon.ErrWaitTXsComplete) + if testEnv.EVMClient.NetworkSimulated() { l.Info(). - 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(*vrfv2Config.General.SubscriptionFundingAmountLink), - linkToken, - coordinator, - consumers, - *vrfv2Config.General.NumberOfSubToCreate, - ) - require.NoError(t, err) + Str("Network Name", testEnv.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - consumer, err := env.ContractLoader.LoadVRFv2LoadTestConsumer(*vrfv2Config.ExistingEnvConfig.ConsumerAddress) - require.NoError(t, err) - consumers = append(consumers, consumer) - subIDs = append(subIDs, *vrfv2Config.ExistingEnvConfig.SubID) + if *vrfv2Config.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, eoaWalletAddress, subIDs, l) + } } + if !*vrfv2Config.General.UseExistingEnv { + if err := testEnv.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } - err = FundNodesIfNeeded(testcontext.Get(t), &testConfig, env.EVMClient, l) - require.NoError(t, err) + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: *vrfv2Config.General.NumberOfSendingKeysToCreate, + UseVRFOwner: true, + UseTestCoordinator: true, + } - vrfContracts = &vrfcommon.VRFContracts{ - CoordinatorV2: coordinator, - VRFV2Consumer: consumers, - BHS: nil, - } + testEnv, vrfContracts, vrfKey, _, err = vrfv2.SetupVRFV2Universe(testcontext.Get(t), t, testConfig, cleanupFn, newEnvConfig, l) + require.NoError(t, err) - vrfKeyData = &vrfcommon.VRFKeyData{ - VRFKey: nil, - EncodedProvingKey: [2]*big.Int{}, - KeyHash: common.HexToHash(*vrfv2Config.ExistingEnvConfig.KeyHash), - } + var consumers []contracts.VRFv2LoadTestConsumer + subIDs, consumers, err = vrfv2.SetupSubsAndConsumersForExistingEnv( + testEnv, + vrfContracts.CoordinatorV2, + vrfContracts.LinkToken, + 1, + *vrfv2Config.General.NumberOfSubToCreate, + testConfig, + l, + ) + vrfContracts.VRFV2Consumers = consumers - } else { - network, err := actions.EthereumNetworkConfigFromConfig(l, &testConfig) - require.NoError(t, err, "Error building ethereum network config") - env, err = test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&testConfig). - WithPrivateEthereumNetwork(network). - WithCLNodes(1). - WithFunding(big.NewFloat(*testConfig.Common.ChainlinkNodeFunding)). - WithCustomCleanup( - func() { - teardown(t, vrfContracts.VRFV2Consumer[0], lc, updatedLabels, testReporter, string(testType), &testConfig) - - if env.EVMClient.NetworkSimulated() { - l.Info(). - Str("Network Name", env.EVMClient.GetNetworkName()). - Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") - } else { - if *testConfig.VRFv2.General.CancelSubsAfterTestRun { - //cancel subs and return funds to sub owner - cancelSubsAndReturnFunds(testcontext.Get(t), subIDs, l) - } - } - if err := env.Cleanup(); err != nil { - l.Error().Err(err).Msg("Error cleaning up test environment") - } - }). - Build() - - require.NoError(t, err, "error creating test env") - - env.ParallelTransactions(true) - - mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*vrfv2Config.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") - - useVRFOwner := true - useTestCoordinator := true - - vrfContracts, subIDs, vrfKeyData, _, err = vrfv2.SetupVRFV2Environment( - env, - []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(), - 0, - 1, - *vrfv2Config.General.NumberOfSubToCreate, - l, - ) - require.NoError(t, err, "error setting up VRF v2 env") - } - eoaWalletAddress = env.EVMClient.GetDefaultWallet().Address() + eoaWalletAddress = testEnv.EVMClient.GetDefaultWallet().Address() l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") for _, subID := range subIDs { @@ -208,30 +119,30 @@ func TestVRFV2Performance(t *testing.T) { require.NoError(t, err, "error getting subscription information for subscription %d", subID) vrfv2.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2) } - singleFeedConfig := &wasp.Config{ - T: t, - LoadType: wasp.RPS, - GenName: "gun", - RateLimitUnitDuration: vrfv2Config.Performance.RateLimitUnitDuration.Duration, - Gun: NewSingleHashGun( - vrfContracts, - vrfKeyData.KeyHash, - subIDs, - &testConfig, - l, - ), - Labels: labels, - LokiConfig: lokiConfig, - CallTimeout: 2 * time.Minute, - } - 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) // is our "job" stable at all, no memory leaks, no flaking performance under some RPS? t.Run("vrfv2 performance test", func(t *testing.T) { + require.Len(t, vrfContracts.VRFV2Consumers, 1, "only one consumer should be created for Load Test") + err = vrfContracts.VRFV2Consumers[0].ResetMetrics() + require.NoError(t, err) + MonitorLoadStats(lc, vrfContracts.VRFV2Consumers[0], updatedLabels) + + singleFeedConfig := &wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: "gun", + RateLimitUnitDuration: vrfv2Config.Performance.RateLimitUnitDuration.Duration, + Gun: NewSingleHashGun( + vrfContracts, + vrfKey.KeyHash, + subIDs, + vrfv2Config, + l, + ), + Labels: labels, + LokiConfig: lokiConfig, + CallTimeout: 2 * time.Minute, + } singleFeedConfig.Schedule = wasp.Plain( *vrfv2Config.Performance.RPS, @@ -245,7 +156,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 := vrfcommon.WaitForRequestCountEqualToFulfilmentCount(testcontext.Get(t), consumer, 2*time.Minute, &wg) + requestCount, fulfilmentCount, err := vrfcommon.WaitForRequestCountEqualToFulfilmentCount(testcontext.Get(t), vrfContracts.VRFV2Consumers[0], 2*time.Minute, &wg) require.NoError(t, err) wg.Wait() @@ -254,65 +165,158 @@ func TestVRFV2Performance(t *testing.T) { Interface("Fulfilment Count", fulfilmentCount). Msg("Final Request/Fulfilment Stats") }) - } -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 := vrfContracts.CoordinatorV2.PendingRequestsExist(ctx, subID) - if err != nil { - l.Error().Err(err).Msg("Error checking if pending requests exist") - } - if !pendingRequestsExist { - _, err := vrfContracts.CoordinatorV2.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) - if err != nil { - l.Error().Err(err).Msg("Error canceling subscription") - } - } else { - l.Error().Uint64("Sub ID", subID).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") - } +func TestVRFV2BHSPerformance(t *testing.T) { + l := logging.GetTestLogger(t) + + testType, err := tc.GetConfigurationNameFromEnv() + require.NoError(t, err) + testConfig, err := tc.GetConfig(testType, tc.VRFv2) + require.NoError(t, err) + + testReporter := &testreporters.VRFV2TestReporter{} + vrfv2Config := testConfig.VRFv2 + + cfgl := testConfig.Logging.Loki + lokiConfig := wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken) + lc, err := wasp.NewLokiClient(lokiConfig) + if err != nil { + l.Error().Err(err).Msg(ErrLokiClient) + return } -} -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(ctx, address) - if err != nil { - return err + updatedLabels := UpdateLabels(labels, t) + + l.Info(). + Str("Test Type", testType). + Str("Test Duration", vrfv2Config.Performance.TestDuration.Duration.Truncate(time.Second).String()). + Int64("RPS", *vrfv2Config.Performance.RPS). + Str("RateLimitUnitDuration", vrfv2Config.Performance.RateLimitUnitDuration.String()). + Uint16("RandomnessRequestCountPerRequest", *vrfv2Config.General.RandomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", *vrfv2Config.General.RandomnessRequestCountPerRequestDeviation). + Bool("UseExistingEnv", *vrfv2Config.General.UseExistingEnv). + Msg("Performance Test Configuration") + + cleanupFn := func() { + teardown(t, vrfContracts.VRFV2Consumers[0], lc, updatedLabels, testReporter, testType, &testConfig) + + if testEnv.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", testEnv.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2Config.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, eoaWalletAddress, subIDs, l) } - fundingAtLeast := conversions.EtherToWei(big.NewFloat(*cfg.ExistingEnvConfig.NodeSendingKeyFundingMin)) - fundingToSendWei := new(big.Int).Sub(fundingAtLeast, sendingKeyBalance) - fundingToSendEth := conversions.WeiToEther(fundingToSendWei) - if fundingToSendWei.Cmp(big.NewInt(0)) == 1 { - l.Info(). - Str("Sending Key", sendingKey). - Str("Sending Key Current Balance", sendingKeyBalance.String()). - Str("Should have at least", fundingAtLeast.String()). - Str("Funding Amount in ETH", fundingToSendEth.String()). - Msg("Funding Node's Sending Key") - err := actions.FundAddress(client, sendingKey, fundingToSendEth) - if err != nil { - return err - } - } else { - l.Info(). - Str("Sending Key", sendingKey). - Str("Sending Key Current Balance", sendingKeyBalance.String()). - Str("Should have at least", fundingAtLeast.String()). - Msg("Skipping Node's Sending Key funding as it has enough funds") + } + if !*vrfv2Config.General.UseExistingEnv { + if err := testEnv.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") } } } - return nil -} + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: *vrfv2Config.General.NumberOfSendingKeysToCreate, + UseVRFOwner: true, + UseTestCoordinator: true, + } + + testEnv, vrfContracts, vrfKey, _, err = vrfv2.SetupVRFV2Universe(testcontext.Get(t), t, testConfig, cleanupFn, newEnvConfig, l) + require.NoError(t, err) + + var consumers []contracts.VRFv2LoadTestConsumer + subIDs, consumers, err = vrfv2.SetupSubsAndConsumersForExistingEnv( + testEnv, + vrfContracts.CoordinatorV2, + vrfContracts.LinkToken, + 1, + *vrfv2Config.General.NumberOfSubToCreate, + testConfig, + l, + ) + vrfContracts.VRFV2Consumers = consumers + + eoaWalletAddress = testEnv.EVMClient.GetDefaultWallet().Address() + + l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") + for _, subID := range subIDs { + 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, vrfContracts.CoordinatorV2) + } + + t.Run("vrfv2 and bhs performance test", func(t *testing.T) { + configCopy := testConfig.MustCopy().(tc.TestConfig) + //Underfund Subscription + configCopy.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) + consumers, subIDs, err = vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, + *configCopy.VRFv2.General.NumberOfSubToCreate, + l, + ) + require.NoError(t, err, "error setting up new consumers and subscriptions") + vrfContracts.VRFV2Consumers = consumers + require.Len(t, vrfContracts.VRFV2Consumers, 1, "only one consumer should be created for Load Test") + err = vrfContracts.VRFV2Consumers[0].ResetMetrics() + require.NoError(t, err, "error resetting consumer metrics") + MonitorLoadStats(lc, vrfContracts.VRFV2Consumers[0], updatedLabels) + + singleFeedConfig := &wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: "gun", + RateLimitUnitDuration: configCopy.VRFv2.Performance.BHSTestRateLimitUnitDuration.Duration, + Gun: NewBHSTestGun( + vrfContracts, + vrfKey.KeyHash, + subIDs, + configCopy.VRFv2, + l, + ), + Labels: labels, + LokiConfig: lokiConfig, + CallTimeout: 2 * time.Minute, + } + + singleFeedConfig.Schedule = wasp.Plain( + *configCopy.VRFv2.Performance.BHSTestRPS, + configCopy.VRFv2.Performance.BHSTestDuration.Duration, + ) + _, err = wasp.NewProfile(). + Add(wasp.NewGenerator(singleFeedConfig)). + Run(true) + require.NoError(t, err) + + var wgBlockNumberTobe sync.WaitGroup + wgBlockNumberTobe.Add(1) + //Wait at least 256 blocks + latestBlockNumber, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + require.NoError(t, err, "error getting latest block number") + _, err = actions.WaitForBlockNumberToBe(latestBlockNumber+uint64(256), testEnv.EVMClient, &wgBlockNumberTobe, configCopy.VRFv2.General.WaitFor256BlocksTimeout.Duration, t) + wgBlockNumberTobe.Wait() + require.NoError(t, err, "error waiting for block number to be") + err = vrfv2.FundSubscriptions(testEnv, big.NewFloat(*configCopy.VRFv2.General.SubscriptionRefundingAmountLink), vrfContracts.LinkToken, vrfContracts.CoordinatorV2, subIDs) + require.NoError(t, err, "error funding subscriptions") + var wgAllRequestsFulfilled sync.WaitGroup + wgAllRequestsFulfilled.Add(1) + requestCount, fulfilmentCount, err := vrfcommon.WaitForRequestCountEqualToFulfilmentCount(testcontext.Get(t), vrfContracts.VRFV2Consumers[0], 2*time.Minute, &wgAllRequestsFulfilled) + require.NoError(t, err) + wgAllRequestsFulfilled.Wait() + + l.Info(). + Interface("Request Count", requestCount). + Interface("Fulfilment Count", fulfilmentCount). + Msg("Final Request/Fulfilment Stats") + }) +} func teardown( t *testing.T, consumer contracts.VRFv2LoadTestConsumer, diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index 22a56d557de..8be30afd412 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -11,16 +11,63 @@ import ( 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" ) -/* SingleHashGun is a gun that constantly requests randomness for one feed */ +type BHSTestGun struct { + contracts *vrfcommon.VRFContracts + keyHash [32]byte + subIDs []*big.Int + testConfig *vrfv2plus_config.Config + logger zerolog.Logger +} + +func NewBHSTestGun( + contracts *vrfcommon.VRFContracts, + keyHash [32]byte, + subIDs []*big.Int, + testConfig *vrfv2plus_config.Config, + logger zerolog.Logger, +) *BHSTestGun { + return &BHSTestGun{ + contracts: contracts, + subIDs: subIDs, + keyHash: keyHash, + testConfig: testConfig, + logger: logger, + } +} +// Call implements example gun call, assertions on response bodies should be done here +func (m *BHSTestGun) Call(_ *wasp.Generator) *wasp.Response { + vrfv2PlusConfig := m.testConfig.General + billingType, err := selectBillingType(*vrfv2PlusConfig.SubscriptionBillingType) + if err != nil { + return &wasp.Response{Error: err.Error(), Failed: true} + } + _, err = vrfv2plus.RequestRandomnessAndWaitForRequestedEvent( + //the same consumer is used for all requests and in all subs + 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)], + billingType, + vrfv2PlusConfig, + m.logger, + ) + //todo - might need to store randRequestBlockNumber and blockhash to verify that it was stored in BHS contract at the end of the test + if err != nil { + return &wasp.Response{Error: err.Error(), Failed: true} + } + return &wasp.Response{} +} + +/* SingleHashGun is a gun that constantly requests randomness for one feed */ type SingleHashGun struct { contracts *vrfcommon.VRFContracts keyHash [32]byte subIDs []*big.Int - testConfig types.VRFv2PlusTestConfig + testConfig *vrfv2plus_config.Config logger zerolog.Logger } @@ -28,7 +75,7 @@ func NewSingleHashGun( contracts *vrfcommon.VRFContracts, keyHash [32]byte, subIDs []*big.Int, - testConfig types.VRFv2PlusTestConfig, + testConfig *vrfv2plus_config.Config, logger zerolog.Logger, ) *SingleHashGun { return &SingleHashGun{ @@ -43,15 +90,15 @@ func NewSingleHashGun( // Call implements example gun call, assertions on response bodies should be done here func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { //todo - should work with multiple consumers and consumers having different keyhashes and wallets - - billingType, err := selectBillingType(*m.testConfig.GetVRFv2PlusConfig().General.SubscriptionBillingType) + vrfv2PlusConfig := m.testConfig.General + billingType, err := selectBillingType(*vrfv2PlusConfig.SubscriptionBillingType) if err != nil { return &wasp.Response{Error: err.Error(), Failed: true} } //randomly increase/decrease randomness request count per TX - reqCount := deviateValue(*m.testConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequest, *m.testConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequestDeviation) - m.testConfig.GetVRFv2PlusConfig().General.RandomnessRequestCountPerRequest = &reqCount + reqCount := deviateValue(*m.testConfig.General.RandomnessRequestCountPerRequest, *m.testConfig.General.RandomnessRequestCountPerRequestDeviation) + m.testConfig.General.RandomnessRequestCountPerRequest = &reqCount _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( //the same consumer is used for all requests and in all subs m.contracts.VRFV2PlusConsumer[0], @@ -59,9 +106,8 @@ func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.Response { &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 billingType, - m.testConfig.GetVRFv2PlusConfig().General, + vrfv2PlusConfig, m.logger, ) if err != nil { diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 13c694c0290..aced84d5832 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -1,37 +1,33 @@ package loadvrfv2plus import ( - "context" "math/big" "sync" "testing" "time" - "github.com/ethereum/go-ethereum/common" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" "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/conversions" + "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/actions/vrf/vrfv2plus" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" "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" ) var ( - env *test_env.CLClusterTestEnv + testEnv *test_env.CLClusterTestEnv vrfContracts *vrfcommon.VRFContracts - vrfv2PlusData *vrfcommon.VRFKeyData + vrfKey *vrfcommon.VRFKeyData subIDs []*big.Int eoaWalletAddress string @@ -53,7 +49,8 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2PlusConfig := testConfig.VRFv2Plus testReporter := &testreporters.VRFV2PlusTestReporter{} - lc, err := wasp.NewLokiClient(wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken)) + lokiConfig := wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken) + lc, err := wasp.NewLokiClient(lokiConfig) if err != nil { l.Error().Err(err).Msg(ErrLokiClient) return @@ -62,7 +59,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { updatedLabels := UpdateLabels(labels, t) l.Info(). - Str("Test Type", string(testType)). + Str("Test Type", testType). Str("Test Duration", vrfv2PlusConfig.Performance.TestDuration.Duration.Truncate(time.Second).String()). Int64("RPS", *vrfv2PlusConfig.Performance.RPS). Str("RateLimitUnitDuration", vrfv2PlusConfig.Performance.RateLimitUnitDuration.String()). @@ -71,132 +68,47 @@ func TestVRFV2PlusPerformance(t *testing.T) { Bool("UseExistingEnv", *vrfv2PlusConfig.General.UseExistingEnv). Msg("Performance Test Configuration") - if *vrfv2PlusConfig.General.UseExistingEnv { - - env, err = test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&testConfig). - WithCustomCleanup( - func() { - teardown(t, vrfContracts.VRFV2PlusConsumer[0], lc, updatedLabels, testReporter, string(testType), &testConfig) - if env.EVMClient.NetworkSimulated() { - l.Info(). - Str("Network Name", env.EVMClient.GetNetworkName()). - Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") - } else { - if *testConfig.VRFv2Plus.General.CancelSubsAfterTestRun { - //cancel subs and return funds to sub owner - cancelSubsAndReturnFunds(testcontext.Get(t), subIDs, l) - } - } - }). - Build() - - require.NoError(t, err, "error creating test env") - - coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2_5(*vrfv2PlusConfig.ExistingEnvConfig.CoordinatorAddress) - require.NoError(t, err) + cleanupFn := func() { + teardown(t, vrfContracts.VRFV2PlusConsumer[0], lc, updatedLabels, testReporter, testType, &testConfig) - var consumers []contracts.VRFv2PlusLoadTestConsumer - if *testConfig.VRFv2Plus.ExistingEnvConfig.CreateFundSubsAndAddConsumers { - 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, vrfcommon.ErrWaitTXsComplete) + if testEnv.EVMClient.NetworkSimulated() { l.Info(). - 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, - big.NewFloat(*testConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), - big.NewFloat(*testConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), - linkToken, - coordinator, - consumers, - *vrfv2PlusConfig.General.NumberOfSubToCreate, - ) - require.NoError(t, err) + Str("Network Name", testEnv.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(*vrfv2PlusConfig.ExistingEnvConfig.ConsumerAddress) - require.NoError(t, err) - consumers = append(consumers, consumer) - var ok bool - subID, ok := new(big.Int).SetString(*vrfv2PlusConfig.ExistingEnvConfig.SubID, 10) - require.True(t, ok) - subIDs = append(subIDs, subID) + if *testConfig.VRFv2Plus.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, eoaWalletAddress, subIDs, l) + } } + if !*testConfig.VRFv2Plus.General.UseExistingEnv { + if err := testEnv.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } - err = FundNodesIfNeeded(testcontext.Get(t), &testConfig, env.EVMClient, l) - require.NoError(t, err) + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: *vrfv2PlusConfig.General.NumberOfSendingKeysToCreate, + } - vrfContracts = &vrfcommon.VRFContracts{ - CoordinatorV2Plus: coordinator, - VRFV2PlusConsumer: consumers, - BHS: nil, - } + testEnv, vrfContracts, vrfKey, _, err = vrfv2plus.SetupVRFV2PlusUniverse(testcontext.Get(t), t, testConfig, cleanupFn, newEnvConfig, l) + require.NoError(t, err) - vrfv2PlusData = &vrfcommon.VRFKeyData{ - VRFKey: nil, - EncodedProvingKey: [2]*big.Int{}, - KeyHash: common.HexToHash(*vrfv2PlusConfig.ExistingEnvConfig.KeyHash), - } + var consumers []contracts.VRFv2PlusLoadTestConsumer + subIDs, consumers, err = vrfv2plus.SetupSubsAndConsumersForExistingEnv( + testEnv, + vrfContracts.CoordinatorV2Plus, + vrfContracts.LinkToken, + 1, + *vrfv2PlusConfig.General.NumberOfSubToCreate, + testConfig, + l, + ) + vrfContracts.VRFV2PlusConsumer = consumers - } else { - network, err := actions.EthereumNetworkConfigFromConfig(l, &testConfig) - require.NoError(t, err, "Error building ethereum network config") - env, err = test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&testConfig). - WithPrivateEthereumNetwork(network). - WithCLNodes(1). - WithFunding(big.NewFloat(*testConfig.Common.ChainlinkNodeFunding)). - WithCustomCleanup( - func() { - teardown(t, vrfContracts.VRFV2PlusConsumer[0], lc, updatedLabels, testReporter, string(testType), &testConfig) - - if env.EVMClient.NetworkSimulated() { - l.Info(). - Str("Network Name", env.EVMClient.GetNetworkName()). - Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") - } else { - if *testConfig.VRFv2Plus.General.CancelSubsAfterTestRun { - //cancel subs and return funds to sub owner - cancelSubsAndReturnFunds(testcontext.Get(t), subIDs, l) - } - } - if err := env.Cleanup(); err != nil { - l.Error().Err(err).Msg("Error cleaning up test environment") - } - }). - Build() - - require.NoError(t, err, "error creating test env") - - env.ParallelTransactions(true) - - mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(*vrfv2PlusConfig.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") - - vrfContracts, subIDs, vrfv2PlusData, _, err = vrfv2plus.SetupVRFV2_5Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &testConfig, - linkToken, - mockETHLinkFeed, - 0, - 1, - *vrfv2PlusConfig.General.NumberOfSubToCreate, - l, - ) - require.NoError(t, err, "error setting up VRF v2_5 env") - } - eoaWalletAddress = env.EVMClient.GetDefaultWallet().Address() + eoaWalletAddress = testEnv.EVMClient.GetDefaultWallet().Address() l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") for _, subID := range subIDs { @@ -205,30 +117,30 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2plus.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2Plus) } - singleFeedConfig := &wasp.Config{ - T: t, - LoadType: wasp.RPS, - GenName: "gun", - RateLimitUnitDuration: vrfv2PlusConfig.Performance.RateLimitUnitDuration.Duration, - Gun: NewSingleHashGun( - vrfContracts, - vrfv2PlusData.KeyHash, - subIDs, - &testConfig, - l, - ), - Labels: labels, - LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), - CallTimeout: 2 * time.Minute, - } - 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) - // is our "job" stable at all, no memory leaks, no flaking performance under some RPS? t.Run("vrfv2plus performance test", func(t *testing.T) { + singleFeedConfig := &wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: "gun", + RateLimitUnitDuration: vrfv2PlusConfig.Performance.RateLimitUnitDuration.Duration, + Gun: NewSingleHashGun( + vrfContracts, + vrfKey.KeyHash, + subIDs, + vrfv2PlusConfig, + l, + ), + Labels: labels, + LokiConfig: wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken), + CallTimeout: 2 * time.Minute, + } + 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) + singleFeedConfig.Schedule = wasp.Plain( *vrfv2PlusConfig.Performance.RPS, vrfv2PlusConfig.Performance.TestDuration.Duration, @@ -250,63 +162,152 @@ func TestVRFV2PlusPerformance(t *testing.T) { Interface("Fulfilment Count", fulfilmentCount). Msg("Final Request/Fulfilment Stats") }) - } -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 := vrfContracts.CoordinatorV2Plus.PendingRequestsExist(ctx, subID) - if err != nil { - l.Error().Err(err).Msg("Error checking if pending requests exist") - } - if !pendingRequestsExist { - _, err := vrfContracts.CoordinatorV2Plus.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) - if err != nil { - l.Error().Err(err).Msg("Error canceling subscription") - } - } else { - l.Error().Str("Sub ID", subID.String()).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") - } +func TestVRFV2PlusBHSPerformance(t *testing.T) { + l := logging.GetTestLogger(t) + + testType, err := tc.GetConfigurationNameFromEnv() + require.NoError(t, err) + testConfig, err := tc.GetConfig(testType, tc.VRFv2Plus) + require.NoError(t, err) + cfgl := testConfig.Logging.Loki + + vrfv2PlusConfig := testConfig.VRFv2Plus + testReporter := &testreporters.VRFV2PlusTestReporter{} + + lokiConfig := wasp.NewLokiConfig(cfgl.Endpoint, cfgl.TenantId, cfgl.BasicAuth, cfgl.BearerToken) + lc, err := wasp.NewLokiClient(lokiConfig) + if err != nil { + l.Error().Err(err).Msg(ErrLokiClient) + return } -} -func FundNodesIfNeeded(ctx context.Context, vrfv2plusTestConfig tc.VRFv2PlusTestConfig, client blockchain.EVMClient, l zerolog.Logger) error { - cfg := vrfv2plusTestConfig.GetVRFv2PlusConfig() - if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin != nil && *cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { - for _, sendingKey := range cfg.ExistingEnvConfig.NodeSendingKeys { - address := common.HexToAddress(sendingKey) - sendingKeyBalance, err := client.BalanceAt(ctx, address) - if err != nil { - return err + updatedLabels := UpdateLabels(labels, t) + + l.Info(). + Str("Test Type", testType). + Str("Test Duration", vrfv2PlusConfig.Performance.TestDuration.Duration.Truncate(time.Second).String()). + Int64("RPS", *vrfv2PlusConfig.Performance.RPS). + Str("RateLimitUnitDuration", vrfv2PlusConfig.Performance.RateLimitUnitDuration.String()). + Uint16("RandomnessRequestCountPerRequest", *vrfv2PlusConfig.General.RandomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", *vrfv2PlusConfig.General.RandomnessRequestCountPerRequestDeviation). + Bool("UseExistingEnv", *vrfv2PlusConfig.General.UseExistingEnv). + Msg("Performance Test Configuration") + + cleanupFn := func() { + teardown(t, vrfContracts.VRFV2PlusConsumer[0], lc, updatedLabels, testReporter, testType, &testConfig) + + if testEnv.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", testEnv.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *testConfig.VRFv2Plus.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, eoaWalletAddress, subIDs, l) } - fundingAtLeast := conversions.EtherToWei(big.NewFloat(*cfg.ExistingEnvConfig.NodeSendingKeyFundingMin)) - fundingToSendWei := new(big.Int).Sub(fundingAtLeast, sendingKeyBalance) - fundingToSendEth := conversions.WeiToEther(fundingToSendWei) - if fundingToSendWei.Cmp(big.NewInt(0)) == 1 { - l.Info(). - Str("Sending Key", sendingKey). - Str("Sending Key Current Balance", sendingKeyBalance.String()). - Str("Should have at least", fundingAtLeast.String()). - Str("Funding Amount in ETH", fundingToSendEth.String()). - Msg("Funding Node's Sending Key") - err := actions.FundAddress(client, sendingKey, fundingToSendEth) - if err != nil { - return err - } - } else { - l.Info(). - Str("Sending Key", sendingKey). - Str("Sending Key Current Balance", sendingKeyBalance.String()). - Str("Should have at least", fundingAtLeast.String()). - Msg("Skipping Node's Sending Key funding as it has enough funds") + } + if !*testConfig.VRFv2Plus.General.UseExistingEnv { + if err := testEnv.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") } } } - return nil + + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: *vrfv2PlusConfig.General.NumberOfSendingKeysToCreate, + } + + testEnv, vrfContracts, vrfKey, _, err = vrfv2plus.SetupVRFV2PlusUniverse(testcontext.Get(t), t, testConfig, cleanupFn, newEnvConfig, l) + require.NoError(t, err) + eoaWalletAddress = testEnv.EVMClient.GetDefaultWallet().Address() + + l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") + for _, subID := range subIDs { + 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, vrfContracts.CoordinatorV2Plus) + } + + t.Run("vrfv2plus and bhs performance test", func(t *testing.T) { + configCopy := testConfig.MustCopy().(tc.TestConfig) + //Underfund Subscription + configCopy.VRFv2Plus.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) + consumers, underfundedSubIDs, err := vrfv2plus.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, + 1, + *configCopy.VRFv2Plus.General.NumberOfSubToCreate, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs for Load Test") + vrfContracts.VRFV2PlusConsumer = consumers + 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) + + singleFeedConfig := &wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: "gun", + RateLimitUnitDuration: configCopy.VRFv2Plus.Performance.BHSTestRateLimitUnitDuration.Duration, + Gun: NewBHSTestGun( + vrfContracts, + vrfKey.KeyHash, + underfundedSubIDs, + configCopy.VRFv2Plus, + l, + ), + Labels: labels, + LokiConfig: lokiConfig, + CallTimeout: 2 * time.Minute, + } + + singleFeedConfig.Schedule = wasp.Plain( + *configCopy.VRFv2Plus.Performance.BHSTestRPS, + configCopy.VRFv2Plus.Performance.BHSTestDuration.Duration, + ) + _, err = wasp.NewProfile(). + Add(wasp.NewGenerator(singleFeedConfig)). + Run(true) + require.NoError(t, err) + + var wgBlockNumberTobe sync.WaitGroup + wgBlockNumberTobe.Add(1) + //Wait at least 256 blocks + latestBlockNumber, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) + require.NoError(t, err, "error getting latest block number") + _, err = actions.WaitForBlockNumberToBe(latestBlockNumber+uint64(256), testEnv.EVMClient, &wgBlockNumberTobe, configCopy.VRFv2Plus.General.WaitFor256BlocksTimeout.Duration, t) + wgBlockNumberTobe.Wait() + require.NoError(t, err, "error waiting for block number to be") + + err = vrfv2plus.FundSubscriptions( + testEnv, + big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionRefundingAmountNative), + big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionRefundingAmountLink), + vrfContracts.LinkToken, + vrfContracts.CoordinatorV2Plus, + subIDs, + ) + require.NoError(t, err, "error funding subscriptions") + + var wgAllRequestsFulfilled sync.WaitGroup + wgAllRequestsFulfilled.Add(1) + requestCount, fulfilmentCount, err := vrfcommon.WaitForRequestCountEqualToFulfilmentCount(testcontext.Get(t), consumer, 2*time.Minute, &wgAllRequestsFulfilled) + require.NoError(t, err) + wgAllRequestsFulfilled.Wait() + + l.Info(). + Interface("Request Count", requestCount). + Interface("Fulfilment Count", fulfilmentCount). + Msg("Final Request/Fulfilment Stats") + }) } func teardown( diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 57f88346614..a02ac4506ba 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -30,74 +30,74 @@ import ( func TestVRFv2Basic(t *testing.T) { t.Parallel() + var ( + testEnv *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []uint64 + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + ) l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.VRFv2) require.NoError(t, err, "Error getting config") + vrfv2Config := config.VRFv2 + cleanupFn := func() { + if testEnv.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", testEnv.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2Config.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2Config.General.UseExistingEnv { + if err := testEnv.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: 0, + UseVRFOwner: false, + UseTestCoordinator: false, + } - useVRFOwner := false - useTestCoordinator := false - 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.VRFv2.General.LinkNativeFeedResponse)) - require.NoError(t, err) - linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + testEnv, vrfContracts, vrfKey, nodeTypeToNodeMap, err = vrfv2.SetupVRFV2Universe(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) require.NoError(t, err) - - // register proving key against oracle address (sending key) in order to test oracleWithdraw - defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() - - numberOfTxKeysToCreate := 1 - vrfv2Contracts, subIDs, vrfv2KeyData, nodesMap, err := vrfv2.SetupVRFV2Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &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) + defaultWalletAddress = testEnv.EVMClient.GetDefaultWallet().Address() t.Run("Request Randomness", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - subBalanceBeforeRequest := subscription.Balance + consumers, subIDsForRequestRandomness, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + subIDForRequestRandomness := subIDsForRequestRandomness[0] + subscription, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForRequestRandomness) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscription, subIDForRequestRandomness, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForRequestRandomness...) - jobRunsBeforeTest, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) - require.NoError(t, err, "error reading job runs") + subBalanceBeforeRequest := subscription.Balance // test and assert randomWordsFulfilledEvent, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, - vrfv2Contracts.VRFV2Consumer[0], - vrfv2Contracts.CoordinatorV2, - subID, - vrfv2KeyData, + consumers[0], + vrfContracts.CoordinatorV2, + subIDForRequestRandomness, + vrfKey, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -108,55 +108,98 @@ 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.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) + subscription, err = vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForRequestRandomness) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := subscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) - 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.VRFV2Consumer[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + status, err := consumers[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") - require.Equal(t, *config.VRFv2.General.NumberOfWords, uint32(len(status.RandomWords))) + require.Equal(t, *configCopy.VRFv2.General.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } }) + t.Run("CL Node VRF Job Runs", func(t *testing.T) { + configCopy := config.MustCopy().(tc.TestConfig) + consumers, subIDsForJobRuns, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + + subIDForJobRuns := subIDsForJobRuns[0] + subscription, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForJobRuns) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscription, subIDForJobRuns, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForJobRuns...) + + jobRunsBeforeTest, err := nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodeTypeToNodeMap[vrfcommon.VRF].Job.Data.ID) + require.NoError(t, err, "error reading job runs") + + // test and assert + _, err = vrfv2.RequestRandomnessAndWaitForFulfillment( + l, + consumers[0], + vrfContracts.CoordinatorV2, + subIDForJobRuns, + vrfKey, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2.General.RandomWordsFulfilledEventTimeout.Duration, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + + jobRuns, err := nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodeTypeToNodeMap[vrfcommon.VRF].Job.Data.ID) + require.NoError(t, err, "error reading job runs") + require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) + }) + t.Run("Direct Funding (VRFV2Wrapper)", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) wrapperContracts, wrapperSubID, err := vrfv2.SetupVRFV2WrapperEnvironment( - env, + testcontext.Get(t), + testEnv, &configCopy, - linkToken, - mockETHLinkFeed, - vrfv2Contracts.CoordinatorV2, - vrfv2KeyData.KeyHash, + vrfContracts.LinkToken, + vrfContracts.MockETHLINKFeed, + vrfContracts.CoordinatorV2, + vrfKey.KeyHash, 1, ) require.NoError(t, err) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, *wrapperSubID) + wrapperConsumer := wrapperContracts.LoadTestConsumers[0] - wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperConsumer.Address()) + wrapperConsumerJuelsBalanceBeforeRequest, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), wrapperConsumer.Address()) require.NoError(t, err, "Error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), *wrapperSubID) + wrapperSubscription, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), *wrapperSubID) require.NoError(t, err, "Error getting subscription information") subBalanceBeforeRequest := wrapperSubscription.Balance // Request Randomness and wait for fulfillment event randomWordsFulfilledEvent, err := vrfv2.DirectFundingRequestRandomnessAndWaitForFulfillment( + testcontext.Get(t), l, wrapperConsumer, - vrfv2Contracts.CoordinatorV2, + vrfContracts.CoordinatorV2, *wrapperSubID, - vrfv2KeyData, + vrfKey, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -168,7 +211,7 @@ func TestVRFv2Basic(t *testing.T) { // Check wrapper subscription balance expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), *wrapperSubID) + wrapperSubscription, err = vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), *wrapperSubID) require.NoError(t, err, "Error getting subscription information") subBalanceAfterRequest := wrapperSubscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) @@ -180,7 +223,7 @@ func TestVRFv2Basic(t *testing.T) { // Check wrapper consumer LINK balance expectedWrapperConsumerJuelsBalance := new(big.Int).Sub(wrapperConsumerJuelsBalanceBeforeRequest, consumerStatus.Paid) - wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperConsumer.Address()) + wrapperConsumerJuelsBalanceAfterRequest, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), wrapperConsumer.Address()) require.NoError(t, err, "Error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) @@ -207,24 +250,29 @@ func TestVRFv2Basic(t *testing.T) { t.Run("Oracle Withdraw", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - subIDsForOracleWithDraw, err := vrfv2.CreateFundSubsAndAddConsumers( - env, - big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), - linkToken, - vrfv2Contracts.CoordinatorV2, - vrfv2Contracts.VRFV2Consumer, + consumers, subIDsForOracleWithDraw, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, 1, + l, ) - require.NoError(t, err) + require.NoError(t, err, "error setting up new consumers and subs") subIDForOracleWithdraw := subIDsForOracleWithDraw[0] + subscription, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForOracleWithdraw) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscription, subIDForOracleWithdraw, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForOracleWithDraw...) fulfilledEventLink, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, - vrfv2Contracts.VRFV2Consumer[0], - vrfv2Contracts.CoordinatorV2, + consumers[0], + vrfContracts.CoordinatorV2, subIDForOracleWithdraw, - vrfv2KeyData, + vrfKey, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -236,7 +284,7 @@ func TestVRFv2Basic(t *testing.T) { amountToWithdrawLink := fulfilledEventLink.Payment - defaultWalletBalanceLinkBeforeOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + defaultWalletBalanceLinkBeforeOracleWithdraw, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) l.Info(). @@ -244,13 +292,13 @@ func TestVRFv2Basic(t *testing.T) { Str("Amount", amountToWithdrawLink.String()). Msg("Invoking Oracle Withdraw for LINK") - err = vrfv2Contracts.CoordinatorV2.OracleWithdraw(common.HexToAddress(defaultWalletAddress), amountToWithdrawLink) + err = vrfContracts.CoordinatorV2.OracleWithdraw(common.HexToAddress(defaultWalletAddress), amountToWithdrawLink) require.NoError(t, err, "Error withdrawing LINK from coordinator to default wallet") - err = env.EVMClient.WaitForEvents() + err = testEnv.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + defaultWalletBalanceLinkAfterOracleWithdraw, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) require.Equal( @@ -263,24 +311,29 @@ func TestVRFv2Basic(t *testing.T) { t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - subIDsForCancelling, err := vrfv2.CreateFundSubsAndAddConsumers( - env, - big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), - linkToken, - vrfv2Contracts.CoordinatorV2, - vrfv2Contracts.VRFV2Consumer, + _, subIDsForCancelling, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, 1, + 1, + l, ) - require.NoError(t, err) + require.NoError(t, err, "error setting up new consumers and subs") subIDForCancelling := subIDsForCancelling[0] + subscription, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscription, subIDForCancelling, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForCancelling...) testWalletAddress, err := actions.GenerateWallet() require.NoError(t, err) - testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) + testWalletBalanceLinkBeforeSubCancelling, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) - subscriptionForCancelling, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -291,12 +344,12 @@ func TestVRFv2Basic(t *testing.T) { Str("Returning funds to", testWalletAddress.String()). Msg("Canceling subscription and returning funds to subscription owner") - tx, err := vrfv2Contracts.CoordinatorV2.CancelSubscription(subIDForCancelling, testWalletAddress) + tx, err := vrfContracts.CoordinatorV2.CancelSubscription(subIDForCancelling, testWalletAddress) require.NoError(t, err, "Error canceling subscription") - subscriptionCanceledEvent, err := vrfv2Contracts.CoordinatorV2.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30) + subscriptionCanceledEvent, err := vrfContracts.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 := testEnv.EVMClient.GetTxReceipt(tx.Hash()) require.NoError(t, err, "error getting tx cancellation Tx Receipt") txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) @@ -320,11 +373,11 @@ func TestVRFv2Basic(t *testing.T) { require.Equal(t, subBalanceLink, subscriptionCanceledEvent.Amount, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") - testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) + testWalletBalanceLinkAfterSubCancelling, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) + _, err = vrfContracts.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) @@ -341,28 +394,27 @@ func TestVRFv2Basic(t *testing.T) { t.Run("Owner Canceling Sub And Returning Funds While Having Pending Requests", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) // Underfund subscription to force fulfillments to fail - configCopy.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0.000000000000000001)) // 1 Juel - - subIDsForCancelling, err := vrfv2.CreateFundSubsAndAddConsumers( - env, - big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), - linkToken, - vrfv2Contracts.CoordinatorV2, - vrfv2Contracts.VRFV2Consumer, + configCopy.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) + + consumers, subIDsForOwnerCancelling, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, 1, + l, ) - require.NoError(t, err) - - subIDForCancelling := subIDsForCancelling[0] - - subscriptionForCancelling, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) - require.NoError(t, err, "Error getting subscription information") - - vrfv2.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2Contracts.CoordinatorV2) + require.NoError(t, err, "error setting up new consumers and subs") + subIDForOwnerCancelling := subIDsForOwnerCancelling[0] + subscriptionForCancelling, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForOwnerCancelling) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscriptionForCancelling, subIDForOwnerCancelling, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForOwnerCancelling...) // No GetActiveSubscriptionIds function available - skipping check - pendingRequestsExist, err := vrfv2Contracts.CoordinatorV2.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + pendingRequestsExist, err := vrfContracts.CoordinatorV2.PendingRequestsExist(testcontext.Get(t), subIDForOwnerCancelling) require.NoError(t, err) require.False(t, pendingRequestsExist, "Pending requests should not exist") @@ -370,10 +422,10 @@ func TestVRFv2Basic(t *testing.T) { randomWordsFulfilledEventTimeout := 5 * time.Second _, err = vrfv2.RequestRandomnessAndWaitForFulfillment( l, - vrfv2Contracts.VRFV2Consumer[0], - vrfv2Contracts.CoordinatorV2, - subIDForCancelling, - vrfv2KeyData, + consumers[0], + vrfContracts.CoordinatorV2, + subIDForOwnerCancelling, + vrfKey, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -383,31 +435,31 @@ func TestVRFv2Basic(t *testing.T) { ) require.Error(t, err, "Error should occur while waiting for fulfilment due to low sub balance") - pendingRequestsExist, err = vrfv2Contracts.CoordinatorV2.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + pendingRequestsExist, err = vrfContracts.CoordinatorV2.PendingRequestsExist(testcontext.Get(t), subIDForOwnerCancelling) 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) + walletBalanceLinkBeforeSubCancelling, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) - subscriptionForCancelling, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err = vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForOwnerCancelling) require.NoError(t, err, "Error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance l.Info(). Str("Subscription Amount Link", subBalanceLink.String()). - Uint64("Returning funds from SubID", subIDForCancelling). + Uint64("Returning funds from SubID", subIDForOwnerCancelling). Str("Returning funds to", defaultWalletAddress). Msg("Canceling subscription and returning funds to subscription owner") // Call OwnerCancelSubscription - tx, err := vrfv2Contracts.CoordinatorV2.OwnerCancelSubscription(subIDForCancelling) + tx, err := vrfContracts.CoordinatorV2.OwnerCancelSubscription(subIDForOwnerCancelling) require.NoError(t, err, "Error canceling subscription") - subscriptionCanceledEvent, err := vrfv2Contracts.CoordinatorV2.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30) + subscriptionCanceledEvent, err := vrfContracts.CoordinatorV2.WaitForSubscriptionCanceledEvent([]uint64{subIDForOwnerCancelling}, time.Second*30) require.NoError(t, err, "error waiting for subscription canceled event") - cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + cancellationTxReceipt, err := testEnv.EVMClient.GetTxReceipt(tx.Hash()) require.NoError(t, err, "error getting tx cancellation Tx Receipt") txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) @@ -431,11 +483,11 @@ func TestVRFv2Basic(t *testing.T) { require.Equal(t, subBalanceLink, subscriptionCanceledEvent.Amount, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") - walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + walletBalanceLinkAfterSubCancelling, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) // Verify that subscription was deleted from Coordinator contract - _, err = vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForCancelling) + _, err = vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForOwnerCancelling) 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") @@ -458,94 +510,98 @@ func TestVRFv2Basic(t *testing.T) { func TestVRFv2MultipleSendingKeys(t *testing.T) { t.Parallel() + var ( + testEnv *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []uint64 + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + ) l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.VRFv2) if err != nil { t.Fatal(err) } + vrfv2Config := config.VRFv2 + cleanupFn := func() { + if testEnv.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", testEnv.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2Config.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2Config.General.UseExistingEnv { + if err := testEnv.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: 2, + UseVRFOwner: false, + UseTestCoordinator: false, + } - useVRFOwner := false - useTestCoordinator := false - - network, err := actions.EthereumNetworkConfigFromConfig(l, &config) - require.NoError(t, err, "Error building ethereum network config") - - env, err := test_env.NewCLTestEnvBuilder(). - WithTestConfig(&config). - WithTestInstance(t). - 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.VRFv2.General.LinkNativeFeedResponse)) + testEnv, vrfContracts, vrfKey, nodeTypeToNodeMap, err = vrfv2.SetupVRFV2Universe(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) 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, vrfv2KeyData, nodesMap, err := vrfv2.SetupVRFV2Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &config, - useVRFOwner, - useTestCoordinator, - linkToken, - mockETHLinkFeed, - defaultWalletAddress, - numberOfTxKeysToCreate, - 1, - 1, - l, - ) - require.NoError(t, err, "error setting up VRF v2 env") + defaultWalletAddress = testEnv.EVMClient.GetDefaultWallet().Address() - subID := subIDs[0] - - subscription, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) - require.NoError(t, err, "error getting subscription information") + t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { + configCopy := config.MustCopy().(tc.TestConfig) - vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.CoordinatorV2) + consumers, subIDsForMultipleSendingKeys, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + subIDForMultipleSendingKeys := subIDsForMultipleSendingKeys[0] + subscriptionForMultipleSendingKeys, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForMultipleSendingKeys) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscriptionForMultipleSendingKeys, subIDForMultipleSendingKeys, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForMultipleSendingKeys...) - t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { - txKeys, _, err := nodesMap[vrfcommon.VRF].CLNode.API.ReadTxKeys("evm") + txKeys, _, err := nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API.ReadTxKeys("evm") require.NoError(t, err, "error reading tx keys") - require.Equal(t, numberOfTxKeysToCreate+1, len(txKeys.Data)) + require.Equal(t, newEnvConfig.NumberOfTxKeysToCreate+1, len(txKeys.Data)) var fulfillmentTxFromAddresses []string - for i := 0; i < numberOfTxKeysToCreate+1; i++ { + for i := 0; i < newEnvConfig.NumberOfTxKeysToCreate+1; i++ { randomWordsFulfilledEvent, err := vrfv2.RequestRandomnessAndWaitForFulfillment( l, - vrfv2Contracts.VRFV2Consumer[0], - vrfv2Contracts.CoordinatorV2, - subID, - vrfv2KeyData, - *config.VRFv2.General.MinimumConfirmations, - *config.VRFv2.General.CallbackGasLimit, - *config.VRFv2.General.NumberOfWords, - *config.VRFv2.General.RandomnessRequestCountPerRequest, - *config.VRFv2.General.RandomnessRequestCountPerRequestDeviation, - config.VRFv2.General.RandomWordsFulfilledEventTimeout.Duration, + consumers[0], + vrfContracts.CoordinatorV2, + subIDForMultipleSendingKeys, + vrfKey, + *configCopy.VRFv2.General.MinimumConfirmations, + *configCopy.VRFv2.General.CallbackGasLimit, + *configCopy.VRFv2.General.NumberOfWords, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, + *configCopy.VRFv2.General.RandomnessRequestCountPerRequestDeviation, + configCopy.VRFv2.General.RandomWordsFulfilledEventTimeout.Duration, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") //todo - move TransactionByHash to EVMClient in CTF - fulfillmentTx, _, err := actions.GetTxByHash(testcontext.Get(t), env.EVMClient, randomWordsFulfilledEvent.Raw.TxHash) + fulfillmentTx, _, err := actions.GetTxByHash(testcontext.Get(t), testEnv.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") fulfillmentTxFromAddresses = append(fulfillmentTxFromAddresses, fulfillmentTxFromAddress) } - require.Equal(t, numberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) + require.Equal(t, newEnvConfig.NumberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) var txKeyAddresses []string for _, txKey := range txKeys.Data { txKeyAddresses = append(txKeyAddresses, txKey.Attributes.Address) @@ -558,109 +614,110 @@ func TestVRFv2MultipleSendingKeys(t *testing.T) { func TestVRFOwner(t *testing.T) { t.Parallel() + var ( + testEnv *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []uint64 + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + ) l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.VRFv2) require.NoError(t, err, "Error getting config") + vrfv2Config := config.VRFv2 + cleanupFn := func() { + if testEnv.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", testEnv.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2Config.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2Config.General.UseExistingEnv { + if err := testEnv.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: 0, + UseVRFOwner: true, + UseTestCoordinator: true, + } - 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(1). - 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)) - + testEnv, vrfContracts, vrfKey, _, err = vrfv2.SetupVRFV2Universe(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) 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 := 1 - vrfv2Contracts, subIDs, vrfv2Data, _, err := vrfv2.SetupVRFV2Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &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) + defaultWalletAddress = testEnv.EVMClient.GetDefaultWallet().Address() t.Run("Request Randomness With Force-Fulfill", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - vrfCoordinatorOwner, err := vrfv2Contracts.CoordinatorV2.GetOwner(testcontext.Get(t)) + consumers, subIDsForForceFulfill, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + subIDForForceFulfill := subIDsForForceFulfill[0] + subscriptionForMultipleSendingKeys, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForForceFulfill) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscriptionForMultipleSendingKeys, subIDForForceFulfill, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForForceFulfill...) + + vrfCoordinatorOwner, err := vrfContracts.CoordinatorV2.GetOwner(testcontext.Get(t)) require.NoError(t, err) - require.Equal(t, vrfv2Contracts.VRFOwner.Address(), vrfCoordinatorOwner.String()) + require.Equal(t, vrfContracts.VRFOwner.Address(), vrfCoordinatorOwner.String()) - err = linkToken.Transfer( - vrfv2Contracts.VRFV2Consumer[0].Address(), + err = vrfContracts.LinkToken.Transfer( + consumers[0].Address(), conversions.EtherToWei(big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink)), ) require.NoError(t, err, "error transferring link to consumer contract") - err = env.EVMClient.WaitForEvents() + err = testEnv.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - consumerLinkBalance, err := linkToken.BalanceOf(testcontext.Get(t), vrfv2Contracts.VRFV2Consumer[0].Address()) + consumerLinkBalance, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), consumers[0].Address()) require.NoError(t, err, "error getting consumer link balance") l.Info(). Str("Balance", conversions.WeiToEther(consumerLinkBalance).String()). - Str("Consumer", vrfv2Contracts.VRFV2Consumer[0].Address()). + Str("Consumer", consumers[0].Address()). Msg("Consumer Link Balance") - err = mockETHLinkFeed.SetBlockTimestampDeduction(big.NewInt(3)) + err = vrfContracts.MockETHLINKFeed.SetBlockTimestampDeduction(big.NewInt(3)) require.NoError(t, err) - err = env.EVMClient.WaitForEvents() + err = testEnv.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) // test and assert _, randFulfilledEvent, _, err := vrfv2.RequestRandomnessWithForceFulfillAndWaitForFulfillment( l, - vrfv2Contracts.VRFV2Consumer[0], - vrfv2Contracts.CoordinatorV2, - vrfv2Contracts.VRFOwner, - vrfv2Data, + consumers[0], + vrfContracts.CoordinatorV2, + vrfContracts.VRFOwner, + vrfKey, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, *configCopy.VRFv2.General.RandomnessRequestCountPerRequest, *configCopy.VRFv2.General.RandomnessRequestCountPerRequestDeviation, conversions.EtherToWei(big.NewFloat(5)), - common.HexToAddress(linkToken.Address()), + common.HexToAddress(vrfContracts.LinkToken.Address()), time.Minute*2, ) 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.VRFV2Consumer[0].GetRequestStatus(testcontext.Get(t), randFulfilledEvent.RequestId) + status, err := consumers[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") @@ -671,13 +728,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.CoordinatorV2.GetConfig(testcontext.Get(t)) + coordinatorConfig, err := vrfContracts.CoordinatorV2.GetConfig(testcontext.Get(t)) require.NoError(t, err, "error getting coordinator config") - coordinatorFeeConfig, err := vrfv2Contracts.CoordinatorV2.GetFeeConfig(testcontext.Get(t)) + coordinatorFeeConfig, err := vrfContracts.CoordinatorV2.GetFeeConfig(testcontext.Get(t)) require.NoError(t, err, "error getting coordinator fee config") - coordinatorFallbackWeiPerUnitLinkConfig, err := vrfv2Contracts.CoordinatorV2.GetFallbackWeiPerUnitLink(testcontext.Get(t)) + coordinatorFallbackWeiPerUnitLinkConfig, err := vrfContracts.CoordinatorV2.GetFallbackWeiPerUnitLink(testcontext.Get(t)) require.NoError(t, err, "error getting coordinator FallbackWeiPerUnitLink") require.Equal(t, *configCopy.VRFv2.General.StalenessSeconds, coordinatorConfig.StalenessSeconds) @@ -691,75 +748,80 @@ func TestVRFOwner(t *testing.T) { func TestVRFV2WithBHS(t *testing.T) { t.Parallel() + var ( + testEnv *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []uint64 + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + ) 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) - - // register proving key against oracle address (sending key) in order to test oracleWithdraw - defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() - - //Underfund Subscription - config.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0.000000000000000001)) // 1 Juel + vrfv2Config := config.VRFv2 + + cleanupFn := func() { + if testEnv.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", testEnv.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2Config.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2Config.General.UseExistingEnv { + if err := testEnv.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } //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, - []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] + vrfv2Config.General.BHSJobWaitBlocks = ptr.Ptr(2) + vrfv2Config.General.BHSJobLookBackBlocks = ptr.Ptr(20) + + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF, vrfcommon.BHS}, + NumberOfTxKeysToCreate: 0, + UseVRFOwner: false, + UseTestCoordinator: false, + } - subscription, err := vrfv2Contracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subID) - require.NoError(t, err, "error getting subscription information") + testEnv, vrfContracts, vrfKey, nodeTypeToNodeMap, err = vrfv2.SetupVRFV2Universe(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) + require.NoError(t, err) - vrfv2.LogSubDetails(l, subscription, subID, vrfv2Contracts.CoordinatorV2) + defaultWalletAddress = testEnv.EVMClient.GetDefaultWallet().Address() 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, + + //Underfund Subscription + configCopy.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) + consumers, subIDsForBHS, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + subIDForBHS := subIDsForBHS[0] + subscriptionForBHS, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForBHS) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscriptionForBHS, subIDForBHS, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForBHS...) + + _, err = consumers[0].RequestRandomness( + vrfKey.KeyHash, + subIDForBHS, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -767,30 +829,30 @@ func TestVRFV2WithBHS(t *testing.T) { ) 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())}, + randomWordsRequestedEvent, err := vrfContracts.CoordinatorV2.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfKey.KeyHash}, + []uint64{subIDForBHS}, + []common.Address{common.HexToAddress(consumers[0].Address())}, time.Minute*1, ) require.NoError(t, err, "error waiting for randomness requested event") - vrfv2.LogRandomnessRequestedEvent(l, vrfv2Contracts.CoordinatorV2, randomWordsRequestedEvent) + vrfv2.LogRandomnessRequestedEvent(l, vrfContracts.CoordinatorV2, randomWordsRequestedEvent) randRequestBlockNumber := randomWordsRequestedEvent.Raw.BlockNumber var wg sync.WaitGroup wg.Add(1) //Wait at least 256 blocks - _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(257), env.EVMClient, &wg, time.Second*260, t) + _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(257), testEnv.EVMClient, &wg, time.Second*260, t) wg.Wait() require.NoError(t, err) - err = vrfv2.FundSubscriptions(env, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), linkToken, vrfv2Contracts.CoordinatorV2, subIDs) + err = vrfv2.FundSubscriptions(testEnv, big.NewFloat(*configCopy.VRFv2.General.SubscriptionFundingAmountLink), vrfContracts.LinkToken, vrfContracts.CoordinatorV2, subIDsForBHS) require.NoError(t, err, "error funding subscriptions") - randomWordsFulfilledEvent, err := vrfv2Contracts.CoordinatorV2.WaitForRandomWordsFulfilledEvent( + randomWordsFulfilledEvent, err := vrfContracts.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) + vrfv2.LogRandomWordsFulfilledEvent(l, vrfContracts.CoordinatorV2, randomWordsFulfilledEvent) + status, err := consumers[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") @@ -799,9 +861,28 @@ func TestVRFV2WithBHS(t *testing.T) { 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, + //Underfund Subscription + configCopy.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) + + consumers, subIDsForBHS, err := vrfv2.SetupNewConsumersAndSubs( + testEnv, + vrfContracts.CoordinatorV2, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + subIDForBHS := subIDsForBHS[0] + subscriptionForBHS, err := vrfContracts.CoordinatorV2.GetSubscription(testcontext.Get(t), subIDForBHS) + require.NoError(t, err, "error getting subscription information") + vrfv2.LogSubDetails(l, subscriptionForBHS, subIDForBHS, vrfContracts.CoordinatorV2) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForBHS...) + + _, err = consumers[0].RequestRandomness( + vrfKey.KeyHash, + subIDForBHS, *configCopy.VRFv2.General.MinimumConfirmations, *configCopy.VRFv2.General.CallbackGasLimit, *configCopy.VRFv2.General.NumberOfWords, @@ -809,28 +890,28 @@ func TestVRFV2WithBHS(t *testing.T) { ) 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())}, + randomWordsRequestedEvent, err := vrfContracts.CoordinatorV2.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfKey.KeyHash}, + []uint64{subIDForBHS}, + []common.Address{common.HexToAddress(consumers[0].Address())}, time.Minute*1, ) require.NoError(t, err, "error waiting for randomness requested event") - vrfv2.LogRandomnessRequestedEvent(l, vrfv2Contracts.CoordinatorV2, randomWordsRequestedEvent) + vrfv2.LogRandomnessRequestedEvent(l, vrfContracts.CoordinatorV2, randomWordsRequestedEvent) randRequestBlockNumber := randomWordsRequestedEvent.Raw.BlockNumber - _, err = vrfv2Contracts.BHS.GetBlockHash(testcontext.Get(t), big.NewInt(int64(randRequestBlockNumber))) + _, 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.VRFv2.General.BHSJobWaitBlocks), env.EVMClient, &wg, time.Minute*1, t) + _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(*configCopy.VRFv2.General.BHSJobWaitBlocks), testEnv.EVMClient, &wg, time.Minute*1, t) wg.Wait() require.NoError(t, err, "error waiting for blocknumber to be") - err = env.EVMClient.WaitForEvents() + err = testEnv.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - metrics, err := vrfv2Contracts.VRFV2Consumer[0].GetLoadTestMetrics(testcontext.Get(t)) + metrics, err := consumers[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))) @@ -838,16 +919,16 @@ func TestVRFV2WithBHS(t *testing.T) { var txHash string gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - clNodeTxs, _, err = nodesMap[vrfcommon.BHS].CLNode.API.ReadTransactions() + clNodeTxs, _, err = nodeTypeToNodeMap[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)) + require.Equal(t, strings.ToLower(vrfContracts.BHS.Address()), strings.ToLower(clNodeTxs.Data[0].Attributes.To)) - bhsStoreTx, _, err := actions.GetTxByHash(testcontext.Get(t), env.EVMClient, common.HexToHash(txHash)) + bhsStoreTx, _, err := actions.GetTxByHash(testcontext.Get(t), testEnv.EVMClient, common.HexToHash(txHash)) require.NoError(t, err, "error getting tx from hash") bhsStoreTxInputData, err := actions.DecodeTxInputData(blockhash_store.BlockhashStoreABI, bhsStoreTx.Data()) @@ -856,12 +937,12 @@ func TestVRFV2WithBHS(t *testing.T) { Msg("BHS Node's Store Blockhash for Blocknumber Method TX") require.Equal(t, randRequestBlockNumber, bhsStoreTxInputData["n"].(*big.Int).Uint64()) - err = env.EVMClient.WaitForEvents() + err = testEnv.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))) + 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(). diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index f7ef5170c84..c621d027f32 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -32,72 +32,78 @@ import ( func TestVRFv2Plus(t *testing.T) { t.Parallel() + var ( + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []*big.Int + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + ) l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) - if err != nil { - t.Fatal(err) + require.NoError(t, err, "Error getting config") + vrfv2PlusConfig := config.VRFv2Plus + + cleanupFn := func() { + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2PlusConfig.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2PlusConfig.General.UseExistingEnv { + if err := env.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: 0, + UseVRFOwner: false, + UseTestCoordinator: false, } - 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") - - // default wallet address is used to test Withdraw - defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() - - numberOfTxKeysToCreate := 2 - vrfv2PlusContracts, subIDs, vrfv2PlusData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &config, - linkToken, - mockETHLinkFeed, - numberOfTxKeysToCreate, - 1, - 1, - l, - ) - require.NoError(t, err, "error setting up VRF v2_5 env") - - subID := subIDs[0] - - subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) - require.NoError(t, err, "error getting subscription information") - - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) + env, vrfContracts, vrfKey, nodeTypeToNodeMap, err = vrfv2plus.SetupVRFV2PlusUniverse(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) + require.NoError(t, err) + defaultWalletAddress = env.EVMClient.GetDefaultWallet().Address() t.Run("Link Billing", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) var isNativeBilling = false + consumers, subIDsForRequestRandomness, err := vrfv2plus.SetupNewConsumersAndSubs( + env, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + subIDForRequestRandomness := subIDsForRequestRandomness[0] + subscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForRequestRandomness) + require.NoError(t, err, "error getting subscription information") + vrfv2plus.LogSubDetails(l, subscription, subIDForRequestRandomness, vrfContracts.CoordinatorV2Plus) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDsForRequestRandomness...) + subBalanceBeforeRequest := subscription.Balance - jobRunsBeforeTest, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) + jobRunsBeforeTest, err := nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodeTypeToNodeMap[vrfcommon.VRF].Job.Data.ID) require.NoError(t, err, "error reading job runs") // test and assert randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.VRFV2PlusConsumer[0], - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData, - subID, + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, + subIDForRequestRandomness, isNativeBilling, configCopy.VRFv2Plus.General, l, @@ -109,16 +115,16 @@ func TestVRFv2Plus(t *testing.T) { require.True(t, randomWordsFulfilledEvent.Success, "RandomWordsFulfilled Event's `Success` field should be true") expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + subscription, err = vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForRequestRandomness) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := subscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) - jobRuns, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) + jobRuns, err := nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodeTypeToNodeMap[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) + status, err := consumers[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") @@ -133,16 +139,33 @@ func TestVRFv2Plus(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) testConfig := configCopy.VRFv2Plus.General var isNativeBilling = true + + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( + env, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + 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) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) + subNativeTokenBalanceBeforeRequest := subscription.NativeBalance - jobRunsBeforeTest, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) + jobRunsBeforeTest, err := nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodeTypeToNodeMap[vrfcommon.VRF].Job.Data.ID) require.NoError(t, err, "error reading job runs") // test and assert randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.VRFV2PlusConsumer[0], - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData, + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, subID, isNativeBilling, configCopy.VRFv2Plus.General, @@ -153,16 +176,16 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, isNativeBilling, randomWordsFulfilledEvent.NativePayment) require.True(t, randomWordsFulfilledEvent.Success) expectedSubBalanceWei := new(big.Int).Sub(subNativeTokenBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + subscription, err = vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err) subBalanceAfterRequest := subscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) - jobRuns, err := nodesMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodesMap[vrfcommon.VRF].Job.Data.ID) + jobRuns, err := nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API.MustReadRunsByJob(nodeTypeToNodeMap[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) + status, err := consumers[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,12 +199,13 @@ func TestVRFv2Plus(t *testing.T) { t.Run("Direct Funding (VRFV2PlusWrapper)", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperEnvironment( + testcontext.Get(t), env, &configCopy, - linkToken, - mockETHLinkFeed, - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData.KeyHash, + vrfContracts.LinkToken, + vrfContracts.MockETHLINKFeed, + vrfContracts.CoordinatorV2Plus, + vrfKey.KeyHash, 1, ) require.NoError(t, err) @@ -191,17 +215,17 @@ func TestVRFv2Plus(t *testing.T) { testConfig := configCopy.VRFv2Plus.General var isNativeBilling = false - wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) + wrapperConsumerJuelsBalanceBeforeRequest, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) + wrapperSubscription, err := vrfContracts.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.CoordinatorV2Plus, - vrfv2PlusData, + vrfContracts.CoordinatorV2Plus, + vrfKey, wrapperSubID, isNativeBilling, configCopy.VRFv2Plus.General, @@ -210,7 +234,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.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) + wrapperSubscription, err = vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := wrapperSubscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) @@ -221,7 +245,7 @@ func TestVRFv2Plus(t *testing.T) { expectedWrapperConsumerJuelsBalance := new(big.Int).Sub(wrapperConsumerJuelsBalanceBeforeRequest, consumerStatus.Paid) - wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) + wrapperConsumerJuelsBalanceAfterRequest, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) @@ -243,14 +267,14 @@ func TestVRFv2Plus(t *testing.T) { wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) require.NoError(t, err, "error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) + wrapperSubscription, err := vrfContracts.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.CoordinatorV2Plus, - vrfv2PlusData, + vrfContracts.CoordinatorV2Plus, + vrfKey, wrapperSubID, isNativeBilling, configCopy.VRFv2Plus.General, @@ -259,7 +283,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.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) + wrapperSubscription, err = vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := wrapperSubscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) @@ -287,17 +311,21 @@ func TestVRFv2Plus(t *testing.T) { }) t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - subIDsForCancelling, err := vrfv2plus.CreateFundSubsAndAddConsumers( + _, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( env, - big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), - big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), - linkToken, - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusContracts.VRFV2PlusConsumer, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, 1, + 1, + l, ) - require.NoError(t, err) - subIDForCancelling := subIDsForCancelling[0] + require.NoError(t, err, "error setting up new consumers and subs") + 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) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) testWalletAddress, err := actions.GenerateWallet() require.NoError(t, err) @@ -305,10 +333,10 @@ func TestVRFv2Plus(t *testing.T) { testWalletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), testWalletAddress) require.NoError(t, err) - testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) + testWalletBalanceLinkBeforeSubCancelling, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) - subscriptionForCancelling, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -316,13 +344,13 @@ func TestVRFv2Plus(t *testing.T) { l.Info(). Str("Subscription Amount Native", subBalanceNative.String()). Str("Subscription Amount Link", subBalanceLink.String()). - Str("Returning funds from SubID", subIDForCancelling.String()). + Str("Returning funds from SubID", subID.String()). Str("Returning funds to", testWalletAddress.String()). Msg("Canceling subscription and returning funds to subscription owner") - tx, err := vrfv2PlusContracts.CoordinatorV2Plus.CancelSubscription(subIDForCancelling, testWalletAddress) + tx, err := vrfContracts.CoordinatorV2Plus.CancelSubscription(subID, testWalletAddress) require.NoError(t, err, "Error canceling subscription") - subscriptionCanceledEvent, err := vrfv2PlusContracts.CoordinatorV2Plus.WaitForSubscriptionCanceledEvent(subIDForCancelling, time.Second*30) + subscriptionCanceledEvent, err := vrfContracts.CoordinatorV2Plus.WaitForSubscriptionCanceledEvent(subID, time.Second*30) require.NoError(t, err, "error waiting for subscription canceled event") cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) @@ -354,11 +382,11 @@ func TestVRFv2Plus(t *testing.T) { testWalletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), testWalletAddress) require.NoError(t, err) - testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) + testWalletBalanceLinkAfterSubCancelling, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) + _, err = vrfContracts.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") subFundsReturnedNativeActual := new(big.Int).Sub(testWalletBalanceNativeAfterSubCancelling, testWalletBalanceNativeBeforeSubCancelling) @@ -387,42 +415,39 @@ func TestVRFv2Plus(t *testing.T) { testConfig := configCopy.VRFv2Plus.General //underfund subs in order rand fulfillments to fail - testConfig.SubscriptionFundingAmountNative = ptr.Ptr(float64(0.000000000000000001)) //1 Wei - testConfig.SubscriptionFundingAmountLink = ptr.Ptr(float64(0.000000000000000001)) //1 Juels + testConfig.SubscriptionFundingAmountNative = ptr.Ptr(float64(0)) + testConfig.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) - subIDsForCancelling, err := vrfv2plus.CreateFundSubsAndAddConsumers( + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( env, - big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), - big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), - linkToken, - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusContracts.VRFV2PlusConsumer, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, 1, + 1, + l, ) - require.NoError(t, err) - - subIDForCancelling := subIDsForCancelling[0] - - subscriptionForCancelling, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) + require.NoError(t, err, "error setting up new consumers and subs") + subID := subIDs[0] + subscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") - - vrfv2plus.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2PlusContracts.CoordinatorV2Plus) - - activeSubscriptionIdsBeforeSubCancellation, err := vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + vrfv2plus.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2Plus) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) + activeSubscriptionIdsBeforeSubCancellation, err := vrfContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err) - require.True(t, it_utils.BigIntSliceContains(activeSubscriptionIdsBeforeSubCancellation, subIDForCancelling)) + require.True(t, it_utils.BigIntSliceContains(activeSubscriptionIdsBeforeSubCancellation, subID)) - pendingRequestsExist, err := vrfv2PlusContracts.CoordinatorV2Plus.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + pendingRequestsExist, err := vrfContracts.CoordinatorV2Plus.PendingRequestsExist(testcontext.Get(t), subID) require.NoError(t, err) require.False(t, pendingRequestsExist, "Pending requests should not exist") configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout = ptr.Ptr(blockchain.StrDuration{Duration: 5 * time.Second}) _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.VRFV2PlusConsumer[0], - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData, - subIDForCancelling, + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, + subID, false, configCopy.VRFv2Plus.General, l, @@ -431,10 +456,10 @@ 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.VRFV2PlusConsumer[0], - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData, - subIDForCancelling, + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, + subID, true, configCopy.VRFv2Plus.General, l, @@ -442,17 +467,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.CoordinatorV2Plus.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) + pendingRequestsExist, err = vrfContracts.CoordinatorV2Plus.PendingRequestsExist(testcontext.Get(t), subID) 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)) require.NoError(t, err) - walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + walletBalanceLinkBeforeSubCancelling, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) - subscriptionForCancelling, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) + subscriptionForCancelling, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -460,13 +485,13 @@ func TestVRFv2Plus(t *testing.T) { l.Info(). Str("Subscription Amount Native", subBalanceNative.String()). Str("Subscription Amount Link", subBalanceLink.String()). - Str("Returning funds from SubID", subIDForCancelling.String()). + Str("Returning funds from SubID", subID.String()). Str("Returning funds to", defaultWalletAddress). Msg("Canceling subscription and returning funds to subscription owner") - tx, err := vrfv2PlusContracts.CoordinatorV2Plus.OwnerCancelSubscription(subIDForCancelling) + tx, err := vrfContracts.CoordinatorV2Plus.OwnerCancelSubscription(subID) require.NoError(t, err, "Error canceling subscription") - subscriptionCanceledEvent, err := vrfv2PlusContracts.CoordinatorV2Plus.WaitForSubscriptionCanceledEvent(subIDForCancelling, time.Second*30) + subscriptionCanceledEvent, err := vrfContracts.CoordinatorV2Plus.WaitForSubscriptionCanceledEvent(subID, time.Second*30) require.NoError(t, err, "error waiting for subscription canceled event") cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) @@ -498,11 +523,11 @@ func TestVRFv2Plus(t *testing.T) { walletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + walletBalanceLinkAfterSubCancelling, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subIDForCancelling) + _, err = vrfContracts.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") subFundsReturnedNativeActual := new(big.Int).Sub(walletBalanceNativeAfterSubCancelling, walletBalanceNativeBeforeSubCancelling) @@ -530,34 +555,38 @@ 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.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + activeSubscriptionIdsAfterSubCancellation, err := vrfContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err, "error getting active subscription ids") require.False( t, - it_utils.BigIntSliceContains(activeSubscriptionIdsAfterSubCancellation, subIDForCancelling), + it_utils.BigIntSliceContains(activeSubscriptionIdsAfterSubCancellation, subID), "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( + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( env, - big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), - big.NewFloat(*configCopy.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), - linkToken, - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusContracts.VRFV2PlusConsumer, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, 1, + 1, + l, ) - require.NoError(t, err) - subIDForWithdraw := subIDsForWithdraw[0] + require.NoError(t, err, "error setting up new consumers and subs") + 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) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) fulfilledEventLink, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.VRFV2PlusConsumer[0], - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData, - subIDForWithdraw, + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, + subID, false, configCopy.VRFv2Plus.General, l, @@ -565,10 +594,10 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err) fulfilledEventNative, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.VRFV2PlusConsumer[0], - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData, - subIDForWithdraw, + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, + subID, true, configCopy.VRFv2Plus.General, l, @@ -579,7 +608,7 @@ func TestVRFv2Plus(t *testing.T) { defaultWalletBalanceNativeBeforeWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - defaultWalletBalanceLinkBeforeWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + defaultWalletBalanceLinkBeforeWithdraw, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) l.Info(). @@ -587,7 +616,7 @@ func TestVRFv2Plus(t *testing.T) { Str("Amount", amountToWithdrawLink.String()). Msg("Invoking Oracle Withdraw for LINK") - err = vrfv2PlusContracts.CoordinatorV2Plus.Withdraw( + err = vrfContracts.CoordinatorV2Plus.Withdraw( common.HexToAddress(defaultWalletAddress), ) require.NoError(t, err, "error withdrawing LINK from coordinator to default wallet") @@ -598,7 +627,7 @@ func TestVRFv2Plus(t *testing.T) { Str("Amount", amountToWithdrawNative.String()). Msg("Invoking Oracle Withdraw for Native") - err = vrfv2PlusContracts.CoordinatorV2Plus.WithdrawNative( + err = vrfContracts.CoordinatorV2Plus.WithdrawNative( common.HexToAddress(defaultWalletAddress), ) require.NoError(t, err, "error withdrawing Native tokens from coordinator to default wallet") @@ -609,7 +638,7 @@ func TestVRFv2Plus(t *testing.T) { defaultWalletBalanceNativeAfterWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - defaultWalletBalanceLinkAfterWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) + defaultWalletBalanceLinkAfterWithdraw, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) //not possible to verify exact amount of Native/LINK returned as defaultWallet is used in other tests in parallel which might affect the balance @@ -620,69 +649,79 @@ func TestVRFv2Plus(t *testing.T) { func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { t.Parallel() + var ( + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []*big.Int + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + ) l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) - if err != nil { - t.Fatal(err) + require.NoError(t, err, "Error getting config") + vrfv2PlusConfig := config.VRFv2Plus + + cleanupFn := func() { + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2PlusConfig.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2PlusConfig.General.UseExistingEnv { + if err := env.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: 2, + UseVRFOwner: false, + UseTestCoordinator: false, } - 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") - - numberOfTxKeysToCreate := 2 - vrfv2PlusContracts, subIDs, vrfv2PlusData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &config, - linkToken, - mockETHLinkFeed, - numberOfTxKeysToCreate, - 1, - 1, - l, - ) - require.NoError(t, err, "error setting up VRF v2_5 env") - - subID := subIDs[0] - - subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) - require.NoError(t, err, "error getting subscription information") - - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) + env, vrfContracts, vrfKey, nodeTypeToNodeMap, err = vrfv2plus.SetupVRFV2PlusUniverse(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) + require.NoError(t, err) + defaultWalletAddress = env.EVMClient.GetDefaultWallet().Address() t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) var isNativeBilling = true - txKeys, _, err := nodesMap[vrfcommon.VRF].CLNode.API.ReadTxKeys("evm") + + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( + env, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + 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) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) + + txKeys, _, err := nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API.ReadTxKeys("evm") require.NoError(t, err, "error reading tx keys") - require.Equal(t, numberOfTxKeysToCreate+1, len(txKeys.Data)) + require.Equal(t, newEnvConfig.NumberOfTxKeysToCreate+1, len(txKeys.Data)) var fulfillmentTxFromAddresses []string - for i := 0; i < numberOfTxKeysToCreate+1; i++ { + for i := 0; i < newEnvConfig.NumberOfTxKeysToCreate+1; i++ { randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.VRFV2PlusConsumer[0], - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData, + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, subID, isNativeBilling, configCopy.VRFv2Plus.General, @@ -697,7 +736,7 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { require.NoError(t, err, "error getting tx from address") fulfillmentTxFromAddresses = append(fulfillmentTxFromAddresses, fulfillmentTxFromAddress) } - require.Equal(t, numberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) + require.Equal(t, newEnvConfig.NumberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) var txKeyAddresses []string for _, txKey := range txKeys.Data { txKeyAddresses = append(txKeyAddresses, txKey.Attributes.Address) @@ -710,121 +749,133 @@ func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { func TestVRFv2PlusMigration(t *testing.T) { t.Parallel() + var ( + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []*big.Int + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + ) l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) - if err != nil { - t.Fatal(err) + require.NoError(t, err, "Error getting config") + vrfv2PlusConfig := config.VRFv2Plus + + cleanupFn := func() { + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2PlusConfig.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2PlusConfig.General.UseExistingEnv { + if err := env.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: 0, + UseVRFOwner: false, + UseTestCoordinator: false, } - 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) - - 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, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &config, - linkAddress, - mockETHLinkFeedAddress, - 0, - 2, - 1, - l, - ) - require.NoError(t, err, "error setting up VRF v2_5 env") + env, vrfContracts, vrfKey, nodeTypeToNodeMap, err = vrfv2plus.SetupVRFV2PlusUniverse(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) + require.NoError(t, err) + defaultWalletAddress = env.EVMClient.GetDefaultWallet().Address() // 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] + configCopy := config.MustCopy().(tc.TestConfig) - subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( + env, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, + 2, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + 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) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) - - activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + activeSubIdsOldCoordinatorBeforeMigration, err := vrfContracts.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.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + oldSubscriptionBeforeMigration, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") //Migration Process - newCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2PlusUpgradedVersion(vrfv2PlusContracts.BHS.Address()) + newCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2PlusUpgradedVersion(vrfContracts.BHS.Address()) require.NoError(t, err, "error deploying VRF CoordinatorV2PlusUpgradedVersion") err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, newCoordinator) + _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfKey.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, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.MaxGasLimitCoordinatorConfig, + *configCopy.VRFv2Plus.General.StalenessSeconds, + *configCopy.VRFv2Plus.General.GasAfterPaymentCalculation, + big.NewInt(*configCopy.VRFv2Plus.General.LinkNativeFeedResponse), + *configCopy.VRFv2Plus.General.FulfillmentFlatFeeNativePPM, + *configCopy.VRFv2Plus.General.FulfillmentFlatFeeLinkDiscountPPM, + *configCopy.VRFv2Plus.General.NativePremiumPercentage, + *configCopy.VRFv2Plus.General.LinkPremiumPercentage, ) require.NoError(t, err) - err = newCoordinator.SetLINKAndLINKNativeFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address()) + err = newCoordinator.SetLINKAndLINKNativeFeed(vrfContracts.LinkToken.Address(), vrfContracts.MockETHLINKFeed.Address()) require.NoError(t, err, vrfv2plus.ErrSetLinkNativeLinkFeed) err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ - ForwardingAllowed: *vrfv2PlusConfig.VRFJobForwardingAllowed, + ForwardingAllowed: *configCopy.VRFv2Plus.General.VRFJobForwardingAllowed, CoordinatorAddress: newCoordinator.Address(), - FromAddresses: nodesMap[vrfcommon.VRF].TXKeyAddressStrings, + FromAddresses: nodeTypeToNodeMap[vrfcommon.VRF].TXKeyAddressStrings, EVMChainID: env.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, + MinIncomingConfirmations: int(*configCopy.VRFv2Plus.General.MinimumConfirmations), + PublicKey: vrfKey.VRFKey.Data.ID, + EstimateGasMultiplier: *configCopy.VRFv2Plus.General.VRFJobEstimateGasMultiplier, + BatchFulfillmentEnabled: *configCopy.VRFv2Plus.General.VRFJobBatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *configCopy.VRFv2Plus.General.VRFJobBatchFulfillmentGasMultiplier, + PollPeriod: configCopy.VRFv2Plus.General.VRFJobPollPeriod.Duration, + RequestTimeout: configCopy.VRFv2Plus.General.VRFJobRequestTimeout.Duration, } _, err = vrfv2plus.CreateVRFV2PlusJob( - nodesMap[vrfcommon.VRF].CLNode.API, + nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API, vrfJobSpecConfig, ) require.NoError(t, err, vrfv2plus.ErrCreateVRFV2PlusJobs) - err = vrfv2PlusContracts.CoordinatorV2Plus.RegisterMigratableCoordinator(newCoordinator.Address()) + err = vrfContracts.CoordinatorV2Plus.RegisterMigratableCoordinator(newCoordinator.Address()) require.NoError(t, err, "error registering migratable coordinator") err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - oldCoordinatorLinkTotalBalanceBeforeMigration, oldCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.CoordinatorV2Plus) + oldCoordinatorLinkTotalBalanceBeforeMigration, oldCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfContracts.CoordinatorV2Plus) require.NoError(t, err) migratedCoordinatorLinkTotalBalanceBeforeMigration, migratedCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) @@ -833,17 +884,17 @@ func TestVRFv2PlusMigration(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - err = vrfv2PlusContracts.CoordinatorV2Plus.Migrate(subID, newCoordinator.Address()) + err = vrfContracts.CoordinatorV2Plus.Migrate(subID, 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 migrating sub id ", subID.String(), " from ", vrfContracts.CoordinatorV2Plus.Address(), " to new Coordinator address ", newCoordinator.Address()) + migrationCompletedEvent, err := vrfContracts.CoordinatorV2Plus.WaitForMigrationCompletedEvent(time.Minute * 1) require.NoError(t, err, "error waiting for MigrationCompleted event") err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - vrfv2plus.LogMigrationCompletedEvent(l, migrationCompletedEvent, vrfv2PlusContracts) + vrfv2plus.LogMigrationCompletedEvent(l, migrationCompletedEvent, vrfContracts) - oldCoordinatorLinkTotalBalanceAfterMigration, oldCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.CoordinatorV2Plus) + oldCoordinatorLinkTotalBalanceAfterMigration, oldCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfContracts.CoordinatorV2Plus) require.NoError(t, err) migratedCoordinatorLinkTotalBalanceAfterMigration, migratedCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) @@ -855,7 +906,7 @@ func TestVRFv2PlusMigration(t *testing.T) { vrfv2plus.LogSubDetailsAfterMigration(l, newCoordinator, subID, migratedSubscription) //Verify that Coordinators were updated in Consumers - for _, consumer := range vrfv2PlusContracts.VRFV2PlusConsumer { + for _, consumer := range consumers { coordinatorAddressInConsumerAfterMigration, err := consumer.GetCoordinator(testcontext.Get(t)) require.NoError(t, err, "error getting Coordinator from Consumer contract") require.Equal(t, newCoordinator.Address(), coordinatorAddressInConsumerAfterMigration.String()) @@ -872,10 +923,10 @@ func TestVRFv2PlusMigration(t *testing.T) { require.Equal(t, oldSubscriptionBeforeMigration.Consumers, migratedSubscription.Consumers) //Verify that old sub was deleted from old Coordinator - _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + _, err = vrfContracts.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)) + _, err = vrfContracts.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") @@ -897,24 +948,24 @@ func TestVRFv2PlusMigration(t *testing.T) { //Verify rand requests fulfills with Link Token billing _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillmentUpgraded( - vrfv2PlusContracts.VRFV2PlusConsumer[0], + consumers[0], newCoordinator, - vrfv2PlusData, + vrfKey, subID, false, - config.VRFv2Plus.General, + configCopy.VRFv2Plus.General, 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], + consumers[1], newCoordinator, - vrfv2PlusData, + vrfKey, subID, true, - config.VRFv2Plus.General, + configCopy.VRFv2Plus.General, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -925,88 +976,89 @@ func TestVRFv2PlusMigration(t *testing.T) { // 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( + testcontext.Get(t), env, &configCopy, - linkAddress, - mockETHLinkFeedAddress, - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData.KeyHash, + vrfContracts.LinkToken, + vrfContracts.MockETHLINKFeed, + vrfContracts.CoordinatorV2Plus, + vrfKey.KeyHash, 1, ) require.NoError(t, err) subID := wrapperSubID - subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + subscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) + vrfv2plus.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2Plus) - activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.CoordinatorV2Plus.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) + activeSubIdsOldCoordinatorBeforeMigration, err := vrfContracts.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) + oldSubscriptionBeforeMigration, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") //Migration Process - newCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2PlusUpgradedVersion(vrfv2PlusContracts.BHS.Address()) + newCoordinator, err := env.ContractDeployer.DeployVRFCoordinatorV2PlusUpgradedVersion(vrfContracts.BHS.Address()) require.NoError(t, err, "error deploying VRF CoordinatorV2PlusUpgradedVersion") err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, newCoordinator) + _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfKey.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, + *configCopy.VRFv2Plus.General.MinimumConfirmations, + *configCopy.VRFv2Plus.General.MaxGasLimitCoordinatorConfig, + *configCopy.VRFv2Plus.General.StalenessSeconds, + *configCopy.VRFv2Plus.General.GasAfterPaymentCalculation, + big.NewInt(*configCopy.VRFv2Plus.General.LinkNativeFeedResponse), + *configCopy.VRFv2Plus.General.FulfillmentFlatFeeNativePPM, + *configCopy.VRFv2Plus.General.FulfillmentFlatFeeLinkDiscountPPM, + *configCopy.VRFv2Plus.General.NativePremiumPercentage, + *configCopy.VRFv2Plus.General.LinkPremiumPercentage, ) require.NoError(t, err) - err = newCoordinator.SetLINKAndLINKNativeFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address()) + err = newCoordinator.SetLINKAndLINKNativeFeed(vrfContracts.LinkToken.Address(), vrfContracts.MockETHLINKFeed.Address()) require.NoError(t, err, vrfv2plus.ErrSetLinkNativeLinkFeed) err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ - ForwardingAllowed: *vrfv2PlusConfig.VRFJobForwardingAllowed, + ForwardingAllowed: *configCopy.VRFv2Plus.General.VRFJobForwardingAllowed, CoordinatorAddress: newCoordinator.Address(), - FromAddresses: nodesMap[vrfcommon.VRF].TXKeyAddressStrings, + FromAddresses: nodeTypeToNodeMap[vrfcommon.VRF].TXKeyAddressStrings, EVMChainID: env.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, + MinIncomingConfirmations: int(*configCopy.VRFv2Plus.General.MinimumConfirmations), + PublicKey: vrfKey.VRFKey.Data.ID, + EstimateGasMultiplier: *configCopy.VRFv2Plus.General.VRFJobEstimateGasMultiplier, + BatchFulfillmentEnabled: *configCopy.VRFv2Plus.General.VRFJobBatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *configCopy.VRFv2Plus.General.VRFJobBatchFulfillmentGasMultiplier, + PollPeriod: configCopy.VRFv2Plus.General.VRFJobPollPeriod.Duration, + RequestTimeout: configCopy.VRFv2Plus.General.VRFJobRequestTimeout.Duration, } _, err = vrfv2plus.CreateVRFV2PlusJob( - nodesMap[vrfcommon.VRF].CLNode.API, + nodeTypeToNodeMap[vrfcommon.VRF].CLNode.API, vrfJobSpecConfig, ) require.NoError(t, err, vrfv2plus.ErrCreateVRFV2PlusJobs) - err = vrfv2PlusContracts.CoordinatorV2Plus.RegisterMigratableCoordinator(newCoordinator.Address()) + err = vrfContracts.CoordinatorV2Plus.RegisterMigratableCoordinator(newCoordinator.Address()) require.NoError(t, err, "error registering migratable coordinator") err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - oldCoordinatorLinkTotalBalanceBeforeMigration, oldCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.CoordinatorV2Plus) + oldCoordinatorLinkTotalBalanceBeforeMigration, oldCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfContracts.CoordinatorV2Plus) require.NoError(t, err) migratedCoordinatorLinkTotalBalanceBeforeMigration, migratedCoordinatorEthTotalBalanceBeforeMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) @@ -1018,15 +1070,15 @@ func TestVRFv2PlusMigration(t *testing.T) { // 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 migrating sub id ", subID.String(), " from ", vrfContracts.CoordinatorV2Plus.Address(), " to new Coordinator address ", newCoordinator.Address()) + migrationCompletedEvent, err := vrfContracts.CoordinatorV2Plus.WaitForMigrationCompletedEvent(time.Minute * 1) require.NoError(t, err, "error waiting for MigrationCompleted event") err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfcommon.ErrWaitTXsComplete) - vrfv2plus.LogMigrationCompletedEvent(l, migrationCompletedEvent, vrfv2PlusContracts) + vrfv2plus.LogMigrationCompletedEvent(l, migrationCompletedEvent, vrfContracts) - oldCoordinatorLinkTotalBalanceAfterMigration, oldCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfv2PlusContracts.CoordinatorV2Plus) + oldCoordinatorLinkTotalBalanceAfterMigration, oldCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetCoordinatorTotalBalance(vrfContracts.CoordinatorV2Plus) require.NoError(t, err) migratedCoordinatorLinkTotalBalanceAfterMigration, migratedCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) @@ -1053,10 +1105,10 @@ func TestVRFv2PlusMigration(t *testing.T) { require.Equal(t, oldSubscriptionBeforeMigration.Consumers, migratedSubscription.Consumers) //Verify that old sub was deleted from old Coordinator - _, err = vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + _, err = vrfContracts.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)) + _, err = vrfContracts.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") @@ -1081,7 +1133,7 @@ func TestVRFv2PlusMigration(t *testing.T) { randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillmentUpgraded( wrapperContracts.LoadTestConsumers[0], newCoordinator, - vrfv2PlusData, + vrfKey, subID, isNativeBilling, configCopy.VRFv2Plus.General, @@ -1097,7 +1149,7 @@ func TestVRFv2PlusMigration(t *testing.T) { randomWordsFulfilledEvent, err = vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillmentUpgraded( wrapperContracts.LoadTestConsumers[0], newCoordinator, - vrfv2PlusData, + vrfKey, subID, isNativeBilling, configCopy.VRFv2Plus.General, @@ -1112,68 +1164,79 @@ func TestVRFv2PlusMigration(t *testing.T) { func TestVRFV2PlusWithBHS(t *testing.T) { t.Parallel() + var ( + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []*big.Int + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + ) 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) - - 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 + vrfv2PlusConfig := config.VRFv2Plus + + cleanupFn := func() { + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2PlusConfig.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2PlusConfig.General.UseExistingEnv { + if err := env.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } //decrease default span for checking blockhashes for unfulfilled requests - config.VRFv2Plus.General.BHSJobWaitBlocks = ptr.Ptr(2) - config.VRFv2Plus.General.BHSJobLookBackBlocks = ptr.Ptr(20) + vrfv2PlusConfig.General.BHSJobWaitBlocks = ptr.Ptr(2) + vrfv2PlusConfig.General.BHSJobLookBackBlocks = ptr.Ptr(20) + + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF, vrfcommon.BHS}, + NumberOfTxKeysToCreate: 0, + UseVRFOwner: false, + UseTestCoordinator: false, + } - numberOfTxKeysToCreate := 0 - vrfContracts, subIDs, vrfKeyData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF, vrfcommon.BHS}, - &config, - linkToken, - mockETHLinkFeed, - numberOfTxKeysToCreate, - 1, - 2, - l, - ) - require.NoError(t, err, "error setting up VRF v2_5 env") + env, vrfContracts, vrfKey, nodeTypeToNodeMap, err = vrfv2plus.SetupVRFV2PlusUniverse(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) + require.NoError(t, err) + defaultWalletAddress = env.EVMClient.GetDefaultWallet().Address() 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") + configCopy := config.MustCopy().(tc.TestConfig) + //Underfund Subscription + configCopy.VRFv2Plus.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) + configCopy.VRFv2Plus.General.SubscriptionFundingAmountNative = ptr.Ptr(float64(0)) + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( + env, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") 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) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) - //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, + _, err = consumers[0].RequestRandomness( + vrfKey.KeyHash, subID, *configCopy.VRFv2Plus.General.MinimumConfirmations, *configCopy.VRFv2Plus.General.CallbackGasLimit, @@ -1184,9 +1247,9 @@ func TestVRFV2PlusWithBHS(t *testing.T) { require.NoError(t, err, "error requesting randomness") randomWordsRequestedEvent, err := vrfContracts.CoordinatorV2Plus.WaitForRandomWordsRequestedEvent( - [][32]byte{vrfKeyData.KeyHash}, + [][32]byte{vrfKey.KeyHash}, []*big.Int{subID}, - []common.Address{common.HexToAddress(vrfContracts.VRFV2PlusConsumer[0].Address())}, + []common.Address{common.HexToAddress(consumers[0].Address())}, time.Minute*1, ) require.NoError(t, err, "error waiting for randomness requested event") @@ -1201,8 +1264,8 @@ func TestVRFV2PlusWithBHS(t *testing.T) { err = vrfv2plus.FundSubscriptions( env, big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionFundingAmountNative), - big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionFundingAmountLink), - linkToken, + big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionRefundingAmountLink), + vrfContracts.LinkToken, vrfContracts.CoordinatorV2Plus, subIDs, ) @@ -1214,7 +1277,7 @@ func TestVRFV2PlusWithBHS(t *testing.T) { ) 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) + status, err := consumers[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") @@ -1230,17 +1293,30 @@ func TestVRFV2PlusWithBHS(t *testing.T) { }) t.Run("BHS Job should fill in blockhashes into BHS contract for unfulfilled requests", func(t *testing.T) { - subID := subIDs[1] + configCopy := config.MustCopy().(tc.TestConfig) + //Underfund Subscription + configCopy.VRFv2Plus.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) + configCopy.VRFv2Plus.General.SubscriptionFundingAmountNative = ptr.Ptr(float64(0)) + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( + env, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up new consumers and subs") + 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) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) //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, + _, err = consumers[0].RequestRandomness( + vrfKey.KeyHash, subID, *configCopy.VRFv2Plus.General.MinimumConfirmations, *configCopy.VRFv2Plus.General.CallbackGasLimit, @@ -1251,9 +1327,9 @@ func TestVRFV2PlusWithBHS(t *testing.T) { require.NoError(t, err, "error requesting randomness") randomWordsRequestedEvent, err := vrfContracts.CoordinatorV2Plus.WaitForRandomWordsRequestedEvent( - [][32]byte{vrfKeyData.KeyHash}, + [][32]byte{vrfKey.KeyHash}, []*big.Int{subID}, - []common.Address{common.HexToAddress(vrfContracts.VRFV2PlusConsumer[0].Address())}, + []common.Address{common.HexToAddress(consumers[0].Address())}, time.Minute*1, ) require.NoError(t, err, "error waiting for randomness requested event") @@ -1264,7 +1340,7 @@ func TestVRFV2PlusWithBHS(t *testing.T) { var wg sync.WaitGroup wg.Add(1) - _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(*config.VRFv2Plus.General.BHSJobWaitBlocks+10), env.EVMClient, &wg, time.Minute*1, t) + _, err = actions.WaitForBlockNumberToBe(randRequestBlockNumber+uint64(*configCopy.VRFv2Plus.General.BHSJobWaitBlocks+10), env.EVMClient, &wg, time.Minute*1, t) wg.Wait() require.NoError(t, err, "error waiting for blocknumber to be") @@ -1275,7 +1351,7 @@ func TestVRFV2PlusWithBHS(t *testing.T) { var txHash string gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - clNodeTxs, _, err = nodesMap[vrfcommon.BHS].CLNode.API.ReadTransactions() + clNodeTxs, _, err = nodeTypeToNodeMap[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)) @@ -1311,114 +1387,107 @@ func TestVRFV2PlusWithBHS(t *testing.T) { func TestVRFv2PlusReplayAfterTimeout(t *testing.T) { t.Parallel() + var ( + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []*big.Int + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + nodeTypeToNodeMap map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode + ) l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) - if err != nil { - t.Fatal(err) + require.NoError(t, err, "Error getting config") + vrfv2PlusConfig := config.VRFv2Plus + + cleanupFn := func() { + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2PlusConfig.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2PlusConfig.General.UseExistingEnv { + if err := env.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: 0, + UseVRFOwner: false, + UseTestCoordinator: false, } - - 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") // 1. Add job spec with requestTimeout = 5 seconds - timeout := time.Duration(time.Second * 5) - numberOfTxKeysToCreate := 0 + timeout := time.Second * 5 config.VRFv2Plus.General.VRFJobRequestTimeout = ptr.Ptr(blockchain.StrDuration{Duration: timeout}) config.VRFv2Plus.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) config.VRFv2Plus.General.SubscriptionFundingAmountNative = ptr.Ptr(float64(0)) - vrfv2PlusContracts, subIDs, vrfv2PlusData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( - env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &config, - linkToken, - mockETHLinkFeed, - numberOfTxKeysToCreate, - 2, - 1, - l, - ) - require.NoError(t, err, "error setting up VRF v2_5 env") - - subID := subIDs[0] - - subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) - require.NoError(t, err, "error getting subscription information") - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) + env, vrfContracts, vrfKey, nodeTypeToNodeMap, err = vrfv2plus.SetupVRFV2PlusUniverse(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) + require.NoError(t, err) + defaultWalletAddress = env.EVMClient.GetDefaultWallet().Address() t.Run("Timed out request fulfilled after node restart with replay", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) var isNativeBilling = false - // 2. create request but without fulfilment - e.g. simulation failure (insufficient balance in the sub, ) - vrfv2plus.LogRandRequest( + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( + env, + vrfContracts.CoordinatorV2Plus, + configCopy, + vrfContracts.LinkToken, + 2, + 1, l, - vrfv2PlusContracts.VRFV2PlusConsumer[0].Address(), - vrfv2PlusContracts.CoordinatorV2Plus.Address(), - subID, - isNativeBilling, - vrfv2PlusData.KeyHash, - configCopy.VRFv2Plus.General, ) - _, err = vrfv2PlusContracts.VRFV2PlusConsumer[0].RequestRandomness( - vrfv2PlusData.KeyHash, + require.NoError(t, err, "error setting up new consumers and subs") + 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) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) + + // 2. create request but without fulfilment - e.g. simulation failure (insufficient balance in the sub, ) + initialReqRandomWordsRequestedEvent, err := vrfv2plus.RequestRandomnessAndWaitForRequestedEvent( + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, 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") - initialReqRandomWordsRequestedEvent, err := vrfv2PlusContracts.CoordinatorV2Plus.WaitForRandomWordsRequestedEvent( - [][32]byte{vrfv2PlusData.KeyHash}, - []*big.Int{subID}, - []common.Address{common.HexToAddress(vrfv2PlusContracts.VRFV2PlusConsumer[0].Address())}, - time.Minute*1, + configCopy.VRFv2Plus.General, + l, ) - require.NoError(t, err, "error waiting for initial request RandomWordsRequestedEvent") + require.NoError(t, err, "error requesting randomness and waiting for requested event") // 3. create new request in a subscription with balance and wait for fulfilment - // TODO: We need this to be parametrized, since these tests will be run on live testnets as well. - fundingLinkAmt := big.NewFloat(5) - fundingNativeAmt := big.NewFloat(0.1) + fundingLinkAmt := big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionRefundingAmountLink) + fundingNativeAmt := big.NewFloat(*configCopy.VRFv2Plus.General.SubscriptionRefundingAmountNative) l.Info(). - Str("Coordinator", vrfv2PlusContracts.CoordinatorV2Plus.Address()). + Str("Coordinator", vrfContracts.CoordinatorV2Plus.Address()). Int("Number of Subs to create", 1). Msg("Creating and funding subscriptions, adding consumers") fundedSubIDs, err := vrfv2plus.CreateFundSubsAndAddConsumers( env, fundingLinkAmt, fundingNativeAmt, - linkToken, - vrfv2PlusContracts.CoordinatorV2Plus, - []contracts.VRFv2PlusLoadTestConsumer{vrfv2PlusContracts.VRFV2PlusConsumer[1]}, + vrfContracts.LinkToken, + vrfContracts.CoordinatorV2Plus, + []contracts.VRFv2PlusLoadTestConsumer{consumers[1]}, 1, ) require.NoError(t, err, "error creating funded sub in replay test") randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( - vrfv2PlusContracts.VRFV2PlusConsumer[1], - vrfv2PlusContracts.CoordinatorV2Plus, - vrfv2PlusData, + consumers[1], + vrfContracts.CoordinatorV2Plus, + vrfKey, fundedSubIDs[0], isNativeBilling, configCopy.VRFv2Plus.General, @@ -1435,44 +1504,45 @@ func TestVRFv2PlusReplayAfterTimeout(t *testing.T) { env, fundingLinkAmt, fundingNativeAmt, - linkToken, - vrfv2PlusContracts.CoordinatorV2Plus, + vrfContracts.LinkToken, + vrfContracts.CoordinatorV2Plus, []*big.Int{subID}, ) require.NoError(t, err, "error funding subs after request timeout") // 6. no fulfilment should happen since timeout+1 seconds passed in the job - pendingReqExists, err := vrfv2PlusContracts.CoordinatorV2Plus.PendingRequestsExist(testcontext.Get(t), subID) + pendingReqExists, err := vrfContracts.CoordinatorV2Plus.PendingRequestsExist(testcontext.Get(t), subID) require.NoError(t, err, "error fetching PendingRequestsExist from coordinator") require.True(t, pendingReqExists, "pendingRequest must exist since subID was underfunded till request timeout") // 7. remove job and add new job with requestTimeout = 1 hour - vrfNode, exists := nodesMap[vrfcommon.VRF] + vrfNode, exists := nodeTypeToNodeMap[vrfcommon.VRF] require.True(t, exists, "VRF Node does not exist") resp, err := vrfNode.CLNode.API.DeleteJob(vrfNode.Job.Data.ID) require.NoError(t, err, "error deleting job after timeout") require.Equal(t, resp.StatusCode, 204) chainID := env.EVMClient.GetChainID() - config.VRFv2Plus.General.VRFJobRequestTimeout = ptr.Ptr(blockchain.StrDuration{Duration: time.Duration(time.Hour * 1)}) + configCopy.VRFv2Plus.General.VRFJobRequestTimeout = ptr.Ptr(blockchain.StrDuration{Duration: time.Duration(time.Hour * 1)}) vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ - ForwardingAllowed: *config.VRFv2Plus.General.VRFJobForwardingAllowed, - CoordinatorAddress: vrfv2PlusContracts.CoordinatorV2Plus.Address(), + ForwardingAllowed: *configCopy.VRFv2Plus.General.VRFJobForwardingAllowed, + CoordinatorAddress: vrfContracts.CoordinatorV2Plus.Address(), FromAddresses: vrfNode.TXKeyAddressStrings, EVMChainID: chainID.String(), - MinIncomingConfirmations: int(*config.VRFv2Plus.General.MinimumConfirmations), - PublicKey: vrfv2PlusData.PubKeyCompressed, - EstimateGasMultiplier: *config.VRFv2Plus.General.VRFJobEstimateGasMultiplier, - BatchFulfillmentEnabled: *config.VRFv2Plus.General.VRFJobBatchFulfillmentEnabled, - BatchFulfillmentGasMultiplier: *config.VRFv2Plus.General.VRFJobBatchFulfillmentGasMultiplier, - PollPeriod: config.VRFv2Plus.General.VRFJobPollPeriod.Duration, - RequestTimeout: config.VRFv2Plus.General.VRFJobRequestTimeout.Duration, - SimulationBlock: config.VRFv2Plus.General.VRFJobSimulationBlock, + MinIncomingConfirmations: int(*configCopy.VRFv2Plus.General.MinimumConfirmations), + PublicKey: vrfKey.PubKeyCompressed, + EstimateGasMultiplier: *configCopy.VRFv2Plus.General.VRFJobEstimateGasMultiplier, + BatchFulfillmentEnabled: *configCopy.VRFv2Plus.General.VRFJobBatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *configCopy.VRFv2Plus.General.VRFJobBatchFulfillmentGasMultiplier, + PollPeriod: configCopy.VRFv2Plus.General.VRFJobPollPeriod.Duration, + RequestTimeout: configCopy.VRFv2Plus.General.VRFJobRequestTimeout.Duration, + SimulationBlock: configCopy.VRFv2Plus.General.VRFJobSimulationBlock, VRFOwnerConfig: nil, } go func() { - l.Info().Msg("Creating VRFV2 Plus Job with higher timeout (1hr)") + l.Info(). + Msg("Creating VRFV2 Plus Job with higher timeout (1hr)") job, err := vrfv2plus.CreateVRFV2PlusJob( vrfNode.CLNode.API, vrfJobSpecConfig, @@ -1485,7 +1555,7 @@ func TestVRFv2PlusReplayAfterTimeout(t *testing.T) { l.Info().Str("reqID", initialReqRandomWordsRequestedEvent.RequestId.String()). Str("subID", subID.String()). Msg("Waiting for initalReqRandomWordsFulfilledEvent") - initalReqRandomWordsFulfilledEvent, err := vrfv2PlusContracts.CoordinatorV2Plus.WaitForRandomWordsFulfilledEvent( + initalReqRandomWordsFulfilledEvent, err := vrfContracts.CoordinatorV2Plus.WaitForRandomWordsFulfilledEvent( []*big.Int{subID}, []*big.Int{initialReqRandomWordsRequestedEvent.RequestId}, configCopy.VRFv2Plus.General.RandomWordsFulfilledEventTimeout.Duration, @@ -1498,7 +1568,7 @@ func TestVRFv2PlusReplayAfterTimeout(t *testing.T) { require.True(t, initalReqRandomWordsFulfilledEvent.Success, "RandomWordsFulfilled Event's `Success` field should be true") // Get request status - status, err := vrfv2PlusContracts.VRFV2PlusConsumer[0].GetRequestStatus(testcontext.Get(t), initalReqRandomWordsFulfilledEvent.RequestId) + status, err := consumers[0].GetRequestStatus(testcontext.Get(t), initalReqRandomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Info().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -1507,71 +1577,76 @@ func TestVRFv2PlusReplayAfterTimeout(t *testing.T) { func TestVRFv2PlusPendingBlockSimulationAndZeroConfirmationDelays(t *testing.T) { t.Parallel() + var ( + env *test_env.CLClusterTestEnv + vrfContracts *vrfcommon.VRFContracts + subIDsForCancellingAfterTest []*big.Int + defaultWalletAddress string + vrfKey *vrfcommon.VRFKeyData + ) l := logging.GetTestLogger(t) config, err := tc.GetConfig("Smoke", tc.VRFv2Plus) - if err != nil { - t.Fatal(err) + require.NoError(t, err, "Error getting config") + vrfv2PlusConfig := config.VRFv2Plus + + cleanupFn := func() { + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + if *vrfv2PlusConfig.General.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, defaultWalletAddress, subIDsForCancellingAfterTest, l) + } + } + if !*vrfv2PlusConfig.General.UseExistingEnv { + if err := env.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + } + } + newEnvConfig := vrfcommon.NewEnvConfig{ + NodesToCreate: []vrfcommon.VRFNodeType{vrfcommon.VRF}, + NumberOfTxKeysToCreate: 0, + UseVRFOwner: false, + UseTestCoordinator: false, } // 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") + env, vrfContracts, vrfKey, _, err = vrfv2plus.SetupVRFV2PlusUniverse(testcontext.Get(t), t, config, cleanupFn, newEnvConfig, l) + require.NoError(t, err) + defaultWalletAddress = env.EVMClient.GetDefaultWallet().Address() - numberOfTxKeysToCreate := 2 - vrfv2PlusContracts, subIDs, vrfv2PlusData, nodesMap, err := vrfv2plus.SetupVRFV2_5Environment( + consumers, subIDs, err := vrfv2plus.SetupNewConsumersAndSubs( env, - []vrfcommon.VRFNodeType{vrfcommon.VRF}, - &config, - linkToken, - mockETHLinkFeed, - numberOfTxKeysToCreate, + vrfContracts.CoordinatorV2Plus, + config, + vrfContracts.LinkToken, 1, 1, l, ) - require.NoError(t, err, "error setting up VRF v2_5 env") - + require.NoError(t, err, "error setting up new consumers and subs") subID := subIDs[0] - - subscription, err := vrfv2PlusContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) + subscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") - - vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.CoordinatorV2Plus) + vrfv2plus.LogSubDetails(l, subscription, subID, vrfContracts.CoordinatorV2Plus) + subIDsForCancellingAfterTest = append(subIDsForCancellingAfterTest, subIDs...) 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, + consumers[0], + vrfContracts.CoordinatorV2Plus, + vrfKey, subID, isNativeBilling, config.VRFv2Plus.General, @@ -1579,12 +1654,8 @@ func TestVRFv2PlusPendingBlockSimulationAndZeroConfirmationDelays(t *testing.T) ) 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) + status, err := consumers[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") + l.Info().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") } diff --git a/integration-tests/testconfig/common/vrf/common.go b/integration-tests/testconfig/common/vrf/common.go index ca6f44f27c0..9b614b33593 100644 --- a/integration-tests/testconfig/common/vrf/common.go +++ b/integration-tests/testconfig/common/vrf/common.go @@ -40,6 +40,10 @@ type PerformanceConfig struct { TestDuration *blockchain.StrDuration `toml:"test_duration"` RPS *int64 `toml:"rps"` RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` + + BHSTestDuration *blockchain.StrDuration `toml:"bhs_test_duration"` + BHSTestRPS *int64 `toml:"bhs_test_rps"` + BHSTestRateLimitUnitDuration *blockchain.StrDuration `toml:"bhs_test_rate_limit_unit_duration"` } func (c *PerformanceConfig) Validate() error { @@ -52,6 +56,15 @@ func (c *PerformanceConfig) Validate() error { if c.RateLimitUnitDuration == nil { return errors.New("rate_limit_unit_duration must be set ") } + if c.BHSTestDuration == nil || c.BHSTestDuration.Duration == 0 { + return errors.New("bhs_test_duration must be set to a positive value") + } + if c.BHSTestRPS == nil || *c.BHSTestRPS == 0 { + return errors.New("bhs_test_rps must be set to a positive value") + } + if c.BHSTestRateLimitUnitDuration == nil { + return errors.New("bhs_test_rate_limit_unit_duration must be set ") + } return nil } @@ -119,25 +132,28 @@ func (c *Funding) Validate() error { } 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 + 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 + SubscriptionRefundingAmountLink *float64 `toml:"subscription_refunding_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 + NumberOfSendingKeysToCreate *int `toml:"number_of_sending_keys_to_create"` // Number of sending keys 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 + WaitFor256BlocksTimeout *blockchain.StrDuration `toml:"wait_for_256_blocks_timeout"` // How long to wait for 256 blocks to be mined // Wrapper Config WrapperGasOverhead *uint32 `toml:"wrapped_gas_overhead"` @@ -168,7 +184,7 @@ func (c *General) Validate() error { 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") + return errors.New("cl_node_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") @@ -179,6 +195,9 @@ func (c *General) Validate() error { if c.SubscriptionFundingAmountLink == nil || *c.SubscriptionFundingAmountLink < 0 { return errors.New("subscription_funding_amount_link must be set to non-negative value") } + if c.SubscriptionRefundingAmountLink == nil || *c.SubscriptionRefundingAmountLink < 0 { + return errors.New("subscription_refunding_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") } @@ -200,6 +219,11 @@ func (c *General) Validate() error { if c.NumberOfSubToCreate == nil || *c.NumberOfSubToCreate == 0 { return errors.New("number_of_sub_to_create must be set to a positive value") } + + if c.NumberOfSendingKeysToCreate == nil || *c.NumberOfSendingKeysToCreate < 0 { + return errors.New("number_of_sending_keys_to_create must be set to 0 or 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") } @@ -209,6 +233,9 @@ func (c *General) Validate() error { 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.WaitFor256BlocksTimeout == nil || c.WaitFor256BlocksTimeout.Duration == 0 { + return errors.New("wait_for_256_blocks_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") } diff --git a/integration-tests/testconfig/vrfv2/vrfv2.toml b/integration-tests/testconfig/vrfv2/vrfv2.toml index 3ce3135b3aa..6d92e2fd6b0 100644 --- a/integration-tests/testconfig/vrfv2/vrfv2.toml +++ b/integration-tests/testconfig/vrfv2/vrfv2.toml @@ -7,6 +7,7 @@ chainlink_node_funding = 0.1 cancel_subs_after_test_run = true use_existing_env = false subscription_funding_amount_link = 5.0 +subscription_refunding_amount_link = 5.0 cl_node_max_gas_price_gwei = 10 link_native_feed_response = 1000000000000000000 @@ -28,9 +29,11 @@ reqs_for_tier_3 = 0 reqs_for_tier_4 = 0 reqs_for_tier_5 = 0 number_of_sub_to_create = 1 +number_of_sending_keys_to_create = 0 randomness_request_count_per_request = 1 randomness_request_count_per_request_deviation = 0 random_words_fulfilled_event_timeout = "2m" +wait_for_256_blocks_timeout = "10m" wrapped_gas_overhead = 50000 coordinator_gas_overhead = 52000 wrapper_premium_percentage = 25 @@ -52,7 +55,6 @@ bhs_job_lookback_blocks = 250 bhs_job_poll_period = "1s" bhs_job_run_timeout = "24h" - # PERFORMANCE test specific config [VRFv2.ExistingEnv] @@ -69,6 +71,22 @@ node_sending_keys = [ "" ] +[VRFv2.Performance] +test_duration = "10s" +rate_limit_unit_duration = "3s" +rps = 1 +bhs_test_duration = "10s" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 + +[Smoke.VRFv2.Performance] +test_duration = "10s" +rate_limit_unit_duration = "3s" +rps = 1 +bhs_test_duration = "10s" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 + #SOAK TEST CONFIG [Soak.Common] chainlink_node_funding = 0.1 @@ -77,12 +95,16 @@ chainlink_node_funding = 0.1 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 +number_of_sending_keys_to_create = 0 subscription_funding_amount_link = 5.0 [Soak.VRFv2.Performance] test_duration = "1m" rate_limit_unit_duration = "3s" rps = 1 +bhs_test_duration = "1m" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 # LOAD TEST CONFIG [Load.Common] @@ -92,13 +114,16 @@ chainlink_node_funding = 0.1 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 +number_of_sending_keys_to_create = 0 subscription_funding_amount_link = 5.0 [Load.VRFv2.Performance] test_duration = "2m" rate_limit_unit_duration = "3s" rps = 1 - +bhs_test_duration = "1m" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 # STRESS TEST CONFIG [Stress.Common] @@ -108,9 +133,13 @@ chainlink_node_funding = 0.1 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 +number_of_sending_keys_to_create = 0 subscription_funding_amount_link = 5.0 [Stress.VRFv2.Performance] test_duration = "2m" rate_limit_unit_duration = "3s" rps = 1 +bhs_test_duration = "1m" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 diff --git a/integration-tests/testconfig/vrfv2plus/config.go b/integration-tests/testconfig/vrfv2plus/config.go index fe05dbd9d18..b87bef6a836 100644 --- a/integration-tests/testconfig/vrfv2plus/config.go +++ b/integration-tests/testconfig/vrfv2plus/config.go @@ -43,6 +43,7 @@ type General struct { *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 + SubscriptionRefundingAmountNative *float64 `toml:"subscription_refunding_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 @@ -60,6 +61,9 @@ 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.SubscriptionRefundingAmountNative == nil || *c.SubscriptionRefundingAmountNative <= 0 { + return errors.New("subscription_refunding_amount_native must be greater than 0") + } if c.FulfillmentFlatFeeNativePPM == nil { return errors.New("fulfillment_flat_fee_native_ppm must not be nil") } diff --git a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml index 96b1a3f7224..5acf9c1839f 100644 --- a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml +++ b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml @@ -9,6 +9,9 @@ use_existing_env = false subscription_funding_amount_link = 5.0 subscription_funding_amount_native=1 +subscription_refunding_amount_link = 5.0 +subscription_refunding_amount_native = 1 + cl_node_max_gas_price_gwei = 10 link_native_feed_response = 1000000000000000000 minimum_confirmations = 3 @@ -21,9 +24,11 @@ fallback_wei_per_unit_link = 60000000000000000 staleness_seconds = 86400 gas_after_payment_calculation = 33825 number_of_sub_to_create = 1 +number_of_sending_keys_to_create = 0 randomness_request_count_per_request = 1 randomness_request_count_per_request_deviation = 0 random_words_fulfilled_event_timeout = "2m" +wait_for_256_blocks_timeout = "10m" wrapped_gas_overhead = 50000 coordinator_gas_overhead = 52000 wrapper_premium_percentage = 25 @@ -62,6 +67,22 @@ link_address = "" node_sending_key_funding_min = 1 node_sending_keys = [] +[VRFv2Plus.Performance] +test_duration = "10s" +rate_limit_unit_duration = "3s" +rps = 1 +bhs_test_duration = "10s" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 + +[Smoke.VRFv2.Performance] +test_duration = "10s" +rate_limit_unit_duration = "3s" +rps = 1 +bhs_test_duration = "10s" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 + #SOAK TEST CONFIG [Soak.Common] chainlink_node_funding = 0.1 @@ -70,6 +91,7 @@ chainlink_node_funding = 0.1 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 +number_of_sending_keys_to_create = 0 subscription_funding_amount_link = 5.0 subscription_funding_amount_native=1 @@ -77,6 +99,9 @@ subscription_funding_amount_native=1 test_duration = "2m" rate_limit_unit_duration = "3s" rps = 1 +bhs_test_duration = "1m" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 # LOAD TEST CONFIG [Load.Common] @@ -86,6 +111,7 @@ chainlink_node_funding = 0.1 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 +number_of_sending_keys_to_create = 0 subscription_funding_amount_link = 5.0 subscription_funding_amount_native=1 @@ -93,7 +119,9 @@ subscription_funding_amount_native=1 test_duration = "2m" rate_limit_unit_duration = "3s" rps = 1 - +bhs_test_duration = "1m" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 # STRESS TEST CONFIG [Stress.Common] @@ -103,6 +131,7 @@ chainlink_node_funding = 0.1 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 +number_of_sending_keys_to_create = 0 subscription_funding_amount_link = 5.0 subscription_funding_amount_native=1 @@ -110,3 +139,7 @@ subscription_funding_amount_native=1 test_duration = "2m" rate_limit_unit_duration = "3s" rps = 1 +bhs_test_duration = "1m" +bhs_test_rate_limit_unit_duration = "3s" +bhs_test_rps = 1 +