chore: tests (#5)

* chore: tests

* fix: add helper to get all metadata from idCommitment

* fix: max runs

* fix: full tree test

* fix: refactor, reuse function for memberExists

* fix: getCommitments range and cut scope from other tests

* fix: range for assert on pagination

* fix: duped conditionals
This commit is contained in:
Aaryamann Challani 2024-05-28 15:33:16 +05:30 committed by GitHub
parent 6d028a1308
commit 4c6b8fb253
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 189 additions and 24 deletions

View File

@ -1 +1,13 @@
WakuRlnV2Test:test__ValidRegistration() (gas: 108661)
WakuRlnV2Test:test__IdCommitmentToMetadata__DoesntExist() (gas: 8299)
WakuRlnV2Test:test__InvalidPaginationQuery__EndIndexGTIdCommitmentIndex() (gas: 13351)
WakuRlnV2Test:test__InvalidPaginationQuery__StartIndexGTEndIndex() (gas: 11184)
WakuRlnV2Test:test__InvalidRegistration__DuplicateIdCommitment() (gas: 111313)
WakuRlnV2Test:test__InvalidRegistration__FullTree() (gas: 97043)
WakuRlnV2Test:test__InvalidRegistration__InvalidIdCommitment__LargerThanField() (gas: 9926)
WakuRlnV2Test:test__InvalidRegistration__InvalidIdCommitment__Zero() (gas: 9139)
WakuRlnV2Test:test__InvalidRegistration__InvalidUserMessageLimit__LargerThanMax() (gas: 10147)
WakuRlnV2Test:test__InvalidRegistration__InvalidUserMessageLimit__Zero() (gas: 9242)
WakuRlnV2Test:test__ValidPaginationQuery(uint32) (runs: 1002, μ: 403497, ~: 136269)
WakuRlnV2Test:test__ValidPaginationQuery__OneElement() (gas: 128461)
WakuRlnV2Test:test__ValidRegistration(uint256,uint32) (runs: 1001, μ: 133518, ~: 133518)
WakuRlnV2Test:test__ValidRegistration__kats() (gas: 108902)

View File

@ -17,6 +17,9 @@
src = "src"
test = "test"
[fuzz]
max_test_rejects = 128_000
[profile.ci]
fuzz = { runs = 10_000 }
verbosity = 4

View File

@ -37,6 +37,7 @@ contract WakuRlnV2 {
/// @notice The index of the next member to be registered
uint32 public idCommitmentIndex = 0;
/// @notice the membership metadata of the member
struct MembershipInfo {
/// @notice the user message limit of each member
uint32 userMessageLimit;
@ -59,17 +60,21 @@ contract WakuRlnV2 {
/// @param index The index of the member in the set
event MemberRegistered(uint256 idCommitment, uint32 userMessageLimit, uint32 index);
/// @notice the modifier to check if the idCommitment is valid
/// @param idCommitment The idCommitment of the member
modifier onlyValidIdCommitment(uint256 idCommitment) {
if (!isValidCommitment(idCommitment)) revert InvalidIdCommitment(idCommitment);
_;
}
modifier onlyValidUserMessageLimit(uint32 messageLimit) {
if (messageLimit > MAX_MESSAGE_LIMIT) revert InvalidUserMessageLimit(messageLimit);
if (messageLimit == 0) revert InvalidUserMessageLimit(messageLimit);
/// @notice the modifier to check if the userMessageLimit is valid
/// @param userMessageLimit The user message limit
modifier onlyValidUserMessageLimit(uint32 userMessageLimit) {
if (!isValidUserMessageLimit(userMessageLimit)) revert InvalidUserMessageLimit(userMessageLimit);
_;
}
/// @notice the constructor of the contract
constructor(uint32 maxMessageLimit) {
MAX_MESSAGE_LIMIT = maxMessageLimit;
SET_SIZE = uint32(1 << DEPTH);
@ -77,9 +82,45 @@ contract WakuRlnV2 {
LazyIMT.init(imtData, DEPTH);
}
function memberExists(uint256 idCommitment) public view returns (bool) {
/// @notice Checks if a commitment is valid
/// @param idCommitment The idCommitment of the member
/// @return true if the commitment is valid, false otherwise
function isValidCommitment(uint256 idCommitment) public pure returns (bool) {
return idCommitment != 0 && idCommitment < Q;
}
/// @notice Checks if a user message limit is valid
/// @param userMessageLimit The user message limit
/// @return true if the user message limit is valid, false otherwise
function isValidUserMessageLimit(uint32 userMessageLimit) public view returns (bool) {
return userMessageLimit > 0 && userMessageLimit <= MAX_MESSAGE_LIMIT;
}
/// @notice Returns the rateCommitment of a member
/// @param index The index of the member
/// @return The rateCommitment of the member
function indexToCommitment(uint32 index) internal view returns (uint256) {
return imtData.elements[LazyIMT.indexForElement(0, index)];
}
/// @notice Returns the metadata of a member
/// @param idCommitment The idCommitment of the member
/// @return The metadata of the member (userMessageLimit, index, rateCommitment)
function idCommitmentToMetadata(uint256 idCommitment) public view returns (uint32, uint32, uint256) {
MembershipInfo memory member = memberInfo[idCommitment];
return member.userMessageLimit > 0 && member.index >= 0;
// we cannot call indexToCommitment for 0 index if the member doesn't exist
if (member.userMessageLimit == 0) {
return (0, 0, 0);
}
return (member.userMessageLimit, member.index, indexToCommitment(member.index));
}
/// @notice Checks if a member exists
/// @param idCommitment The idCommitment of the member
/// @return true if the member exists, false otherwise
function memberExists(uint256 idCommitment) public view returns (bool) {
(,, uint256 rateCommitment) = idCommitmentToMetadata(idCommitment);
return rateCommitment != 0;
}
/// Allows a user to register as a member
@ -112,29 +153,30 @@ contract WakuRlnV2 {
idCommitmentIndex += 1;
}
function isValidCommitment(uint256 idCommitment) public pure returns (bool) {
return idCommitment != 0 && idCommitment < Q;
}
function indexToCommitment(uint32 index) public view returns (uint256) {
return imtData.elements[LazyIMT.indexForElement(0, index)];
}
/// @notice Returns the commitments of a range of members
/// @param startIndex The start index of the range
/// @param endIndex The end index of the range
/// @return The commitments of the members
function getCommitments(uint32 startIndex, uint32 endIndex) public view returns (uint256[] memory) {
if (startIndex >= endIndex) revert InvalidPaginationQuery(startIndex, endIndex);
if (startIndex > endIndex) revert InvalidPaginationQuery(startIndex, endIndex);
if (endIndex > idCommitmentIndex) revert InvalidPaginationQuery(startIndex, endIndex);
uint256[] memory commitments = new uint256[](endIndex - startIndex);
for (uint32 i = startIndex; i < endIndex; i++) {
uint256[] memory commitments = new uint256[](endIndex - startIndex + 1);
for (uint32 i = startIndex; i <= endIndex; i++) {
commitments[i - startIndex] = indexToCommitment(i);
}
return commitments;
}
/// @notice Returns the root of the IMT
/// @return The root of the IMT
function root() external view returns (uint256) {
return LazyIMT.root(imtData, DEPTH);
}
/// @notice Returns the merkle proof elements of a given membership
/// @param index The index of the member
/// @return The merkle proof elements of the member
function merkleProofElements(uint40 index) public view returns (uint256[] memory) {
return LazyIMT.merkleProofElements(imtData, index, DEPTH);
}

View File

@ -1,15 +1,18 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19 <0.9.0;
import { Test, console } from "forge-std/Test.sol";
import { Test } from "forge-std/Test.sol";
import { stdStorage, StdStorage } from "forge-std/Test.sol";
import { Deploy } from "../script/Deploy.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
import { WakuRlnV2 } from "../src/WakuRlnV2.sol";
import "../src/WakuRlnV2.sol";
import { PoseidonT3 } from "poseidon-solidity/PoseidonT3.sol";
import { LazyIMT } from "@zk-kit/imt.sol/LazyIMT.sol";
contract WakuRlnV2Test is Test {
using stdStorage for StdStorage;
WakuRlnV2 internal w;
DeploymentConfig internal deploymentConfig;
@ -20,7 +23,7 @@ contract WakuRlnV2Test is Test {
(w, deploymentConfig) = deployment.run();
}
function test__ValidRegistration() external {
function test__ValidRegistration__kats() external {
vm.pauseGasMetering();
uint256 idCommitment = 2;
uint32 userMessageLimit = 2;
@ -35,14 +38,119 @@ contract WakuRlnV2Test is Test {
// kats from zerokit
uint256 rateCommitment =
4_699_387_056_273_519_054_140_667_386_511_343_037_709_699_938_246_587_880_795_929_666_834_307_503_001;
assertEq(w.indexToCommitment(0), rateCommitment);
uint256[] memory commitments = w.getCommitments(0, 1);
assertEq(commitments.length, 1);
assertEq(commitments[index], rateCommitment);
assertEq(
w.root(),
13_801_897_483_540_040_307_162_267_952_866_411_686_127_372_014_953_358_983_481_592_640_000_001_877_295
);
(uint32 fetchedUserMessageLimit2, uint32 index2, uint256 rateCommitment2) =
w.idCommitmentToMetadata(idCommitment);
assertEq(fetchedUserMessageLimit2, userMessageLimit);
assertEq(index2, 0);
assertEq(rateCommitment2, rateCommitment);
vm.resumeGasMetering();
}
function test__ValidRegistration(uint256 idCommitment, uint32 userMessageLimit) external {
vm.assume(w.isValidCommitment(idCommitment) && w.isValidUserMessageLimit(userMessageLimit));
assertEq(w.memberExists(idCommitment), false);
w.register(idCommitment, userMessageLimit);
uint256 rateCommitment = PoseidonT3.hash([idCommitment, userMessageLimit]);
(uint32 fetchedUserMessageLimit, uint32 index, uint256 fetchedRateCommitment) =
w.idCommitmentToMetadata(idCommitment);
assertEq(fetchedUserMessageLimit, userMessageLimit);
assertEq(index, 0);
assertEq(fetchedRateCommitment, rateCommitment);
}
function test__IdCommitmentToMetadata__DoesntExist() external {
uint256 idCommitment = 2;
(uint32 userMessageLimit, uint32 index, uint256 rateCommitment) = w.idCommitmentToMetadata(idCommitment);
assertEq(userMessageLimit, 0);
assertEq(index, 0);
assertEq(rateCommitment, 0);
}
function test__InvalidRegistration__InvalidIdCommitment__Zero() external {
uint256 idCommitment = 0;
uint32 userMessageLimit = 2;
vm.expectRevert(abi.encodeWithSelector(InvalidIdCommitment.selector, 0));
w.register(idCommitment, userMessageLimit);
}
function test__InvalidRegistration__InvalidIdCommitment__LargerThanField() external {
uint256 idCommitment = w.Q() + 1;
uint32 userMessageLimit = 2;
vm.expectRevert(abi.encodeWithSelector(InvalidIdCommitment.selector, idCommitment));
w.register(idCommitment, userMessageLimit);
}
function test__InvalidRegistration__InvalidUserMessageLimit__Zero() external {
uint256 idCommitment = 2;
uint32 userMessageLimit = 0;
vm.expectRevert(abi.encodeWithSelector(InvalidUserMessageLimit.selector, 0));
w.register(idCommitment, userMessageLimit);
}
function test__InvalidRegistration__InvalidUserMessageLimit__LargerThanMax() external {
uint256 idCommitment = 2;
uint32 userMessageLimit = w.MAX_MESSAGE_LIMIT() + 1;
vm.expectRevert(abi.encodeWithSelector(InvalidUserMessageLimit.selector, userMessageLimit));
w.register(idCommitment, userMessageLimit);
}
function test__InvalidRegistration__DuplicateIdCommitment() external {
uint256 idCommitment = 2;
uint32 userMessageLimit = 2;
w.register(idCommitment, userMessageLimit);
vm.expectRevert(DuplicateIdCommitment.selector);
w.register(idCommitment, userMessageLimit);
}
function test__InvalidRegistration__FullTree() external {
uint32 userMessageLimit = 2;
// we progress the tree to the last leaf
stdstore.target(address(w)).sig("idCommitmentIndex()").checked_write(1 << w.DEPTH());
vm.expectRevert(FullTree.selector);
w.register(1, userMessageLimit);
}
function test__InvalidPaginationQuery__StartIndexGTEndIndex() external {
vm.expectRevert(abi.encodeWithSelector(InvalidPaginationQuery.selector, 1, 0));
w.getCommitments(1, 0);
}
function test__InvalidPaginationQuery__EndIndexGTIdCommitmentIndex() external {
vm.expectRevert(abi.encodeWithSelector(InvalidPaginationQuery.selector, 0, 2));
w.getCommitments(0, 2);
}
function test__ValidPaginationQuery__OneElement() external {
uint32 userMessageLimit = 2;
uint256 idCommitment = 1;
w.register(idCommitment, userMessageLimit);
uint256[] memory commitments = w.getCommitments(0, 0);
assertEq(commitments.length, 1);
uint256 rateCommitment = PoseidonT3.hash([idCommitment, userMessageLimit]);
assertEq(commitments[0], rateCommitment);
}
function test__ValidPaginationQuery(uint32 idCommitmentsLength) external {
vm.assume(idCommitmentsLength > 0 && idCommitmentsLength <= 100);
uint32 userMessageLimit = 2;
vm.pauseGasMetering();
for (uint256 i = 0; i < idCommitmentsLength; i++) {
w.register(i + 1, userMessageLimit);
}
vm.resumeGasMetering();
uint256[] memory commitments = w.getCommitments(0, idCommitmentsLength);
assertEq(commitments.length, idCommitmentsLength + 1);
for (uint256 i = 0; i < idCommitmentsLength; i++) {
uint256 rateCommitment = PoseidonT3.hash([i + 1, userMessageLimit]);
assertEq(commitments[i], rateCommitment);
}
}
}