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
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 345 additions and 364 deletions

View File

@ -1,15 +1,17 @@
RlnTest:test__Constants() (gas: 7921)
RlnTest:test__InvalidRegistration__DuplicateCommitment(uint256) (runs: 1000, μ: 120248, ~: 120248)
RlnTest:test__InvalidRegistration__FullSet() (gas: 1242442)
RlnTest:test__InvalidRegistration__InsufficientDeposit(uint256) (runs: 1000, μ: 17189, ~: 17189)
RlnTest:test__InvalidRegistration__InvalidIdCommitment(uint256) (runs: 1000, μ: 16963, ~: 16968)
RlnTest:test__InvalidSlash__InvalidProof() (gas: 958986)
RlnTest:test__InvalidSlash__MemberNotRegistered(uint256) (runs: 1000, μ: 30287, ~: 30287)
RlnTest:test__InvalidSlash__NoStake(uint256,address) (runs: 1000, μ: 301136, ~: 301143)
RlnTest:test__InvalidSlash__ToRlnAddress() (gas: 128396)
RlnTest:test__InvalidSlash__ToZeroAddress() (gas: 128301)
RlnTest:test__InvalidWithdraw__InsufficientContractBalance() (gas: 126872)
RlnTest:test__InvalidWithdraw__InsufficientWithdrawalBalance() (gas: 10494)
RlnTest:test__ValidRegistration(uint256) (runs: 1000, μ: 112053, ~: 112053)
RlnTest:test__ValidSlash(uint256,address) (runs: 1000, μ: 184955, ~: 184966)
RlnTest:test__ValidWithdraw(address) (runs: 1000, μ: 183699, ~: 183687)
RlnTest:test__Constants() (gas: 8619)
RlnTest:test__InvalidRegistration__DuplicateCommitment(uint256) (runs: 1000, μ: 144113, ~: 144113)
RlnTest:test__InvalidRegistration__FullSet() (gas: 1433224)
RlnTest:test__InvalidRegistration__InsufficientDeposit(uint256) (runs: 1000, μ: 17440, ~: 17440)
RlnTest:test__InvalidRegistration__InvalidIdCommitment(uint256) (runs: 1000, μ: 17053, ~: 17058)
RlnTest:test__InvalidRegistration__InvalidUserMessageLimit() (gas: 17055)
RlnTest:test__InvalidRegistration__MaxUserMessageLimit() (gas: 17203)
RlnTest:test__InvalidSlash__InvalidProof() (gas: 1081919)
RlnTest:test__InvalidSlash__MemberNotRegistered(uint256) (runs: 1000, μ: 32521, ~: 32521)
RlnTest:test__InvalidSlash__NoStake(uint256,address) (runs: 1000, μ: 319630, ~: 319682)
RlnTest:test__InvalidSlash__ToRlnAddress() (gas: 151034)
RlnTest:test__InvalidSlash__ToZeroAddress() (gas: 150939)
RlnTest:test__InvalidWithdraw__InsufficientContractBalance() (gas: 145224)
RlnTest:test__InvalidWithdraw__InsufficientWithdrawalBalance() (gas: 10538)
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
Verifier verifier = new Verifier();
// step 2: deploy the rln contract
rln = new Rln(0, 20, address(verifier));
rln = new Rln(0, 20, 20, address(verifier));
vm.stopBroadcast();
}
}

View File

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

View File

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

View File

@ -21,6 +21,9 @@ error FailedValidation();
/// Invalid idCommitment
error InvalidIdCommitment(uint256 idCommitment);
/// Invalid userMessageLimit
error InvalidUserMessageLimit(uint256 messageLimit);
/// Invalid receiver address, when the receiver is the contract itself or 0x0
error InvalidReceiverAddress(address to);
@ -47,6 +50,9 @@ abstract contract RlnBase {
uint256 public constant Q =
21_888_242_871_839_275_222_246_405_745_257_275_088_548_364_400_416_034_343_698_204_186_575_808_495_617;
/// @notice The max message limit per epoch
uint256 public immutable MAX_MESSAGE_LIMIT;
/// @notice The deposit amount required to register as a member
uint256 public immutable MEMBERSHIP_DEPOSIT;
@ -67,6 +73,10 @@ abstract contract RlnBase {
/// maps from idCommitment to their index in the set
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
mapping(uint256 => uint256) public indexToCommitment;
@ -84,8 +94,9 @@ abstract contract RlnBase {
/// Emitted when a new member is added to the set
/// @param idCommitment The idCommitment of the member
/// @param userMessageLimit the user message limit of the member
/// @param index The index of the member in the set
event MemberRegistered(uint256 idCommitment, uint256 index);
event MemberRegistered(uint256 idCommitment, uint256 userMessageLimit, uint256 index);
/// Emitted when a member is removed from the set
/// @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;
MAX_MESSAGE_LIMIT = maxMessageLimit;
DEPTH = depth;
SET_SIZE = 1 << depth;
verifier = IVerifier(_verifier);
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
/// @param idCommitment The idCommitment of the member
function register(uint256 idCommitment) external payable virtual onlyValidIdCommitment(idCommitment) {
if (msg.value != MEMBERSHIP_DEPOSIT) {
/// @param userMessageLimit The message limit of the member
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);
}
_validateRegistration(idCommitment);
_register(idCommitment, msg.value);
_register(idCommitment, userMessageLimit, msg.value);
}
/// Registers a 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
function _register(uint256 idCommitment, uint256 stake) internal virtual {
function _register(uint256 idCommitment, uint256 userMessageLimit, uint256 stake) internal virtual {
if (memberExists[idCommitment]) revert DuplicateIdCommitment();
if (idCommitmentIndex >= SET_SIZE) revert FullTree();
@ -126,8 +163,9 @@ abstract contract RlnBase {
indexToCommitment[idCommitmentIndex] = idCommitment;
memberExists[idCommitment] = true;
stakedAmounts[idCommitment] = stake;
userMessageLimits[idCommitment] = userMessageLimit;
emit MemberRegistered(idCommitment, idCommitmentIndex);
emit MemberRegistered(idCommitment, userMessageLimit, idCommitmentIndex);
idCommitmentIndex += 1;
}
@ -158,6 +196,7 @@ abstract contract RlnBase {
revert InvalidReceiverAddress(receiver);
}
uint256 userMessageLimit = userMessageLimits[idCommitment];
if (memberExists[idCommitment] == false) revert MemberNotRegistered(idCommitment);
// check if member is registered
if (stakedAmounts[idCommitment] == 0) {
@ -176,7 +215,7 @@ abstract contract RlnBase {
indexToCommitment[index] = 0;
memberExists[idCommitment] = false;
stakedAmounts[idCommitment] = 0;
// TODO: remove from IMT
userMessageLimits[idCommitment] = 0;
// refund deposit
withdrawalBalance[receiver] += amountToTransfer;

View File

@ -1,330 +1,203 @@
// File: https://github.com/Rate-Limiting-Nullifier/rln-contract-v1/blob/main/src/RLNVerifier.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
//
//
// File: https://github.com/Rate-Limiting-Nullifier/rln-contract/blob/main/src/Verifier.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;
/*
Copyright 2021 0KIMS association.
library Pairing {
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[0] * z + X[1]
This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
/// @return the generator of G1
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.
function P1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
/// @return the generator of G2
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.
function P2() internal pure returns (G2Point memory) {
// Original code point
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
]
);
You should have received a copy of the GNU General Public License
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
*/
/*
// 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);
}
}
pragma solidity >=0.7.0 <0.9.0;
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 {
Pairing.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[] IC;
}
// 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;
struct Proof {
Pairing.G1Point A;
Pairing.G2Point B;
Pairing.G1Point C;
}
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;
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(
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,
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 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;
vk.beta2 = Pairing.G2Point(
[
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,
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
],
[
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);
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;
vk.IC[0] = Pairing.G1Point(
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,
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
);
// Memory data
uint16 constant pVk = 0;
uint16 constant pPairing = 128;
vk.IC[1] = Pairing.G1Point(
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
uint16 constant pLastMem = 896;
function verifyProof(
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c,
uint256[2] memory input
uint256[2] calldata _pA,
uint256[2][2] calldata _pB,
uint256[2] calldata _pC,
uint256[2] calldata _pubSignals
)
public
view
returns (bool r)
returns (bool)
{
Proof memory proof;
proof.A = Pairing.G1Point(a[0], a[1]);
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
proof.C = Pairing.G1Point(c[0], c[1]);
uint256[] memory inputValues = new uint256[](input.length);
for (uint256 i = 0; i < input.length; i++) {
inputValues[i] = input[i];
}
if (verify(inputValues, proof) == 0) {
return true;
} else {
return false;
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)
}
}
}

View File

@ -21,10 +21,11 @@ contract RlnTest is Test {
trueVerifier = new TrueVerifier();
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 MAX_MESSAGE_LIMIT = 20;
uint256 public constant DEPTH = 20;
uint256 public constant SET_SIZE = 1_048_576;
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.DEPTH(), DEPTH);
assertEq(rln.SET_SIZE(), SET_SIZE);
assertEq(rln.MAX_MESSAGE_LIMIT(), MAX_MESSAGE_LIMIT);
assertEq(rln.deployedBlockNumber(), 1);
}
function test__ValidRegistration(uint256 idCommitment) public {
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.memberExists(idCommitment), true);
assertEq(rln.members(idCommitment), 0);
assertEq(rln.userMessageLimits(idCommitment), 1);
}
function test__InvalidRegistration__DuplicateCommitment(uint256 idCommitment) public {
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.memberExists(idCommitment), true);
assertEq(rln.members(idCommitment), 0);
assertEq(rln.userMessageLimits(idCommitment), 1);
vm.expectRevert(DuplicateIdCommitment.selector);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
}
function test__InvalidRegistration__InvalidIdCommitment(uint256 idCommitment) public {
vm.assume(!rln.isValidCommitment(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 {
vm.assume(rln.isValidCommitment(idCommitment));
uint256 badDepositAmount = MEMBERSHIP_DEPOSIT - 1;
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 {
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();
for (uint256 i = 1; i <= setSize; i++) {
tempRln.register{ value: MEMBERSHIP_DEPOSIT }(i);
tempRln.register{ value: MEMBERSHIP_DEPOSIT }(i, 1);
}
assertEq(tempRln.idCommitmentIndex(), 4);
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 {
@ -87,7 +107,7 @@ contract RlnTest is Test {
vm.assume(to != address(0));
vm.assume(rln.isValidCommitment(idCommitment));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
uint256 balanceBefore = to.balance;
@ -105,7 +125,7 @@ contract RlnTest is Test {
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;
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
vm.expectRevert(abi.encodeWithSelector(InvalidReceiverAddress.selector, address(0)));
rln.slash(idCommitment, payable(address(0)), zeroedProof);
@ -114,7 +134,7 @@ contract RlnTest is Test {
function test__InvalidSlash__ToRlnAddress() public {
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;
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
vm.expectRevert(abi.encodeWithSelector(InvalidReceiverAddress.selector, address(rln)));
rln.slash(idCommitment, payable(address(rln)), zeroedProof);
@ -134,7 +154,7 @@ contract RlnTest is Test {
vm.assume(to != address(0));
vm.assume(rln.isValidCommitment(idCommitment));
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
rln.slash(idCommitment, to, zeroedProof);
@ -152,9 +172,9 @@ contract RlnTest is Test {
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;
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);
tempRln.slash(idCommitment, payable(address(this)), zeroedProof);
@ -168,7 +188,7 @@ contract RlnTest is Test {
function test__InvalidWithdraw__InsufficientContractBalance() public {
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;
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
rln.slash(idCommitment, payable(address(this)), zeroedProof);
assertEq(rln.stakedAmounts(idCommitment), 0);
@ -187,7 +207,7 @@ contract RlnTest is Test {
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;
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment);
rln.register{ value: MEMBERSHIP_DEPOSIT }(idCommitment, 1);
assertEq(rln.stakedAmounts(idCommitment), MEMBERSHIP_DEPOSIT);
rln.slash(idCommitment, to, zeroedProof);
assertEq(rln.stakedAmounts(idCommitment), 0);