-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
vrfv2plus: Add batch coordinator tests #13497
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
dc1f494
vrfv2plus: Add batch coordinator tests
leeyikjiun e5e21e8
Add function comment for print generate proof v2 plus
leeyikjiun 0eaea4c
Merge branch 'develop' into vrf-add-batch-coordinator-tests
leeyikjiun c78e2c6
Merge branch 'develop' into vrf-add-batch-coordinator-tests
leeyikjiun 23ab851
Merge branch 'develop' into vrf-add-batch-coordinator-tests
leeyikjiun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
contracts/src/v0.8/vrf/test/BatchVRFCoordinatorV2Plus.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
pragma solidity 0.8.19; | ||
|
||
import {console} from "forge-std/console.sol"; | ||
import {VRF} from "../VRF.sol"; | ||
import {VRFTypes} from "../VRFTypes.sol"; | ||
import {BatchVRFCoordinatorV2Plus} from "../dev/BatchVRFCoordinatorV2Plus.sol"; | ||
import {VRFV2PlusClient} from "../dev/libraries/VRFV2PlusClient.sol"; | ||
import {VRFCoordinatorV2_5} from "../dev/VRFCoordinatorV2_5.sol"; | ||
import "./BaseTest.t.sol"; | ||
import {FixtureVRFCoordinatorV2_5} from "./FixtureVRFCoordinatorV2_5.t.sol"; | ||
|
||
contract BatchVRFCoordinatorV2PlusTest is FixtureVRFCoordinatorV2_5 { | ||
BatchVRFCoordinatorV2Plus private s_batchCoordinator; | ||
|
||
event RandomWordsFulfilled( | ||
uint256 indexed requestId, | ||
uint256 outputSeed, | ||
uint256 indexed subId, | ||
uint96 payment, | ||
bool nativePayment, | ||
bool success, | ||
bool onlyPremium | ||
); | ||
|
||
function setUp() public override { | ||
FixtureVRFCoordinatorV2_5.setUp(); | ||
|
||
s_batchCoordinator = new BatchVRFCoordinatorV2Plus(address(s_coordinator)); | ||
} | ||
|
||
function test_fulfillRandomWords() public { | ||
_setUpConfig(); | ||
_setUpProvingKey(); | ||
_setUpSubscription(); | ||
|
||
uint32 requestBlock = 10; | ||
vm.roll(requestBlock); | ||
|
||
vm.startPrank(SUBSCRIPTION_OWNER); | ||
vm.deal(SUBSCRIPTION_OWNER, 10 ether); | ||
s_coordinator.fundSubscriptionWithNative{value: 10 ether}(s_subId); | ||
|
||
// Request random words. | ||
s_consumer.requestRandomWords(CALLBACK_GAS_LIMIT, MIN_CONFIRMATIONS, NUM_WORDS, VRF_KEY_HASH, true); | ||
vm.stopPrank(); | ||
|
||
// Move on to the next block. | ||
// Store the previous block's blockhash. | ||
vm.roll(requestBlock + 1); | ||
s_bhs.store(requestBlock); | ||
|
||
VRFTypes.Proof[] memory proofs = new VRFTypes.Proof[](2); | ||
VRFTypes.RequestCommitmentV2Plus[] memory rcs = new VRFTypes.RequestCommitmentV2Plus[](2); | ||
|
||
// Proof generated via the generate-proof-v2-plus script command. Example usage: | ||
// _printGenerateProofV2PlusCommand(address(s_consumer), 1, requestBlock, true); | ||
/* | ||
go run . generate-proof-v2-plus \ | ||
-key-hash 0x9f2353bde94264dbc3d554a94cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528 \ | ||
-pre-seed 33855227690351884611579800220581891477580182035146587491531555927634180294480 \ | ||
-block-hash 0x0a \ | ||
-block-num 10 \ | ||
-sender 0xdc90e8ce61c1af8a638b95264037c8e67ee5765c \ | ||
-native-payment true | ||
|
||
*/ | ||
proofs[0] = VRFTypes.Proof({ | ||
pk: [ | ||
72488970228380509287422715226575535698893157273063074627791787432852706183111, | ||
62070622898698443831883535403436258712770888294397026493185421712108624767191 | ||
], | ||
gamma: [ | ||
80420391742429647505172101941811820476888293644816377569181566466584288434705, | ||
24046736031266889997051641830469514057863365715722268340801477580836256044582 | ||
], | ||
c: 74775128390693502914275156263410881155583102046081919417827483535122161050585, | ||
s: 69563235412360165148368009853509434870917653835330501139204071967997764190111, | ||
seed: 33855227690351884611579800220581891477580182035146587491531555927634180294480, | ||
uWitness: 0xfB0663eaf48785540dE0FD0F837FD9c09BF4B80A, | ||
cGammaWitness: [ | ||
53711159452748734758194447734939737695995909567499536035707522847057731697403, | ||
113650002631484103366420937668971311744887820666944514581352028601506700116835 | ||
], | ||
sHashWitness: [ | ||
89656531714223714144489731263049239277719465105516547297952288438117443488525, | ||
90859682705760125677895017864538514058733199985667976488434404721197234427011 | ||
], | ||
zInv: 97275608653505690744303242942631893944856831559408852202478373762878300587548 | ||
}); | ||
rcs[0] = VRFTypes.RequestCommitmentV2Plus({ | ||
blockNum: requestBlock, | ||
subId: s_subId, | ||
callbackGasLimit: CALLBACK_GAS_LIMIT, | ||
numWords: 1, | ||
sender: address(s_consumer), | ||
extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: true})) | ||
}); | ||
|
||
VRFCoordinatorV2_5.Output memory output = s_coordinator.getRandomnessFromProofExternal( | ||
abi.decode(abi.encode(proofs[0]), (VRF.Proof)), | ||
rcs[0] | ||
); | ||
|
||
requestBlock = 20; | ||
vm.roll(requestBlock); | ||
|
||
vm.startPrank(SUBSCRIPTION_OWNER); | ||
s_linkToken.setBalance(address(SUBSCRIPTION_OWNER), 10 ether); | ||
s_linkToken.transferAndCall(address(s_coordinator), 10 ether, abi.encode(s_subId)); | ||
|
||
// Request random words. | ||
s_consumer1.requestRandomWords(CALLBACK_GAS_LIMIT, MIN_CONFIRMATIONS, NUM_WORDS, VRF_KEY_HASH, false); | ||
vm.stopPrank(); | ||
|
||
// Move on to the next block. | ||
// Store the previous block's blockhash. | ||
vm.roll(requestBlock + 1); | ||
s_bhs.store(requestBlock); | ||
|
||
// Proof generated via the generate-proof-v2-plus script command. Example usage: | ||
// _printGenerateProofV2PlusCommand(address(s_consumer1), 1, requestBlock, false); | ||
/* | ||
go run . generate-proof-v2-plus \ | ||
-key-hash 0x9f2353bde94264dbc3d554a94cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528 \ | ||
-pre-seed 76568185840201037774581758921393822690942290841865097674309745036496166431060 \ | ||
-block-hash 0x14 \ | ||
-block-num 20 \ | ||
-sender 0x2f1c0761d6e4b1e5f01968d6c746f695e5f3e25d \ | ||
-native-payment false | ||
*/ | ||
proofs[1] = VRFTypes.Proof({ | ||
pk: [ | ||
72488970228380509287422715226575535698893157273063074627791787432852706183111, | ||
62070622898698443831883535403436258712770888294397026493185421712108624767191 | ||
], | ||
gamma: [ | ||
21323932463597506192387578758854201988004673105893105492473194972397109828006, | ||
96834737826889397196571646974355352644437196500310392203712129010026003355112 | ||
], | ||
c: 8775807990949224376582975115621037245862755412370175152581490650310350359728, | ||
s: 6805708577951013810918872616271445638109899206333819877111740872779453350091, | ||
seed: 76568185840201037774581758921393822690942290841865097674309745036496166431060, | ||
uWitness: 0xE82fF24Fecfbe73d682f38308bE3E039Dfabdf5c, | ||
cGammaWitness: [ | ||
92810770919624535241476539842820168209710445519252592382122118536598338376923, | ||
17271305664006119131434661141858450289379246199095231636439133258170648418554 | ||
], | ||
sHashWitness: [ | ||
29540023305939374439696120003978246982707698669656874393367212257432197207536, | ||
93902323936532381028323379401739289810874348405259732508442252936582467730050 | ||
], | ||
zInv: 88845170436601946907659333156418518556235340365885668267853966404617557948692 | ||
}); | ||
rcs[1] = VRFTypes.RequestCommitmentV2Plus({ | ||
blockNum: requestBlock, | ||
subId: s_subId, | ||
callbackGasLimit: CALLBACK_GAS_LIMIT, | ||
numWords: 1, | ||
sender: address(s_consumer1), | ||
extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false})) | ||
}); | ||
|
||
VRFCoordinatorV2_5.Output memory output1 = s_coordinator.getRandomnessFromProofExternal( | ||
abi.decode(abi.encode(proofs[1]), (VRF.Proof)), | ||
rcs[1] | ||
); | ||
|
||
// The payments are NOT pre-calculated and simply copied from the actual event. | ||
// We can assert and ignore the payment field but the code will be considerably longer. | ||
vm.expectEmit(true, true, false, true, address(s_coordinator)); | ||
emit RandomWordsFulfilled(output.requestId, output.randomness, s_subId, 500000000000142215, true, true, false); | ||
vm.expectEmit(true, true, false, true, address(s_coordinator)); | ||
emit RandomWordsFulfilled(output1.requestId, output1.randomness, s_subId, 800000000000304103, false, true, false); | ||
|
||
// Fulfill the requests. | ||
s_batchCoordinator.fulfillRandomWords(proofs, rcs); | ||
} | ||
} |
120 changes: 120 additions & 0 deletions
120
contracts/src/v0.8/vrf/test/FixtureVRFCoordinatorV2_5.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
pragma solidity ^0.8.19; | ||
|
||
import {console} from "forge-std/console.sol"; | ||
import "@openzeppelin/contracts/utils/Strings.sol"; | ||
import {VRF} from "../VRF.sol"; | ||
import {VRFTypes} from "../VRFTypes.sol"; | ||
import {BlockhashStore} from "../dev/BlockhashStore.sol"; | ||
import {VRFV2PlusClient} from "../dev/libraries/VRFV2PlusClient.sol"; | ||
import {ExposedVRFCoordinatorV2_5} from "../dev/testhelpers/ExposedVRFCoordinatorV2_5.sol"; | ||
import {VRFV2PlusConsumerExample} from "../dev/testhelpers/VRFV2PlusConsumerExample.sol"; | ||
import {MockLinkToken} from "../../mocks/MockLinkToken.sol"; | ||
import {MockV3Aggregator} from "../../tests/MockV3Aggregator.sol"; | ||
import "./BaseTest.t.sol"; | ||
|
||
contract FixtureVRFCoordinatorV2_5 is BaseTest, VRF { | ||
address internal SUBSCRIPTION_OWNER = makeAddr("SUBSCRIPTION_OWNER"); | ||
|
||
uint64 internal constant GAS_LANE_MAX_GAS = 5000 gwei; | ||
uint16 internal constant MIN_CONFIRMATIONS = 0; | ||
uint32 internal constant CALLBACK_GAS_LIMIT = 1_000_000; | ||
uint32 internal constant NUM_WORDS = 1; | ||
|
||
// VRF KeyV2 generated from a node; not sensitive information. | ||
// The secret key used to generate this key is: 10. | ||
bytes internal constant VRF_UNCOMPRESSED_PUBLIC_KEY = | ||
hex"a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7"; | ||
bytes internal constant VRF_COMPRESSED_PUBLIC_KEY = | ||
hex"a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c701"; | ||
bytes32 internal constant VRF_KEY_HASH = hex"9f2353bde94264dbc3d554a94cceba2d7d2b4fdce4304d3e09a1fea9fbeb1528"; | ||
|
||
BlockhashStore internal s_bhs; | ||
ExposedVRFCoordinatorV2_5 internal s_coordinator; | ||
|
||
// Use multiple consumers because VRFV2PlusConsumerExample cannot have multiple pending requests. | ||
uint256 internal s_subId; | ||
VRFV2PlusConsumerExample internal s_consumer; | ||
VRFV2PlusConsumerExample internal s_consumer1; | ||
|
||
MockLinkToken internal s_linkToken; | ||
MockV3Aggregator internal s_linkNativeFeed; | ||
|
||
function setUp() public virtual override { | ||
BaseTest.setUp(); | ||
vm.stopPrank(); | ||
|
||
vm.startPrank(OWNER); | ||
s_bhs = new BlockhashStore(); | ||
|
||
// Deploy coordinator. | ||
s_coordinator = new ExposedVRFCoordinatorV2_5(address(s_bhs)); | ||
s_linkToken = new MockLinkToken(); | ||
s_linkNativeFeed = new MockV3Aggregator(18, 500000000000000000); // .5 ETH (good for testing) | ||
|
||
// Configure the coordinator. | ||
s_coordinator.setLINKAndLINKNativeFeed(address(s_linkToken), address(s_linkNativeFeed)); | ||
vm.stopPrank(); | ||
|
||
// Deploy consumers. | ||
vm.startPrank(SUBSCRIPTION_OWNER); | ||
s_consumer = new VRFV2PlusConsumerExample(address(s_coordinator), address(s_linkToken)); | ||
s_consumer1 = new VRFV2PlusConsumerExample(address(s_coordinator), address(s_linkToken)); | ||
vm.stopPrank(); | ||
} | ||
|
||
function _setUpConfig() internal { | ||
vm.prank(OWNER); | ||
s_coordinator.setConfig( | ||
0, // minRequestConfirmations | ||
2_500_000, // maxGasLimit | ||
1, // stalenessSeconds | ||
50_000, // gasAfterPaymentCalculation | ||
50000000000000000, // fallbackWeiPerUnitLink | ||
500_000, // fulfillmentFlatFeeNativePPM | ||
100_000, // fulfillmentFlatFeeLinkDiscountPPM | ||
15, // nativePremiumPercentage | ||
10 // linkPremiumPercentage | ||
); | ||
} | ||
|
||
function _setUpProvingKey() internal { | ||
uint256[2] memory uncompressedKeyParts = this._getProvingKeyParts(VRF_UNCOMPRESSED_PUBLIC_KEY); | ||
vm.prank(OWNER); | ||
s_coordinator.registerProvingKey(uncompressedKeyParts, GAS_LANE_MAX_GAS); | ||
} | ||
|
||
function _setUpSubscription() internal { | ||
vm.startPrank(SUBSCRIPTION_OWNER); | ||
s_subId = s_coordinator.createSubscription(); | ||
s_coordinator.addConsumer(s_subId, address(s_consumer)); | ||
s_consumer.setSubId(s_subId); | ||
s_coordinator.addConsumer(s_subId, address(s_consumer1)); | ||
s_consumer1.setSubId(s_subId); | ||
vm.stopPrank(); | ||
} | ||
|
||
// note: Call this function via this.getProvingKeyParts to be able to pass memory as calldata and | ||
// index over the byte array. | ||
function _getProvingKeyParts(bytes calldata uncompressedKey) public pure returns (uint256[2] memory) { | ||
uint256 keyPart1 = uint256(bytes32(uncompressedKey[0:32])); | ||
uint256 keyPart2 = uint256(bytes32(uncompressedKey[32:64])); | ||
return [keyPart1, keyPart2]; | ||
} | ||
|
||
function _printGenerateProofV2PlusCommand( | ||
ibrajer marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice 👍 |
||
address sender, | ||
uint64 nonce, | ||
uint256 requestBlock, | ||
bool nativePayment | ||
) internal { | ||
(, uint256 preSeed) = s_coordinator.computeRequestIdExternal(VRF_KEY_HASH, sender, s_subId, nonce); | ||
|
||
console.log("go run . generate-proof-v2-plus \\"); | ||
console.log(string.concat(" -key-hash ", Strings.toHexString(uint256(VRF_KEY_HASH)), " \\")); | ||
console.log(string.concat(" -pre-seed ", Strings.toString(preSeed), " \\")); | ||
console.log(string.concat(" -block-hash ", Strings.toHexString(uint256(blockhash(requestBlock))), " \\")); | ||
console.log(string.concat(" -block-num ", Strings.toString(requestBlock), " \\")); | ||
console.log(string.concat(" -sender ", Strings.toHexString(sender), " \\")); | ||
console.log(string.concat(" -native-payment ", nativePayment ? "true" : "false")); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to understand what this test is supposed to check in the end. If it's supposed to check that
fulfillRandomWords
will not revert, then it's fine. But if it's supposed to check the expected state caused by calling thefulfillRandomWords
function, then I guess checking the subscription balance might be the best one to check?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, the main motivation for the test is to have some code from the test to trigger the
fulfillRandomWords
so that I can get its gas usage. 😅So right now, the assertion is quite minimal and it just asserts that the 2 requests are fulfilled.
On hindsight, I don't think we should be doing extensive assertions here as well, as subscription balances check should be done by the coordinator itself. The batch coordinator's job is just to proxy over to the coordinator and fulfill multiple requests. We could do those checks here as well but it will really be a redundant check already covered by the coordinator. If the logic were to change, then we'll need to fix multiple places as well.