mirror of
https://github.com/vacp2p/staking-reward-streamer.git
synced 2026-01-06 22:14:05 -05:00
chore(RLN): import rln-contracts
This commit is contained in:
committed by
r4bbit
parent
bfba516b28
commit
458e07cc70
8
.env
Normal file
8
.env
Normal file
@@ -0,0 +1,8 @@
|
||||
GOERLI_RPC_URL=https://rpc.ankr.com/eth_goerli
|
||||
ERC20TOKEN=0xA13dE8dF4Ef9D6016F0826858D48045848429390
|
||||
MINIMAL_DEPOSIT=100
|
||||
MAXIMAL_RATE=65535
|
||||
DEPTH=20
|
||||
FEE_PERCENTAGE=5
|
||||
FEE_RECEIVER=0x90579a82BB296A6DA61D4d035E42Fc1aD75f090e
|
||||
FREEZE_PERIOD=100
|
||||
8
.env.example
Normal file
8
.env.example
Normal file
@@ -0,0 +1,8 @@
|
||||
GOERLI_RPC_URL=https://rpc.ankr.com/eth_goerli
|
||||
ERC20TOKEN=0xA13dE8dF4Ef9D6016F0826858D48045848429390
|
||||
MINIMAL_DEPOSIT=100
|
||||
MAXIMAL_RATE=65535
|
||||
DEPTH=20
|
||||
FEE_PERCENTAGE=5
|
||||
FEE_RECEIVER=0x90579a82BB296A6DA61D4d035E42Fc1aD75f090e
|
||||
FREEZE_PERIOD=100
|
||||
118
.gas-report
118
.gas-report
@@ -186,7 +186,7 @@
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| getAccountVaults | 5230 | 5230 | 5230 | 5230 | 4 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| getVault | 13653 | 13653 | 13653 | 13653 | 4182 |
|
||||
| getVault | 13653 | 13653 | 13653 | 13653 | 4181 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| initialize | 92752 | 92752 | 92752 | 92752 | 95 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
@@ -194,7 +194,7 @@
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| leave | 66348 | 66348 | 66348 | 66348 | 2 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| lock | 7040 | 43591 | 46713 | 87964 | 1034 |
|
||||
| lock | 7040 | 43473 | 46713 | 87673 | 1034 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| migrateToVault | 9294 | 53513 | 17021 | 170715 | 4 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
@@ -212,7 +212,7 @@
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| rewardStartTime | 2364 | 2364 | 2364 | 2364 | 2 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| rewardsBalanceOf | 20295 | 24430 | 25908 | 26129 | 268 |
|
||||
| rewardsBalanceOf | 20295 | 24409 | 25908 | 26129 | 268 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| rewardsBalanceOfAccount | 62220 | 62220 | 62220 | 62220 | 1 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
@@ -222,33 +222,33 @@
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| setTrustedCodehash | 24238 | 24238 | 24238 | 24238 | 95 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| stake | 2639 | 131862 | 60725 | 228623 | 2670 |
|
||||
| stake | 2639 | 132481 | 60725 | 228623 | 2670 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| stakedBalanceOf | 2622 | 2622 | 2622 | 2622 | 1 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| totalMP | 6805 | 8257 | 8257 | 9710 | 6 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| totalMPAccrued | 2385 | 2385 | 2385 | 2385 | 4162 |
|
||||
| totalMPAccrued | 2385 | 2385 | 2385 | 2385 | 4161 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| totalMPStaked | 2429 | 2429 | 2429 | 2429 | 4165 |
|
||||
| totalMPStaked | 2429 | 2429 | 2429 | 2429 | 4164 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| totalMaxMP | 2407 | 2407 | 2407 | 2407 | 4162 |
|
||||
| totalMaxMP | 2407 | 2407 | 2407 | 2407 | 4161 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| totalRewardsAccrued | 2407 | 2407 | 2407 | 2407 | 3 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| totalRewardsSupply | 6737 | 11068 | 11792 | 11903 | 290 |
|
||||
| totalRewardsSupply | 6737 | 11058 | 11792 | 11903 | 290 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| totalShares | 4597 | 4597 | 4597 | 4597 | 6 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| totalStaked | 2408 | 2408 | 2408 | 2408 | 4169 |
|
||||
| totalStaked | 2408 | 2408 | 2408 | 2408 | 4168 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| unstake | 9886 | 41391 | 39781 | 79550 | 271 |
|
||||
| unstake | 9886 | 41170 | 39781 | 79550 | 271 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| updateAccount | 347677 | 347677 | 347677 | 347677 | 1 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| updateGlobalState | 15820 | 25876 | 29230 | 29230 | 8 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| updateVault | 31948 | 34543 | 31948 | 110579 | 1024 |
|
||||
| updateVault | 31948 | 34668 | 31948 | 110579 | 1023 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| upgradeTo | 10279 | 10772 | 10279 | 12745 | 5 |
|
||||
|--------------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
@@ -276,9 +276,9 @@
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| leave | 12223 | 113137 | 84120 | 356508 | 5 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| lock | 12151 | 59083 | 62251 | 103499 | 1035 |
|
||||
| lock | 12151 | 58965 | 62251 | 103208 | 1035 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| lockUntil | 2363 | 2363 | 2363 | 2363 | 7769 |
|
||||
| lockUntil | 2363 | 2363 | 2363 | 2363 | 7766 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| migrateToVault | 24910 | 77530 | 32637 | 219937 | 4 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
@@ -286,15 +286,15 @@
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| register | 12742 | 78218 | 78761 | 78761 | 374 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| stake | 12131 | 166249 | 76290 | 284275 | 2671 |
|
||||
| stake | 12131 | 167018 | 76290 | 284275 | 2671 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| stakeManager | 393 | 393 | 393 | 393 | 373 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| trustStakeManager | 7650 | 7650 | 7650 | 7650 | 1 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| unstake | 12108 | 58059 | 55296 | 110656 | 272 |
|
||||
| unstake | 12108 | 57840 | 55296 | 110656 | 272 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| updateLockUntil | 4432 | 20722 | 21532 | 21532 | 524 |
|
||||
| updateLockUntil | 4432 | 20702 | 21532 | 21532 | 532 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| withdraw | 20817 | 20817 | 20817 | 20817 | 1 |
|
||||
|----------------------------------------+-----------------+--------+--------+--------+---------|
|
||||
@@ -312,9 +312,9 @@
|
||||
|----------------------------------------------------+-----------------+-------+--------+--------+---------|
|
||||
| Function Name | Min | Avg | Median | Max | # Calls |
|
||||
|----------------------------------------------------+-----------------+-------+--------+--------+---------|
|
||||
| fallback | 5208 | 12842 | 7353 | 374054 | 23167 |
|
||||
| fallback | 5208 | 12845 | 7353 | 374054 | 23161 |
|
||||
|----------------------------------------------------+-----------------+-------+--------+--------+---------|
|
||||
| implementation | 346 | 2131 | 2346 | 2346 | 4886 |
|
||||
| implementation | 346 | 2128 | 2346 | 2346 | 4894 |
|
||||
╰----------------------------------------------------+-----------------+-------+--------+--------+---------╯
|
||||
|
||||
╭--------------------------------------------+-----------------+--------+--------+--------+---------╮
|
||||
@@ -373,6 +373,82 @@
|
||||
| urlSuffix | 3228 | 3228 | 3228 | 3228 | 1 |
|
||||
╰------------------------------------------------------------------------------------------+-----------------+-------+--------+-------+---------╯
|
||||
|
||||
╭------------------------------+-----------------+--------+--------+--------+---------╮
|
||||
| src/rln/RLN.sol:RLN Contract | | | | | |
|
||||
+=====================================================================================+
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| 1396408 | 7094 | | | | |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| | | | | | |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| Function Name | Min | Avg | Median | Max | # Calls |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| FEE_PERCENTAGE | 217 | 217 | 217 | 217 | 1 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| FEE_RECEIVER | 226 | 226 | 226 | 226 | 1 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| FREEZE_PERIOD | 218 | 218 | 218 | 218 | 1 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| MINIMAL_DEPOSIT | 241 | 241 | 241 | 241 | 1 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| SET_SIZE | 284 | 284 | 284 | 284 | 1 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| identityCommitmentIndex | 2362 | 2362 | 2362 | 2362 | 27 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| members | 6826 | 6826 | 6826 | 6826 | 18 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| register | 23877 | 105927 | 123897 | 126709 | 18 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| release | 28093 | 37708 | 28192 | 66355 | 4 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| slash | 23015 | 49020 | 34650 | 99415 | 6 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| token | 292 | 292 | 292 | 292 | 1 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| verifier | 272 | 272 | 272 | 272 | 1 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| withdraw | 29336 | 79495 | 106774 | 106774 | 8 |
|
||||
|------------------------------+-----------------+--------+--------+--------+---------|
|
||||
| withdrawals | 6792 | 6792 | 6792 | 6792 | 5 |
|
||||
╰------------------------------+-----------------+--------+--------+--------+---------╯
|
||||
|
||||
╭--------------------------------------+-----------------+-------+--------+-------+---------╮
|
||||
| test/RLN.t.sol:MockVerifier Contract | | | | | |
|
||||
+===========================================================================================+
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
|--------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| 204321 | 642 | | | | |
|
||||
|--------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| | | | | | |
|
||||
|--------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| Function Name | Min | Avg | Median | Max | # Calls |
|
||||
|--------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| changeResult | 21649 | 21649 | 21649 | 21649 | 2 |
|
||||
|--------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| result | 2298 | 2298 | 2298 | 2298 | 5 |
|
||||
|--------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| verifyProof | 4790 | 4790 | 4790 | 4790 | 9 |
|
||||
╰--------------------------------------+-----------------+-------+--------+-------+---------╯
|
||||
|
||||
╭-----------------------------------+-----------------+-------+--------+-------+---------╮
|
||||
| test/RLN.t.sol:TestERC20 Contract | | | | | |
|
||||
+========================================================================================+
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
|-----------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| 765176 | 3558 | | | | |
|
||||
|-----------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| | | | | | |
|
||||
|-----------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| Function Name | Min | Avg | Median | Max | # Calls |
|
||||
|-----------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| approve | 46175 | 46179 | 46175 | 46199 | 18 |
|
||||
|-----------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| balanceOf | 2561 | 2561 | 2561 | 2561 | 62 |
|
||||
|-----------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| mint | 51064 | 64368 | 68164 | 68188 | 18 |
|
||||
╰-----------------------------------+-----------------+-------+--------+-------+---------╯
|
||||
|
||||
╭-------------------------------------------------------------------+-----------------+-------+--------+-------+---------╮
|
||||
| test/mocks/KarmaDistributorMock.sol:KarmaDistributorMock Contract | | | | | |
|
||||
+========================================================================================================================+
|
||||
@@ -418,11 +494,11 @@
|
||||
|---------------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| Function Name | Min | Avg | Median | Max | # Calls |
|
||||
|---------------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| approve | 29075 | 31544 | 29183 | 46259 | 2676 |
|
||||
| approve | 29075 | 31546 | 29183 | 46259 | 2676 |
|
||||
|---------------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| balanceOf | 2561 | 2561 | 2561 | 2561 | 4960 |
|
||||
| balanceOf | 2561 | 2561 | 2561 | 2561 | 4959 |
|
||||
|---------------------------------------------+-----------------+-------+--------+-------+---------|
|
||||
| mint | 33964 | 37189 | 34072 | 68248 | 2685 |
|
||||
| mint | 33964 | 37192 | 34072 | 68248 | 2685 |
|
||||
╰---------------------------------------------+-----------------+-------+--------+-------+---------╯
|
||||
|
||||
╭-----------------------------------------------------------------------------+-----------------+--------+--------+--------+---------╮
|
||||
|
||||
@@ -16,15 +16,15 @@ EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 479110)
|
||||
EmergencyExitTest:test_EmergencyExitWithLock() (gas: 452444)
|
||||
EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 484810)
|
||||
EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39176)
|
||||
FuzzTests:testFuzz_AccrueMP(uint128,uint64,uint64) (runs: 1009, μ: 586777, ~: 549070)
|
||||
FuzzTests:testFuzz_AccrueMP_Relock(uint128,uint64,uint64,uint64) (runs: 1009, μ: 811994, ~: 777237)
|
||||
FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1001, μ: 588323, ~: 578267)
|
||||
FuzzTests:testFuzz_Lock(uint256,uint64) (runs: 1008, μ: 961825, ~: 961235)
|
||||
FuzzTests:testFuzz_Relock(uint256,uint64,uint64) (runs: 1008, μ: 598425, ~: 574225)
|
||||
FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1001, μ: 650378, ~: 653205)
|
||||
FuzzTests:testFuzz_Stake(uint256,uint64) (runs: 1008, μ: 375317, ~: 346086)
|
||||
FuzzTests:testFuzz_Unstake(uint128,uint64,uint16,uint128) (runs: 1009, μ: 806735, ~: 780622)
|
||||
FuzzTests:testFuzz_UpdateVault(uint128,uint64,uint64) (runs: 1009, μ: 586800, ~: 549093)
|
||||
FuzzTests:testFuzz_AccrueMP(uint128,uint64,uint64) (runs: 1024, μ: 587151, ~: 549070)
|
||||
FuzzTests:testFuzz_AccrueMP_Relock(uint128,uint64,uint64,uint64) (runs: 1024, μ: 812396, ~: 777234)
|
||||
FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1007, μ: 588350, ~: 578267)
|
||||
FuzzTests:testFuzz_Lock(uint256,uint64) (runs: 1025, μ: 961806, ~: 961235)
|
||||
FuzzTests:testFuzz_Relock(uint256,uint64,uint64) (runs: 1025, μ: 599078, ~: 574234)
|
||||
FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1001, μ: 650369, ~: 653205)
|
||||
FuzzTests:testFuzz_Stake(uint256,uint64) (runs: 1025, μ: 376001, ~: 346086)
|
||||
FuzzTests:testFuzz_Unstake(uint128,uint64,uint16,uint128) (runs: 1024, μ: 806573, ~: 780622)
|
||||
FuzzTests:testFuzz_UpdateVault(uint128,uint64,uint64) (runs: 1024, μ: 587174, ~: 549093)
|
||||
IntegrationTest:testStakeFoo() (gas: 2348931)
|
||||
KarmaNFTTest:testApproveNotAllowed() (gas: 10507)
|
||||
KarmaNFTTest:testGetApproved() (gas: 10531)
|
||||
@@ -58,7 +58,7 @@ LeaveTest:test_LeaveShouldKeepFundsLockedInStakeVault() (gas: 9938411)
|
||||
LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 10011059)
|
||||
LeaveTest:test_RevertWhenStakeManagerIsTrusted() (gas: 333238)
|
||||
LeaveTest:test_TrustNewStakeManager() (gas: 9944491)
|
||||
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1008, μ: 384561, ~: 384588)
|
||||
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1025, μ: 384562, ~: 384588)
|
||||
LockTest:test_LockFailsWithNoStake() (gas: 89700)
|
||||
LockTest:test_LockFailsWithZero() (gas: 343310)
|
||||
LockTest:test_LockMultipleTimesExceedMaxLock() (gas: 746921)
|
||||
@@ -89,6 +89,23 @@ OverflowTest:testTotalSupply() (gas: 359166)
|
||||
OverflowTest:testTransfersNotAllowed() (gas: 61763)
|
||||
OverflowTest:test_RevertWhen_MintingCausesOverflow() (gas: 129464)
|
||||
OverflowTest:test_RevertWhen_SettingRewardCausesOverflow() (gas: 127792)
|
||||
RLNTest:test_initial_state() (gas: 65400)
|
||||
RLNTest:test_register_fails_when_amount_lt_minimal_deposit() (gas: 161453)
|
||||
RLNTest:test_register_fails_when_duplicate_identity_commitments() (gas: 444949)
|
||||
RLNTest:test_register_fails_when_index_exceeds_set_size() (gas: 2824923)
|
||||
RLNTest:test_register_succeeds() (gas: 579610)
|
||||
RLNTest:test_release_fails_when_freeze_period() (gas: 529887)
|
||||
RLNTest:test_release_fails_when_no_withdrawal() (gas: 38343)
|
||||
RLNTest:test_release_succeeds() (gas: 576511)
|
||||
RLNTest:test_slash_fails_when_invalid_proof() (gas: 399809)
|
||||
RLNTest:test_slash_fails_when_not_registered() (gas: 59667)
|
||||
RLNTest:test_slash_fails_when_receiver_is_zero() (gas: 351215)
|
||||
RLNTest:test_slash_fails_when_self_slashing() (gas: 360067)
|
||||
RLNTest:test_slash_succeeds() (gas: 1024025)
|
||||
RLNTest:test_withdraw_fails_when_already_underways() (gas: 468594)
|
||||
RLNTest:test_withdraw_fails_when_invalid_proof() (gas: 399356)
|
||||
RLNTest:test_withdraw_fails_when_not_registered() (gas: 57129)
|
||||
RLNTest:test_withdraw_succeeds() (gas: 480413)
|
||||
RemoveRewardDistributorTest:testAddKarmaDistributorOnlyAdmin() (gas: 438045)
|
||||
RemoveRewardDistributorTest:testBalanceOf() (gas: 449366)
|
||||
RemoveRewardDistributorTest:testBalanceOfWithNoSystemTotalKarma() (gas: 69633)
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,7 +4,6 @@ node_modules
|
||||
out
|
||||
|
||||
# files
|
||||
*.env
|
||||
*.log
|
||||
.DS_Store
|
||||
.pnp.*
|
||||
|
||||
27
script/RLN.s.sol
Normal file
27
script/RLN.s.sol
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import "../src/rln/RLN.sol";
|
||||
import "../src/rln/Verifier.sol";
|
||||
|
||||
contract RLNScript is Script {
|
||||
function run() public {
|
||||
uint256 minimalDeposit = vm.envUint("MINIMAL_DEPOSIT");
|
||||
uint256 maximalRate = vm.envUint("MAXIMAL_RATE");
|
||||
uint256 depth = vm.envUint("DEPTH");
|
||||
uint8 feePercentage = uint8(vm.envUint("FEE_PERCENTAGE"));
|
||||
address feeReceiver = vm.envAddress("FEE_RECEIVER");
|
||||
uint256 freezePeriod = vm.envUint("FREEZE_PERIOD");
|
||||
address token = vm.envAddress("ERC20TOKEN");
|
||||
|
||||
vm.startBroadcast();
|
||||
|
||||
Groth16Verifier verifier = new Groth16Verifier();
|
||||
RLN rln = new RLN(
|
||||
minimalDeposit, maximalRate, depth, feePercentage, feeReceiver, freezePeriod, token, address(verifier)
|
||||
);
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
||||
14
src/rln/IVerifier.sol
Normal file
14
src/rln/IVerifier.sol
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
interface IVerifier {
|
||||
function verifyProof(
|
||||
uint256[2] calldata a,
|
||||
uint256[2][2] calldata b,
|
||||
uint256[2] calldata c,
|
||||
uint256[2] calldata input
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool);
|
||||
}
|
||||
209
src/rln/RLN.sol
Normal file
209
src/rln/RLN.sol
Normal file
@@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import { IVerifier } from "./IVerifier.sol";
|
||||
|
||||
/// @title Rate-Limiting Nullifier registry contract
|
||||
/// @dev This contract allows you to register RLN commitment and withdraw/slash.
|
||||
contract RLN {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
/// @dev User metadata struct.
|
||||
/// @param userAddress: address of depositor;
|
||||
/// @param messageLimit: user's message limit (stakeAmount / MINIMAL_DEPOSIT).
|
||||
struct User {
|
||||
address userAddress;
|
||||
uint256 messageLimit;
|
||||
uint256 index;
|
||||
}
|
||||
|
||||
/// @dev Withdrawal time-lock struct
|
||||
/// @param blockNumber: number of block when a withdraw was initialized;
|
||||
/// @param messageLimit: amount of tokens to freeze/release;
|
||||
/// @param receiver: address of receiver.
|
||||
struct Withdrawal {
|
||||
uint256 blockNumber;
|
||||
uint256 amount;
|
||||
address receiver;
|
||||
}
|
||||
|
||||
/// @dev Minimal membership deposit (stake amount) value - cost of 1 message.
|
||||
uint256 public immutable MINIMAL_DEPOSIT;
|
||||
|
||||
/// @dev Maximal rate.
|
||||
uint256 public immutable MAXIMAL_RATE;
|
||||
|
||||
/// @dev Registry set size (1 << DEPTH).
|
||||
uint256 public immutable SET_SIZE;
|
||||
|
||||
/// @dev Address of the fee receiver.
|
||||
address public immutable FEE_RECEIVER;
|
||||
|
||||
/// @dev Fee percentage.
|
||||
uint8 public immutable FEE_PERCENTAGE;
|
||||
|
||||
/// @dev Freeze period - number of blocks for which the withdrawal of money is frozen.
|
||||
uint256 public immutable FREEZE_PERIOD;
|
||||
|
||||
/// @dev Current index where identityCommitment will be stored.
|
||||
uint256 public identityCommitmentIndex;
|
||||
|
||||
/// @dev Registry set. The keys are `identityCommitment`s.
|
||||
/// The values are addresses of accounts that call `register` transaction.
|
||||
mapping(uint256 => User) public members;
|
||||
|
||||
/// @dev Withdrawals logic.
|
||||
mapping(uint256 => Withdrawal) public withdrawals;
|
||||
|
||||
/// @dev ERC20 Token used for staking.
|
||||
IERC20 public immutable token;
|
||||
|
||||
/// @dev Groth16 verifier.
|
||||
IVerifier public immutable verifier;
|
||||
|
||||
/// @dev Emmited when a new member registered.
|
||||
/// @param identityCommitment: `identityCommitment`;
|
||||
/// @param messageLimit: user's message limit;
|
||||
/// @param index: idCommitmentIndex value.
|
||||
event MemberRegistered(uint256 identityCommitment, uint256 messageLimit, uint256 index);
|
||||
|
||||
/// @dev Emmited when a member was withdrawn.
|
||||
/// @param index: index of `identityCommitment`;
|
||||
event MemberWithdrawn(uint256 index);
|
||||
|
||||
/// @dev Emmited when a member was slashed.
|
||||
/// @param index: index of `identityCommitment`;
|
||||
/// @param slasher: address of slasher (msg.sender).
|
||||
event MemberSlashed(uint256 index, address slasher);
|
||||
|
||||
/// @param minimalDeposit: minimal membership deposit;
|
||||
/// @param maximalRate: maximal rate;
|
||||
/// @param depth: depth of the merkle tree;
|
||||
/// @param feePercentage: fee percentage;
|
||||
/// @param feeReceiver: address of the fee receiver;
|
||||
/// @param freezePeriod: amount of blocks for withdrawal time-lock;
|
||||
/// @param _token: address of the ERC20 contract;
|
||||
/// @param _verifier: address of the Groth16 Verifier.
|
||||
constructor(
|
||||
uint256 minimalDeposit,
|
||||
uint256 maximalRate,
|
||||
uint256 depth,
|
||||
uint8 feePercentage,
|
||||
address feeReceiver,
|
||||
uint256 freezePeriod,
|
||||
address _token,
|
||||
address _verifier
|
||||
) {
|
||||
require(feeReceiver != address(0), "RLN, constructor: fee receiver cannot be 0x0 address");
|
||||
|
||||
MINIMAL_DEPOSIT = minimalDeposit;
|
||||
MAXIMAL_RATE = maximalRate;
|
||||
SET_SIZE = 1 << depth;
|
||||
|
||||
FEE_PERCENTAGE = feePercentage;
|
||||
FEE_RECEIVER = feeReceiver;
|
||||
FREEZE_PERIOD = freezePeriod;
|
||||
|
||||
token = IERC20(_token);
|
||||
verifier = IVerifier(_verifier);
|
||||
}
|
||||
|
||||
/// @dev Adds `identityCommitment` to the registry set and takes the necessary stake amount.
|
||||
///
|
||||
/// NOTE: The set must not be full.
|
||||
///
|
||||
/// @param identityCommitment: `identityCommitment`;
|
||||
/// @param amount: stake amount.
|
||||
function register(uint256 identityCommitment, uint256 amount) external {
|
||||
uint256 index = identityCommitmentIndex;
|
||||
|
||||
require(index < SET_SIZE, "RLN, register: set is full");
|
||||
require(amount >= MINIMAL_DEPOSIT, "RLN, register: amount is lower than minimal deposit");
|
||||
require(amount % MINIMAL_DEPOSIT == 0, "RLN, register: amount should be a multiple of minimal deposit");
|
||||
require(members[identityCommitment].userAddress == address(0), "RLN, register: idCommitment already registered");
|
||||
|
||||
uint256 messageLimit = amount / MINIMAL_DEPOSIT;
|
||||
require(messageLimit <= MAXIMAL_RATE, "RLN, register: message limit cannot be more than MAXIMAL_RATE");
|
||||
|
||||
token.safeTransferFrom(msg.sender, address(this), amount);
|
||||
|
||||
members[identityCommitment] = User(msg.sender, messageLimit, index);
|
||||
emit MemberRegistered(identityCommitment, messageLimit, index);
|
||||
|
||||
unchecked {
|
||||
identityCommitmentIndex = index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Request for withdraw and freeze the stake to prevent self-slashing. Stake can be
|
||||
/// released after FREEZE_PERIOD blocks.
|
||||
/// @param identityCommitment: `identityCommitment`;
|
||||
/// @param proof: snarkjs's format generated proof (without public inputs) packed consequently.
|
||||
function withdraw(uint256 identityCommitment, uint256[8] calldata proof) external {
|
||||
User memory member = members[identityCommitment];
|
||||
require(member.userAddress != address(0), "RLN, withdraw: member doesn't exist");
|
||||
require(withdrawals[identityCommitment].blockNumber == 0, "RLN, release: such withdrawal exists");
|
||||
require(_verifyProof(identityCommitment, member.userAddress, proof), "RLN, withdraw: invalid proof");
|
||||
|
||||
uint256 withdrawAmount = member.messageLimit * MINIMAL_DEPOSIT;
|
||||
withdrawals[identityCommitment] = Withdrawal(block.number, withdrawAmount, member.userAddress);
|
||||
emit MemberWithdrawn(member.index);
|
||||
}
|
||||
|
||||
/// @dev Releases stake amount.
|
||||
/// @param identityCommitment: `identityCommitment` of withdrawn user.
|
||||
function release(uint256 identityCommitment) external {
|
||||
Withdrawal memory withdrawal = withdrawals[identityCommitment];
|
||||
require(withdrawal.blockNumber != 0, "RLN, release: no such withdrawals");
|
||||
require(block.number - withdrawal.blockNumber > FREEZE_PERIOD, "RLN, release: cannot release yet");
|
||||
|
||||
delete withdrawals[identityCommitment];
|
||||
delete members[identityCommitment];
|
||||
|
||||
token.safeTransfer(withdrawal.receiver, withdrawal.amount);
|
||||
}
|
||||
|
||||
/// @dev Slashes identity with identityCommitment.
|
||||
/// @param identityCommitment: `identityCommitment`;
|
||||
/// @param receiver: stake receiver;
|
||||
/// @param proof: snarkjs's format generated proof (without public inputs) packed consequently.
|
||||
function slash(uint256 identityCommitment, address receiver, uint256[8] calldata proof) external {
|
||||
require(receiver != address(0), "RLN, slash: empty receiver address");
|
||||
|
||||
User memory member = members[identityCommitment];
|
||||
require(member.userAddress != address(0), "RLN, slash: member doesn't exist");
|
||||
require(member.userAddress != receiver, "RLN, slash: self-slashing is prohibited");
|
||||
|
||||
require(_verifyProof(identityCommitment, receiver, proof), "RLN, slash: invalid proof");
|
||||
|
||||
delete members[identityCommitment];
|
||||
delete withdrawals[identityCommitment];
|
||||
|
||||
uint256 withdrawAmount = member.messageLimit * MINIMAL_DEPOSIT;
|
||||
uint256 feeAmount = (FEE_PERCENTAGE * withdrawAmount) / 100;
|
||||
|
||||
token.safeTransfer(receiver, withdrawAmount - feeAmount);
|
||||
token.safeTransfer(FEE_RECEIVER, feeAmount);
|
||||
emit MemberSlashed(member.index, receiver);
|
||||
}
|
||||
|
||||
/// @dev Groth16 proof verification
|
||||
function _verifyProof(
|
||||
uint256 identityCommitment,
|
||||
address receiver,
|
||||
uint256[8] calldata proof
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return verifier.verifyProof(
|
||||
[proof[0], proof[1]],
|
||||
[[proof[2], proof[3]], [proof[4], proof[5]]],
|
||||
[proof[6], proof[7]],
|
||||
[identityCommitment, uint256(uint160(receiver))]
|
||||
);
|
||||
}
|
||||
}
|
||||
202
src/rln/Verifier.sol
Normal file
202
src/rln/Verifier.sol
Normal file
@@ -0,0 +1,202 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
/*
|
||||
Copyright 2021 0KIMS association.
|
||||
|
||||
This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
|
||||
|
||||
snarkJS is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
snarkJS is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
contract Groth16Verifier {
|
||||
// Scalar field size
|
||||
uint256 constant r =
|
||||
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;
|
||||
// Base field size
|
||||
uint256 constant q =
|
||||
21_888_242_871_839_275_222_246_405_745_257_275_088_696_311_157_297_823_662_689_037_894_645_226_208_583;
|
||||
|
||||
// Verification Key data
|
||||
uint256 constant alphax =
|
||||
20_491_192_805_390_485_299_153_009_773_594_534_940_189_261_866_228_447_918_068_658_471_970_481_763_042;
|
||||
uint256 constant alphay =
|
||||
9_383_485_363_053_290_200_918_347_156_157_836_566_562_967_994_039_712_273_449_902_621_266_178_545_958;
|
||||
uint256 constant betax1 =
|
||||
4_252_822_878_758_300_859_123_897_981_450_591_353_533_073_413_197_771_768_651_442_665_752_259_397_132;
|
||||
uint256 constant betax2 =
|
||||
6_375_614_351_688_725_206_403_948_262_868_962_793_625_744_043_794_305_715_222_011_528_459_656_738_731;
|
||||
uint256 constant betay1 =
|
||||
21_847_035_105_528_745_403_288_232_691_147_584_728_191_162_732_299_865_338_377_159_692_350_059_136_679;
|
||||
uint256 constant betay2 =
|
||||
10_505_242_626_370_262_277_552_901_082_094_356_697_409_835_680_220_590_971_873_171_140_371_331_206_856;
|
||||
uint256 constant gammax1 =
|
||||
11_559_732_032_986_387_107_991_004_021_392_285_783_925_812_861_821_192_530_917_403_151_452_391_805_634;
|
||||
uint256 constant gammax2 =
|
||||
10_857_046_999_023_057_135_944_570_762_232_829_481_370_756_359_578_518_086_990_519_993_285_655_852_781;
|
||||
uint256 constant gammay1 =
|
||||
4_082_367_875_863_433_681_332_203_403_145_435_568_316_851_327_593_401_208_105_741_076_214_120_093_531;
|
||||
uint256 constant gammay2 =
|
||||
8_495_653_923_123_431_417_604_973_247_489_272_438_418_190_587_263_600_148_770_280_649_306_958_101_930;
|
||||
uint256 constant deltax1 =
|
||||
11_551_021_181_461_167_826_461_759_140_322_976_337_427_023_987_127_500_996_455_457_136_635_486_400_849;
|
||||
uint256 constant deltax2 =
|
||||
16_440_331_697_014_556_916_876_897_072_677_669_680_121_386_347_171_004_539_353_252_116_876_383_229_648;
|
||||
uint256 constant deltay1 =
|
||||
16_935_666_883_311_644_237_478_257_793_713_075_046_951_222_500_094_443_222_621_864_730_929_684_383_196;
|
||||
uint256 constant deltay2 =
|
||||
15_542_122_065_551_809_636_611_024_015_159_689_732_218_122_471_819_623_718_488_423_844_152_861_811_271;
|
||||
|
||||
uint256 constant IC0x =
|
||||
19_490_069_286_251_317_200_471_893_224_761_952_280_235_157_078_692_599_655_063_040_494_106_083_015_102;
|
||||
uint256 constant IC0y =
|
||||
15_613_730_057_977_833_735_664_106_983_317_680_013_118_142_165_231_654_768_046_521_650_638_333_652_991;
|
||||
|
||||
uint256 constant IC1x =
|
||||
1_563_543_155_852_853_229_359_605_494_188_815_884_199_915_022_658_219_002_707_722_789_976_065_966_419;
|
||||
uint256 constant IC1y =
|
||||
858_819_375_930_654_753_672_617_171_465_307_097_688_802_650_498_051_619_587_167_586_479_724_200_799;
|
||||
|
||||
uint256 constant IC2x =
|
||||
3_808_889_614_445_935_800_597_561_392_085_733_302_718_838_702_771_107_544_944_545_050_886_958_022_904;
|
||||
uint256 constant IC2y =
|
||||
13_293_649_293_049_947_010_793_838_294_353_767_499_934_999_769_633_605_908_974_566_715_226_392_122_400;
|
||||
|
||||
// Memory data
|
||||
uint16 constant pVk = 0;
|
||||
uint16 constant pPairing = 128;
|
||||
|
||||
uint16 constant pLastMem = 896;
|
||||
|
||||
function verifyProof(
|
||||
uint256[2] calldata _pA,
|
||||
uint256[2][2] calldata _pB,
|
||||
uint256[2] calldata _pC,
|
||||
uint256[2] calldata _pubSignals
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
assembly {
|
||||
function checkField(v) {
|
||||
if iszero(lt(v, q)) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
// G1 function to multiply a G1 value(x,y) to value in an address
|
||||
function g1_mulAccC(pR, x, y, s) {
|
||||
let success
|
||||
let mIn := mload(0x40)
|
||||
mstore(mIn, x)
|
||||
mstore(add(mIn, 32), y)
|
||||
mstore(add(mIn, 64), s)
|
||||
|
||||
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
|
||||
|
||||
if iszero(success) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
|
||||
mstore(add(mIn, 64), mload(pR))
|
||||
mstore(add(mIn, 96), mload(add(pR, 32)))
|
||||
|
||||
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
|
||||
|
||||
if iszero(success) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk {
|
||||
let _pPairing := add(pMem, pPairing)
|
||||
let _pVk := add(pMem, pVk)
|
||||
|
||||
mstore(_pVk, IC0x)
|
||||
mstore(add(_pVk, 32), IC0y)
|
||||
|
||||
// Compute the linear combination vk_x
|
||||
|
||||
g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0)))
|
||||
|
||||
g1_mulAccC(_pVk, IC2x, IC2y, calldataload(add(pubSignals, 32)))
|
||||
|
||||
// -A
|
||||
mstore(_pPairing, calldataload(pA))
|
||||
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))
|
||||
|
||||
// B
|
||||
mstore(add(_pPairing, 64), calldataload(pB))
|
||||
mstore(add(_pPairing, 96), calldataload(add(pB, 32)))
|
||||
mstore(add(_pPairing, 128), calldataload(add(pB, 64)))
|
||||
mstore(add(_pPairing, 160), calldataload(add(pB, 96)))
|
||||
|
||||
// alpha1
|
||||
mstore(add(_pPairing, 192), alphax)
|
||||
mstore(add(_pPairing, 224), alphay)
|
||||
|
||||
// beta2
|
||||
mstore(add(_pPairing, 256), betax1)
|
||||
mstore(add(_pPairing, 288), betax2)
|
||||
mstore(add(_pPairing, 320), betay1)
|
||||
mstore(add(_pPairing, 352), betay2)
|
||||
|
||||
// vk_x
|
||||
mstore(add(_pPairing, 384), mload(add(pMem, pVk)))
|
||||
mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32))))
|
||||
|
||||
// gamma2
|
||||
mstore(add(_pPairing, 448), gammax1)
|
||||
mstore(add(_pPairing, 480), gammax2)
|
||||
mstore(add(_pPairing, 512), gammay1)
|
||||
mstore(add(_pPairing, 544), gammay2)
|
||||
|
||||
// C
|
||||
mstore(add(_pPairing, 576), calldataload(pC))
|
||||
mstore(add(_pPairing, 608), calldataload(add(pC, 32)))
|
||||
|
||||
// delta2
|
||||
mstore(add(_pPairing, 640), deltax1)
|
||||
mstore(add(_pPairing, 672), deltax2)
|
||||
mstore(add(_pPairing, 704), deltay1)
|
||||
mstore(add(_pPairing, 736), deltay2)
|
||||
|
||||
let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20)
|
||||
|
||||
isOk := and(success, mload(_pPairing))
|
||||
}
|
||||
|
||||
let pMem := mload(0x40)
|
||||
mstore(0x40, add(pMem, pLastMem))
|
||||
|
||||
// Validate that all evaluations ∈ F
|
||||
|
||||
checkField(calldataload(add(_pubSignals, 0)))
|
||||
|
||||
checkField(calldataload(add(_pubSignals, 32)))
|
||||
|
||||
checkField(calldataload(add(_pubSignals, 64)))
|
||||
|
||||
// Validate all evaluations
|
||||
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)
|
||||
|
||||
mstore(0, isValid)
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
}
|
||||
395
test/RLN.t.sol
Normal file
395
test/RLN.t.sol
Normal file
@@ -0,0 +1,395 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
import "../src/rln/RLN.sol";
|
||||
import { IVerifier } from "../src/rln/IVerifier.sol";
|
||||
|
||||
// A ERC20 token contract which allows arbitrary minting for testing
|
||||
contract TestERC20 is ERC20 {
|
||||
constructor() ERC20("TestERC20", "TST") { }
|
||||
|
||||
function mint(address to, uint256 amount) external {
|
||||
_mint(to, amount);
|
||||
}
|
||||
}
|
||||
|
||||
// A mock verifier which makes us skip the proof verification.
|
||||
contract MockVerifier is IVerifier {
|
||||
bool public result;
|
||||
|
||||
constructor() {
|
||||
result = true;
|
||||
}
|
||||
|
||||
function verifyProof(
|
||||
uint256[2] memory,
|
||||
uint256[2][2] memory,
|
||||
uint256[2] memory,
|
||||
uint256[2] memory
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
function changeResult(bool _result) external {
|
||||
result = _result;
|
||||
}
|
||||
}
|
||||
|
||||
contract RLNTest is Test {
|
||||
event MemberRegistered(uint256 identityCommitment, uint256 messageLimit, uint256 index);
|
||||
event MemberWithdrawn(uint256 index);
|
||||
event MemberSlashed(uint256 index, address slasher);
|
||||
|
||||
RLN rln;
|
||||
TestERC20 token;
|
||||
MockVerifier verifier;
|
||||
|
||||
uint256 rlnInitialTokenBalance = 1_000_000;
|
||||
uint256 minimalDeposit = 100;
|
||||
uint256 maximalRate = 1 << 16 - 1;
|
||||
uint256 depth = 20;
|
||||
uint8 feePercentage = 10;
|
||||
address feeReceiver = makeAddr("feeReceiver");
|
||||
uint256 freezePeriod = 1;
|
||||
|
||||
uint256 identityCommitment0 = 1234;
|
||||
uint256 identityCommitment1 = 5678;
|
||||
|
||||
address user0 = makeAddr("user0");
|
||||
address user1 = makeAddr("user1");
|
||||
address slashedReceiver = makeAddr("slashedReceiver");
|
||||
|
||||
uint256 messageLimit0 = 2;
|
||||
uint256 messageLimit1 = 3;
|
||||
|
||||
uint256[8] mockProof =
|
||||
[uint256(0), uint256(1), uint256(2), uint256(3), uint256(4), uint256(5), uint256(6), uint256(7)];
|
||||
|
||||
function setUp() public {
|
||||
token = new TestERC20();
|
||||
verifier = new MockVerifier();
|
||||
rln = new RLN(
|
||||
minimalDeposit,
|
||||
maximalRate,
|
||||
depth,
|
||||
feePercentage,
|
||||
feeReceiver,
|
||||
freezePeriod,
|
||||
address(token),
|
||||
address(verifier)
|
||||
);
|
||||
}
|
||||
|
||||
function test_initial_state() public {
|
||||
assertEq(rln.MINIMAL_DEPOSIT(), minimalDeposit);
|
||||
assertEq(rln.SET_SIZE(), 1 << depth);
|
||||
assertEq(rln.FEE_PERCENTAGE(), feePercentage);
|
||||
assertEq(rln.FEE_RECEIVER(), feeReceiver);
|
||||
assertEq(rln.FREEZE_PERIOD(), freezePeriod);
|
||||
assertEq(address(rln.token()), address(token));
|
||||
assertEq(address(rln.verifier()), address(verifier));
|
||||
assertEq(rln.identityCommitmentIndex(), 0);
|
||||
}
|
||||
|
||||
/* register */
|
||||
|
||||
function test_register_succeeds() public {
|
||||
// Test: register one user
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Test: register second user
|
||||
register(user1, identityCommitment1, messageLimit1);
|
||||
}
|
||||
|
||||
function test_register_fails_when_index_exceeds_set_size() public {
|
||||
// Set size is (1 << smallDepth) = 2, and thus there can
|
||||
// only be 2 members, otherwise reverts.
|
||||
uint256 smallDepth = 1;
|
||||
TestERC20 _token = new TestERC20();
|
||||
RLN smallRLN = new RLN(
|
||||
minimalDeposit, maximalRate, smallDepth, feePercentage, feeReceiver, 0, address(_token), address(verifier)
|
||||
);
|
||||
|
||||
// Register the first user
|
||||
_token.mint(user0, minimalDeposit);
|
||||
vm.startPrank(user0);
|
||||
_token.approve(address(smallRLN), minimalDeposit);
|
||||
smallRLN.register(identityCommitment0, minimalDeposit);
|
||||
vm.stopPrank();
|
||||
// Register the second user
|
||||
_token.mint(user1, minimalDeposit);
|
||||
vm.startPrank(user1);
|
||||
_token.approve(address(smallRLN), minimalDeposit);
|
||||
smallRLN.register(identityCommitment1, minimalDeposit);
|
||||
vm.stopPrank();
|
||||
// Now tree (set) is full. Try register the third. It should revert.
|
||||
address user2 = makeAddr("user2");
|
||||
uint256 identityCommitment2 = 9999;
|
||||
token.mint(user2, minimalDeposit);
|
||||
vm.startPrank(user2);
|
||||
token.approve(address(smallRLN), minimalDeposit);
|
||||
// `register` should revert
|
||||
vm.expectRevert("RLN, register: set is full");
|
||||
smallRLN.register(identityCommitment2, minimalDeposit);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_register_fails_when_amount_lt_minimal_deposit() public {
|
||||
uint256 insufficientAmount = minimalDeposit - 1;
|
||||
token.mint(user0, rlnInitialTokenBalance);
|
||||
vm.startPrank(user0);
|
||||
token.approve(address(rln), rlnInitialTokenBalance);
|
||||
vm.expectRevert("RLN, register: amount is lower than minimal deposit");
|
||||
rln.register(identityCommitment0, insufficientAmount);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_register_fails_when_duplicate_identity_commitments() public {
|
||||
// Register first with user0 with identityCommitment0
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Register again with user1 with identityCommitment0
|
||||
token.mint(user1, rlnInitialTokenBalance);
|
||||
vm.startPrank(user1);
|
||||
token.approve(address(rln), rlnInitialTokenBalance);
|
||||
// `register` should revert
|
||||
vm.expectRevert("RLN, register: idCommitment already registered");
|
||||
rln.register(identityCommitment0, rlnInitialTokenBalance);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
/* withdraw */
|
||||
|
||||
function test_withdraw_succeeds() public {
|
||||
// Register first
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Make sure proof verification is skipped
|
||||
assertEq(verifier.result(), true);
|
||||
|
||||
// Withdraw user0
|
||||
// Ensure event is emitted
|
||||
(,, uint256 index) = rln.members(identityCommitment0);
|
||||
vm.expectEmit(true, true, false, true);
|
||||
emit MemberWithdrawn(index);
|
||||
rln.withdraw(identityCommitment0, mockProof);
|
||||
// Check withdrawal entry is set correctly
|
||||
(uint256 blockNumber, uint256 amount, address receiver) = rln.withdrawals(identityCommitment0);
|
||||
assertEq(blockNumber, block.number);
|
||||
assertEq(amount, getRegisterAmount(messageLimit0));
|
||||
assertEq(receiver, user0);
|
||||
}
|
||||
|
||||
function test_withdraw_fails_when_not_registered() public {
|
||||
// Withdraw fails if the user has not registered before
|
||||
vm.expectRevert("RLN, withdraw: member doesn't exist");
|
||||
rln.withdraw(identityCommitment0, mockProof);
|
||||
}
|
||||
|
||||
function test_withdraw_fails_when_already_underways() public {
|
||||
// Register first
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Withdraw user0
|
||||
rln.withdraw(identityCommitment0, mockProof);
|
||||
// Withdraw again and it should fail
|
||||
vm.expectRevert("RLN, release: such withdrawal exists");
|
||||
rln.withdraw(identityCommitment0, mockProof);
|
||||
}
|
||||
|
||||
function test_withdraw_fails_when_invalid_proof() public {
|
||||
// Register first
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Make sure mock verifier always return false
|
||||
// And thus the proof is always considered invalid
|
||||
verifier.changeResult(false);
|
||||
assertEq(verifier.result(), false);
|
||||
vm.expectRevert("RLN, withdraw: invalid proof");
|
||||
rln.withdraw(identityCommitment0, mockProof);
|
||||
}
|
||||
|
||||
/* release */
|
||||
|
||||
function test_release_succeeds() public {
|
||||
// Register first
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Withdraw user0
|
||||
// Make sure proof verification is skipped
|
||||
assertEq(verifier.result(), true);
|
||||
rln.withdraw(identityCommitment0, mockProof);
|
||||
|
||||
// Test: release succeeds after freeze period
|
||||
// Set block.number to `blockNumbersToRelease`
|
||||
uint256 blockNumbersToRelease = getUnfrozenBlockHeight();
|
||||
vm.roll(blockNumbersToRelease);
|
||||
|
||||
uint256 user0BalanceBefore = token.balanceOf(user0);
|
||||
uint256 rlnBalanceBefore = token.balanceOf(address(rln));
|
||||
// Calls release and check balances
|
||||
rln.release(identityCommitment0);
|
||||
uint256 user0BalanceDiff = token.balanceOf(user0) - user0BalanceBefore;
|
||||
uint256 rlnBalanceDiff = rlnBalanceBefore - token.balanceOf(address(rln));
|
||||
uint256 expectedUser0BalanceDiff = getRegisterAmount(messageLimit0);
|
||||
assertEq(user0BalanceDiff, expectedUser0BalanceDiff);
|
||||
assertEq(rlnBalanceDiff, expectedUser0BalanceDiff);
|
||||
checkUserIsDeleted(identityCommitment0);
|
||||
}
|
||||
|
||||
function test_release_fails_when_no_withdrawal() public {
|
||||
// Release fails if there is no withdrawal for the user
|
||||
vm.expectRevert("RLN, release: no such withdrawals");
|
||||
rln.release(identityCommitment0);
|
||||
}
|
||||
|
||||
function test_release_fails_when_freeze_period() public {
|
||||
// Register first
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Make sure mock verifier always return true to skip proof verification
|
||||
assertEq(verifier.result(), true);
|
||||
// Withdraw user0
|
||||
rln.withdraw(identityCommitment0, mockProof);
|
||||
// Ensure withdrawal is set
|
||||
(uint256 blockNumber, uint256 amount, address receiver) = rln.withdrawals(identityCommitment0);
|
||||
assertEq(blockNumber, block.number);
|
||||
assertEq(amount, getRegisterAmount(messageLimit0));
|
||||
assertEq(receiver, user0);
|
||||
|
||||
// Test: release fails in freeze period
|
||||
vm.expectRevert("RLN, release: cannot release yet");
|
||||
rln.release(identityCommitment0);
|
||||
// Set block.number to blockNumbersToRelease - 1, which is still in freeze period
|
||||
uint256 blockNumbersToRelease = getUnfrozenBlockHeight();
|
||||
vm.roll(blockNumbersToRelease - 1);
|
||||
vm.expectRevert("RLN, release: cannot release yet");
|
||||
rln.release(identityCommitment0);
|
||||
}
|
||||
|
||||
/* slash */
|
||||
|
||||
function test_slash_succeeds() public {
|
||||
// Test: register and get slashed
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
uint256 registerAmount = getRegisterAmount(messageLimit0);
|
||||
uint256 slashFee = getSlashFee(registerAmount);
|
||||
uint256 slashReward = registerAmount - slashFee;
|
||||
uint256 slashedReceiverBalanceBefore = token.balanceOf(slashedReceiver);
|
||||
uint256 rlnBalanceBefore = token.balanceOf(address(rln));
|
||||
uint256 feeReceiverBalanceBefore = token.balanceOf(feeReceiver);
|
||||
// ensure event is emitted
|
||||
(,, uint256 index) = rln.members(identityCommitment0);
|
||||
vm.expectEmit(true, true, false, true);
|
||||
emit MemberSlashed(index, slashedReceiver);
|
||||
// Slash and check balances
|
||||
rln.slash(identityCommitment0, slashedReceiver, mockProof);
|
||||
uint256 slashedReceiverBalanceDiff = token.balanceOf(slashedReceiver) - slashedReceiverBalanceBefore;
|
||||
uint256 rlnBalanceDiff = rlnBalanceBefore - token.balanceOf(address(rln));
|
||||
uint256 feeReceiverBalanceDiff = token.balanceOf(feeReceiver) - feeReceiverBalanceBefore;
|
||||
assertEq(slashedReceiverBalanceDiff, slashReward);
|
||||
assertEq(rlnBalanceDiff, registerAmount);
|
||||
assertEq(feeReceiverBalanceDiff, slashFee);
|
||||
// Check the record of user0 has been deleted
|
||||
checkUserIsDeleted(identityCommitment0);
|
||||
|
||||
// Test: register, withdraw, ang get slashed before release
|
||||
register(user1, identityCommitment1, messageLimit1);
|
||||
rln.withdraw(identityCommitment1, mockProof);
|
||||
rln.slash(identityCommitment1, slashedReceiver, mockProof);
|
||||
// Check the record of user1 has been deleted
|
||||
checkUserIsDeleted(identityCommitment1);
|
||||
}
|
||||
|
||||
function test_slash_fails_when_receiver_is_zero() public {
|
||||
// Register first
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Try slash user0 and it fails because of the zero address
|
||||
vm.expectRevert("RLN, slash: empty receiver address");
|
||||
rln.slash(identityCommitment0, address(0), mockProof);
|
||||
}
|
||||
|
||||
function test_slash_fails_when_not_registered() public {
|
||||
// It fails if the user is not registered yet
|
||||
vm.expectRevert("RLN, slash: member doesn't exist");
|
||||
rln.slash(identityCommitment0, slashedReceiver, mockProof);
|
||||
}
|
||||
|
||||
function test_slash_fails_when_self_slashing() public {
|
||||
// `slash` fails when receiver is the same as the registered msg.sender
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
vm.expectRevert("RLN, slash: self-slashing is prohibited");
|
||||
rln.slash(identityCommitment0, user0, mockProof);
|
||||
}
|
||||
|
||||
function test_slash_fails_when_invalid_proof() public {
|
||||
// It fails if the proof is invalid
|
||||
// Register first
|
||||
register(user0, identityCommitment0, messageLimit0);
|
||||
// Make sure mock verifier always return false
|
||||
// And thus the proof is always considered invalid
|
||||
verifier.changeResult(false);
|
||||
assertEq(verifier.result(), false);
|
||||
vm.expectRevert("RLN, slash: invalid proof");
|
||||
// Slash fails because of the invalid proof
|
||||
rln.slash(identityCommitment0, slashedReceiver, mockProof);
|
||||
}
|
||||
|
||||
/* Helpers */
|
||||
function getRegisterAmount(uint256 messageLimit) public view returns (uint256) {
|
||||
return messageLimit * minimalDeposit;
|
||||
}
|
||||
|
||||
function register(address user, uint256 identityCommitment, uint256 messageLimit) public {
|
||||
// Mint to user first
|
||||
uint256 registerTokenAmount = getRegisterAmount(messageLimit);
|
||||
token.mint(user, registerTokenAmount);
|
||||
// Remember the balance for later check
|
||||
uint256 tokenRLNBefore = token.balanceOf(address(rln));
|
||||
uint256 tokenUserBefore = token.balanceOf(user);
|
||||
uint256 identityCommitmentIndexBefore = rln.identityCommitmentIndex();
|
||||
// User approves to rln and calls register
|
||||
vm.startPrank(user);
|
||||
token.approve(address(rln), registerTokenAmount);
|
||||
// Ensure event is emitted
|
||||
vm.expectEmit(true, true, false, true);
|
||||
emit MemberRegistered(identityCommitment, messageLimit, identityCommitmentIndexBefore);
|
||||
rln.register(identityCommitment, registerTokenAmount);
|
||||
vm.stopPrank();
|
||||
|
||||
// Check states
|
||||
uint256 tokenRLNDiff = token.balanceOf(address(rln)) - tokenRLNBefore;
|
||||
uint256 tokenUserDiff = tokenUserBefore - token.balanceOf(user);
|
||||
// RLN state
|
||||
assertEq(rln.identityCommitmentIndex(), identityCommitmentIndexBefore + 1);
|
||||
assertEq(tokenRLNDiff, registerTokenAmount);
|
||||
// User state
|
||||
(address userAddress, uint256 actualMessageLimit, uint256 index) = rln.members(identityCommitment);
|
||||
assertEq(userAddress, user);
|
||||
assertEq(actualMessageLimit, messageLimit);
|
||||
assertEq(index, identityCommitmentIndexBefore);
|
||||
assertEq(tokenUserDiff, registerTokenAmount);
|
||||
}
|
||||
|
||||
function getUnfrozenBlockHeight() public view returns (uint256) {
|
||||
return block.number + freezePeriod + 1;
|
||||
}
|
||||
|
||||
function checkUserIsDeleted(uint256 identityCommitment) public {
|
||||
// User state
|
||||
(address userAddress, uint256 actualMessageLimit, uint256 index) = rln.members(identityCommitment);
|
||||
assertEq(userAddress, address(0));
|
||||
assertEq(actualMessageLimit, 0);
|
||||
assertEq(index, 0);
|
||||
// Withdrawal state
|
||||
(uint256 blockNumber, uint256 amount, address receiver) = rln.withdrawals(identityCommitment);
|
||||
assertEq(blockNumber, 0);
|
||||
assertEq(amount, 0);
|
||||
assertEq(receiver, address(0));
|
||||
}
|
||||
|
||||
function getSlashFee(uint256 registerAmount) public view returns (uint256) {
|
||||
return registerAmount * feePercentage / 100;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user