Skip to content

Commit

Permalink
VRF-669: adding CTF tests for VRF v2.5 cancel subscription, oracle wi… (
Browse files Browse the repository at this point in the history
#11159)

* VRF-669: adding CTF tests for VRF v2.5 cancel subscription, oracle withdraw; added fund return as a teardown for the WASP load test

* VRF-669: updating tests to have better balance calculation

* VRF-669: small update

* VRF-669: enabling tests; making use of big.Int Cmp() method

* VRF-669: adding sub cancellation test for the coordinator owner; code refactoring; fixing load test
  • Loading branch information
iljapavlovs authored Nov 6, 2023
1 parent cea3e6e commit 59bf37c
Show file tree
Hide file tree
Showing 12 changed files with 737 additions and 131 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ jobs:
pyroscope_env: ci-smoke-vrf2-evm-simulated
- name: vrfv2plus
nodes: 1
os: ubuntu-latest
os: ubuntu20.04-8cores-32GB
pyroscope_env: ci-smoke-vrf2plus-evm-simulated
- name: forwarder_ocr
nodes: 1
Expand Down
16 changes: 16 additions & 0 deletions integration-tests/actions/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
package actions

import (
"crypto/ecdsa"
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"math/big"
"strings"
"testing"
Expand Down Expand Up @@ -443,3 +445,17 @@ func DeployMockETHLinkFeed(cd contracts.ContractDeployer, answer *big.Int) (cont
}
return mockETHLINKFeed, err
}

// todo - move to CTF
func GenerateWallet() (common.Address, error) {
privateKey, err := crypto.GenerateKey()
if err != nil {
return common.Address{}, err
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
return common.Address{}, errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
}
return crypto.PubkeyToAddress(*publicKeyECDSA), nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ type VRFV2PlusConfig struct {
IsNativePayment bool `envconfig:"IS_NATIVE_PAYMENT" default:"false"` // Whether to use native payment or LINK token
LinkNativeFeedResponse int64 `envconfig:"LINK_NATIVE_FEED_RESPONSE" default:"1000000000000000000"` // Response of the LINK/ETH feed
MinimumConfirmations uint16 `envconfig:"MINIMUM_CONFIRMATIONS" default:"3"` // Minimum number of confirmations for the VRF Coordinator
SubscriptionFundingAmountLink int64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_LINK" default:"10"` // Amount of LINK to fund the subscription with
SubscriptionFundingAmountNative int64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_NATIVE" default:"1"` // Amount of native currency to fund the subscription with
SubscriptionFundingAmountLink float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_LINK" default:"5"` // Amount of LINK to fund the subscription with
SubscriptionFundingAmountNative float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_NATIVE" default:"1"` // Amount of native currency to fund the subscription with
NumberOfWords uint32 `envconfig:"NUMBER_OF_WORDS" default:"3"` // Number of words to request
CallbackGasLimit uint32 `envconfig:"CALLBACK_GAS_LIMIT" default:"1000000"` // Gas limit for the callback
MaxGasLimitCoordinatorConfig uint32 `envconfig:"MAX_GAS_LIMIT_COORDINATOR_CONFIG" default:"2500000"` // Max gas limit for the VRF Coordinator config
Expand All @@ -23,6 +23,8 @@ type VRFV2PlusConfig struct {
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

RandomWordsFulfilledEventTimeout time.Duration `envconfig:"RANDOM_WORDS_FULFILLED_EVENT_TIMEOUT" default:"2m"` // How long to wait for the RandomWordsFulfilled event to be emitted

//Wrapper Config
WrapperGasOverhead uint32 `envconfig:"WRAPPER_GAS_OVERHEAD" default:"50000"`
CoordinatorGasOverhead uint32 `envconfig:"COORDINATOR_GAS_OVERHEAD" default:"52000"`
Expand Down
118 changes: 95 additions & 23 deletions integration-tests/actions/vrfv2plus/vrfv2plus_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package vrfv2plus
import (
"context"
"fmt"
"github.com/smartcontractkit/chainlink-testing-framework/utils"
"math/big"
"sync"
"time"
Expand Down Expand Up @@ -221,12 +222,18 @@ func VRFV2PlusUpgradedVersionRegisterProvingKey(
return provingKey, nil
}

func FundVRFCoordinatorV2_5Subscription(linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, chainClient blockchain.EVMClient, subscriptionID *big.Int, linkFundingAmount *big.Int) error {
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 errors.Wrap(err, ErrABIEncodingFunding)
}
_, err = linkToken.TransferAndCall(coordinator.Address(), big.NewInt(0).Mul(linkFundingAmount, big.NewInt(1e18)), encodedSubId)
_, err = linkToken.TransferAndCall(coordinator.Address(), linkFundingAmountJuels, encodedSubId)
if err != nil {
return errors.Wrap(err, ErrSendingLinkToken)
}
Expand All @@ -236,9 +243,10 @@ func FundVRFCoordinatorV2_5Subscription(linkToken contracts.LinkToken, coordinat
// 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,
registerProvingKeyAgainstAddress string,
numberOfConsumers int,
numberOfSubToCreate int,
l zerolog.Logger,
Expand Down Expand Up @@ -275,8 +283,12 @@ func SetupVRFV2_5Environment(
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete)
}
l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Creating and funding subscriptions, adding consumers")
subIDs, err := CreateFundSubsAndAddConsumers(env, vrfv2PlusConfig, linkToken, vrfv2_5Contracts.Coordinator, vrfv2_5Contracts.LoadTestConsumers, numberOfSubToCreate)
l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Int("Number of Subs to create", numberOfSubToCreate).Msg("Creating and funding subscriptions, adding consumers")
subIDs, err := CreateFundSubsAndAddConsumers(
env,
vrfv2PlusConfig,
linkToken,
vrfv2_5Contracts.Coordinator, vrfv2_5Contracts.LoadTestConsumers, numberOfSubToCreate)
if err != nil {
return nil, nil, nil, err
}
Expand All @@ -287,12 +299,8 @@ func SetupVRFV2_5Environment(
}
pubKeyCompressed := vrfKey.Data.ID

nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress()
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrNodePrimaryKey)
}
l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Registering Proving Key")
provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, nativeTokenPrimaryKeyAddress, vrfv2_5Contracts.Coordinator)
provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, registerProvingKeyAgainstAddress, vrfv2_5Contracts.Coordinator)
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrRegisteringProvingKey)
}
Expand All @@ -303,6 +311,11 @@ func SetupVRFV2_5Environment(

chainID := env.EVMClient.GetChainID()

nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress()
if err != nil {
return nil, nil, nil, errors.Wrap(err, ErrNodePrimaryKey)
}

l.Info().Msg("Creating VRFV2 Plus Job")
job, err := CreateVRFV2PlusJob(
env.ClCluster.NodeAPIs()[0],
Expand Down Expand Up @@ -351,7 +364,7 @@ func SetupVRFV2_5Environment(

func CreateFundSubsAndAddConsumers(
env *test_env.CLClusterTestEnv,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
linkToken contracts.LinkToken,
coordinator contracts.VRFCoordinatorV2_5,
consumers []contracts.VRFv2PlusLoadTestConsumer,
Expand Down Expand Up @@ -385,7 +398,7 @@ func CreateFundSubsAndAddConsumers(

func CreateSubsAndFund(
env *test_env.CLClusterTestEnv,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
linkToken contracts.LinkToken,
coordinator contracts.VRFCoordinatorV2_5,
subAmountToCreate int,
Expand Down Expand Up @@ -439,7 +452,7 @@ func AddConsumersToSubs(

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 @@ -574,19 +587,24 @@ func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkT

func FundSubscriptions(
env *test_env.CLClusterTestEnv,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
linkAddress contracts.LinkToken,
coordinator contracts.VRFCoordinatorV2_5,
subIDs []*big.Int,
) error {
for _, subID := range subIDs {
//Native Billing
err := coordinator.FundSubscriptionWithNative(subID, big.NewInt(0).Mul(big.NewInt(vrfv2PlusConfig.SubscriptionFundingAmountNative), big.NewInt(1e18)))
amountWei := utils.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative))
err := coordinator.FundSubscriptionWithNative(
subID,
amountWei,
)
if err != nil {
return errors.Wrap(err, ErrFundSubWithNativeToken)
}
//Link Billing
err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, big.NewInt(vrfv2PlusConfig.SubscriptionFundingAmountLink))
amountJuels := utils.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink))
err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels)
if err != nil {
return errors.Wrap(err, ErrFundSubWithLinkToken)
}
Expand All @@ -605,7 +623,8 @@ func RequestRandomnessAndWaitForFulfillment(
subID *big.Int,
isNativeBilling bool,
randomnessRequestCountPerRequest uint16,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
randomWordsFulfilledEventTimeout time.Duration,
l zerolog.Logger,
) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) {
logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l)
Expand All @@ -622,7 +641,15 @@ func RequestRandomnessAndWaitForFulfillment(
return nil, errors.Wrap(err, ErrRequestRandomness)
}

return WaitForRequestAndFulfillmentEvents(consumer.Address(), coordinator, vrfv2PlusData, subID, isNativeBilling, l)
return WaitForRequestAndFulfillmentEvents(
consumer.Address(),
coordinator,
vrfv2PlusData,
subID,
isNativeBilling,
randomWordsFulfilledEventTimeout,
l,
)
}

func RequestRandomnessAndWaitForFulfillmentUpgraded(
Expand All @@ -631,7 +658,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 @@ -679,7 +706,8 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment(
vrfv2PlusData *VRFV2PlusData,
subID *big.Int,
isNativeBilling bool,
vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig,
vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig,
randomWordsFulfilledEventTimeout time.Duration,
l zerolog.Logger,
) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) {
logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l)
Expand Down Expand Up @@ -708,7 +736,15 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment(
if err != nil {
return nil, errors.Wrap(err, "error getting wrapper address")
}
return WaitForRequestAndFulfillmentEvents(wrapperAddress.String(), coordinator, vrfv2PlusData, subID, isNativeBilling, l)
return WaitForRequestAndFulfillmentEvents(
wrapperAddress.String(),
coordinator,
vrfv2PlusData,
subID,
isNativeBilling,
randomWordsFulfilledEventTimeout,
l,
)
}

func WaitForRequestAndFulfillmentEvents(
Expand All @@ -717,6 +753,7 @@ func WaitForRequestAndFulfillmentEvents(
vrfv2PlusData *VRFV2PlusData,
subID *big.Int,
isNativeBilling bool,
randomWordsFulfilledEventTimeout time.Duration,
l zerolog.Logger,
) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) {
randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent(
Expand All @@ -734,7 +771,7 @@ func WaitForRequestAndFulfillmentEvents(
randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent(
[]*big.Int{subID},
[]*big.Int{randomWordsRequestedEvent.RequestId},
time.Minute*2,
randomWordsFulfilledEventTimeout,
)
if err != nil {
return nil, errors.Wrap(err, ErrWaitRandomWordsFulfilledEvent)
Expand Down Expand Up @@ -777,6 +814,41 @@ func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2PlusLoadT
}
}

func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator contracts.VRFCoordinatorV2_5, l zerolog.Logger) error {
linkTotalBalance, err := coordinator.GetLinkTotalBalance(context.Background())
if err != nil {
return errors.Wrap(err, "Error getting LINK total balance")
}
defaultWallet := client.GetDefaultWallet().Address()
l.Info().
Str("LINK amount", linkTotalBalance.String()).
Str("Returning to", defaultWallet).
Msg("Returning LINK for fulfilled requests")
err = coordinator.OracleWithdraw(
common.HexToAddress(defaultWallet),
linkTotalBalance,
)
if err != nil {
return errors.Wrap(err, "Error withdrawing LINK from coordinator to default wallet")
}
nativeTotalBalance, err := coordinator.GetNativeTokenTotalBalance(context.Background())
if err != nil {
return errors.Wrap(err, "Error getting NATIVE total balance")
}
l.Info().
Str("Native Token amount", linkTotalBalance.String()).
Str("Returning to", defaultWallet).
Msg("Returning Native Token for fulfilled requests")
err = coordinator.OracleWithdrawNative(
common.HexToAddress(defaultWallet),
nativeTotalBalance,
)
if err != nil {
return errors.Wrap(err, "Error withdrawing NATIVE from coordinator to default wallet")
}
return nil
}

func getLoadTestMetrics(
consumer contracts.VRFv2PlusLoadTestConsumer,
metricsChannel chan *contracts.VRFLoadTestMetrics,
Expand Down Expand Up @@ -934,7 +1006,7 @@ 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).
Expand Down
6 changes: 6 additions & 0 deletions integration-tests/contracts/contract_vrf_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,17 @@ type VRFCoordinatorV2_5 interface {
AddConsumer(subId *big.Int, consumerAddress string) error
FundSubscriptionWithNative(subId *big.Int, nativeTokenAmount *big.Int) error
Address() string
PendingRequestsExist(ctx context.Context, subID *big.Int) (bool, error)
GetSubscription(ctx context.Context, subID *big.Int) (vrf_coordinator_v2_5.GetSubscription, error)
OwnerCancelSubscription(subID *big.Int) (*types.Transaction, error)
CancelSubscription(subID *big.Int, to common.Address) (*types.Transaction, error)
OracleWithdraw(recipient common.Address, amount *big.Int) error
OracleWithdrawNative(recipient common.Address, amount *big.Int) error
GetNativeTokenTotalBalance(ctx context.Context) (*big.Int, error)
GetLinkTotalBalance(ctx context.Context) (*big.Int, error)
FindSubscriptionID(subID *big.Int) (*big.Int, error)
WaitForSubscriptionCreatedEvent(timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCreated, error)
WaitForSubscriptionCanceledEvent(subID *big.Int, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCanceled, 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 59bf37c

Please sign in to comment.