feat: rln-v2 (#35)

* feat: init rln-v2 changes

* chore: remove poseidont3, dont calculate rate commitments

* fix: deploy script

* feat: add g16 verifier

* chore: deploy to sepolia and polygon-zkevm-testnet

* chore: snapshot

* fix(rln): reuse getDepositAmount func
This commit is contained in:
Aaryamann Challani
2024-01-23 23:20:33 +05:30
committed by GitHub
parent 9ee3217886
commit 5d9108a138
9 changed files with 345 additions and 364 deletions

View File

@@ -1,15 +1,17 @@
RlnTest:test__Constants() (gas: 7921) RlnTest:test__Constants() (gas: 8619)
RlnTest:test__InvalidRegistration__DuplicateCommitment(uint256) (runs: 1000, μ: 120248, ~: 120248) RlnTest:test__InvalidRegistration__DuplicateCommitment(uint256) (runs: 1000, μ: 144113, ~: 144113)
RlnTest:test__InvalidRegistration__FullSet() (gas: 1242442) RlnTest:test__InvalidRegistration__FullSet() (gas: 1433224)
RlnTest:test__InvalidRegistration__InsufficientDeposit(uint256) (runs: 1000, μ: 17189, ~: 17189) RlnTest:test__InvalidRegistration__InsufficientDeposit(uint256) (runs: 1000, μ: 17440, ~: 17440)
RlnTest:test__InvalidRegistration__InvalidIdCommitment(uint256) (runs: 1000, μ: 16963, ~: 16968) RlnTest:test__InvalidRegistration__InvalidIdCommitment(uint256) (runs: 1000, μ: 17053, ~: 17058)
RlnTest:test__InvalidSlash__InvalidProof() (gas: 958986) RlnTest:test__InvalidRegistration__InvalidUserMessageLimit() (gas: 17055)
RlnTest:test__InvalidSlash__MemberNotRegistered(uint256) (runs: 1000, μ: 30287, ~: 30287) RlnTest:test__InvalidRegistration__MaxUserMessageLimit() (gas: 17203)
RlnTest:test__InvalidSlash__NoStake(uint256,address) (runs: 1000, μ: 301136, ~: 301143) RlnTest:test__InvalidSlash__InvalidProof() (gas: 1081919)
RlnTest:test__InvalidSlash__ToRlnAddress() (gas: 128396) RlnTest:test__InvalidSlash__MemberNotRegistered(uint256) (runs: 1000, μ: 32521, ~: 32521)
RlnTest:test__InvalidSlash__ToZeroAddress() (gas: 128301) RlnTest:test__InvalidSlash__NoStake(uint256,address) (runs: 1000, μ: 319630, ~: 319682)
RlnTest:test__InvalidWithdraw__InsufficientContractBalance() (gas: 126872) RlnTest:test__InvalidSlash__ToRlnAddress() (gas: 151034)
RlnTest:test__InvalidWithdraw__InsufficientWithdrawalBalance() (gas: 10494) RlnTest:test__InvalidSlash__ToZeroAddress() (gas: 150939)
RlnTest:test__ValidRegistration(uint256) (runs: 1000, μ: 112053, ~: 112053) RlnTest:test__InvalidWithdraw__InsufficientContractBalance() (gas: 145224)
RlnTest:test__ValidSlash(uint256,address) (runs: 1000, μ: 184955, ~: 184966) RlnTest:test__InvalidWithdraw__InsufficientWithdrawalBalance() (gas: 10538)
RlnTest:test__ValidWithdraw(address) (runs: 1000, μ: 183699, ~: 183687) RlnTest:test__ValidRegistration(uint256) (runs: 1000, μ: 135760, ~: 135760)
RlnTest:test__ValidSlash(uint256,address) (runs: 1000, μ: 203402, ~: 203412)
RlnTest:test__ValidWithdraw(address) (runs: 1000, μ: 202120, ~: 202108)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,7 @@ contract Deploy is BaseScript {
// step 1: deploy the verifier // step 1: deploy the verifier
Verifier verifier = new Verifier(); Verifier verifier = new Verifier();
// step 2: deploy the rln contract // step 2: deploy the rln contract
rln = new Rln(0, 20, address(verifier)); rln = new Rln(0, 20, 20, address(verifier));
vm.stopBroadcast(); vm.stopBroadcast();
} }
} }

View File

@@ -3,10 +3,10 @@ pragma solidity >=0.8.19;
interface IVerifier { interface IVerifier {
function verifyProof( function verifyProof(
uint256[2] memory a, uint256[2] calldata a,
uint256[2][2] memory b, uint256[2][2] calldata b,
uint256[2] memory c, uint256[2] calldata c,
uint256[2] memory input uint256[2] calldata input
) )
external external
view view

View File

@@ -7,9 +7,10 @@ contract Rln is RlnBase {
constructor( constructor(
uint256 membershipDeposit, uint256 membershipDeposit,
uint256 depth, uint256 depth,
uint256 maxMessageLimit,
address _verifier address _verifier
) )
RlnBase(membershipDeposit, depth, _verifier) RlnBase(membershipDeposit, depth, maxMessageLimit, _verifier)
{ } { }
function _validateRegistration(uint256 idCommitment) internal pure override { } function _validateRegistration(uint256 idCommitment) internal pure override { }

View File

@@ -21,6 +21,9 @@ error FailedValidation();
/// Invalid idCommitment /// Invalid idCommitment
error InvalidIdCommitment(uint256 idCommitment); error InvalidIdCommitment(uint256 idCommitment);
/// Invalid userMessageLimit
error InvalidUserMessageLimit(uint256 messageLimit);
/// Invalid receiver address, when the receiver is the contract itself or 0x0 /// Invalid receiver address, when the receiver is the contract itself or 0x0
error InvalidReceiverAddress(address to); error InvalidReceiverAddress(address to);
@@ -47,6 +50,9 @@ abstract contract RlnBase {
uint256 public constant Q = 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; 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
uint256 public immutable MAX_MESSAGE_LIMIT;
/// @notice The deposit amount required to register as a member /// @notice The deposit amount required to register as a member
uint256 public immutable MEMBERSHIP_DEPOSIT; uint256 public immutable MEMBERSHIP_DEPOSIT;
@@ -67,6 +73,10 @@ abstract contract RlnBase {
/// maps from idCommitment to their index in the set /// maps from idCommitment to their index in the set
mapping(uint256 => uint256) public members; mapping(uint256 => uint256) public members;
/// @notice the user message limit of each member
/// maps from idCommitment to their user message limit
mapping(uint256 => uint256) public userMessageLimits;
/// @notice the index to commitment mapping /// @notice the index to commitment mapping
mapping(uint256 => uint256) public indexToCommitment; mapping(uint256 => uint256) public indexToCommitment;
@@ -84,8 +94,9 @@ abstract contract RlnBase {
/// Emitted when a new member is added to the set /// Emitted when a new member is added to the set
/// @param idCommitment The idCommitment of the member /// @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 /// @param index The index of the member in the set
event MemberRegistered(uint256 idCommitment, uint256 index); event MemberRegistered(uint256 idCommitment, uint256 userMessageLimit, uint256 index);
/// Emitted when a member is removed from the set /// Emitted when a member is removed from the set
/// @param idCommitment The idCommitment of the member /// @param idCommitment The idCommitment of the member
@@ -97,28 +108,54 @@ abstract contract RlnBase {
_; _;
} }
constructor(uint256 membershipDeposit, uint256 depth, address _verifier) { modifier onlyValidUserMessageLimit(uint256 messageLimit) {
if (messageLimit > MAX_MESSAGE_LIMIT) revert InvalidUserMessageLimit(messageLimit);
if (messageLimit == 0) revert InvalidUserMessageLimit(messageLimit);
_;
}
constructor(uint256 membershipDeposit, uint256 depth, uint256 maxMessageLimit, address _verifier) {
MEMBERSHIP_DEPOSIT = membershipDeposit; MEMBERSHIP_DEPOSIT = membershipDeposit;
MAX_MESSAGE_LIMIT = maxMessageLimit;
DEPTH = depth; DEPTH = depth;
SET_SIZE = 1 << depth; SET_SIZE = 1 << depth;
verifier = IVerifier(_verifier); verifier = IVerifier(_verifier);
deployedBlockNumber = uint32(block.number); deployedBlockNumber = uint32(block.number);
} }
/// Returns the deposit amount required to register as a member
/// @param userMessageLimit The message limit of the member
/// TODO: update this function as per tokenomics design
function getDepositAmount(uint256 userMessageLimit) public view returns (uint256) {
return userMessageLimit * MEMBERSHIP_DEPOSIT;
}
/// Allows a user to register as a member /// Allows a user to register as a member
/// @param idCommitment The idCommitment of the member /// @param idCommitment The idCommitment of the member
function register(uint256 idCommitment) external payable virtual onlyValidIdCommitment(idCommitment) { /// @param userMessageLimit The message limit of the member
if (msg.value != MEMBERSHIP_DEPOSIT) { function register(
uint256 idCommitment,
uint256 userMessageLimit
)
external
payable
virtual
onlyValidIdCommitment(idCommitment)
onlyValidUserMessageLimit(userMessageLimit)
{
uint256 requiredDeposit = getDepositAmount(userMessageLimit);
if (msg.value != requiredDeposit) {
revert InsufficientDeposit(MEMBERSHIP_DEPOSIT, msg.value); revert InsufficientDeposit(MEMBERSHIP_DEPOSIT, msg.value);
} }
_validateRegistration(idCommitment); _validateRegistration(idCommitment);
_register(idCommitment, msg.value); _register(idCommitment, userMessageLimit, msg.value);
} }
/// Registers a member /// Registers a member
/// @param idCommitment The idCommitment of the member /// @param idCommitment The idCommitment of the member
/// @param userMessageLimit The message limit of the member
/// @param stake The amount of eth staked by the member /// @param stake The amount of eth staked by the member
function _register(uint256 idCommitment, uint256 stake) internal virtual { function _register(uint256 idCommitment, uint256 userMessageLimit, uint256 stake) internal virtual {
if (memberExists[idCommitment]) revert DuplicateIdCommitment(); if (memberExists[idCommitment]) revert DuplicateIdCommitment();
if (idCommitmentIndex >= SET_SIZE) revert FullTree(); if (idCommitmentIndex >= SET_SIZE) revert FullTree();
@@ -126,8 +163,9 @@ abstract contract RlnBase {
indexToCommitment[idCommitmentIndex] = idCommitment; indexToCommitment[idCommitmentIndex] = idCommitment;
memberExists[idCommitment] = true; memberExists[idCommitment] = true;
stakedAmounts[idCommitment] = stake; stakedAmounts[idCommitment] = stake;
userMessageLimits[idCommitment] = userMessageLimit;
emit MemberRegistered(idCommitment, idCommitmentIndex); emit MemberRegistered(idCommitment, userMessageLimit, idCommitmentIndex);
idCommitmentIndex += 1; idCommitmentIndex += 1;
} }
@@ -158,6 +196,7 @@ abstract contract RlnBase {
revert InvalidReceiverAddress(receiver); revert InvalidReceiverAddress(receiver);
} }
uint256 userMessageLimit = userMessageLimits[idCommitment];
if (memberExists[idCommitment] == false) revert MemberNotRegistered(idCommitment); if (memberExists[idCommitment] == false) revert MemberNotRegistered(idCommitment);
// check if member is registered // check if member is registered
if (stakedAmounts[idCommitment] == 0) { if (stakedAmounts[idCommitment] == 0) {
@@ -176,7 +215,7 @@ abstract contract RlnBase {
indexToCommitment[index] = 0; indexToCommitment[index] = 0;
memberExists[idCommitment] = false; memberExists[idCommitment] = false;
stakedAmounts[idCommitment] = 0; stakedAmounts[idCommitment] = 0;
// TODO: remove from IMT userMessageLimits[idCommitment] = 0;
// refund deposit // refund deposit
withdrawalBalance[receiver] += amountToTransfer; withdrawalBalance[receiver] += amountToTransfer;

View File

@@ -1,330 +1,203 @@
// File: https://github.com/Rate-Limiting-Nullifier/rln-contract-v1/blob/main/src/RLNVerifier.sol // File: https://github.com/Rate-Limiting-Nullifier/rln-contract/blob/main/src/Verifier.sol
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// 2019 OKIMS
// ported to solidity 0.6
// fixed linter warnings
// added requiere error messages
//
//
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19; /*
Copyright 2021 0KIMS association.
library Pairing { This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point { snarkJS is a free software: you can redistribute it and/or modify it
uint256[2] X; under the terms of the GNU General Public License as published by
uint256[2] Y; the Free Software Foundation, either version 3 of the License, or
} (at your option) any later version.
/// @return the generator of G1
function P1() internal pure returns (G1Point memory) { snarkJS is distributed in the hope that it will be useful, but WITHOUT
return G1Point(1, 2); ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
} or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
/// @return the generator of G2 License for more details.
function P2() internal pure returns (G2Point memory) { You should have received a copy of the GNU General Public License
// Original code point along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
return G2Point( */
[
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,
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
],
[
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,
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
]
);
/* pragma solidity >=0.7.0 <0.9.0;
// Changed by Jordi point
return G2Point(
[10857046999023057135944570762232829481370756359578518086990519993285655852781,
11559732032986387107991004021392285783925812861821192530917403151452391805634],
[8495653923123431417604973247489272438418190587263600148770280649306958101930,
4082367875863433681332203403145435568316851327593401208105741076214120093531]
);*/
}
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
// The prime q in the base field F_q for G1
uint256 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;
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
}
return G1Point(p.X, q - (p.Y % q));
}
/// @return r the sum of two points of G1
function addition(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
// Use "invalid" to make gas estimation work
switch success
case 0 { invalid() }
}
require(success, "pairing-add-failed");
}
/// @return r the product of a point on G1 and a scalar, i.e.
/// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
// Use "invalid" to make gas estimation work
switch success
case 0 { invalid() }
}
require(success, "pairing-mul-failed");
}
/// @return the result of computing the pairing check
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should
/// return true.
function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) {
require(p1.length == p2.length, "pairing-lengths-failed");
uint256 elements = p1.length;
uint256 inputSize = elements * 6;
uint256[] memory input = new uint256[](inputSize);
for (uint256 i = 0; i < elements; i++) {
input[i * 6 + 0] = p1[i].X;
input[i * 6 + 1] = p1[i].Y;
input[i * 6 + 2] = p2[i].X[0];
input[i * 6 + 3] = p2[i].X[1];
input[i * 6 + 4] = p2[i].Y[0];
input[i * 6 + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 { invalid() }
}
require(success, "pairing-opcode-failed");
return out[0] != 0;
}
/// Convenience method for a pairing check for two pairs.
function pairingProd2(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2
)
internal
view
returns (bool)
{
G1Point[] memory p1 = new G1Point[](2);
G2Point[] memory p2 = new G2Point[](2);
p1[0] = a1;
p1[1] = b1;
p2[0] = a2;
p2[1] = b2;
return pairing(p1, p2);
}
/// Convenience method for a pairing check for three pairs.
function pairingProd3(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
G1Point memory c1,
G2Point memory c2
)
internal
view
returns (bool)
{
G1Point[] memory p1 = new G1Point[](3);
G2Point[] memory p2 = new G2Point[](3);
p1[0] = a1;
p1[1] = b1;
p1[2] = c1;
p2[0] = a2;
p2[1] = b2;
p2[2] = c2;
return pairing(p1, p2);
}
/// Convenience method for a pairing check for four pairs.
function pairingProd4(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
G1Point memory c1,
G2Point memory c2,
G1Point memory d1,
G2Point memory d2
)
internal
view
returns (bool)
{
G1Point[] memory p1 = new G1Point[](4);
G2Point[] memory p2 = new G2Point[](4);
p1[0] = a1;
p1[1] = b1;
p1[2] = c1;
p1[3] = d1;
p2[0] = a2;
p2[1] = b2;
p2[2] = c2;
p2[3] = d2;
return pairing(p1, p2);
}
}
contract Verifier { contract Verifier {
using Pairing for *; // 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;
struct VerifyingKey { // Verification Key data
Pairing.G1Point alfa1; uint256 constant alphax =
Pairing.G2Point beta2; 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;
Pairing.G2Point gamma2; uint256 constant alphay =
Pairing.G2Point delta2; 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;
Pairing.G1Point[] IC; 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;
struct Proof { uint256 constant IC0x =
Pairing.G1Point A; 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;
Pairing.G2Point B; uint256 constant IC0y =
Pairing.G1Point C; 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;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) { uint256 constant IC1x =
vk.alfa1 = Pairing.G1Point( 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;
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 IC1y =
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 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;
);
vk.beta2 = Pairing.G2Point( 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;
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 IC2y =
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 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;
],
[
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,
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
]
);
vk.gamma2 = Pairing.G2Point(
[
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,
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
],
[
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,
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
]
);
vk.delta2 = Pairing.G2Point(
[
12_423_666_958_566_268_737_444_308_034_237_892_912_702_648_013_927_558_883_280_319_245_968_679_130_649,
15_986_964_528_637_281_931_410_749_976_607_406_939_789_163_617_014_270_799_373_312_764_775_965_360_012
],
[
8_394_023_076_056_524_902_583_796_202_128_496_802_110_914_536_948_580_183_128_578_071_394_816_660_799,
4_964_607_673_011_101_982_600_772_762_445_991_192_038_811_950_832_626_693_345_350_322_823_626_470_007
]
);
vk.IC = new Pairing.G1Point[](3);
vk.IC[0] = Pairing.G1Point( // Memory data
1_655_549_413_518_972_190_198_478_012_616_802_994_254_462_093_161_203_201_613_599_472_264_958_303_841, uint16 constant pVk = 0;
21_742_734_017_792_296_281_216_385_119_397_138_748_114_275_727_065_024_271_646_515_586_404_591_497_876 uint16 constant pPairing = 128;
);
vk.IC[1] = Pairing.G1Point( uint16 constant pLastMem = 896;
16_497_930_821_522_159_474_595_176_304_955_625_435_616_718_625_609_462_506_360_632_944_366_974_274_906,
10_404_924_572_941_018_678_793_755_094_259_635_830_045_501_866_471_999_610_240_845_041_996_101_882_275
);
vk.IC[2] = Pairing.G1Point(
9_567_910_551_099_174_794_221_497_568_036_631_681_620_409_346_997_815_381_833_929_247_558_241_020_796,
17_282_591_858_786_007_768_931_802_126_325_866_705_896_012_606_427_630_592_145_070_155_065_868_649_172
);
}
function verify(uint256[] memory input, Proof memory proof) internal view returns (uint256) {
uint256 snark_scalar_field =
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;
VerifyingKey memory vk = verifyingKey();
require(input.length + 1 == vk.IC.length, "verifier-bad-input");
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
for (uint256 i = 0; i < input.length; i++) {
require(input[i] < snark_scalar_field, "verifier-gte-snark-scalar-field");
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}
vk_x = Pairing.addition(vk_x, vk.IC[0]);
if (
!Pairing.pairingProd4(
Pairing.negate(proof.A), proof.B, vk.alfa1, vk.beta2, vk_x, vk.gamma2, proof.C, vk.delta2
)
) return 1;
return 0;
}
/// @return r bool true if proof is valid
function verifyProof( function verifyProof(
uint256[2] memory a, uint256[2] calldata _pA,
uint256[2][2] memory b, uint256[2][2] calldata _pB,
uint256[2] memory c, uint256[2] calldata _pC,
uint256[2] memory input uint256[2] calldata _pubSignals
) )
public public
view view
returns (bool r) returns (bool)
{ {
Proof memory proof; assembly {
proof.A = Pairing.G1Point(a[0], a[1]); function checkField(v) {
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); if iszero(lt(v, q)) {
proof.C = Pairing.G1Point(c[0], c[1]); mstore(0, 0)
uint256[] memory inputValues = new uint256[](input.length); return(0, 0x20)
for (uint256 i = 0; i < input.length; i++) { }
inputValues[i] = input[i]; }
}
if (verify(inputValues, proof) == 0) { // G1 function to multiply a G1 value(x,y) to value in an address
return true; function g1_mulAccC(pR, x, y, s) {
} else { let success
return false; 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)
} }
} }
} }

View File

@@ -21,10 +21,11 @@ contract RlnTest is Test {
trueVerifier = new TrueVerifier(); trueVerifier = new TrueVerifier();
falseVerifier = new FalseVerifier(); falseVerifier = new FalseVerifier();
rln = new Rln(MEMBERSHIP_DEPOSIT, DEPTH, address(trueVerifier)); rln = new Rln(MEMBERSHIP_DEPOSIT, DEPTH, MAX_MESSAGE_LIMIT, address(trueVerifier));
} }
uint256 public constant MEMBERSHIP_DEPOSIT = 1_000_000_000_000_000; uint256 public constant MEMBERSHIP_DEPOSIT = 1_000_000_000_000_000;
uint256 public constant MAX_MESSAGE_LIMIT = 20;
uint256 public constant DEPTH = 20; uint256 public constant DEPTH = 20;
uint256 public constant SET_SIZE = 1_048_576; uint256 public constant SET_SIZE = 1_048_576;
uint256[8] public zeroedProof = [0, 0, 0, 0, 0, 0, 0, 0]; uint256[8] public zeroedProof = [0, 0, 0, 0, 0, 0, 0, 0];
@@ -34,49 +35,68 @@ contract RlnTest is Test {
assertEq(rln.MEMBERSHIP_DEPOSIT(), MEMBERSHIP_DEPOSIT); assertEq(rln.MEMBERSHIP_DEPOSIT(), MEMBERSHIP_DEPOSIT);
assertEq(rln.DEPTH(), DEPTH); assertEq(rln.DEPTH(), DEPTH);
assertEq(rln.SET_SIZE(), SET_SIZE); assertEq(rln.SET_SIZE(), SET_SIZE);
assertEq(rln.MAX_MESSAGE_LIMIT(), MAX_MESSAGE_LIMIT);
assertEq(rln.deployedBlockNumber(), 1); assertEq(rln.deployedBlockNumber(), 1);
} }
function test__ValidRegistration(uint256 idCommitment) public { function test__ValidRegistration(uint256 idCommitment) public {
vm.assume(rln.isValidCommitment(idCommitment)); vm.assume(rln.isValidCommitment(idCommitment));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT); assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
assertEq(rln.memberExists(idCommitment), true); assertEq(rln.memberExists(idCommitment), true);
assertEq(rln.members(idCommitment), 0); assertEq(rln.members(idCommitment), 0);
assertEq(rln.userMessageLimits(idCommitment), 1);
} }
function test__InvalidRegistration__DuplicateCommitment(uint256 idCommitment) public { function test__InvalidRegistration__DuplicateCommitment(uint256 idCommitment) public {
vm.assume(rln.isValidCommitment(idCommitment)); vm.assume(rln.isValidCommitment(idCommitment));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT); assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
assertEq(rln.memberExists(idCommitment), true); assertEq(rln.memberExists(idCommitment), true);
assertEq(rln.members(idCommitment), 0); assertEq(rln.members(idCommitment), 0);
assertEq(rln.userMessageLimits(idCommitment), 1);
vm.expectRevert(DuplicateIdCommitment.selector); vm.expectRevert(DuplicateIdCommitment.selector);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
} }
function test__InvalidRegistration__InvalidIdCommitment(uint256 idCommitment) public { function test__InvalidRegistration__InvalidIdCommitment(uint256 idCommitment) public {
vm.assume(!rln.isValidCommitment(idCommitment)); vm.assume(!rln.isValidCommitment(idCommitment));
vm.expectRevert(abi.encodeWithSelector(InvalidIdCommitment.selector, idCommitment)); vm.expectRevert(abi.encodeWithSelector(InvalidIdCommitment.selector, idCommitment));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
}
function test__InvalidRegistration__InvalidUserMessageLimit() public {
uint256 idCommitment =
9_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820;
vm.assume(rln.isValidCommitment(idCommitment));
vm.expectRevert(abi.encodeWithSelector(InvalidUserMessageLimit.selector, 0));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 0);
}
function test__InvalidRegistration__MaxUserMessageLimit() public {
uint256 idCommitment =
9_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820;
vm.assume(rln.isValidCommitment(idCommitment));
vm.expectRevert(abi.encodeWithSelector(InvalidUserMessageLimit.selector, MAX_MESSAGE_LIMIT + 1));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, MAX_MESSAGE_LIMIT + 1);
} }
function test__InvalidRegistration__InsufficientDeposit(uint256 idCommitment) public { function test__InvalidRegistration__InsufficientDeposit(uint256 idCommitment) public {
vm.assume(rln.isValidCommitment(idCommitment)); vm.assume(rln.isValidCommitment(idCommitment));
uint256 badDepositAmount = MEMBERSHIP_DEPOSIT - 1; uint256 badDepositAmount = MEMBERSHIP_DEPOSIT - 1;
vm.expectRevert(abi.encodeWithSelector(InsufficientDeposit.selector, MEMBERSHIP_DEPOSIT, badDepositAmount)); vm.expectRevert(abi.encodeWithSelector(InsufficientDeposit.selector, MEMBERSHIP_DEPOSIT, badDepositAmount));
rln.register{ value: badDepositAmount }(idCommitment); rln.register{ value: badDepositAmount }(idCommitment, 1);
} }
function test__InvalidRegistration__FullSet() public { function test__InvalidRegistration__FullSet() public {
Rln tempRln = new Rln(MEMBERSHIP_DEPOSIT, 2, address(rln.verifier())); Rln tempRln = new Rln(MEMBERSHIP_DEPOSIT, 2, MAX_MESSAGE_LIMIT, address(rln.verifier()));
uint256 setSize = tempRln.SET_SIZE(); uint256 setSize = tempRln.SET_SIZE();
for (uint256 i = 1; i <= setSize; i++) { for (uint256 i = 1; i <= setSize; i++) {
tempRln.register{ value: MEMBERSHIP_DEPOSIT }(i); tempRln.register{ value: MEMBERSHIP_DEPOSIT }(i, 1);
} }
assertEq(tempRln.idCommitmentIndex(), 4); assertEq(tempRln.idCommitmentIndex(), 4);
vm.expectRevert(FullTree.selector); vm.expectRevert(FullTree.selector);
tempRln.register{ value: MEMBERSHIP_DEPOSIT }(setSize + 1); tempRln.register{ value: MEMBERSHIP_DEPOSIT }(setSize + 1, 1);
} }
function test__ValidSlash(uint256 idCommitment, address payable to) public { function test__ValidSlash(uint256 idCommitment, address payable to) public {
@@ -87,7 +107,7 @@ contract RlnTest is Test {
vm.assume(to != address(0)); vm.assume(to != address(0));
vm.assume(rln.isValidCommitment(idCommitment)); vm.assume(rln.isValidCommitment(idCommitment));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT); assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
uint256 balanceBefore = to.balance; uint256 balanceBefore = to.balance;
@@ -105,7 +125,7 @@ contract RlnTest is Test {
uint256 idCommitment = uint256 idCommitment =
9_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820; 9_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820;
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT); assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
vm.expectRevert(abi.encodeWithSelector(InvalidReceiverAddress.selector, address(0))); vm.expectRevert(abi.encodeWithSelector(InvalidReceiverAddress.selector, address(0)));
rln.slash(idCommitment, payable(address(0)), zeroedProof); rln.slash(idCommitment, payable(address(0)), zeroedProof);
@@ -114,7 +134,7 @@ contract RlnTest is Test {
function test__InvalidSlash__ToRlnAddress() public { function test__InvalidSlash__ToRlnAddress() public {
uint256 idCommitment = uint256 idCommitment =
19_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820; 19_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820;
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT); assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
vm.expectRevert(abi.encodeWithSelector(InvalidReceiverAddress.selector, address(rln))); vm.expectRevert(abi.encodeWithSelector(InvalidReceiverAddress.selector, address(rln)));
rln.slash(idCommitment, payable(address(rln)), zeroedProof); rln.slash(idCommitment, payable(address(rln)), zeroedProof);
@@ -134,7 +154,7 @@ contract RlnTest is Test {
vm.assume(to != address(0)); vm.assume(to != address(0));
vm.assume(rln.isValidCommitment(idCommitment)); vm.assume(rln.isValidCommitment(idCommitment));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT); assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
rln.slash(idCommitment, to, zeroedProof); rln.slash(idCommitment, to, zeroedProof);
@@ -152,9 +172,9 @@ contract RlnTest is Test {
uint256 idCommitment = uint256 idCommitment =
19_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820; 19_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820;
Rln tempRln = new Rln(MEMBERSHIP_DEPOSIT, 2, address(falseVerifier)); Rln tempRln = new Rln(MEMBERSHIP_DEPOSIT, 2, MAX_MESSAGE_LIMIT, address(falseVerifier));
tempRln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); tempRln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
vm.expectRevert(InvalidProof.selector); vm.expectRevert(InvalidProof.selector);
tempRln.slash(idCommitment, payable(address(this)), zeroedProof); tempRln.slash(idCommitment, payable(address(this)), zeroedProof);
@@ -168,7 +188,7 @@ contract RlnTest is Test {
function test__InvalidWithdraw__InsufficientContractBalance() public { function test__InvalidWithdraw__InsufficientContractBalance() public {
uint256 idCommitment = uint256 idCommitment =
19_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820; 19_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820;
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT); assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
rln.slash(idCommitment, payable(address(this)), zeroedProof); rln.slash(idCommitment, payable(address(this)), zeroedProof);
assertEq(rln.stakedAmounts(idCommitment), 0); assertEq(rln.stakedAmounts(idCommitment), 0);
@@ -187,7 +207,7 @@ contract RlnTest is Test {
uint256 idCommitment = uint256 idCommitment =
19_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820; 19_014_214_495_641_488_759_237_505_126_948_346_942_972_912_379_615_652_741_039_992_445_865_937_985_820;
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment); rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT); assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
rln.slash(idCommitment, to, zeroedProof); rln.slash(idCommitment, to, zeroedProof);
assertEq(rln.stakedAmounts(idCommitment), 0); assertEq(rln.stakedAmounts(idCommitment), 0);