Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added smoke test for canceling subscription on VRFv2 #11587

Merged
merged 3 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -54,10 +54,12 @@ type VRFCoordinatorV2 interface {
Address() string
GetSubscription(ctx context.Context, subID uint64) (vrf_coordinator_v2.GetSubscription, error)
PendingRequestsExist(ctx context.Context, subID uint64) (bool, error)
OwnerCancelSubscription(subID uint64) (*types.Transaction, 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)
WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []uint64, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error)
WaitForSubscriptionCanceledEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error)
}

type VRFCoordinatorV2_5 interface {
Expand Down
39 changes: 39 additions & 0 deletions integration-tests/contracts/ethereum_vrfv2_contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,25 @@ func (v *EthereumVRFCoordinatorV2) PendingRequestsExist(ctx context.Context, sub
return pendingRequestExists, nil
}

// OwnerCancelSubscription cancels subscription,
// return funds to the subscription owner,
// down not check if pending requests for a sub exist,
// outstanding requests may fail onchain
func (v *EthereumVRFCoordinatorV2) OwnerCancelSubscription(subID uint64) (*types.Transaction, error) {
opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet())
if err != nil {
return nil, err
}
tx, err := v.coordinator.OwnerCancelSubscription(
opts,
subID,
)
if err != nil {
return nil, err
}
return tx, v.client.ProcessTransaction(tx)
}

// CancelSubscription cancels subscription by Sub owner,
// return funds to specified address,
// checks if pending requests for a sub exist
Expand Down Expand Up @@ -300,6 +319,26 @@ func (v *EthereumVRFCoordinatorV2) WaitForRandomWordsRequestedEvent(keyHash [][3
}
}

func (v *EthereumVRFCoordinatorV2) WaitForSubscriptionCanceledEvent(subID []uint64, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled, error) {
eventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2SubscriptionCanceled)
subscription, err := v.coordinator.WatchSubscriptionCanceled(nil, eventsChannel, 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 SubscriptionCanceled event")
case sub := <-eventsChannel:
return sub, nil
}
}
}

// GetAllRandomWords get all VRFv2 randomness output words
func (v *EthereumVRFConsumerV2) GetAllRandomWords(ctx context.Context, num int) ([]*big.Int, error) {
words := make([]*big.Int, 0)
Expand Down
187 changes: 187 additions & 0 deletions integration-tests/smoke/vrfv2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import (
"context"
"math/big"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/kelseyhightower/envconfig"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink-testing-framework/blockchain"
"github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext"
"github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions"
"github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config"

Expand Down Expand Up @@ -113,6 +115,191 @@ func TestVRFv2Basic(t *testing.T) {
require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0")
}
})

t.Run("Canceling Sub And Returning Funds", func(t *testing.T) {
testConfig := vrfv2Config
subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers(
env,
testConfig,
linkToken,
vrfv2Contracts.Coordinator,
vrfv2Contracts.LoadTestConsumers,
1,
)
require.NoError(t, err)
subIDForCancelling := subIDsForCancelling[0]

testWalletAddress, err := actions.GenerateWallet()
require.NoError(t, err)

testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String())
require.NoError(t, err)

subscriptionForCancelling, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling)
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).
Str("Returning funds to", testWalletAddress.String()).
Msg("Canceling subscription and returning funds to subscription owner")

tx, err := vrfv2Contracts.Coordinator.CancelSubscription(subIDForCancelling, testWalletAddress)
require.NoError(t, err, "Error canceling subscription")

subscriptionCanceledEvent, err := vrfv2Contracts.Coordinator.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30)
require.NoError(t, err, "error waiting for subscription canceled event")

cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash())
require.NoError(t, err, "error getting tx cancellation Tx Receipt")

txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed)
cancellationTxFeeWei := new(big.Int).Mul(txGasUsed, cancellationTxReceipt.EffectiveGasPrice)

l.Info().
Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()).
Str("Effective Gas Price", cancellationTxReceipt.EffectiveGasPrice.String()).
Uint64("Gas Used", cancellationTxReceipt.GasUsed).
Msg("Cancellation TX Receipt")

l.Info().
Str("Returned Subscription Amount Link", subscriptionCanceledEvent.Amount.String()).
Uint64("SubID", subscriptionCanceledEvent.SubId).
Str("Returned to", subscriptionCanceledEvent.To.String()).
Msg("Subscription Canceled Event")

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())
require.NoError(t, err)

//Verify that sub was deleted from Coordinator
_, err = vrfv2Contracts.Coordinator.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)

l.Info().
Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()).
Str("Sub Funds Returned Actual - Link", subFundsReturnedLinkActual.String()).
Str("Sub Balance - Link", subBalanceLink.String()).
Msg("Sub funds returned")

require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled")
})

t.Run("Owner Canceling Sub And Returning Funds While Having Pending Requests", func(t *testing.T) {
testConfig := vrfv2Config

// Underfund subscription to force fulfillments to fail
testConfig.SubscriptionFundingAmountLink = float64(0.000000000000000001) // 1 Juel

subIDsForCancelling, err := vrfv2_actions.CreateFundSubsAndAddConsumers(
env,
testConfig,
linkToken,
vrfv2Contracts.Coordinator,
vrfv2Contracts.LoadTestConsumers,
1,
)
require.NoError(t, err)

subIDForCancelling := subIDsForCancelling[0]

subscriptionForCancelling, err := vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling)
require.NoError(t, err, "Error getting subscription information")

vrfv2_actions.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2Contracts.Coordinator)

// No GetActiveSubscriptionIds function available - skipping check

pendingRequestsExist, err := vrfv2Contracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling)
require.NoError(t, err)
require.False(t, pendingRequestsExist, "Pending requests should not exist")

// Request randomness - should fail due to underfunded subscription
randomWordsFulfilledEventTimeout := 5 * time.Second
_, err = vrfv2_actions.RequestRandomnessAndWaitForFulfillment(
vrfv2Contracts.LoadTestConsumers[0],
vrfv2Contracts.Coordinator,
vrfv2Data,
subIDForCancelling,
testConfig.RandomnessRequestCountPerRequest,
testConfig,
randomWordsFulfilledEventTimeout,
l,
)
require.Error(t, err, "Error should occur while waiting for fulfilment due to low sub balance")

pendingRequestsExist, err = vrfv2Contracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling)
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)
require.NoError(t, err)

subscriptionForCancelling, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling)
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).
Str("Returning funds to", defaultWalletAddress).
Msg("Canceling subscription and returning funds to subscription owner")

// Call OwnerCancelSubscription
tx, err := vrfv2Contracts.Coordinator.OwnerCancelSubscription(subIDForCancelling)
require.NoError(t, err, "Error canceling subscription")

subscriptionCanceledEvent, err := vrfv2Contracts.Coordinator.WaitForSubscriptionCanceledEvent([]uint64{subIDForCancelling}, time.Second*30)
require.NoError(t, err, "error waiting for subscription canceled event")

cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash())
require.NoError(t, err, "error getting tx cancellation Tx Receipt")

txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed)
cancellationTxFeeWei := new(big.Int).Mul(txGasUsed, cancellationTxReceipt.EffectiveGasPrice)

l.Info().
Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()).
Str("Effective Gas Price", cancellationTxReceipt.EffectiveGasPrice.String()).
Uint64("Gas Used", cancellationTxReceipt.GasUsed).
Msg("Cancellation TX Receipt")

l.Info().
Str("Returned Subscription Amount Link", subscriptionCanceledEvent.Amount.String()).
Uint64("SubID", subscriptionCanceledEvent.SubId).
Str("Returned to", subscriptionCanceledEvent.To.String()).
Msg("Subscription Canceled Event")

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)
require.NoError(t, err)

// Verify that subscription was deleted from Coordinator contract
_, err = vrfv2Contracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling)
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")

subFundsReturnedLinkActual := new(big.Int).Sub(walletBalanceLinkAfterSubCancelling, walletBalanceLinkBeforeSubCancelling)
l.Info().
Str("Wallet Balance Before Owner Cancelation", walletBalanceLinkBeforeSubCancelling.String()).
Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()).
Str("Sub Funds Returned Actual - Link", subFundsReturnedLinkActual.String()).
Str("Sub Balance - Link", subBalanceLink.String()).
Str("Wallet Balance After Owner Cancelation", walletBalanceLinkAfterSubCancelling.String()).
Msg("Sub funds returned")

require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled")

// Again, there is no GetActiveSubscriptionIds method on the v2 Coordinator contract, so we can't double check that the cancelled
// subID is no longer in the list of active subs
})
}

func TestVRFv2MultipleSendingKeys(t *testing.T) {
Expand Down
Loading