Skip to content

Commit

Permalink
Chore/vrf 584 add vrfv2plus ctf test native billing (#10386)
Browse files Browse the repository at this point in the history
* fix VRF V3 setup script

* VRF-584: add VRF V2 Plus CTF test with native billing

* VRF-584: enabling CL Client debug mode

* try large runner

* VRF-584: refactor test to include sub tests

* VRF-584: disable CL client debug mode

* VRF-584: reverting to default runner

* VRF-584: PR comments

* VRF-584: fixing test state leak

---------

Co-authored-by: skudasov <f4hrenh9it@gmail.com>
  • Loading branch information
iljapavlovs and skudasov authored Aug 31, 2023
1 parent 14edc7c commit 74596ea
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 103 deletions.
16 changes: 16 additions & 0 deletions core/scripts/vrfv2plus/testnet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,22 @@ func main() {
tx, err := coordinator.CancelSubscription(e.Owner, parseSubID(*subID), e.Owner.From)
helpers.PanicErr(err)
helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID)
case "eoa-fund-sub-with-native-token":
fund := flag.NewFlagSet("eoa-fund-sub-with-native-token", flag.ExitOnError)
coordinatorAddress := fund.String("coordinator-address", "", "coordinator address")
amountStr := fund.String("amount", "", "amount to fund in wei")
subID := fund.String("sub-id", "", "sub-id")
helpers.ParseArgs(fund, os.Args[2:], "coordinator-address", "amount", "sub-id")
amount, s := big.NewInt(0).SetString(*amountStr, 10)
if !s {
panic(fmt.Sprintf("failed to parse top up amount '%s'", *amountStr))
}
coordinator, err := vrf_coordinator_v2plus.NewVRFCoordinatorV2Plus(common.HexToAddress(*coordinatorAddress), e.Ec)
helpers.PanicErr(err)
e.Owner.Value = amount
tx, err := coordinator.FundSubscriptionWithEth(e.Owner, parseSubID(*subID))
helpers.PanicErr(err)
helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID)
case "eoa-fund-sub":
fund := flag.NewFlagSet("eoa-fund-sub", flag.ExitOnError)
coordinatorAddress := fund.String("coordinator-address", "", "coordinator address")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import (
)

var (
LinkEthFeedResponse = big.NewInt(1e18)
MinimumConfirmations = uint16(3)
RandomnessRequestCountPerRequest = uint16(1)
VRFSubscriptionFundingAmountLink = big.NewInt(100)
ChainlinkNodeFundingAmountEth = big.NewFloat(0.1)
NumberOfWords = uint32(3)
MaxGasPriceGWei = 1000
CallbackGasLimit = uint32(1000000)
MaxGasLimitVRFCoordinatorConfig = uint32(2.5e6)
StalenessSeconds = uint32(86400)
GasAfterPaymentCalculation = uint32(33825)
LinkEthFeedResponse = big.NewInt(1e18)
MinimumConfirmations = uint16(3)
RandomnessRequestCountPerRequest = uint16(1)
VRFSubscriptionFundingAmountLink = big.NewInt(100)
VRFSubscriptionFundingAmountNativeToken = big.NewInt(1)
ChainlinkNodeFundingAmountEth = big.NewFloat(0.1)
NumberOfWords = uint32(3)
CallbackGasLimit = uint32(1000000)
MaxGasLimitVRFCoordinatorConfig = uint32(2.5e6)
StalenessSeconds = uint32(86400)
GasAfterPaymentCalculation = uint32(33825)

VRFCoordinatorV2PlusFeeConfig = vrf_coordinator_v2plus.VRFCoordinatorV2PlusFeeConfig{
FulfillmentFlatFeeLinkPPM: 500,
Expand Down
121 changes: 117 additions & 4 deletions integration-tests/actions/vrfv2plus/vrfv2plus_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (
"github.com/pkg/errors"
"github.com/smartcontractkit/chainlink-testing-framework/blockchain"
"github.com/smartcontractkit/chainlink/integration-tests/actions"
"github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_constants"
"github.com/smartcontractkit/chainlink/integration-tests/client"
"github.com/smartcontractkit/chainlink/integration-tests/contracts"
"github.com/smartcontractkit/chainlink/integration-tests/docker/test_env"
"github.com/smartcontractkit/chainlink/integration-tests/types/config/node"
chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils"
"math/big"
)
Expand All @@ -27,6 +30,19 @@ var (
ErrSendingLinkToken = "error sending Link token"
ErrCreatingVRFv2PlusJob = "error creating VRFv2Plus job"
ErrParseJob = "error parsing job definition"

ErrDeployVRFV2PlusContracts = "error deploying VRFV2Plus contracts"
ErrSetVRFCoordinatorConfig = "error setting config for VRF Coordinator contract"
ErrCreateVRFSubscription = "error creating VRF Subscription"
ErrFindSubID = "error finding created subscription ID"
ErrAddConsumerToSub = "error adding consumer to VRF Subscription"
ErrFundSubWithNativeToken = "error funding subscription with native token"
ErrSetLinkETHLinkFeed = "error setting Link and ETH/LINK feed for VRF Coordinator contract"
ErrFundSubWithLinkToken = "error funding subscription with Link tokens"
ErrCreateVRFV2PlusJobs = "error creating VRF V2 Plus Jobs"
ErrGetPrimaryKey = "error getting primary ETH key address"
ErrRestartCLNode = "error restarting CL node"
ErrWaitTXsComplete = "error waiting for TXs to complete"
)

func DeployVRFV2PlusContracts(
Expand All @@ -47,7 +63,7 @@ func DeployVRFV2PlusContracts(
}
err = chainClient.WaitForEvents()
if err != nil {
return nil, err
return nil, errors.Wrap(err, ErrWaitTXsComplete)
}
return &VRFV2PlusContracts{coordinator, bhs, loadTestConsumer}, nil
}
Expand All @@ -57,8 +73,8 @@ func CreateVRFV2PlusJobs(
coordinator contracts.VRFCoordinatorV2Plus,
c blockchain.EVMClient,
minIncomingConfirmations uint16,
) ([]VRFV2PlusJobInfo, error) {
jobInfo := make([]VRFV2PlusJobInfo, 0)
) ([]*VRFV2PlusJobInfo, error) {
jobInfo := make([]*VRFV2PlusJobInfo, 0)
for _, chainlinkNode := range chainlinkNodes {
vrfKey, err := chainlinkNode.MustCreateVRFKey()
if err != nil {
Expand Down Expand Up @@ -99,7 +115,7 @@ func CreateVRFV2PlusJobs(
if err != nil {
return nil, errors.Wrap(err, ErrCreatingProvingKeyHash)
}
ji := VRFV2PlusJobInfo{
ji := &VRFV2PlusJobInfo{
Job: job,
VRFKey: vrfKey,
EncodedProvingKey: provingKey,
Expand Down Expand Up @@ -140,3 +156,100 @@ func FundVRFCoordinatorV2PlusSubscription(linkToken contracts.LinkToken, coordin
}
return chainClient.WaitForEvents()
}

func SetupVRFV2PlusEnvironment(
env *test_env.CLClusterTestEnv,
linkAddress contracts.LinkToken,
mockETHLinkFeedAddress contracts.MockETHLINKFeed,
) (*test_env.CLClusterTestEnv, *VRFV2PlusContracts, *big.Int, *VRFV2PlusJobInfo, error) {

vrfv2PlusContracts, err := DeployVRFV2PlusContracts(env.ContractDeployer, env.EVMClient)
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrDeployVRFV2PlusContracts)
}

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

err = vrfv2PlusContracts.Coordinator.SetConfig(
vrfv2plus_constants.MinimumConfirmations,
vrfv2plus_constants.MaxGasLimitVRFCoordinatorConfig,
vrfv2plus_constants.StalenessSeconds,
vrfv2plus_constants.GasAfterPaymentCalculation,
vrfv2plus_constants.LinkEthFeedResponse,
vrfv2plus_constants.VRFCoordinatorV2PlusFeeConfig,
)
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrSetVRFCoordinatorConfig)
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete)
}

err = vrfv2PlusContracts.Coordinator.CreateSubscription()
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrCreateVRFSubscription)
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete)
}

subID, err := vrfv2PlusContracts.Coordinator.FindSubscriptionID()
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrFindSubID)
}

err = vrfv2PlusContracts.Coordinator.AddConsumer(subID, vrfv2PlusContracts.LoadTestConsumer.Address())
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrAddConsumerToSub)
}

//Native Billing
err = vrfv2PlusContracts.Coordinator.FundSubscriptionWithEth(subID, big.NewInt(0).Mul(vrfv2plus_constants.VRFSubscriptionFundingAmountNativeToken, big.NewInt(1e18)))
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrFundSubWithNativeToken)
}

//Link Billing
err = vrfv2PlusContracts.Coordinator.SetLINKAndLINKETHFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address())
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrSetLinkETHLinkFeed)
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete)
}
err = FundVRFCoordinatorV2PlusSubscription(linkAddress, vrfv2PlusContracts.Coordinator, env.EVMClient, subID, vrfv2plus_constants.VRFSubscriptionFundingAmountLink)
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrFundSubWithLinkToken)
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete)
}

vrfV2PlusJobs, err := CreateVRFV2PlusJobs(env.GetAPIs(), vrfv2PlusContracts.Coordinator, env.EVMClient, vrfv2plus_constants.MinimumConfirmations)
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrCreateVRFV2PlusJobs)
}

// this part is here because VRFv2 can work with only a specific key
// [[EVM.KeySpecific]]
// Key = '...'
addr, err := env.CLNodes[0].API.PrimaryEthAddress()
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrGetPrimaryKey)
}
nodeConfig := node.NewConfig(env.CLNodes[0].NodeConfig,
node.WithVRFv2EVMEstimator(addr),
)
err = env.CLNodes[0].Restart(nodeConfig)
if err != nil {
return nil, nil, nil, nil, errors.Wrap(err, ErrRestartCLNode)
}
return env, vrfv2PlusContracts, subID, vrfV2PlusJobs[0], nil
}
2 changes: 2 additions & 0 deletions integration-tests/contracts/contract_vrf_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ type VRFCoordinatorV2Plus interface {
HashOfKey(ctx context.Context, pubKey [2]*big.Int) ([32]byte, error)
CreateSubscription() error
AddConsumer(subId *big.Int, consumerAddress string) error
FundSubscriptionWithEth(subId *big.Int, nativeTokenAmount *big.Int) error
Address() string
GetSubscription(ctx context.Context, subID *big.Int) (vrf_coordinator_v2plus.GetSubscription, error)
FindSubscriptionID() (*big.Int, error)
WaitForRandomWordsFulfilledEvent(subID []*big.Int, requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2plus.VRFCoordinatorV2PlusRandomWordsFulfilled, error)
}

type VRFConsumer interface {
Expand Down
37 changes: 37 additions & 0 deletions integration-tests/contracts/ethereum_vrfv2plus_contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics"
"math/big"
"time"
)

type EthereumVRFCoordinatorV2Plus struct {
Expand Down Expand Up @@ -150,6 +151,22 @@ func (v *EthereumVRFCoordinatorV2Plus) AddConsumer(subId *big.Int, consumerAddre
return v.client.ProcessTransaction(tx)
}

func (v *EthereumVRFCoordinatorV2Plus) FundSubscriptionWithEth(subId *big.Int, nativeTokenAmount *big.Int) error {
opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet())
if err != nil {
return err
}
opts.Value = nativeTokenAmount
tx, err := v.coordinator.FundSubscriptionWithEth(
opts,
subId,
)
if err != nil {
return err
}
return v.client.ProcessTransaction(tx)
}

func (v *EthereumVRFCoordinatorV2Plus) FindSubscriptionID() (*big.Int, error) {
owner := v.client.GetDefaultWallet().Address()
subscriptionIterator, err := v.coordinator.FilterSubscriptionCreated(
Expand All @@ -167,6 +184,26 @@ func (v *EthereumVRFCoordinatorV2Plus) FindSubscriptionID() (*big.Int, error) {
return subscriptionIterator.Event.SubId, nil
}

func (v *EthereumVRFCoordinatorV2Plus) WaitForRandomWordsFulfilledEvent(subID []*big.Int, requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2plus.VRFCoordinatorV2PlusRandomWordsFulfilled, error) {
randomWordsFulfilledEventsChannel := make(chan *vrf_coordinator_v2plus.VRFCoordinatorV2PlusRandomWordsFulfilled)
subscription, err := v.coordinator.WatchRandomWordsFulfilled(nil, randomWordsFulfilledEventsChannel, requestID, subID)
if err != nil {
return nil, err
}
defer subscription.Unsubscribe()

for {
select {
case err := <-subscription.Err():
return nil, err
case <-time.After(timeout):
return nil, fmt.Errorf("timeout waiting for RandomWordsFulfilled event")
case randomWordsFulfilledEvent := <-randomWordsFulfilledEventsChannel:
return randomWordsFulfilledEvent, nil
}
}
}

func (v *EthereumVRFv2PlusLoadTestConsumer) Address() string {
return v.address.Hex()
}
Expand Down
Loading

0 comments on commit 74596ea

Please sign in to comment.