diff --git a/contracts/WakuRln.sol b/contracts/WakuRln.sol index 765e408..21d5291 100644 --- a/contracts/WakuRln.sol +++ b/contracts/WakuRln.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.15; import {IPoseidonHasher} from "rln-contract/PoseidonHasher.sol"; -import {RlnBase, DuplicateIdCommitment} from "rln-contract/RlnBase.sol"; +import {RlnBase, DuplicateIdCommitment, FullTree} from "rln-contract/RlnBase.sol"; import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; error NotImplemented(); @@ -25,7 +25,7 @@ contract WakuRln is Ownable, RlnBase { idCommitmentIndex += 1; } - function register(uint256[] memory idCommitments) external onlyOwner { + function register(uint256[] calldata idCommitments) external onlyOwner { uint256 len = idCommitments.length; for (uint256 i = 0; i < len;) { _register(idCommitments[i]); @@ -45,6 +45,7 @@ contract WakuRln is Ownable, RlnBase { function _validateRegistration(uint256 idCommitment) internal view override { if (members[idCommitment] != 0) revert DuplicateIdCommitment(); + if (idCommitmentIndex >= SET_SIZE) revert FullTree(); } function _validateSlash(uint256 idCommitment, address payable receiver, uint256[8] calldata proof) diff --git a/contracts/WakuRlnRegistry.sol b/contracts/WakuRlnRegistry.sol index a4f1094..ae48ec2 100644 --- a/contracts/WakuRlnRegistry.sol +++ b/contracts/WakuRlnRegistry.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.15; import {WakuRln} from "./WakuRln.sol"; import {IPoseidonHasher} from "rln-contract/PoseidonHasher.sol"; import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; -import "forge-std/console.sol"; error StorageAlreadyExists(address storageAddress); error NoStorageContractAvailable(); @@ -56,12 +55,28 @@ contract WakuRlnRegistry is Ownable { assembly { revert(add(32, err), mload(err)) } + // when there are no further storage contracts available, revert + } else if (usingStorageIndex + 1 >= nextStorageIndex) { + revert NoStorageContractAvailable(); } usingStorageIndex += 1; } } } + function register(uint16 storageIndex, uint256[] calldata commitments) external payable { + if (storageIndex >= nextStorageIndex) revert NoStorageContractAvailable(); + WakuRln(storages[storageIndex]).register(commitments); + } + + function register(uint16 storageIndex, uint256 commitment) external payable { + if (storageIndex >= nextStorageIndex) revert NoStorageContractAvailable(); + // optimize the gas used below + uint256[] memory commitments = new uint256[](1); + commitments[0] = commitment; + WakuRln(storages[storageIndex]).register(commitments); + } + function forceProgress() external onlyOwner { if (usingStorageIndex >= nextStorageIndex) revert NoStorageContractAvailable(); usingStorageIndex += 1; diff --git a/docs/index.md b/docs/index.md index 0b17306..c934859 100644 --- a/docs/index.md +++ b/docs/index.md @@ -178,6 +178,18 @@ function newStorage() external function register(uint256[] commitments) external payable ``` +### register + +```solidity +function register(uint16 storageIndex, uint256[] commitments) external payable +``` + +### register + +```solidity +function register(uint16 storageIndex, uint256 commitment) external payable +``` + ### forceProgress ```solidity diff --git a/foundry.toml b/foundry.toml index b58d672..7d129c5 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,4 +7,6 @@ cache_path = 'cache_forge' # 5667: Unused function parameter. Remove or comment out the variable name to silence this warning. # 5740: Unreachable code. -ignored_error_codes = [5667, 5740] \ No newline at end of file +ignored_error_codes = [5667, 5740] +# setting this to allow expensive larger inserts during tests +memory_limit = 100_000_000 diff --git a/test/WakuRlnRegistry.t.sol b/test/WakuRlnRegistry.t.sol index e83fd16..e49d536 100644 --- a/test/WakuRlnRegistry.t.sol +++ b/test/WakuRlnRegistry.t.sol @@ -3,12 +3,14 @@ pragma solidity ^0.8.15; import "../contracts/WakuRlnRegistry.sol"; import {PoseidonHasher} from "rln-contract/PoseidonHasher.sol"; -import {DuplicateIdCommitment} from "rln-contract/RlnBase.sol"; +import {DuplicateIdCommitment, FullTree} from "rln-contract/RlnBase.sol"; import {noDuplicate} from "./utils.sol"; import "forge-std/Test.sol"; import "forge-std/StdCheats.sol"; contract WakuRlnRegistryTest is Test { + using stdStorage for StdStorage; + WakuRlnRegistry public wakuRlnRegistry; PoseidonHasher public poseidonHasher; @@ -40,7 +42,7 @@ contract WakuRlnRegistryTest is Test { wakuRlnRegistry.register(commitments); } - function test__BadRegister(uint256[] calldata commitments) public { + function test__InvalidRegistration_Duplicate(uint256[] calldata commitments) public { vm.assume(!noDuplicate(commitments)); wakuRlnRegistry.newStorage(); vm.expectRevert(DuplicateIdCommitment.selector); @@ -52,4 +54,50 @@ contract WakuRlnRegistryTest is Test { wakuRlnRegistry.forceProgress(); require(wakuRlnRegistry.usingStorageIndex() == 1); } + + function test__SingleRegistration(uint256 commitment) public { + wakuRlnRegistry.newStorage(); + wakuRlnRegistry.register(0, commitment); + } + + function test__InvalidSingleRegistration__NoStorageContract(uint256 commitment) public { + wakuRlnRegistry.newStorage(); + vm.expectRevert(NoStorageContractAvailable.selector); + wakuRlnRegistry.register(1, commitment); + } + + function test__InvalidSingleRegistration__Duplicate(uint256 commitment) public { + wakuRlnRegistry.newStorage(); + wakuRlnRegistry.register(0, commitment); + vm.expectRevert(DuplicateIdCommitment.selector); + wakuRlnRegistry.register(0, commitment); + } + + function test__InvalidSingleRegistration__FullTree() public { + vm.pauseGasMetering(); + wakuRlnRegistry.newStorage(); + WakuRln wakuRln = WakuRln(wakuRlnRegistry.storages(0)); + uint256 setSize = wakuRln.SET_SIZE(); + // setSize - 1 because RlnBase uses 1-indexing + for (uint256 i = 0; i < setSize - 1; i++) { + wakuRlnRegistry.register(0, i); + } + vm.resumeGasMetering(); + vm.expectRevert(FullTree.selector); + wakuRlnRegistry.register(0, setSize); + } + + function test__InvalidRegistration__NoStorageContract() public { + vm.pauseGasMetering(); + wakuRlnRegistry.newStorage(); + address storageContract = wakuRlnRegistry.storages(0); + uint256 setSize = WakuRln(storageContract).SET_SIZE(); + + uint256[] memory commitments = new uint256[](setSize); + for (uint256 i = 1; i < setSize; i++) { + commitments[i] = i; + } + vm.expectRevert(NoStorageContractAvailable.selector); + wakuRlnRegistry.register(commitments); + } }