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: add the initial contract after few optimizations #4

Merged
merged 13 commits into from
May 23, 2024
18 changes: 0 additions & 18 deletions .gas-report
Original file line number Diff line number Diff line change
@@ -1,18 +0,0 @@
| script/Deploy.s.sol:Deploy contract | | | | | |
|-------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost | Deployment Size | | | | |
| 320782 | 2729 | | | | |
| Function Name | min | avg | median | max | # calls |
| run | 221942 | 221942 | 221942 | 221942 | 1 |


| src/Foo.sol:Foo contract | | | | | |
|--------------------------|-----------------|-----|--------|-----|---------|
| Deployment Cost | Deployment Size | | | | |
| 20275 | 131 | | | | |
| Function Name | min | avg | median | max | # calls |
| id | 235 | 235 | 235 | 235 | 1 |




2 changes: 1 addition & 1 deletion .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1 +1 @@
FooTest:test_Example() (gas: 8662)
WakuRlnV2Test:test__ValidRegistration() (gas: 108661)
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ jobs:
- name: "Install Foundry"
uses: "foundry-rs/foundry-toolchain@v1"

- name: "Install Pnpm"
uses: "pnpm/action-setup@v2"
with:
version: "8"

- name: "Install Node.js"
uses: "actions/setup-node@v3"
with:
cache: "pnpm"
node-version: "lts/*"

- name: "Install the Node.js dependencies"
run: "pnpm install"

- name: "Build the contracts and print their size"
run: "forge build --sizes"

Expand All @@ -79,6 +93,20 @@ jobs:
- name: "Install Foundry"
uses: "foundry-rs/foundry-toolchain@v1"

- name: "Install Pnpm"
uses: "pnpm/action-setup@v2"
with:
version: "8"

- name: "Install Node.js"
uses: "actions/setup-node@v3"
with:
cache: "pnpm"
node-version: "lts/*"

- name: "Install the Node.js dependencies"
run: "pnpm install"

- name: "Show the Foundry config"
run: "forge config"

Expand Down Expand Up @@ -108,6 +136,20 @@ jobs:
- name: "Install Foundry"
uses: "foundry-rs/foundry-toolchain@v1"

- name: "Install Pnpm"
uses: "pnpm/action-setup@v2"
with:
version: "8"

- name: "Install Node.js"
uses: "actions/setup-node@v3"
with:
cache: "pnpm"
node-version: "lts/*"

- name: "Install the Node.js dependencies"
run: "pnpm install"

- name: "Generate the coverage report using the unit and the integration tests"
run: 'forge coverage --match-path "test/**/*.sol" --report lcov'

Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"solhint-community": "^3.6.0",
"commit-and-tag-version": "^12.2.0"
},
"dependencies": {
"@zk-kit/imt.sol": "https://gitpkg.now.sh/privacy-scaling-explorations/zk-kit/packages/imt.sol?0699fd1e5ad3683ae0090e0626f75d7834145500",
"poseidon-solidity": "^0.0.5"
},
"keywords": [
"blockchain",
"ethereum",
Expand Down
20 changes: 20 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
forge-std/=lib/forge-std/src/
@zk-kit/imt.sol/=node_modules/@zk-kit/imt.sol/contracts
poseidon-solidity/=node_modules/poseidon-solidity/
6 changes: 3 additions & 3 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19 <=0.9.0;

import { Foo } from "../src/Foo.sol";
import { WakuRlnV2 } from "../src/WakuRlnV2.sol";
import { BaseScript } from "./Base.s.sol";
import { DeploymentConfig } from "./DeploymentConfig.s.sol";

contract Deploy is BaseScript {
function run() public returns (Foo foo, DeploymentConfig deploymentConfig) {
function run() public returns (WakuRlnV2 w, DeploymentConfig deploymentConfig) {
deploymentConfig = new DeploymentConfig(broadcaster);
foo = new Foo();
w = new WakuRlnV2(20);
}
}
8 changes: 0 additions & 8 deletions src/Foo.sol

This file was deleted.

141 changes: 141 additions & 0 deletions src/WakuRlnV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

import { LazyIMT, LazyIMTData } from "@zk-kit/imt.sol/LazyIMT.sol";
import { PoseidonT3 } from "poseidon-solidity/PoseidonT3.sol";

/// The tree is full
error FullTree();

/// Member is already registered
error DuplicateIdCommitment();

/// Invalid idCommitment
error InvalidIdCommitment(uint256 idCommitment);

/// Invalid userMessageLimit
error InvalidUserMessageLimit(uint32 messageLimit);

/// Invalid pagination query
error InvalidPaginationQuery(uint256 startIndex, uint256 endIndex);

contract WakuRlnV2 {
/// @notice The Field
uint256 public constant Q =
21_888_242_871_839_275_222_246_405_745_257_275_088_548_364_400_416_034_343_698_204_186_575_808_495_617;

/// @notice The max message limit per epoch
uint32 public immutable MAX_MESSAGE_LIMIT;

Check warning on line 29 in src/WakuRlnV2.sol

View workflow job for this annotation

GitHub Actions / lint

Variable name must be in mixedCase

/// @notice The depth of the merkle tree
uint8 public constant DEPTH = 20;

/// @notice The size of the merkle tree, i.e 2^depth
uint32 public immutable SET_SIZE;

Check warning on line 35 in src/WakuRlnV2.sol

View workflow job for this annotation

GitHub Actions / lint

Variable name must be in mixedCase

/// @notice The index of the next member to be registered
uint32 public idCommitmentIndex = 0;

struct MembershipInfo {
/// @notice the user message limit of each member
uint32 userMessageLimit;
/// @notice the index of the member in the set
uint32 index;
}

/// @notice the member metadata
mapping(uint256 => MembershipInfo) public memberInfo;

Check warning on line 48 in src/WakuRlnV2.sol

View workflow job for this annotation

GitHub Actions / lint

Main key parameter in mapping memberInfo is not named

Check warning on line 48 in src/WakuRlnV2.sol

View workflow job for this annotation

GitHub Actions / lint

Value parameter in mapping memberInfo is not named

/// @notice the deployed block number
uint32 public immutable deployedBlockNumber;

/// @notice the stored imt data
LazyIMTData public imtData;

/// Emitted when a new member is added to the set
/// @param idCommitment The idCommitment of the member
/// @param userMessageLimit the user message limit of the member
/// @param index The index of the member in the set
event MemberRegistered(uint256 idCommitment, uint32 userMessageLimit, uint32 index);

modifier onlyValidIdCommitment(uint256 idCommitment) {
alrevuelta marked this conversation as resolved.
Show resolved Hide resolved
if (!isValidCommitment(idCommitment)) revert InvalidIdCommitment(idCommitment);
_;
}

modifier onlyValidUserMessageLimit(uint32 messageLimit) {
if (messageLimit > MAX_MESSAGE_LIMIT) revert InvalidUserMessageLimit(messageLimit);
if (messageLimit == 0) revert InvalidUserMessageLimit(messageLimit);
_;
}

constructor(uint32 maxMessageLimit) {
MAX_MESSAGE_LIMIT = maxMessageLimit;
SET_SIZE = uint32(1 << DEPTH);
deployedBlockNumber = uint32(block.number);
LazyIMT.init(imtData, DEPTH);
}

function memberExists(uint256 idCommitment) public view returns (bool) {
MembershipInfo memory member = memberInfo[idCommitment];
return member.userMessageLimit > 0 && member.index >= 0;
}

/// Allows a user to register as a member
/// @param idCommitment The idCommitment of the member
/// @param userMessageLimit The message limit of the member
function register(
uint256 idCommitment,
uint32 userMessageLimit
)
external
onlyValidIdCommitment(idCommitment)
onlyValidUserMessageLimit(userMessageLimit)
{
_register(idCommitment, userMessageLimit);
}

/// Registers a member
/// @param idCommitment The idCommitment of the member
/// @param userMessageLimit The message limit of the member
function _register(uint256 idCommitment, uint32 userMessageLimit) internal {
if (memberExists(idCommitment)) revert DuplicateIdCommitment();
if (idCommitmentIndex >= SET_SIZE) revert FullTree();

uint256 rateCommitment = PoseidonT3.hash([idCommitment, userMessageLimit]);
MembershipInfo memory member = MembershipInfo({ userMessageLimit: userMessageLimit, index: idCommitmentIndex });
LazyIMT.insert(imtData, rateCommitment);
memberInfo[idCommitment] = member;
alrevuelta marked this conversation as resolved.
Show resolved Hide resolved

emit MemberRegistered(idCommitment, userMessageLimit, idCommitmentIndex);
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)];
}

function getCommitments(uint32 startIndex, uint32 endIndex) public view returns (uint256[] memory) {
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++) {
commitments[i - startIndex] = indexToCommitment(i);
}
return commitments;
}

function root() external view returns (uint256) {
return LazyIMT.root(imtData, DEPTH);
}

function merkleProofElements(uint40 index) public view returns (uint256[] memory) {
return LazyIMT.merkleProofElements(imtData, index, DEPTH);
}
}
26 changes: 0 additions & 26 deletions test/Foo.t.sol

This file was deleted.

48 changes: 48 additions & 0 deletions test/WakuRlnV2.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19 <0.9.0;

import { Test, console } from "forge-std/Test.sol";

Check warning on line 4 in test/WakuRlnV2.t.sol

View workflow job for this annotation

GitHub Actions / lint

imported name console is not used

import { Deploy } from "../script/Deploy.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
import { WakuRlnV2 } from "../src/WakuRlnV2.sol";
import { PoseidonT3 } from "poseidon-solidity/PoseidonT3.sol";

Check warning on line 9 in test/WakuRlnV2.t.sol

View workflow job for this annotation

GitHub Actions / lint

imported name PoseidonT3 is not used
import { LazyIMT } from "@zk-kit/imt.sol/LazyIMT.sol";

Check warning on line 10 in test/WakuRlnV2.t.sol

View workflow job for this annotation

GitHub Actions / lint

imported name LazyIMT is not used

contract WakuRlnV2Test is Test {
WakuRlnV2 internal w;
DeploymentConfig internal deploymentConfig;

address internal deployer;

function setUp() public virtual {
Deploy deployment = new Deploy();
(w, deploymentConfig) = deployment.run();
}

function test__ValidRegistration() external {
vm.pauseGasMetering();
uint256 idCommitment = 2;
uint32 userMessageLimit = 2;
vm.resumeGasMetering();
w.register(idCommitment, userMessageLimit);
vm.pauseGasMetering();
assertEq(w.idCommitmentIndex(), 1);
assertEq(w.memberExists(idCommitment), true);
(uint32 fetchedUserMessageLimit, uint32 index) = w.memberInfo(idCommitment);
assertEq(fetchedUserMessageLimit, userMessageLimit);
assertEq(index, 0);
// 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
);
vm.resumeGasMetering();
}
}
Loading