Skip to content

Commit

Permalink
chore/VRF-325 - Added smoke test for direct funding on VRFv2 (#11690)
Browse files Browse the repository at this point in the history
* Added VRF v2 smoke test for direct funding

* prettier

* Increased max gas in vrfv2 smoke test setup and fixed sub ID in direct funding request

* Addressing PR comment about error messages
  • Loading branch information
dkneisly authored Jan 12, 2024
1 parent 64b5ba7 commit c0f9838
Show file tree
Hide file tree
Showing 11 changed files with 1,729 additions and 1 deletion.
1 change: 1 addition & 0 deletions contracts/scripts/native_solc_compile_all_vrf
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ compileContract vrf/VRFV2Wrapper.sol
compileContract vrf/interfaces/VRFV2WrapperInterface.sol
compileContract vrf/VRFV2WrapperConsumerBase.sol
compileContract vrf/testhelpers/VRFV2WrapperConsumerExample.sol
compileContract vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol
compileContract vrf/testhelpers/VRFv2Consumer.sol

# VRF Consumers and Mocks
Expand Down
137 changes: 137 additions & 0 deletions contracts/src/v0.8/vrf/testhelpers/VRFV2WrapperLoadTestConsumer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

import {VRFV2WrapperConsumerBase} from "../VRFV2WrapperConsumerBase.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
import {ChainSpecificUtil} from "../../ChainSpecificUtil.sol";
import {VRFV2WrapperInterface} from "../interfaces/VRFV2WrapperInterface.sol";

contract VRFV2WrapperLoadTestConsumer is VRFV2WrapperConsumerBase, ConfirmedOwner {
VRFV2WrapperInterface public immutable i_vrfV2Wrapper;
uint256 public s_responseCount;
uint256 public s_requestCount;
uint256 public s_averageFulfillmentInMillions = 0; // in millions for better precision
uint256 public s_slowestFulfillment = 0;
uint256 public s_fastestFulfillment = 999;
uint256 public s_lastRequestId;
// solhint-disable-next-line chainlink-solidity/prefix-storage-variables-with-s-underscore
mapping(uint256 => uint256) internal requestHeights; // requestIds to block number when rand request was made
mapping(uint256 => RequestStatus) /* requestId */ /* requestStatus */ public s_requests;

event WrappedRequestFulfilled(uint256 requestId, uint256[] randomWords, uint256 payment);
event WrapperRequestMade(uint256 indexed requestId, uint256 paid);

struct RequestStatus {
uint256 paid;
bool fulfilled;
uint256[] randomWords;
uint256 requestTimestamp;
uint256 fulfilmentTimestamp;
uint256 requestBlockNumber;
uint256 fulfilmentBlockNumber;
}

constructor(
address _link,
address _vrfV2Wrapper
) ConfirmedOwner(msg.sender) VRFV2WrapperConsumerBase(_link, _vrfV2Wrapper) {
i_vrfV2Wrapper = VRFV2WrapperInterface(_vrfV2Wrapper);
}

function makeRequests(
uint32 _callbackGasLimit,
uint16 _requestConfirmations,
uint32 _numWords,
uint16 _requestCount
) external onlyOwner {
for (uint16 i = 0; i < _requestCount; i++) {
uint256 requestId = requestRandomness(_callbackGasLimit, _requestConfirmations, _numWords);
s_lastRequestId = requestId;
uint256 requestBlockNumber = ChainSpecificUtil._getBlockNumber();
uint256 paid = VRF_V2_WRAPPER.calculateRequestPrice(_callbackGasLimit);
s_requests[requestId] = RequestStatus({
paid: paid,
fulfilled: false,
randomWords: new uint256[](0),
requestTimestamp: block.timestamp,
fulfilmentTimestamp: 0,
requestBlockNumber: requestBlockNumber,
fulfilmentBlockNumber: 0
});
s_requestCount++;
requestHeights[requestId] = requestBlockNumber;
emit WrapperRequestMade(requestId, paid);
}
}

function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
// solhint-disable-next-line custom-errors
require(s_requests[_requestId].paid > 0, "request not found");
uint256 fulfilmentBlockNumber = ChainSpecificUtil._getBlockNumber();
uint256 requestDelay = fulfilmentBlockNumber - requestHeights[_requestId];
uint256 requestDelayInMillions = requestDelay * 1_000_000;

if (requestDelay > s_slowestFulfillment) {
s_slowestFulfillment = requestDelay;
}
if (requestDelay < s_fastestFulfillment) {
s_fastestFulfillment = requestDelay;
}
s_averageFulfillmentInMillions = s_responseCount > 0
? (s_averageFulfillmentInMillions * s_responseCount + requestDelayInMillions) / (s_responseCount + 1)
: requestDelayInMillions;

s_responseCount++;
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
s_requests[_requestId].fulfilmentTimestamp = block.timestamp;
s_requests[_requestId].fulfilmentBlockNumber = fulfilmentBlockNumber;

emit WrappedRequestFulfilled(_requestId, _randomWords, s_requests[_requestId].paid);
}

function getRequestStatus(
uint256 _requestId
)
external
view
returns (
uint256 paid,
bool fulfilled,
uint256[] memory randomWords,
uint256 requestTimestamp,
uint256 fulfilmentTimestamp,
uint256 requestBlockNumber,
uint256 fulfilmentBlockNumber
)
{
// solhint-disable-next-line custom-errors
require(s_requests[_requestId].paid > 0, "request not found");
RequestStatus memory request = s_requests[_requestId];
return (
request.paid,
request.fulfilled,
request.randomWords,
request.requestTimestamp,
request.fulfilmentTimestamp,
request.requestBlockNumber,
request.fulfilmentBlockNumber
);
}

/// @notice withdrawLink withdraws the amount specified in amount to the owner
/// @param amount the amount to withdraw, in juels
function withdrawLink(uint256 amount) external onlyOwner {
LINK.transfer(owner(), amount);
}

function reset() external {
s_averageFulfillmentInMillions = 0;
s_slowestFulfillment = 0;
s_fastestFulfillment = 999;
s_requestCount = 0;
s_responseCount = 0;
}

receive() external payable {}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ vrfv2_transparent_upgradeable_proxy: ../../contracts/solc/v0.8.6/VRFV2Transparen
vrfv2_wrapper: ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.bin d5e9a982325d2d4f517c4f2bc818795f61555408ef4b38fb59b923d144970e38
vrfv2_wrapper_consumer_example: ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.bin 3c5c9f1c501e697a7e77e959b48767e2a0bb1372393fd7686f7aaef3eb794231
vrfv2_wrapper_interface: ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.abi ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.bin ff8560169de171a68b360b7438d13863682d07040d984fd0fb096b2379421003
vrfv2_wrapper_load_test_consumer: ../../contracts/solc/v0.8.6/VRFV2WrapperLoadTestConsumer/VRFV2WrapperLoadTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2WrapperLoadTestConsumer/VRFV2WrapperLoadTestConsumer.bin 664ca7fdf4dd65cc183bc25f20708c4b369c3401bba3ee12797a93bcd70138b6
vrfv2plus_client: ../../contracts/solc/v0.8.6/VRFV2PlusClient/VRFV2PlusClient.abi ../../contracts/solc/v0.8.6/VRFV2PlusClient/VRFV2PlusClient.bin 3ffbfa4971a7e5f46051a26b1722613f265d89ea1867547ecec58500953a9501
vrfv2plus_consumer_example: ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample/VRFV2PlusConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample/VRFV2PlusConsumerExample.bin 2c480a6d7955d33a00690fdd943486d95802e48a03f3cc243df314448e4ddb2c
vrfv2plus_malicious_migrator: ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator/VRFV2PlusMaliciousMigrator.abi ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator/VRFV2PlusMaliciousMigrator.bin 80dbc98be5e42246960c889d29488f978d3db0127e95e9b295352c481d8c9b07
Expand Down
1 change: 1 addition & 0 deletions core/gethwrappers/go_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ package gethwrappers
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.bin VRFV2Wrapper vrfv2_wrapper
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.abi ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.bin VRFV2WrapperInterface vrfv2_wrapper_interface
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.bin VRFV2WrapperConsumerExample vrfv2_wrapper_consumer_example
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperLoadTestConsumer/VRFV2WrapperLoadTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2WrapperLoadTestConsumer/VRFV2WrapperLoadTestConsumer.bin VRFV2WrapperLoadTestConsumer vrfv2_wrapper_load_test_consumer

// Keepers X VRF v2
//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.bin KeepersVRFConsumer keepers_vrf_consumer
Expand Down
5 changes: 5 additions & 0 deletions integration-tests/actions/vrfv2_actions/vrfv2_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ type VRFV2Contracts struct {
LoadTestConsumers []contracts.VRFv2LoadTestConsumer
}

type VRFV2WrapperContracts struct {
VRFV2Wrapper contracts.VRFV2Wrapper
LoadTestConsumers []contracts.VRFv2WrapperLoadTestConsumer
}

// VRFV2PlusKeyData defines a jobs into and proving key info
type VRFV2KeyData struct {
VRFKey *client.VRFKey
Expand Down
152 changes: 151 additions & 1 deletion integration-tests/actions/vrfv2_actions/vrfv2_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var (

ErrWaitRandomWordsRequestedEvent = "error waiting for RandomWordsRequested event"
ErrWaitRandomWordsFulfilledEvent = "error waiting for RandomWordsFulfilled event"
ErrDeployWrapper = "error deploying VRFV2PlusWrapper"
)

func DeployVRFV2Contracts(
Expand Down Expand Up @@ -103,6 +104,46 @@ func DeployVRFV2Consumers(contractDeployer contracts.ContractDeployer, coordinat
return consumers, nil
}

func DeployVRFV2WrapperConsumers(contractDeployer contracts.ContractDeployer, linkTokenAddress string, vrfV2Wrapper contracts.VRFV2Wrapper, consumerContractsAmount int) ([]contracts.VRFv2WrapperLoadTestConsumer, error) {
var consumers []contracts.VRFv2WrapperLoadTestConsumer
for i := 1; i <= consumerContractsAmount; i++ {
loadTestConsumer, err := contractDeployer.DeployVRFV2WrapperLoadTestConsumer(linkTokenAddress, vrfV2Wrapper.Address())
if err != nil {
return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err)
}
consumers = append(consumers, loadTestConsumer)
}
return consumers, nil
}

func DeployVRFV2DirectFundingContracts(
contractDeployer contracts.ContractDeployer,
chainClient blockchain.EVMClient,
linkTokenAddress string,
linkEthFeedAddress string,
coordinator contracts.VRFCoordinatorV2,
consumerContractsAmount int,
) (*VRFV2WrapperContracts, error) {
vrfv2Wrapper, err := contractDeployer.DeployVRFV2Wrapper(linkTokenAddress, linkEthFeedAddress, coordinator.Address())
if err != nil {
return nil, fmt.Errorf("%s, err %w", ErrDeployWrapper, err)
}
err = chainClient.WaitForEvents()
if err != nil {
return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err)
}

consumers, err := DeployVRFV2WrapperConsumers(contractDeployer, linkTokenAddress, vrfv2Wrapper, consumerContractsAmount)
if err != nil {
return nil, err
}
err = chainClient.WaitForEvents()
if err != nil {
return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err)
}
return &VRFV2WrapperContracts{vrfv2Wrapper, consumers}, nil
}

func CreateVRFV2Job(
chainlinkNode *client.ChainlinkClient,
coordinatorAddress string,
Expand Down Expand Up @@ -213,7 +254,7 @@ func SetupVRFV2Environment(
l.Info().Str("Coordinator", vrfv2Contracts.Coordinator.Address()).Msg("Setting Coordinator Config")
err = vrfv2Contracts.Coordinator.SetConfig(
vrfv2Config.MinimumConfirmations,
vrfv2Config.CallbackGasLimit,
vrfv2Config.MaxGasLimitCoordinatorConfig,
vrfv2Config.StalenessSeconds,
vrfv2Config.GasAfterPaymentCalculation,
big.NewInt(vrfv2Config.LinkNativeFeedResponse),
Expand Down Expand Up @@ -308,6 +349,80 @@ func SetupVRFV2Environment(
return vrfv2Contracts, subIDs, &data, nil
}

func SetupVRFV2WrapperEnvironment(
env *test_env.CLClusterTestEnv,
vrfv2Config vrfv2_config.VRFV2Config,
linkToken contracts.LinkToken,
mockNativeLINKFeed contracts.MockETHLINKFeed,
coordinator contracts.VRFCoordinatorV2,
keyHash [32]byte,
wrapperConsumerContractsAmount int,
) (*VRFV2WrapperContracts, *uint64, error) {
// Deploy VRF v2 direct funding contracts
wrapperContracts, err := DeployVRFV2DirectFundingContracts(
env.ContractDeployer,
env.EVMClient,
linkToken.Address(),
mockNativeLINKFeed.Address(),
coordinator,
wrapperConsumerContractsAmount,
)
if err != nil {
return nil, nil, err
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err)
}

// Configure VRF v2 wrapper contract
err = wrapperContracts.VRFV2Wrapper.SetConfig(
vrfv2Config.WrapperGasOverhead,
vrfv2Config.CoordinatorGasOverhead,
vrfv2Config.WrapperPremiumPercentage,
keyHash,
vrfv2Config.WrapperMaxNumberOfWords,
)
if err != nil {
return nil, nil, err
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err)
}

// Fetch wrapper subscription ID
wrapperSubID, err := wrapperContracts.VRFV2Wrapper.GetSubID(context.Background())
if err != nil {
return nil, nil, err
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err)
}

// Fund wrapper subscription
err = FundSubscriptions(env, vrfv2Config, linkToken, coordinator, []uint64{wrapperSubID})
if err != nil {
return nil, nil, err
}

// Fund consumer with LINK
err = linkToken.Transfer(
wrapperContracts.LoadTestConsumers[0].Address(),
big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(vrfv2Config.WrapperConsumerFundingAmountLink)),
)
if err != nil {
return nil, nil, err
}
err = env.EVMClient.WaitForEvents()
if err != nil {
return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err)
}

return wrapperContracts, &wrapperSubID, nil
}

func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) {
var newNativeTokenKeyAddresses []string
for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ {
Expand Down Expand Up @@ -458,6 +573,41 @@ func FundSubscriptions(
return nil
}

func DirectFundingRequestRandomnessAndWaitForFulfillment(
consumer contracts.VRFv2WrapperLoadTestConsumer,
coordinator contracts.VRFCoordinatorV2,
vrfv2Data *VRFV2Data,
subID uint64,
randomnessRequestCountPerRequest uint16,
vrfv2Config vrfv2_config.VRFV2Config,
randomWordsFulfilledEventTimeout time.Duration,
l zerolog.Logger,
) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) {
logRandRequest(consumer.Address(), coordinator.Address(), subID, vrfv2Config, l)
_, err := consumer.RequestRandomness(
vrfv2Config.MinimumConfirmations,
vrfv2Config.CallbackGasLimit,
vrfv2Config.NumberOfWords,
randomnessRequestCountPerRequest,
)
if err != nil {
return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err)
}
wrapperAddress, err := consumer.GetWrapper(context.Background())
if err != nil {
return nil, fmt.Errorf("error getting wrapper address, err: %w", err)
}
fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents(
wrapperAddress.String(),
coordinator,
vrfv2Data,
subID,
randomWordsFulfilledEventTimeout,
l,
)
return fulfillmentEvents, err
}

func RequestRandomnessAndWaitForFulfillment(
consumer contracts.VRFv2LoadTestConsumer,
coordinator contracts.VRFCoordinatorV2,
Expand Down
2 changes: 2 additions & 0 deletions integration-tests/contracts/contract_deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,14 @@ type ContractDeployer interface {
DeployVRFConsumerV2(linkAddr string, coordinatorAddr string) (VRFConsumerV2, error)
DeployVRFv2Consumer(coordinatorAddr string) (VRFv2Consumer, error)
DeployVRFv2LoadTestConsumer(coordinatorAddr string) (VRFv2LoadTestConsumer, error)
DeployVRFV2WrapperLoadTestConsumer(linkAddr string, vrfV2WrapperAddr string) (VRFv2WrapperLoadTestConsumer, error)
DeployVRFv2PlusLoadTestConsumer(coordinatorAddr string) (VRFv2PlusLoadTestConsumer, error)
DeployVRFV2PlusWrapperLoadTestConsumer(linkAddr string, vrfV2PlusWrapperAddr string) (VRFv2PlusWrapperLoadTestConsumer, error)
DeployVRFCoordinator(linkAddr string, bhsAddr string) (VRFCoordinator, error)
DeployVRFCoordinatorV2(linkAddr string, bhsAddr string, linkEthFeedAddr string) (VRFCoordinatorV2, error)
DeployVRFCoordinatorV2_5(bhsAddr string) (VRFCoordinatorV2_5, error)
DeployVRFCoordinatorV2PlusUpgradedVersion(bhsAddr string) (VRFCoordinatorV2PlusUpgradedVersion, error)
DeployVRFV2Wrapper(linkAddr string, linkEthFeedAddr string, coordinatorAddr string) (VRFV2Wrapper, error)
DeployVRFV2PlusWrapper(linkAddr string, linkEthFeedAddr string, coordinatorAddr string) (VRFV2PlusWrapper, error)
DeployDKG() (DKG, error)
DeployOCR2VRFCoordinator(beaconPeriodBlocksCount *big.Int, linkAddr string) (VRFCoordinatorV3, error)
Expand Down
Loading

0 comments on commit c0f9838

Please sign in to comment.