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

chore/VRF-325 - Added smoke test for direct funding on VRFv2 #11690

Merged
merged 5 commits into from
Jan 12, 2024
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
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;
iljapavlovs marked this conversation as resolved.
Show resolved Hide resolved

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is everything commented out here? You uncomment and run a specific line only when you need it?

Copy link
Collaborator

@iljapavlovs iljapavlovs Jan 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its just how it works, it should be commented out. check https://go.dev/blog/generate.

so //go:generate will be invoked when "go generate" cmd will be invoked from cli. Which in turn you need to invoke from make command - https://github.com/smartcontractkit/chainlink/blob/develop/contracts/GNUmakefile#L72C1-L72C1

So whenever you add a solidity contract or update it, you need to invoke make wrappers-all from contracts folder.

In case if you have added new contract, then afterwards you need to ensure that you have added the contracts' golang binding, in this case it's vrfv2_wrapper_load_test_consumer.go. you need to add it from git unversioned files


// 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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to do assignment expressions in Go (assign the variable and check the condition in one line)? Or this is the standard way to do error handling in Go? Just curious if anyone knows... I have very little knowledge of Go.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it would look smth like this:

	if err := env.EVMClient.WaitForEvents(); err != nil {
		return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err)
	}

but its matter of preference

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)
}
dkneisly marked this conversation as resolved.
Show resolved Hide resolved

// 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
Loading