diff --git a/core/scripts/common/vrf/constants/constants.go b/core/scripts/common/vrf/constants/constants.go index b21f6b0b323..a5ffe85996b 100644 --- a/core/scripts/common/vrf/constants/constants.go +++ b/core/scripts/common/vrf/constants/constants.go @@ -31,4 +31,8 @@ var ( //vrfv2plus FlatFeeLinkPPM = int64(500) FlatFeeNativePPM = int64(500) + + //consumer + CallBackGasLimit = uint(1_000_000) + IsNativePayment = false ) diff --git a/core/scripts/common/vrf/setup-envs/main.go b/core/scripts/common/vrf/setup-envs/main.go index 8a7b1c8b439..11a8e50652c 100644 --- a/core/scripts/common/vrf/setup-envs/main.go +++ b/core/scripts/common/vrf/setup-envs/main.go @@ -21,6 +21,8 @@ import ( "math/big" "os" "strings" + "sync" + "time" ) func newApp(remoteNodeURL string, writer io.Writer) (*clcmd.Shell, *cli.App) { @@ -171,7 +173,7 @@ func main() { } var jobSpecs model.JobSpecs - + var randRequestConfig v2plusscripts.RandRequestConfig switch *vrfVersion { case "v2": feeConfigV2 := vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ @@ -218,7 +220,7 @@ func main() { FeeConfig: feeConfigV2Plus, } - jobSpecs = v2plusscripts.VRFV2PlusDeployUniverse( + jobSpecs, randRequestConfig = v2plusscripts.VRFV2PlusDeployUniverse( e, subscriptionBalanceJuels, subscriptionBalanceNativeWei, @@ -230,34 +232,76 @@ func main() { ) } - for key, node := range nodesMap { - client, app := connectToNode(&node.URL, output, node.CredsFile) + createJobs(nodesMap, output, jobSpecs) + if *vrfVersion == "v2plus" { + verifyRandomnessRequestFulfills(e, randRequestConfig) + } + } +} - //GET ALL JOBS - jobIDs := getAllJobIDs(client, app, output) +func createJobs(nodesMap map[string]model.Node, output *bytes.Buffer, jobSpecs model.JobSpecs) { + for key, node := range nodesMap { + client, app := connectToNode(&node.URL, output, node.CredsFile) - //DELETE ALL EXISTING JOBS - for _, jobID := range jobIDs { - deleteJob(jobID, client, app, output) - } - //CREATE JOBS - - switch key { - case model.VRFPrimaryNodeName: - createJob(jobSpecs.VRFPrimaryNode, client, app, output) - case model.VRFBackupNodeName: - createJob(jobSpecs.VRFBackupyNode, client, app, output) - case model.BHSNodeName: - createJob(jobSpecs.BHSNode, client, app, output) - case model.BHSBackupNodeName: - createJob(jobSpecs.BHSBackupNode, client, app, output) - case model.BHFNodeName: - createJob(jobSpecs.BHFNode, client, app, output) - } + //GET ALL JOBS + jobIDs := getAllJobIDs(client, app, output) + + //DELETE ALL EXISTING JOBS + for _, jobID := range jobIDs { + deleteJob(jobID, client, app, output) + } + //CREATE JOBS + + switch key { + case model.VRFPrimaryNodeName: + createJob(jobSpecs.VRFPrimaryNode, client, app, output) + case model.VRFBackupNodeName: + createJob(jobSpecs.VRFBackupyNode, client, app, output) + case model.BHSNodeName: + createJob(jobSpecs.BHSNode, client, app, output) + case model.BHSBackupNodeName: + createJob(jobSpecs.BHSBackupNode, client, app, output) + case model.BHFNodeName: + createJob(jobSpecs.BHFNode, client, app, output) } } } +func verifyRandomnessRequestFulfills(e helpers.Environment, randRequestConfig v2plusscripts.RandRequestConfig) { + + time.Sleep(10 * time.Second) + v2plusscripts.LoadTestRequestRandomness( + e, + randRequestConfig.ConsumerAddress, + randRequestConfig.SubID, + randRequestConfig.MinConfs, + randRequestConfig.KeyHash, + constants.CallBackGasLimit, + false, + 1, + 1, + 1, + ) + // + //v2plusscripts.LoadTestRequestRandomness( + // e, + // randRequestConfig.ConsumerAddress, + // randRequestConfig.SubID, + // randRequestConfig.MinConfs, + // randRequestConfig.KeyHash, + // constants.CallBackGasLimit, + // false, + // 1, + // 1, + // 1, + //) + var wg sync.WaitGroup + wg.Add(1) + _, _, err := v2plusscripts.WaitForRequestCountEqualToFulfilmentCount(e, randRequestConfig.ConsumerAddress, 30*time.Second, &wg) + helpers.PanicErr(err) + wg.Wait() +} + func fundNodesIfNeeded(node model.Node, key string, e helpers.Environment) { if node.SendingKeyFundingAmount.Cmp(big.NewInt(0)) == 1 { fmt.Println("\nFunding", key, "Node's Sending Keys. Need to fund each key with", node.SendingKeyFundingAmount, "wei") diff --git a/core/scripts/vrfv2plus/testnet/main.go b/core/scripts/vrfv2plus/testnet/main.go index 0d1bf9a9481..bf1b53ef0cd 100644 --- a/core/scripts/vrfv2plus/testnet/main.go +++ b/core/scripts/vrfv2plus/testnet/main.go @@ -869,54 +869,23 @@ func main() { runs := request.Uint("runs", 1, "number of runs to do. total randomness requests will be (requests * runs).") helpers.ParseArgs(request, os.Args[2:], "consumer-address", "sub-id", "key-hash") keyHashBytes := common.HexToHash(*keyHash) - consumer, err := vrf_v2plus_load_test_with_metrics.NewVRFV2PlusLoadTestWithMetrics( + v2plusscripts.LoadTestRequestRandomness( + e, common.HexToAddress(*consumerAddress), - e.Ec) - helpers.PanicErr(err) - var txes []*types.Transaction - for i := 0; i < int(*runs); i++ { - tx, err := consumer.RequestRandomWords( - e.Owner, - decimal.RequireFromString(*subID).BigInt(), - uint16(*requestConfirmations), - keyHashBytes, - uint32(*cbGasLimit), - *nativePaymentEnabled, - uint32(*numWords), - uint16(*requests), - ) - helpers.PanicErr(err) - fmt.Printf("TX %d: %s\n", i+1, helpers.ExplorerLink(e.ChainID, tx.Hash())) - txes = append(txes, tx) - } - fmt.Println("Total number of requests sent:", (*requests)*(*runs)) - fmt.Println("fetching receipts for all transactions") - for i, tx := range txes { - helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, fmt.Sprintf("load test %d", i+1)) - } + decimal.RequireFromString(*subID).BigInt(), + *requestConfirmations, + keyHashBytes, + *cbGasLimit, + *nativePaymentEnabled, + *numWords, + *requests, + *runs, + ) case "eoa-load-test-read-metrics": request := flag.NewFlagSet("eoa-load-test-read-metrics", flag.ExitOnError) consumerAddress := request.String("consumer-address", "", "consumer address") helpers.ParseArgs(request, os.Args[2:], "consumer-address") - consumer, err := vrf_v2plus_load_test_with_metrics.NewVRFV2PlusLoadTestWithMetrics( - common.HexToAddress(*consumerAddress), - e.Ec) - helpers.PanicErr(err) - responseCount, err := consumer.SResponseCount(nil) - helpers.PanicErr(err) - fmt.Println("Response Count: ", responseCount) - requestCount, err := consumer.SRequestCount(nil) - helpers.PanicErr(err) - fmt.Println("Request Count: ", requestCount) - averageFulfillmentInMillions, err := consumer.SAverageFulfillmentInMillions(nil) - helpers.PanicErr(err) - fmt.Println("Average Fulfillment In Millions: ", averageFulfillmentInMillions) - slowestFulfillment, err := consumer.SSlowestFulfillment(nil) - helpers.PanicErr(err) - fmt.Println("Slowest Fulfillment: ", slowestFulfillment) - fastestFulfillment, err := consumer.SFastestFulfillment(nil) - helpers.PanicErr(err) - fmt.Println("Fastest Fulfillment: ", fastestFulfillment) + v2plusscripts.GetLoadTestMetricsFromConsumer(common.HexToAddress(*consumerAddress), e) case "eoa-load-test-reset-metrics": request := flag.NewFlagSet("eoa-load-test-reset-metrics", flag.ExitOnError) consumerAddress := request.String("consumer-address", "", "consumer address") diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go index 4e93d20f6d6..344d1396244 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go @@ -6,6 +6,12 @@ import ( "encoding/hex" "flag" "fmt" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/shopspring/decimal" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/constants" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/jobs" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/model" @@ -16,13 +22,6 @@ import ( "os" "strings" - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/shopspring/decimal" - helpers "github.com/smartcontractkit/chainlink/core/scripts/common" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" @@ -46,6 +45,13 @@ type CoordinatorConfigV2Plus struct { FeeConfig vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig } +type RandRequestConfig struct { + ConsumerAddress common.Address + SubID *big.Int + KeyHash common.Hash + MinConfs uint +} + func SmokeTestVRF(e helpers.Environment) { smokeCmd := flag.NewFlagSet("smoke", flag.ExitOnError) @@ -554,7 +560,8 @@ func DeployUniverseViaCLI(e helpers.Environment) { } } -func VRFV2PlusDeployUniverse(e helpers.Environment, +func VRFV2PlusDeployUniverse( + e helpers.Environment, subscriptionBalanceJuels *big.Int, subscriptionBalanceNativeWei *big.Int, registerKeyUncompressedPubKey *string, @@ -562,7 +569,7 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, coordinatorConfig CoordinatorConfigV2Plus, batchFulfillmentEnabled bool, nodesMap map[string]model.Node, -) model.JobSpecs { +) (model.JobSpecs, RandRequestConfig) { // Put key in ECDSA format if strings.HasPrefix(*registerKeyUncompressedPubKey, "0x") { *registerKeyUncompressedPubKey = strings.Replace(*registerKeyUncompressedPubKey, "0x", "04", 1) @@ -758,12 +765,18 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, ) return model.JobSpecs{ - VRFPrimaryNode: formattedVrfV2PlusPrimaryJobSpec, - VRFBackupyNode: formattedVrfV2PlusBackupJobSpec, - BHSNode: formattedBHSJobSpec, - BHSBackupNode: formattedBHSBackupJobSpec, - BHFNode: formattedBHFJobSpec, - } + VRFPrimaryNode: formattedVrfV2PlusPrimaryJobSpec, + VRFBackupyNode: formattedVrfV2PlusBackupJobSpec, + BHSNode: formattedBHSJobSpec, + BHSBackupNode: formattedBHSBackupJobSpec, + BHFNode: formattedBHFJobSpec, + }, + RandRequestConfig{ + ConsumerAddress: consumerAddress, + SubID: subID, + KeyHash: keyHash, + MinConfs: uint(*coordinatorConfig.MinConfs), + } } func DeployWrapperUniverse(e helpers.Environment) { diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go index ebe881a9951..aeddab071ad 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/util.go @@ -4,8 +4,11 @@ import ( "context" "encoding/hex" "fmt" + "github.com/ethereum/go-ethereum/core/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "math/big" + "sync" + "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -23,6 +26,14 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +type VRFLoadTestMetrics struct { + RequestCount *big.Int + FulfilmentCount *big.Int + AverageFulfillmentInMillions *big.Int + SlowestFulfillment *big.Int + FastestFulfillment *big.Int +} + func DeployBHS(e helpers.Environment) (blockhashStoreAddress common.Address) { _, tx, _, err := blockhash_store.DeployBlockhashStore(e.Owner, e.Ec) helpers.PanicErr(err) @@ -271,3 +282,127 @@ func EoaV2PlusLoadTestConsumerWithMetricsDeploy(e helpers.Environment, consumerC helpers.PanicErr(err) return helpers.ConfirmContractDeployed(context.Background(), e.Ec, tx, e.ChainID) } + +func LoadTestRequestRandomness( + e helpers.Environment, + consumerAddress common.Address, + subID *big.Int, + requestConfirmations uint, + keyHashBytes common.Hash, + cbGasLimit uint, + nativePaymentEnabled bool, + numWords uint, + requests uint, + runs uint, +) { + consumer, err := vrf_v2plus_load_test_with_metrics.NewVRFV2PlusLoadTestWithMetrics( + consumerAddress, + e.Ec) + helpers.PanicErr(err) + var txes []*types.Transaction + for i := 0; i < int(runs); i++ { + + //todo - debug + fmt.Println("consumer", consumer.Address().String()) + fmt.Println("e.Owner", e.Owner) + fmt.Println("subID", subID) + fmt.Println("requestConfirmations", requestConfirmations) + fmt.Println("keyHashBytes", keyHashBytes) + fmt.Println("cbGasLimit", cbGasLimit) + fmt.Println("nativePaymentEnabled", nativePaymentEnabled) + fmt.Println("numWords", numWords) + fmt.Println("requests", requests) + + //todo - fails with "execution reverted" when calling this function from setup-env script, + //even though when I run the same command in the console it works (go run . eoa-load-test-request-with-metrics ) + tx, err := consumer.RequestRandomWords( + e.Owner, + subID, + uint16(requestConfirmations), + keyHashBytes, + uint32(cbGasLimit), + nativePaymentEnabled, + uint32(numWords), + uint16(requests), + ) + fmt.Println("tx", tx) + helpers.PanicErr(err) + fmt.Printf("TX %d: %s\n", i+1, helpers.ExplorerLink(e.ChainID, tx.Hash())) + txes = append(txes, tx) + } + fmt.Println("Total number of requests sent:", (requests)*(runs)) + fmt.Println("fetching receipts for all transactions") + for i, tx := range txes { + helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, fmt.Sprintf("load test %d", i+1)) + } +} + +func WaitForRequestCountEqualToFulfilmentCount(e helpers.Environment, consumerAddress common.Address, timeout time.Duration, wg *sync.WaitGroup) (*big.Int, *big.Int, error) { + metricsChannel := make(chan *VRFLoadTestMetrics) + metricsErrorChannel := make(chan error) + + testContext, testCancel := context.WithTimeout(context.Background(), timeout) + defer testCancel() + + ticker := time.NewTicker(time.Second * 1) + var metrics *VRFLoadTestMetrics + for { + select { + case <-testContext.Done(): + ticker.Stop() + wg.Done() + return metrics.RequestCount, metrics.FulfilmentCount, + fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", + metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) + case <-ticker.C: + go getLoadTestMetrics(e, consumerAddress, metricsChannel) + case metrics = <-metricsChannel: + if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { + wg.Done() + return metrics.RequestCount, metrics.FulfilmentCount, nil + } + case err := <-metricsErrorChannel: + wg.Done() + return nil, nil, err + } + } +} + +func getLoadTestMetrics( + e helpers.Environment, + consumerAddress common.Address, + metricsChannel chan *VRFLoadTestMetrics, +) { + metrics := GetLoadTestMetricsFromConsumer(consumerAddress, e) + metricsChannel <- metrics +} + +func GetLoadTestMetricsFromConsumer(consumerAddress common.Address, e helpers.Environment) *VRFLoadTestMetrics { + consumer, err := vrf_v2plus_load_test_with_metrics.NewVRFV2PlusLoadTestWithMetrics( + consumerAddress, + e.Ec) + helpers.PanicErr(err) + responseCount, err := consumer.SResponseCount(nil) + helpers.PanicErr(err) + fmt.Println("Response Count: ", responseCount) + requestCount, err := consumer.SRequestCount(nil) + helpers.PanicErr(err) + fmt.Println("Request Count: ", requestCount) + averageFulfillmentInMillions, err := consumer.SAverageFulfillmentInMillions(nil) + helpers.PanicErr(err) + fmt.Println("Average Fulfillment In Millions: ", averageFulfillmentInMillions) + slowestFulfillment, err := consumer.SSlowestFulfillment(nil) + helpers.PanicErr(err) + fmt.Println("Slowest Fulfillment: ", slowestFulfillment) + fastestFulfillment, err := consumer.SFastestFulfillment(nil) + helpers.PanicErr(err) + fmt.Println("Fastest Fulfillment: ", fastestFulfillment) + + return &VRFLoadTestMetrics{ + requestCount, + responseCount, + averageFulfillmentInMillions, + slowestFulfillment, + fastestFulfillment, + } +} diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index 0e68f785237..9f1ac11dd81 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math/big" + "sync" "time" "github.com/ethereum/go-ethereum/common" @@ -671,3 +672,46 @@ func WaitForRequestAndFulfillmentEvents( Msg("RandomWordsFulfilled Event (TX metadata)") return randomWordsFulfilledEvent, err } + +func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2PlusLoadTestConsumer, timeout time.Duration, wg *sync.WaitGroup) (*big.Int, *big.Int, error) { + metricsChannel := make(chan *contracts.VRFLoadTestMetrics) + metricsErrorChannel := make(chan error) + + testContext, testCancel := context.WithTimeout(context.Background(), timeout) + defer testCancel() + + ticker := time.NewTicker(time.Second * 1) + var metrics *contracts.VRFLoadTestMetrics + for { + select { + case <-testContext.Done(): + ticker.Stop() + wg.Done() + return metrics.RequestCount, metrics.FulfilmentCount, + fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", + metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) + case <-ticker.C: + go getLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) + case metrics = <-metricsChannel: + if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { + wg.Done() + return metrics.RequestCount, metrics.FulfilmentCount, nil + } + case err := <-metricsErrorChannel: + wg.Done() + return nil, nil, err + } + } +} + +func getLoadTestMetrics( + consumer contracts.VRFv2PlusLoadTestConsumer, + metricsChannel chan *contracts.VRFLoadTestMetrics, + metricsErrorChannel chan error, +) { + metrics, err := consumer.GetLoadTestMetrics(context.Background()) + if err != nil { + metricsErrorChannel <- err + } + metricsChannel <- metrics +} diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index ee8be6bd8a5..ec0b08b5469 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -2,13 +2,11 @@ package loadvrfv2plus import ( "context" - "fmt" "github.com/kelseyhightower/envconfig" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/wasp" "github.com/stretchr/testify/require" @@ -105,7 +103,7 @@ func TestVRFV2PlusLoad(t *testing.T) { var wg sync.WaitGroup wg.Add(1) - requestCount, fulfilmentCount, err := WaitForRequestCountEqualToFulfilmentCount(vrfv2PlusContracts.LoadTestConsumers[0], 30*time.Second, &wg) + requestCount, fulfilmentCount, err := vrfv2plus.WaitForRequestCountEqualToFulfilmentCount(vrfv2PlusContracts.LoadTestConsumers[0], 30*time.Second, &wg) l.Info(). Interface("Request Count", requestCount). Interface("Fulfilment Count", fulfilmentCount). @@ -116,46 +114,3 @@ func TestVRFV2PlusLoad(t *testing.T) { }) } - -func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2PlusLoadTestConsumer, timeout time.Duration, wg *sync.WaitGroup) (*big.Int, *big.Int, error) { - metricsChannel := make(chan *contracts.VRFLoadTestMetrics) - metricsErrorChannel := make(chan error) - - testContext, testCancel := context.WithTimeout(context.Background(), timeout) - defer testCancel() - - ticker := time.NewTicker(time.Second * 1) - var metrics *contracts.VRFLoadTestMetrics - for { - select { - case <-testContext.Done(): - ticker.Stop() - wg.Done() - return metrics.RequestCount, metrics.FulfilmentCount, - fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", - metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) - case <-ticker.C: - go getLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) - case metrics = <-metricsChannel: - if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { - wg.Done() - return metrics.RequestCount, metrics.FulfilmentCount, nil - } - case err := <-metricsErrorChannel: - wg.Done() - return nil, nil, err - } - } -} - -func getLoadTestMetrics( - consumer contracts.VRFv2PlusLoadTestConsumer, - metricsChannel chan *contracts.VRFLoadTestMetrics, - metricsErrorChannel chan error, -) { - metrics, err := consumer.GetLoadTestMetrics(context.Background()) - if err != nil { - metricsErrorChannel <- err - } - metricsChannel <- metrics -}