Skip to content

Commit

Permalink
VRF-663: adding better load test model for VRFV2 Plus (#11025)
Browse files Browse the repository at this point in the history
* VRF-663: adding better load test model for VRFV2 Plus

* VRF-663: adding more perf test types

* VRF-663: small fix

* VRF-663: fixing workflow

* VRF-663: small fix
  • Loading branch information
iljapavlovs authored Oct 21, 2023
1 parent bb5c5d7 commit 958c37c
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 173 deletions.
22 changes: 12 additions & 10 deletions .github/workflows/on-demand-vrfv2plus-load-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,25 @@ on:
chainlinkVersion:
description: Container image version for the Chainlink nodes
required: true
default: "2.6.0-beta0"
default: "2.6.0"
performanceTestType:
description: Performance Test Type of test to run
type: choice
options:
- "Soak"
- "Load"
- "Stress"
- "Spike"
testDuration:
description: Duration of the test (time string)
required: false
default: 1m
randomnessRequestCountPerRequest:
description: Randomness request Count per one TX request
required: false
default: 1
useExistingEnv:
description: Whether to deploy a new contracts or use an existing one
description: Set `true` to use existing environment or `false` to deploy CL node and all contracts
required: false
default: false
configBase64:
description: TOML config in base64
description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env)
required: false
jobs:
vrfv2plus_load_test:
Expand All @@ -68,10 +72,8 @@ jobs:
LOKI_URL: ${{ secrets.LOKI_URL }}
LOKI_TOKEN: ${{ secrets.LOKI_TOKEN }}
SELECTED_NETWORKS: ${{ inputs.network }}
TEST_TYPE: ${{ inputs.performanceTestType }}
VRFV2PLUS_TEST_DURATION: ${{ inputs.testDuration }}
VRFV2PLUS_RATE_LIMIT_UNIT_DURATION: 1m
VRFV2PLUS_RPS: 1
VRFV2PLUS_RANDOMNESS_REQUEST_COUNT_PER_REQUEST: ${{ inputs.randomnessRequestCountPerRequest }}
VRFV2PLUS_USE_EXISTING_ENV: ${{ inputs.useExistingEnv }}
CONFIG: ${{ inputs.configBase64 }}
TEST_LOG_LEVEL: debug
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ type VRFV2PlusConfig struct {
FulfillmentFlatFeeLinkPPM uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM" default:"500"` // Flat fee in ppm for LINK for the VRF Coordinator config
FulfillmentFlatFeeNativePPM uint32 `envconfig:"FULFILLMENT_FLAT_FEE_NATIVE_PPM" default:"500"` // Flat fee in ppm for native currency for the VRF Coordinator config

RandomnessRequestCountPerRequest uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST" default:"1"` // How many randomness requests to send per request
RandomnessRequestCountPerRequest uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST" default:"1"` // How many randomness requests to send per request
RandomnessRequestCountPerRequestDeviation uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST_DEVIATION" default:"0"` // How many randomness requests to send per request

//Wrapper Config
WrapperGasOverhead uint32 `envconfig:"WRAPPER_GAS_OVERHEAD" default:"50000"`
Expand Down
205 changes: 159 additions & 46 deletions integration-tests/actions/vrfv2plus/vrfv2plus_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"math/big"
"sync"
"time"

"github.com/smartcontractkit/chainlink/v2/core/assets"
Expand Down Expand Up @@ -232,15 +233,17 @@ func FundVRFCoordinatorV2_5Subscription(linkToken contracts.LinkToken, coordinat
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,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
linkToken contracts.LinkToken,
mockNativeLINKFeed contracts.MockETHLINKFeed,
consumerContractsAmount int,
) (*VRFV2_5Contracts, *big.Int, *VRFV2PlusData, error) {
numberOfConsumers int,
numberOfSubToCreate int,
) (*VRFV2_5Contracts, []*big.Int, *VRFV2PlusData, error) {

vrfv2_5Contracts, err := DeployVRFV2_5Contracts(env.ContractDeployer, env.EVMClient, consumerContractsAmount)
vrfv2_5Contracts, err := DeployVRFV2_5Contracts(env.ContractDeployer, env.EVMClient, numberOfConsumers)
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrDeployVRFV2_5Contracts)
}
Expand All @@ -260,34 +263,39 @@ func SetupVRFV2_5Environment(
return nil, nil, nil, errors.Wrap(err, ErrSetVRFCoordinatorConfig)
}

subID, err := CreateSubAndFindSubID(env, vrfv2_5Contracts.Coordinator)
err = vrfv2_5Contracts.Coordinator.SetLINKAndLINKNativeFeed(linkToken.Address(), mockNativeLINKFeed.Address())
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, errors.Wrap(err, ErrSetLinkNativeLinkFeed)
}

err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete)
}
for _, consumer := range vrfv2_5Contracts.LoadTestConsumers {
err = vrfv2_5Contracts.Coordinator.AddConsumer(subID, consumer.Address())
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrAddConsumerToSub)
}

subIDs, err := CreateSubsAndFund(env, vrfv2PlusConfig, linkToken, vrfv2_5Contracts, numberOfSubToCreate)
if err != nil {
return nil, nil, nil, err
}

err = vrfv2_5Contracts.Coordinator.SetLINKAndLINKNativeFeed(linkToken.Address(), mockNativeLINKFeed.Address())
subToConsumersMap := map[*big.Int][]contracts.VRFv2PlusLoadTestConsumer{}

//each subscription will have the same consumers
for _, subID := range subIDs {
subToConsumersMap[subID] = vrfv2_5Contracts.LoadTestConsumers
}

err = AddConsumersToSubs(
subToConsumersMap,
vrfv2_5Contracts.Coordinator,
)
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrSetLinkNativeLinkFeed)
return nil, nil, nil, err
}

err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete)
}
err = FundSubscription(env, vrfv2PlusConfig, linkToken, vrfv2_5Contracts.Coordinator, subID)
if err != nil {
return nil, nil, nil, err
}

vrfKey, err := env.ClCluster.NodeAPIs()[0].MustCreateVRFKey()
if err != nil {
Expand Down Expand Up @@ -350,12 +358,66 @@ func SetupVRFV2_5Environment(
chainID,
}

return vrfv2_5Contracts, subID, &data, nil
return vrfv2_5Contracts, subIDs, &data, nil
}

func CreateSubsAndFund(
env *test_env.CLClusterTestEnv,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
linkToken contracts.LinkToken,
vrfv2_5Contracts *VRFV2_5Contracts,
subAmountToCreate int,
) ([]*big.Int, error) {
subs, err := CreateSubs(env, vrfv2_5Contracts.Coordinator, subAmountToCreate)
if err != nil {
return nil, err
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, errors.Wrap(err, ErrWaitTXsComplete)
}
err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, vrfv2_5Contracts.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 errors.Wrap(err, ErrAddConsumerToSub)
}
}
}
return nil
}

func SetupVRFV2PlusWrapperEnvironment(
env *test_env.CLClusterTestEnv,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
linkToken contracts.LinkToken,
mockNativeLINKFeed contracts.MockETHLINKFeed,
coordinator contracts.VRFCoordinatorV2_5,
Expand Down Expand Up @@ -411,7 +473,7 @@ func SetupVRFV2PlusWrapperEnvironment(
return nil, nil, errors.Wrap(err, ErrWaitTXsComplete)
}

err = FundSubscription(env, vrfv2PlusConfig, linkToken, coordinator, wrapperSubID)
err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, coordinator, []*big.Int{wrapperSubID})
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -445,15 +507,17 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts
if err != nil {
return nil, errors.Wrap(err, ErrCreateVRFSubscription)
}
err = env.EVMClient.WaitForEvents()

sub, err := coordinator.WaitForSubscriptionCreatedEvent(time.Second * 10)
if err != nil {
return nil, errors.Wrap(err, ErrWaitTXsComplete)
return nil, errors.Wrap(err, ErrFindSubID)
}
subID, err := coordinator.FindSubscriptionID()

err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, errors.Wrap(err, ErrFindSubID)
return nil, errors.Wrap(err, ErrWaitTXsComplete)
}
return subID, nil
return sub.SubId, nil
}

func GetUpgradedCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) {
Expand All @@ -480,28 +544,29 @@ func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkT
return
}

func FundSubscription(
func FundSubscriptions(
env *test_env.CLClusterTestEnv,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
linkAddress contracts.LinkToken,
coordinator contracts.VRFCoordinatorV2_5,
subID *big.Int,
subIDs []*big.Int,
) error {
//Native Billing
err := coordinator.FundSubscriptionWithNative(subID, big.NewInt(0).Mul(big.NewInt(vrfv2PlusConfig.SubscriptionFundingAmountNative), big.NewInt(1e18)))
if err != nil {
return errors.Wrap(err, ErrFundSubWithNativeToken)
}

err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, big.NewInt(vrfv2PlusConfig.SubscriptionFundingAmountLink))
if err != nil {
return errors.Wrap(err, ErrFundSubWithLinkToken)
for _, subID := range subIDs {
//Native Billing
err := coordinator.FundSubscriptionWithNative(subID, big.NewInt(0).Mul(big.NewInt(vrfv2PlusConfig.SubscriptionFundingAmountNative), big.NewInt(1e18)))
if err != nil {
return errors.Wrap(err, ErrFundSubWithNativeToken)
}
//Link Billing
err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, big.NewInt(vrfv2PlusConfig.SubscriptionFundingAmountLink))
if err != nil {
return errors.Wrap(err, ErrFundSubWithLinkToken)
}
}
err = env.EVMClient.WaitForEvents()
err := env.EVMClient.WaitForEvents()
if err != nil {
return errors.Wrap(err, ErrWaitTXsComplete)
}

return nil
}

Expand All @@ -511,7 +576,8 @@ func RequestRandomnessAndWaitForFulfillment(
vrfv2PlusData *VRFV2PlusData,
subID *big.Int,
isNativeBilling bool,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
randomnessRequestCountPerRequest uint16,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
l zerolog.Logger,
) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) {
logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l)
Expand All @@ -522,7 +588,7 @@ func RequestRandomnessAndWaitForFulfillment(
vrfv2PlusConfig.CallbackGasLimit,
isNativeBilling,
vrfv2PlusConfig.NumberOfWords,
vrfv2PlusConfig.RandomnessRequestCountPerRequest,
randomnessRequestCountPerRequest,
)
if err != nil {
return nil, errors.Wrap(err, ErrRequestRandomness)
Expand All @@ -537,7 +603,7 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded(
vrfv2PlusData *VRFV2PlusData,
subID *big.Int,
isNativeBilling bool,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
l zerolog.Logger,
) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) {
logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l)
Expand Down Expand Up @@ -585,7 +651,7 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment(
vrfv2PlusData *VRFV2PlusData,
subID *big.Int,
isNativeBilling bool,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
l zerolog.Logger,
) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) {
logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l)
Expand Down Expand Up @@ -650,6 +716,51 @@ func WaitForRequestAndFulfillmentEvents(
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 {
ticker.Stop()
wg.Done()
return metrics.RequestCount, metrics.FulfilmentCount, nil
}
case err := <-metricsErrorChannel:
ticker.Stop()
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
}

func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2_5.GetSubscription, subID *big.Int, coordinator contracts.VRFCoordinatorV2_5) {
l.Debug().
Str("Coordinator", coordinator.Address()).
Expand Down Expand Up @@ -795,14 +906,16 @@ func logRandRequest(
coordinator string,
subID *big.Int,
isNativeBilling bool,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
l zerolog.Logger) {
l.Debug().
Str("Consumer", consumer).
Str("Coordinator", coordinator).
Str("subID", subID.String()).
Str("SubID", subID.String()).
Bool("IsNativePayment", isNativeBilling).
Uint16("MinimumConfirmations", vrfv2PlusConfig.MinimumConfirmations).
Uint32("CallbackGasLimit", vrfv2PlusConfig.CallbackGasLimit).
Uint16("RandomnessRequestCountPerRequest", vrfv2PlusConfig.RandomnessRequestCountPerRequest).
Uint16("RandomnessRequestCountPerRequestDeviation", vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation).
Msg("Requesting randomness")
}
1 change: 1 addition & 0 deletions integration-tests/contracts/contract_vrf_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type VRFCoordinatorV2_5 interface {
GetNativeTokenTotalBalance(ctx context.Context) (*big.Int, error)
GetLinkTotalBalance(ctx context.Context) (*big.Int, error)
FindSubscriptionID() (*big.Int, error)
WaitForSubscriptionCreatedEvent(timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCreated, error)
WaitForRandomWordsFulfilledEvent(subID []*big.Int, requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error)
WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []*big.Int, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested, error)
WaitForMigrationCompletedEvent(timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25MigrationCompleted, error)
Expand Down
Loading

0 comments on commit 958c37c

Please sign in to comment.