mirror of
https://github.com/0xbow-io/privacy-pools-core.git
synced 2026-01-08 17:13:52 -05:00
feat: CommitmentHasher and Withdrawal circuits (#1)
# 🤖 Linear Closes 0XB-72 Closes 0XB-70 Closes 0XB-69 --------- Co-authored-by: drgorillamd <83670532+drgorillamd@users.noreply.github.com>
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
name: tests
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
on: [push]
|
||||
|
||||
concurrency:
|
||||
group: ${{github.workflow}}-${{github.ref}}
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: packages/circuits
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Run Circuits Tests
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -22,9 +28,9 @@ jobs:
|
||||
nasm \
|
||||
nlohmann-json3-dev
|
||||
|
||||
- name: Download Circom Binary v2.1.5
|
||||
- name: Download Circom Binary v2.2.1
|
||||
run: |
|
||||
wget -qO /home/runner/work/circom https://github.com/iden3/circom/releases/download/v2.1.5/circom-linux-amd64
|
||||
wget -qO /home/runner/work/circom https://github.com/iden3/circom/releases/download/v2.2.1/circom-linux-amd64
|
||||
chmod +x /home/runner/work/circom
|
||||
sudo mv /home/runner/work/circom /bin/circom
|
||||
|
||||
@@ -34,5 +40,8 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Compile circuits
|
||||
run: yarn compile
|
||||
|
||||
- name: Run tests
|
||||
run: yarn test
|
||||
@@ -1,3 +1,5 @@
|
||||
To use circomspect locally: `cargo install circomspect` then `yarn circomspec` from the packages/circuits directory.
|
||||
|
||||
# Circomkit Examples
|
||||
|
||||
In this repository, we are using [Circomkit](https://github.com/erhant/circomkit) to test some example circuits using Mocha. The circuits and the statements that they prove are as follows:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"version": "2.1.2",
|
||||
"proofSystem": "plonk",
|
||||
"curve": "bn128"
|
||||
"proofSystem": "groth16",
|
||||
"curve": "bn128",
|
||||
"include": "../../node_modules/"
|
||||
}
|
||||
|
||||
@@ -1,39 +1,10 @@
|
||||
{
|
||||
"multiplier_3": {
|
||||
"file": "multiplier",
|
||||
"template": "Multiplier",
|
||||
"params": [3]
|
||||
"commitment": {
|
||||
"file": "commitment",
|
||||
"template": "CommitmentHasher"
|
||||
},
|
||||
"sha256_32": {
|
||||
"file": "sha256",
|
||||
"template": "Sha256Bytes",
|
||||
"params": [32]
|
||||
},
|
||||
"sudoku_9x9": {
|
||||
"file": "sudoku",
|
||||
"template": "Sudoku",
|
||||
"pubs": ["puzzle"],
|
||||
"params": [3]
|
||||
},
|
||||
"sudoku_4x4": {
|
||||
"file": "sudoku",
|
||||
"template": "Sudoku",
|
||||
"pubs": ["puzzle"],
|
||||
"params": [2]
|
||||
},
|
||||
"fp64": {
|
||||
"file": "float_add",
|
||||
"template": "FloatAdd",
|
||||
"params": [11, 52]
|
||||
},
|
||||
"fp32": {
|
||||
"file": "float_add",
|
||||
"template": "FloatAdd",
|
||||
"params": [8, 23]
|
||||
},
|
||||
"fibonacci_11": {
|
||||
"file": "fibonacci",
|
||||
"template": "Fibonacci",
|
||||
"params": [11]
|
||||
"merkleTree": {
|
||||
"file": "merkleTree",
|
||||
"template": "MerkleTreeChecker"
|
||||
}
|
||||
}
|
||||
|
||||
43
packages/circuits/circuits/commitment.circom
Normal file
43
packages/circuits/circuits/commitment.circom
Normal file
@@ -0,0 +1,43 @@
|
||||
pragma circom 2.2.0;
|
||||
|
||||
include "../../../node_modules/circomlib/circuits/poseidon.circom";
|
||||
|
||||
/**
|
||||
* @title CommitmentHasher template
|
||||
* @dev Template for computing commitment hashes, precommitments and nullifier hashes
|
||||
*/
|
||||
template CommitmentHasher() {
|
||||
|
||||
//////////////////////// SIGNALS ////////////////////////
|
||||
|
||||
signal input value; // Value of commitment
|
||||
signal input label; // keccak256(pool_scope, nonce)
|
||||
signal input nullifier; // Nullifier of commitment
|
||||
signal input secret; // Secret of commitment
|
||||
|
||||
signal output commitment; // Commitment hash
|
||||
signal output precommitmentHash; // Precommitment hash
|
||||
signal output nullifierHash; // Nullifier hash
|
||||
|
||||
///////////////////// END OF SIGNALS /////////////////////
|
||||
|
||||
// 1. Compute nullifier hash
|
||||
component nullifierHasher = Poseidon(1);
|
||||
nullifierHasher.inputs[0] <== nullifier;
|
||||
|
||||
// 2. Compute precommitment
|
||||
component precommitmentHasher = Poseidon(2);
|
||||
precommitmentHasher.inputs[0] <== nullifier;
|
||||
precommitmentHasher.inputs[1] <== secret;
|
||||
|
||||
// 3. Compute commitment hash
|
||||
component commitmentHasher = Poseidon(3);
|
||||
commitmentHasher.inputs[0] <== value;
|
||||
commitmentHasher.inputs[1] <== label;
|
||||
commitmentHasher.inputs[2] <== precommitmentHasher.out;
|
||||
|
||||
// 4. Populate output signals
|
||||
commitment <== commitmentHasher.out;
|
||||
precommitmentHash <== precommitmentHasher.out;
|
||||
nullifierHash <== nullifierHasher.out;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
// Fibonacci with custom starting numbers
|
||||
template Fibonacci(n) {
|
||||
assert(n >= 2);
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
|
||||
signal fib[n+1];
|
||||
fib[0] <== in[0];
|
||||
fib[1] <== in[1];
|
||||
for (var i = 2; i <= n; i++) {
|
||||
fib[i] <== fib[i-2] + fib[i-1];
|
||||
}
|
||||
|
||||
out <== fib[n];
|
||||
}
|
||||
|
||||
// Fibonacci with custom starting numbers, recursive & inefficient
|
||||
template FibonacciRecursive(n) {
|
||||
signal input in[2];
|
||||
signal output out;
|
||||
component f1, f2;
|
||||
if (n <= 1) {
|
||||
out <== in[n];
|
||||
} else {
|
||||
f1 = FibonacciRecursive(n-1);
|
||||
f1.in <== in;
|
||||
f2 = FibonacciRecursive(n-2);
|
||||
f2.in <== in;
|
||||
out <== f1.out + f2.out;
|
||||
}
|
||||
}
|
||||
@@ -1,417 +0,0 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
// circuits adapted from https://github.com/rdi-berkeley/zkp-mooc-lab
|
||||
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "circomlib/circuits/switcher.circom";
|
||||
include "circomlib/circuits/gates.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
|
||||
/*
|
||||
* Finds Math.floor(log2(n))
|
||||
*/
|
||||
function log2(n) {
|
||||
var tmp = 1, ans = 1;
|
||||
while (tmp < n) {
|
||||
ans++;
|
||||
tmp *= 2;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
/*
|
||||
* Basically `out = cond ? ifTrue : ifFalse`
|
||||
*/
|
||||
template IfElse() {
|
||||
signal input cond;
|
||||
signal input ifTrue;
|
||||
signal input ifFalse;
|
||||
signal output out;
|
||||
|
||||
// cond * T - cond * F + F
|
||||
// 0 * T - 0 * F + F = 0 - 0 + F = F
|
||||
// 1 * T - 1 * F + F = T - F + F = T
|
||||
out <== cond * (ifTrue - ifFalse) + ifFalse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Outputs `out` = 1 if `in` is at most `b` bits long, and 0 otherwise.
|
||||
*/
|
||||
template CheckBitLength(b) {
|
||||
assert(b < 254);
|
||||
signal input in;
|
||||
signal output out;
|
||||
|
||||
// compute b-bit representation of the number
|
||||
signal bits[b];
|
||||
var sum_of_bits = 0;
|
||||
for (var i = 0; i < b; i++) {
|
||||
bits[i] <-- (in >> i) & 1;
|
||||
bits[i] * (1 - bits[i]) === 0;
|
||||
sum_of_bits += (2 ** i) * bits[i];
|
||||
}
|
||||
|
||||
// check if sum is equal to number itself
|
||||
component eq = IsEqual();
|
||||
eq.in[0] <== sum_of_bits;
|
||||
eq.in[1] <== in;
|
||||
out <== eq.out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enforces the well-formedness of an exponent-mantissa pair (e, m), which is defined as follows:
|
||||
* if `e` is zero, then `m` must be zero
|
||||
* else, `e` must be at most `k` bits long, and `m` must be in the range [2^p, 2^p+1)
|
||||
*/
|
||||
template CheckWellFormedness(k, p) {
|
||||
signal input e;
|
||||
signal input m;
|
||||
|
||||
// check if `e` is zero
|
||||
component is_e_zero = IsZero();
|
||||
is_e_zero.in <== e;
|
||||
|
||||
// Case I: `e` is zero
|
||||
//// `m` must be zero
|
||||
component is_m_zero = IsZero();
|
||||
is_m_zero.in <== m;
|
||||
|
||||
// Case II: `e` is nonzero
|
||||
//// `e` is `k` bits
|
||||
component check_e_bits = CheckBitLength(k);
|
||||
check_e_bits.in <== e;
|
||||
//// `m` is `p`+1 bits with the MSB equal to 1
|
||||
//// equivalent to check `m` - 2^`p` is in `p` bits
|
||||
component check_m_bits = CheckBitLength(p);
|
||||
check_m_bits.in <== m - (1 << p);
|
||||
|
||||
// choose the right checks based on `is_e_zero`
|
||||
component if_else = IfElse();
|
||||
if_else.cond <== is_e_zero.out;
|
||||
if_else.ifTrue <== is_m_zero.out;
|
||||
//// check_m_bits.out * check_e_bits.out is equivalent to check_m_bits.out AND check_e_bits.out
|
||||
if_else.ifFalse <== check_m_bits.out * check_e_bits.out;
|
||||
|
||||
// assert that those checks passed
|
||||
if_else.out === 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Right-shifts `x` by `shift` bits to output `y`, where `shift` is a public circuit parameter.
|
||||
*/
|
||||
template RightShift(b, shift) {
|
||||
assert(shift < b);
|
||||
signal input x;
|
||||
signal output y;
|
||||
|
||||
// convert number to bits
|
||||
component x_bits = Num2Bits(b);
|
||||
x_bits.in <== x;
|
||||
|
||||
// do the shifting
|
||||
signal y_bits[b-shift];
|
||||
for (var i = 0; i < b-shift; i++) {
|
||||
y_bits[i] <== x_bits.out[shift+i];
|
||||
}
|
||||
|
||||
// convert shifted bits to number
|
||||
component y_num = Bits2Num(b-shift);
|
||||
y_num.in <== y_bits;
|
||||
y <== y_num.out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rounds the input floating-point number and checks to ensure that rounding does not make the mantissa unnormalized.
|
||||
* Rounding is necessary to prevent the bitlength of the mantissa from growing with each successive operation.
|
||||
* The input is a normalized floating-point number (e, m) with precision `P`, where `e` is a `k`-bit exponent and `m` is a `P`+1-bit mantissa.
|
||||
* The output is a normalized floating-point number (e_out, m_out) representing the same value with a lower precision `p`.
|
||||
*/
|
||||
template RoundAndCheck(k, p, P) {
|
||||
signal input e;
|
||||
signal input m;
|
||||
signal output e_out;
|
||||
signal output m_out;
|
||||
assert(P > p);
|
||||
|
||||
// check if no overflow occurs
|
||||
component if_no_overflow = LessThan(P+1);
|
||||
if_no_overflow.in[0] <== m;
|
||||
if_no_overflow.in[1] <== (1 << (P+1)) - (1 << (P-p-1));
|
||||
signal no_overflow <== if_no_overflow.out;
|
||||
|
||||
var round_amt = P-p;
|
||||
// Case I: no overflow
|
||||
// compute (m + 2^{round_amt-1}) >> round_amt
|
||||
var m_prime = m + (1 << (round_amt-1));
|
||||
component right_shift = RightShift(P+2, round_amt);
|
||||
right_shift.x <== m_prime;
|
||||
var m_out_1 = right_shift.y;
|
||||
var e_out_1 = e;
|
||||
|
||||
// Case II: overflow
|
||||
var e_out_2 = e + 1;
|
||||
var m_out_2 = (1 << p);
|
||||
|
||||
// select right output based on no_overflow
|
||||
component if_else[2];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
if_else[i] = IfElse();
|
||||
if_else[i].cond <== no_overflow;
|
||||
}
|
||||
if_else[0].ifTrue <== e_out_1;
|
||||
if_else[0].ifFalse <== e_out_2;
|
||||
if_else[1].ifTrue <== m_out_1;
|
||||
if_else[1].ifFalse <== m_out_2;
|
||||
e_out <== if_else[0].out;
|
||||
m_out <== if_else[1].out;
|
||||
}
|
||||
|
||||
template Num2BitsWithSkipChecks(b) {
|
||||
signal input in;
|
||||
signal input skip_checks;
|
||||
signal output out[b];
|
||||
|
||||
for (var i = 0; i < b; i++) {
|
||||
out[i] <-- (in >> i) & 1;
|
||||
out[i] * (1 - out[i]) === 0;
|
||||
}
|
||||
var sum_of_bits = 0;
|
||||
for (var i = 0; i < b; i++) {
|
||||
sum_of_bits += (2 ** i) * out[i];
|
||||
}
|
||||
|
||||
// is always true if skip_checks is 1
|
||||
(sum_of_bits - in) * (1 - skip_checks) === 0;
|
||||
}
|
||||
|
||||
template LessThanWithSkipChecks(n) {
|
||||
assert(n <= 252);
|
||||
signal input in[2];
|
||||
signal input skip_checks;
|
||||
signal output out;
|
||||
|
||||
component n2b = Num2BitsWithSkipChecks(n+1);
|
||||
n2b.in <== in[0] + (1<<n) - in[1];
|
||||
n2b.skip_checks <== skip_checks;
|
||||
out <== 1-n2b.out[n];
|
||||
}
|
||||
|
||||
/*
|
||||
* Left-shifts `x` by `shift` bits to output `y`.
|
||||
* Enforces 0 <= `shift` < `shift_bound`.
|
||||
* If `skip_checks` = 1, then we don't care about the output
|
||||
* and the `shift_bound` constraint is not enforced.
|
||||
*/
|
||||
template LeftShift(shift_bound) {
|
||||
signal input x;
|
||||
signal input shift;
|
||||
signal input skip_checks;
|
||||
signal output y;
|
||||
|
||||
// find number of bits in shift_bound
|
||||
var n = log2(shift_bound) + 1;
|
||||
|
||||
// convert "shift" to bits
|
||||
component shift_bits = Num2BitsWithSkipChecks(n);
|
||||
shift_bits.in <== shift;
|
||||
shift_bits.skip_checks <== skip_checks;
|
||||
|
||||
// check "shift" < "shift_bound"
|
||||
component less_than = LessThanWithSkipChecks(n);
|
||||
less_than.in[0] <== shift;
|
||||
less_than.in[1] <== shift_bound;
|
||||
less_than.skip_checks <== skip_checks;
|
||||
(less_than.out - 1) * (1 - skip_checks) === 0;
|
||||
|
||||
// compute pow2_shift from bits
|
||||
// represents the shift amount
|
||||
var pow2_shift = 1;
|
||||
component muxes[n];
|
||||
for (var i = 0; i < n; i++) {
|
||||
muxes[i] = IfElse();
|
||||
muxes[i].cond <== shift_bits.out[i];
|
||||
muxes[i].ifTrue <== pow2_shift * (2 ** (2 ** i));
|
||||
muxes[i].ifFalse <== pow2_shift;
|
||||
pow2_shift = muxes[i].out;
|
||||
}
|
||||
|
||||
// if skip checks, set pow2_shift to 0
|
||||
component if_else = IfElse();
|
||||
if_else.cond <== skip_checks;
|
||||
if_else.ifTrue <== 0;
|
||||
if_else.ifFalse <== pow2_shift;
|
||||
pow2_shift = if_else.out; // not <== because it's a variable
|
||||
|
||||
// do the shift
|
||||
y <== x * pow2_shift;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the Most-Significant Non-Zero Bit (MSNZB) of `in`, where `in` is assumed to be non-zero value of `b` bits.
|
||||
* Outputs the MSNZB as a one-hot vector `one_hot` of `b` bits, where `one_hot`[i] = 1 if MSNZB(`in`) = i and 0 otherwise.
|
||||
* The MSNZB is output as a one-hot vector to reduce the number of constraints in the subsequent `Normalize` template.
|
||||
* Enforces that `in` is non-zero as MSNZB(0) is undefined.
|
||||
* If `skip_checks` = 1, then we don't care about the output and the non-zero constraint is not enforced.
|
||||
*/
|
||||
template MSNZB(b) {
|
||||
signal input in;
|
||||
signal input skip_checks;
|
||||
signal output one_hot[b];
|
||||
|
||||
// compute ell, ensuring that it is made of bits too
|
||||
for (var i = 0; i < b; i++) {
|
||||
var temp;
|
||||
if (((1 << i) <= in) && (in < (1 << (i + 1)))) {
|
||||
temp = 1;
|
||||
} else {
|
||||
temp = 0;
|
||||
}
|
||||
one_hot[i] <-- temp;
|
||||
}
|
||||
|
||||
// verify that one_hot only has bits, and has only one set bit
|
||||
var sum_of_bits = 0;
|
||||
for (var i = 0; i < b; i++) {
|
||||
sum_of_bits += one_hot[i];
|
||||
one_hot[i] * (1 - one_hot[i]) === 0; // is bit
|
||||
}
|
||||
(1 - sum_of_bits) * (1 - skip_checks) === 0;
|
||||
|
||||
// verify that the set bit is at correct place
|
||||
var pow2_ell = 0;
|
||||
var pow2_ell_plus1 = 0;
|
||||
for (var i = 0; i < b; i++) {
|
||||
pow2_ell += one_hot[i] * (1 << i);
|
||||
pow2_ell_plus1 += one_hot[i] * (1 << (i + 1));
|
||||
}
|
||||
component lt1 = LessThan(b+1);
|
||||
lt1.in[0] <== in;
|
||||
lt1.in[1] <== pow2_ell_plus1;
|
||||
(lt1.out - 1) * (1 - skip_checks) === 0;
|
||||
component lt2 = LessThan(b);
|
||||
lt2.in[0] <== pow2_ell - 1;
|
||||
lt2.in[1] <== in;
|
||||
(lt2.out - 1) * (1 - skip_checks) === 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the input floating-point number.
|
||||
* The input is a floating-point number with a `k`-bit exponent `e` and a `P`+1-bit *unnormalized* mantissa `m` with precision `p`, where `m` is assumed to be non-zero.
|
||||
* The output is a floating-point number representing the same value with exponent `e_out` and a *normalized* mantissa `m_out` of `P`+1-bits and precision `P`.
|
||||
* Enforces that `m` is non-zero as a zero-value can not be normalized.
|
||||
* If `skip_checks` = 1, then we don't care about the output and the non-zero constraint is not enforced.
|
||||
*/
|
||||
template Normalize(k, p, P) {
|
||||
signal input e;
|
||||
signal input m;
|
||||
signal input skip_checks;
|
||||
signal output e_out;
|
||||
signal output m_out;
|
||||
assert(P > p);
|
||||
|
||||
// compute ell = MSNZB
|
||||
component msnzb = MSNZB(P+1);
|
||||
msnzb.in <== m;
|
||||
msnzb.skip_checks <== skip_checks;
|
||||
|
||||
// compute ell and L = 2 ** (P - ell)
|
||||
var ell, L;
|
||||
for (var i = 0; i < P+1; i++) {
|
||||
ell += msnzb.one_hot[i] * i;
|
||||
L += msnzb.one_hot[i] * (1 << (P - i));
|
||||
}
|
||||
|
||||
// return
|
||||
e_out <== e + ell - p;
|
||||
m_out <== m * L;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds two floating-point numbers.
|
||||
* The inputs are normalized floating-point numbers with `k`-bit exponents `e` and `p`+1-bit mantissas `m` with scale `p`.
|
||||
* Does not assume that the inputs are well-formed and makes appropriate checks for the same.
|
||||
* The output is a normalized floating-point number with exponent `e_out` and mantissa `m_out` of `p`+1-bits and scale `p`.
|
||||
* Enforces that inputs are well-formed.
|
||||
*/
|
||||
template FloatAdd(k, p) {
|
||||
signal input e[2];
|
||||
signal input m[2];
|
||||
signal output e_out;
|
||||
signal output m_out;
|
||||
|
||||
// check well formedness
|
||||
component well_form[2];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
well_form[i] = CheckWellFormedness(k, p);
|
||||
well_form[i].e <== e[i];
|
||||
well_form[i].m <== m[i];
|
||||
}
|
||||
|
||||
// find the larger magnitude
|
||||
var magn[2];
|
||||
component larger_magn = LessThan(k+p+1);
|
||||
for (var i = 0; i < 2; i++) {
|
||||
magn[i] = (e[i] * (1 << (p+1))) + m[i];
|
||||
larger_magn.in[i] <== magn[i];
|
||||
}
|
||||
signal is_input2_larger <== larger_magn.out;
|
||||
|
||||
// arrange by magnitude
|
||||
var input_1[2] = [e[0], m[0]];
|
||||
var input_2[2] = [e[1], m[1]];
|
||||
component switcher[2];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
switcher[i] = Switcher();
|
||||
switcher[i].sel <== is_input2_larger;
|
||||
switcher[i].L <== input_1[i];
|
||||
switcher[i].R <== input_2[i];
|
||||
}
|
||||
var alpha_e = switcher[0].outL;
|
||||
var alpha_m = switcher[1].outL;
|
||||
var beta_e = switcher[0].outR;
|
||||
var beta_m = switcher[1].outR;
|
||||
|
||||
// if-else part
|
||||
var diff_e = alpha_e - beta_e;
|
||||
component compare_diff_e = LessThan(k);
|
||||
compare_diff_e.in[0] <== p+1;
|
||||
compare_diff_e.in[1] <== diff_e;
|
||||
component is_alpha_e_zero = IsZero();
|
||||
is_alpha_e_zero.in <== alpha_e;
|
||||
|
||||
//// case 1
|
||||
component or = OR();
|
||||
or.a <== compare_diff_e.out;
|
||||
or.b <== is_alpha_e_zero.out;
|
||||
signal is_case_1 <== or.out; // true branch
|
||||
var case_1_output[2] = [alpha_e, alpha_m];
|
||||
|
||||
//// case 2
|
||||
component shl = LeftShift(p+2);
|
||||
shl.x <== alpha_m;
|
||||
shl.shift <== diff_e;
|
||||
shl.skip_checks <== is_case_1; // skip if this isnt the case
|
||||
alpha_m = shl.y;
|
||||
var mantissa = alpha_m + beta_m;
|
||||
var exponent = beta_e;
|
||||
component normalize = Normalize(k, p, 2*p + 1);
|
||||
normalize.m <== mantissa;
|
||||
normalize.e <== exponent;
|
||||
normalize.skip_checks <== is_case_1;
|
||||
component rnc = RoundAndCheck(k, p, 2*p + 1);
|
||||
rnc.m <== normalize.m_out;
|
||||
rnc.e <== normalize.e_out;
|
||||
var case_2_output[2] = [rnc.e_out, rnc.m_out];
|
||||
|
||||
// return
|
||||
component if_else[2];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
if_else[i] = IfElse();
|
||||
if_else[i].cond <== is_case_1;
|
||||
if_else[i].ifTrue <== case_1_output[i];
|
||||
if_else[i].ifFalse <== case_2_output[i];
|
||||
}
|
||||
e_out <== if_else[0].out;
|
||||
m_out <== if_else[1].out;
|
||||
}
|
||||
75
packages/circuits/circuits/merkleTree.circom
Normal file
75
packages/circuits/circuits/merkleTree.circom
Normal file
@@ -0,0 +1,75 @@
|
||||
pragma circom 2.2.0;
|
||||
|
||||
include "../../../node_modules/circomlib/circuits/poseidon.circom";
|
||||
include "../../../node_modules/circomlib/circuits/comparators.circom";
|
||||
include "../../../node_modules/circomlib/circuits/mux1.circom";
|
||||
|
||||
/**
|
||||
* @title LeanIMTInclusionProof template
|
||||
* @dev Template for generating and verifying inclusion proofs in a Lean Incremental Merkle Tree
|
||||
* @notice This circuit follows the LeanIMT design where:
|
||||
* 1. Every node with two children is the hash of its left and right nodes
|
||||
* 2. Every node with one child has the same value as its child node
|
||||
* 3. Tree is always built from leaves to root
|
||||
* 4. Tree is always balanced by construction
|
||||
* 5. Tree depth is dynamic and can increase with insertion of new leaves
|
||||
* @param maxDepth The maximum depth of the Merkle tree
|
||||
*/
|
||||
template LeanIMTInclusionProof(maxDepth) {
|
||||
|
||||
//////////////////////// SIGNALS ////////////////////////
|
||||
|
||||
signal input leaf; // The leaf value to prove inclusion for
|
||||
signal input leafIndex; // The index of the leaf in the tree
|
||||
signal input siblings[maxDepth]; // The sibling values along the path to the root
|
||||
signal input actualDepth; // Current tree depth (unused as |siblings| <= actualDepth)
|
||||
_ <== actualDepth; // Silence unused signal warning
|
||||
|
||||
signal output out; // The computed root value
|
||||
|
||||
/////////////////// INTERNAL SIGNALS ///////////////////
|
||||
|
||||
signal nodes[maxDepth + 1]; // Array to store computed node values at each level
|
||||
// signal intermediateRoots[maxDepth + 1]; // Array to store intermediate root values
|
||||
signal indices[maxDepth]; // Array to store path indices for each level
|
||||
|
||||
////////////////// COMPONENT SIGNALS //////////////////
|
||||
|
||||
component siblingIsEmpty[maxDepth]; // Checks if sibling node is empty (zero)
|
||||
component hashInCorrectOrder[maxDepth]; // Orders node pairs for hashing
|
||||
component latestValidHash[maxDepth]; // Selects between hash and propagation
|
||||
component poseidons[maxDepth];
|
||||
|
||||
/////////////////////// LOGIC ///////////////////////
|
||||
|
||||
// Convert leaf index to binary path
|
||||
component indexToPath = Num2Bits(maxDepth);
|
||||
indexToPath.in <== leafIndex;
|
||||
indices <== indexToPath.out;
|
||||
|
||||
// Initialize with leaf value
|
||||
nodes[0] <== leaf;
|
||||
|
||||
// For each level up to maxDepth
|
||||
for (var i = 0; i < maxDepth; i++) {
|
||||
// Prepare node pairs for both possible orderings (left/right)
|
||||
var childrenToSort[2][2] = [ [nodes[i], siblings[i]], [siblings[i], nodes[i]] ];
|
||||
hashInCorrectOrder[i] = MultiMux1(2);
|
||||
hashInCorrectOrder[i].c <== childrenToSort;
|
||||
hashInCorrectOrder[i].s <== indices[i];
|
||||
|
||||
// hash the nodes
|
||||
poseidons[i] = Poseidon(2);
|
||||
poseidons[i].inputs <== hashInCorrectOrder[i].out;
|
||||
|
||||
// Check if sibling is empty (zero)
|
||||
siblingIsEmpty[i] = IsZero();
|
||||
siblingIsEmpty[i].in <== siblings[i];
|
||||
|
||||
// Either keep the previous hash (no more siblings) or the new one
|
||||
nodes[i + 1] <== (nodes[i] - poseidons[i].out) * siblingIsEmpty[i].out + poseidons[i].out;
|
||||
}
|
||||
|
||||
// Output final computed root
|
||||
out <== nodes[maxDepth];
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
template MultiplicationGate() {
|
||||
signal input in[2];
|
||||
signal output out <== in[0] * in[1];
|
||||
}
|
||||
|
||||
template Multiplier(N) {
|
||||
assert(N > 1);
|
||||
signal input in[N];
|
||||
signal output out;
|
||||
component gate[N-1];
|
||||
|
||||
// instantiate gates
|
||||
for (var i = 0; i < N-1; i++) {
|
||||
gate[i] = MultiplicationGate();
|
||||
}
|
||||
|
||||
// multiply
|
||||
gate[0].in <== [in[0], in[1]];
|
||||
for (var i = 0; i < N-2; i++) {
|
||||
gate[i+1].in <== [gate[i].out, in[i+2]];
|
||||
}
|
||||
out <== gate[N-2].out;
|
||||
}
|
||||
|
||||
// Alternative way using anonymous components
|
||||
template MultiplierAnonymous(N) {
|
||||
assert(N > 1);
|
||||
signal input in[N];
|
||||
signal output out;
|
||||
|
||||
signal inner[N-1];
|
||||
inner[0] <== MultiplicationGate()([in[0], in[1]]);
|
||||
for(var i = 0; i < N-2; i++) {
|
||||
inner[i+1] <== MultiplicationGate()([inner[i], in[i+2]]);
|
||||
}
|
||||
out <== inner[N-2];
|
||||
}
|
||||
|
||||
// Alternative way without the gate component
|
||||
template MultiplierSimple(N) {
|
||||
assert(N > 1);
|
||||
signal input in[N];
|
||||
signal output out;
|
||||
|
||||
signal inner[N-1];
|
||||
inner[0] <== in[0] * in[1];
|
||||
for(var i = 2; i < N; i++) {
|
||||
inner[i-1] <== inner[i-2] * in[i];
|
||||
}
|
||||
out <== inner[N-2];
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "circomlib/circuits/sha256/sha256.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
|
||||
/**
|
||||
* Wrapper around SHA256 to support bytes as input instead of bits
|
||||
* @param N The number of input bytes
|
||||
* @input in The input bytes
|
||||
* @output out The SHA256 output of the n input bytes, in bytes
|
||||
*
|
||||
* SOURCE: https://github.com/celer-network/zk-benchmark/blob/main/circom/circuits/sha256/sha256_bytes.circom
|
||||
*/
|
||||
template Sha256Bytes(N) {
|
||||
signal input in[N];
|
||||
signal output out[32];
|
||||
|
||||
// convert input bytes to bits
|
||||
component byte_to_bits[N];
|
||||
for (var i = 0; i < N; i++) {
|
||||
byte_to_bits[i] = Num2Bits(8);
|
||||
byte_to_bits[i].in <== in[i];
|
||||
}
|
||||
|
||||
// sha256 over bits
|
||||
component sha256 = Sha256(N*8);
|
||||
for (var i = 0; i < N; i++) {
|
||||
for (var j = 0; j < 8; j++) {
|
||||
sha256.in[i*8+j] <== byte_to_bits[i].out[7-j];
|
||||
}
|
||||
}
|
||||
|
||||
// convert output bytes to bits
|
||||
component bits_to_bytes[32];
|
||||
for (var i = 0; i < 32; i++) {
|
||||
bits_to_bytes[i] = Bits2Num(8);
|
||||
for (var j = 0; j < 8; j++) {
|
||||
bits_to_bytes[i].in[7-j] <== sha256.out[i*8+j];
|
||||
}
|
||||
out[i] <== bits_to_bytes[i].out;
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
|
||||
// Finds Math.floor(log2(n))
|
||||
function log2(n) {
|
||||
var tmp = 1, ans = 1;
|
||||
while (tmp < n) {
|
||||
ans++;
|
||||
tmp *= 2;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
// Assert that two elements are not equal
|
||||
template NonEqual() {
|
||||
signal input in[2];
|
||||
signal inv;
|
||||
|
||||
// we check if (in[0] - in[1] != 0)
|
||||
// because 1/0 results in 0, so the constraint won't hold
|
||||
inv <-- 1 / (in[1] - in[0]);
|
||||
inv * (in[1] - in[0]) === 1;
|
||||
}
|
||||
|
||||
// Assert that number is representable by b-bits
|
||||
template AssertBitLength(b) {
|
||||
assert(b < 254);
|
||||
signal input in;
|
||||
|
||||
// compute b-bit representation of the number
|
||||
signal bits[b];
|
||||
var sum_of_bits = 0;
|
||||
for (var i = 0; i < b; i++) {
|
||||
bits[i] <-- (in >> i) & 1;
|
||||
bits[i] * (1 - bits[i]) === 0;
|
||||
sum_of_bits += (2 ** i) * bits[i];
|
||||
}
|
||||
|
||||
// check if sum is equal to number itself
|
||||
in === sum_of_bits;
|
||||
}
|
||||
|
||||
// Checks that `in` is in range [MIN, MAX]
|
||||
template InRange(MIN, MAX) {
|
||||
assert(MIN < MAX);
|
||||
signal input in;
|
||||
|
||||
// number of bits to represent MAX
|
||||
var b = log2(MAX) + 1;
|
||||
|
||||
component lowerBound = AssertBitLength(b);
|
||||
component upperBound = AssertBitLength(b);
|
||||
lowerBound.in <== in - MIN; // e.g. 1 - 1 = 0 (for 0 <= in)
|
||||
upperBound.in <== in + (2 ** b) - MAX - 1; // e.g. 9 + (15 - 9) = 15 (for in <= 15)
|
||||
}
|
||||
|
||||
// Assert that all given values are unique
|
||||
template Distinct(n) {
|
||||
signal input in[n];
|
||||
component nonEqual[n][n]; // TODO; has extra comps here
|
||||
for(var i = 0; i < n; i++){
|
||||
for(var j = 0; j < i; j++){
|
||||
nonEqual[i][j] = NonEqual();
|
||||
nonEqual[i][j].in <== [in[i], in[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template Sudoku(n_sqrt) {
|
||||
var n = n_sqrt * n_sqrt;
|
||||
signal input solution[n][n]; // solution is a 2D array of numbers
|
||||
signal input puzzle[n][n]; // puzzle is the same, but a zero indicates a blank
|
||||
|
||||
// ensure that solution & puzzle agrees
|
||||
for (var row_i = 0; row_i < n; row_i++) {
|
||||
for (var col_i = 0; col_i < n; col_i++) {
|
||||
// puzzle is either empty (0), or the same as solution
|
||||
puzzle[row_i][col_i] * (puzzle[row_i][col_i] - solution[row_i][col_i]) === 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure all values in the solution are in range
|
||||
component inRange[n][n];
|
||||
for (var row_i = 0; row_i < n; row_i++) {
|
||||
for (var col_i = 0; col_i < n; col_i++) {
|
||||
inRange[row_i][col_i] = InRange(1, n);
|
||||
inRange[row_i][col_i].in <== solution[row_i][col_i];
|
||||
}
|
||||
}
|
||||
|
||||
// ensure all values in the solution are distinct
|
||||
component distinctRows[n];
|
||||
for (var row_i = 0; row_i < n; row_i++) {
|
||||
for (var col_i = 0; col_i < n; col_i++) {
|
||||
if (row_i == 0) {
|
||||
distinctRows[col_i] = Distinct(n);
|
||||
}
|
||||
distinctRows[col_i].in[row_i] <== solution[row_i][col_i];
|
||||
}
|
||||
}
|
||||
component distinctCols[n];
|
||||
for (var col_i = 0; col_i < n; col_i++) {
|
||||
for (var row_i = 0; row_i < n; row_i++) {
|
||||
if (col_i == 0) {
|
||||
distinctCols[row_i] = Distinct(n);
|
||||
}
|
||||
distinctCols[row_i].in[col_i] <== solution[row_i][col_i];
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that all values in squares are distinct
|
||||
component distinctSquares[n];
|
||||
var s_i = 0;
|
||||
for (var sr_i = 0; sr_i < n_sqrt; sr_i++) {
|
||||
for (var sc_i = 0; sc_i < n_sqrt; sc_i++) {
|
||||
// square index
|
||||
distinctSquares[s_i] = Distinct(n);
|
||||
|
||||
// (r, c) now marks the start of this square
|
||||
var r = sr_i * n_sqrt;
|
||||
var c = sc_i * n_sqrt;
|
||||
var i = 0;
|
||||
for (var row_i = r; row_i < r + n_sqrt; row_i++) {
|
||||
for (var col_i = c; col_i < c + n_sqrt; col_i++) {
|
||||
distinctSquares[s_i].in[i] <== solution[row_i][col_i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
s_i++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
108
packages/circuits/circuits/withdraw.circom
Normal file
108
packages/circuits/circuits/withdraw.circom
Normal file
@@ -0,0 +1,108 @@
|
||||
pragma circom 2.2.0;
|
||||
|
||||
include "./commitment.circom";
|
||||
include "./merkleTree.circom";
|
||||
include "../../../node_modules/circomlib/circuits/comparators.circom";
|
||||
|
||||
/**
|
||||
* @title Withdraw template
|
||||
* @dev Template for withdrawing value from a commitment
|
||||
* @param maxTreeDepth The maximum depth of the Merkle trees
|
||||
*/
|
||||
template Withdraw(maxTreeDepth) {
|
||||
|
||||
//////////////////////// PUBLIC SIGNALS ////////////////////////
|
||||
|
||||
// Signals to compute commitments
|
||||
signal input withdrawnValue; // Value being withdrawn
|
||||
|
||||
// Signals for merkle tree inclusion proofs
|
||||
signal input stateRoot; // A known state root
|
||||
signal input stateTreeDepth; // Current state tree depth
|
||||
signal input ASPRoot; // Latest ASP root
|
||||
signal input ASPTreeDepth; // Current ASP tree depth
|
||||
signal input context; // keccak256(scope, Withdrawal)
|
||||
|
||||
//////////////////// END OF PUBLIC SIGNALS ////////////////////
|
||||
|
||||
|
||||
/////////////////////// PRIVATE SIGNALS ///////////////////////
|
||||
|
||||
// Signals to compute commitments
|
||||
signal input label; // keccak256(scope, nonce)
|
||||
signal input existingValue; // Value of the existing commitment
|
||||
signal input existingNullifier; // Nullifier of the existing commitment
|
||||
signal input existingSecret; // Secret of the existing commitment
|
||||
signal input newNullifier; // Nullifier for the new commitment
|
||||
signal input newSecret; // Secret for the new commitment
|
||||
|
||||
// Signals for merkle tree inclusion proofs
|
||||
signal input stateSiblings[maxTreeDepth]; // Siblings of the state tree
|
||||
signal input stateIndex; // Indices for the state tree
|
||||
signal input ASPSiblings[maxTreeDepth]; // Siblings of the ASP tree
|
||||
signal input ASPIndex; // Indices for the ASP tree
|
||||
|
||||
/////////////////// END OF PRIVATE SIGNALS ///////////////////
|
||||
|
||||
|
||||
/////////////////////// OUTPUT SIGNALS ///////////////////////
|
||||
|
||||
signal output newCommitmentHash; // Hash of new commitment
|
||||
signal output existingNullifierHash; // Hash of the existing commitment nullifier
|
||||
|
||||
/////////////////// END OF OUTPUT SIGNALS ///////////////////
|
||||
|
||||
// 1. Compute existing commitment
|
||||
component existingCommitmentHasher = CommitmentHasher();
|
||||
existingCommitmentHasher.value <== existingValue;
|
||||
existingCommitmentHasher.label <== label;
|
||||
existingCommitmentHasher.nullifier <== existingNullifier;
|
||||
existingCommitmentHasher.secret <== existingSecret;
|
||||
signal existingCommitment <== existingCommitmentHasher.commitment;
|
||||
|
||||
// 2. Output existing nullifier hash
|
||||
existingNullifierHash <== existingCommitmentHasher.nullifierHash;
|
||||
_ <== existingCommitmentHasher.precommitmentHash;
|
||||
|
||||
// 3. Verify existing commitment is in state tree
|
||||
component stateRootChecker = LeanIMTInclusionProof(maxTreeDepth);
|
||||
stateRootChecker.leaf <== existingCommitment;
|
||||
stateRootChecker.leafIndex <== stateIndex;
|
||||
stateRootChecker.siblings <== stateSiblings;
|
||||
stateRootChecker.actualDepth <== stateTreeDepth;
|
||||
|
||||
stateRoot === stateRootChecker.out;
|
||||
|
||||
// 4. Verify label is in ASP tree
|
||||
component ASPRootChecker = LeanIMTInclusionProof(maxTreeDepth);
|
||||
ASPRootChecker.leaf <== label;
|
||||
ASPRootChecker.leafIndex <== ASPIndex;
|
||||
ASPRootChecker.siblings <== ASPSiblings;
|
||||
ASPRootChecker.actualDepth <== ASPTreeDepth;
|
||||
|
||||
ASPRoot === ASPRootChecker.out;
|
||||
|
||||
// 5. Check the withdrawn amount is valid
|
||||
signal remainingValue <== existingValue - withdrawnValue;
|
||||
component remainingValueRangeCheck = Num2Bits(128);
|
||||
remainingValueRangeCheck.in <== remainingValue;
|
||||
_ <== remainingValueRangeCheck.out;
|
||||
component withdrawnValueRangeCheck = Num2Bits(128);
|
||||
withdrawnValueRangeCheck.in <== withdrawnValue;
|
||||
_ <== withdrawnValueRangeCheck.out;
|
||||
|
||||
// 6. Compute new commitment
|
||||
component newCommitmentHasher = CommitmentHasher();
|
||||
newCommitmentHasher.value <== remainingValue;
|
||||
newCommitmentHasher.label <== label;
|
||||
newCommitmentHasher.nullifier <== newNullifier;
|
||||
newCommitmentHasher.secret <== newSecret;
|
||||
|
||||
// 7. Output new commitment hash
|
||||
newCommitmentHash <== newCommitmentHasher.commitment;
|
||||
_ <== newCommitmentHasher.precommitmentHash;
|
||||
_ <== newCommitmentHasher.nullifierHash;
|
||||
|
||||
// 8. Square context for integrity
|
||||
signal contextSquared <== context * context;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"in": [1, 1]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"e": ["1122", "1024"],
|
||||
"m": ["7807742059002284", "7045130465601185"]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"in": [2, 4, 10]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"in": [
|
||||
116, 111, 100, 97, 121, 32, 105, 115, 32, 97, 32, 103, 111, 111, 100, 32, 100, 97, 121, 44, 32, 110, 111, 116, 32,
|
||||
101, 118, 101, 114, 121, 100, 97, 121, 32, 105, 115
|
||||
]
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"solution": [
|
||||
[1, 9, 4, 8, 6, 5, 2, 3, 7],
|
||||
[7, 3, 5, 4, 1, 2, 9, 6, 8],
|
||||
[8, 6, 2, 3, 9, 7, 1, 4, 5],
|
||||
[9, 2, 1, 7, 4, 8, 3, 5, 6],
|
||||
[6, 7, 8, 5, 3, 1, 4, 2, 9],
|
||||
[4, 5, 3, 9, 2, 6, 8, 7, 1],
|
||||
[3, 8, 9, 6, 5, 4, 7, 1, 2],
|
||||
[2, 4, 6, 1, 7, 9, 5, 8, 3],
|
||||
[5, 1, 7, 2, 8, 3, 6, 9, 4]
|
||||
],
|
||||
"puzzle": [
|
||||
[0, 0, 0, 8, 6, 0, 2, 3, 0],
|
||||
[7, 0, 5, 0, 0, 0, 9, 0, 8],
|
||||
[0, 6, 0, 3, 0, 7, 0, 4, 0],
|
||||
[0, 2, 0, 7, 0, 8, 0, 5, 0],
|
||||
[0, 7, 8, 5, 0, 0, 0, 0, 0],
|
||||
[4, 0, 0, 9, 0, 6, 0, 7, 0],
|
||||
[3, 0, 9, 0, 5, 0, 7, 0, 2],
|
||||
[0, 4, 0, 1, 0, 9, 0, 8, 0],
|
||||
[5, 0, 7, 0, 8, 0, 0, 9, 4]
|
||||
]
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
{
|
||||
"name": "@privacy-pool-core/circuits",
|
||||
"description": "Circomkit examples",
|
||||
"version": "1.0.0",
|
||||
"description": "Circom circuits for the Privacy Pool protocol",
|
||||
"scripts": {
|
||||
"start": "npx ts-node ./src/index.ts",
|
||||
"test": "npx mocha"
|
||||
"compile": "npx ts-node ./src/index.ts",
|
||||
"test": "npx mocha",
|
||||
"test:merkle": "npx mocha --ignore tests/commitment.test.ts --ignore tests/withdrawal.test.ts",
|
||||
"test:withdraw": "npx mocha --ignore tests/commitment.test.ts --ignore tests/lean-imt.test.ts",
|
||||
"circomspect": "circomspect -L ../../node_modules/circomlib/circuits/comparators.circom -L ../../node_modules/circomlib/circuits/mux1.circom -L ../../node_modules/circomlib/circuits/poseidon.circom ./circuits/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"circomkit": "^0.0.22",
|
||||
"circomlib": "^2.0.5"
|
||||
"@zk-kit/lean-imt": "^2.2.2",
|
||||
"chai": "^5.1.2",
|
||||
"circomkit": "^0.3.2",
|
||||
"circomlib": "^2.0.5",
|
||||
"maci-circuits": "^2.5.0",
|
||||
"maci-crypto": "^2.5.0",
|
||||
"viem": "^2.21.57"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^10.0.1",
|
||||
|
||||
@@ -4,25 +4,32 @@ async function main() {
|
||||
// create circomkit
|
||||
const circomkit = new Circomkit({
|
||||
protocol: "groth16",
|
||||
include: ["../../node_modules/circomlib/circuits", "../../node_modules/maci-circuits/circom"],
|
||||
inspect: true,
|
||||
});
|
||||
|
||||
// artifacts output at `build/multiplier_3` directory
|
||||
await circomkit.compile("multiplier_3", {
|
||||
file: "multiplier",
|
||||
template: "Multiplier",
|
||||
params: [3],
|
||||
// artifacts output at `build/commitment` directory
|
||||
await circomkit.compile("commitment", {
|
||||
file: "commitment",
|
||||
template: "CommitmentHasher",
|
||||
pubs: ["value", "label", "nullifier", "secret"],
|
||||
});
|
||||
|
||||
// proof & public signals at `build/multiplier_3/my_input` directory
|
||||
await circomkit.prove("multiplier_3", "my_input", { in: [3, 5, 7] });
|
||||
// artifacts output at `build/withdraw` directory
|
||||
await circomkit.compile("withdraw", {
|
||||
file: "withdraw",
|
||||
template: "Withdraw",
|
||||
params: [32],
|
||||
pubs: ["withdrawnValue", "stateRoot", "stateTreeDepth", "ASPRoot", "ASPTreeDepth", "context"],
|
||||
});
|
||||
|
||||
// verify with proof & public signals at `build/multiplier_3/my_input`
|
||||
const ok = await circomkit.verify("multiplier_3", "my_input");
|
||||
if (ok) {
|
||||
circomkit.log("Proof verified!", "success");
|
||||
} else {
|
||||
circomkit.log("Verification failed.", "error");
|
||||
}
|
||||
// artifacts output at `build/merkleTree` directory
|
||||
await circomkit.compile("merkleTree", {
|
||||
file: "merkleTree",
|
||||
template: "LeanIMTInclusionProof",
|
||||
params: [32],
|
||||
pubs: ["leaf", "leafIndex", "siblings", "actualDepth"],
|
||||
});
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
212
packages/circuits/tests/commitment.test.ts
Normal file
212
packages/circuits/tests/commitment.test.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
import { WitnessTester } from "circomkit";
|
||||
import { circomkit, hashCommitment, randomBigInt, Commitment } from "./common";
|
||||
import { poseidon } from "../../../node_modules/maci-crypto/build/ts/hashing.js"; // TODO: fix maci import
|
||||
import { parseEther, hexToBigInt, getAddress } from "viem";
|
||||
|
||||
describe("CommitmentHasher Circuit", () => {
|
||||
let circuit: WitnessTester<
|
||||
["value", "label", "nullifier", "secret"],
|
||||
["commitment", "precommitmentHash", "nullifierHash"]
|
||||
>;
|
||||
|
||||
const depositor = getAddress("0x9F2db792a6F2dAdf25D894cEd791080950bDE56f");
|
||||
const NONCE = BigInt(1);
|
||||
const LABEL = poseidon([hexToBigInt(depositor), randomBigInt(), NONCE]);
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`commitment`, {
|
||||
file: "commitment",
|
||||
template: "CommitmentHasher",
|
||||
pubs: ["value", "label", "nullifier", "secret"],
|
||||
});
|
||||
});
|
||||
|
||||
it("should compute commitment hash correctly", async () => {
|
||||
const input = {
|
||||
value: parseEther("1"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
const [commitmentHash, precommitmentHash, nullifierHash] = hashCommitment(input);
|
||||
|
||||
await circuit.expectPass(
|
||||
{
|
||||
value: input.value,
|
||||
label: LABEL,
|
||||
nullifier: input.nullifier,
|
||||
secret: input.secret,
|
||||
},
|
||||
{ commitment: commitmentHash, precommitmentHash, nullifierHash }
|
||||
);
|
||||
});
|
||||
|
||||
it("should compute child commitment hash correctly", async () => {
|
||||
const parentInput: Commitment = {
|
||||
value: parseEther("2"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
const [parentHash] = hashCommitment(parentInput);
|
||||
|
||||
const childInput: Commitment = {
|
||||
value: parseEther("1"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
const [commitmentHash, precommitmentHash, nullifierHash] = hashCommitment(childInput);
|
||||
|
||||
await circuit.expectPass(
|
||||
{
|
||||
value: childInput.value,
|
||||
label: LABEL,
|
||||
nullifier: childInput.nullifier,
|
||||
secret: childInput.secret,
|
||||
},
|
||||
{ commitment: commitmentHash, precommitmentHash, nullifierHash }
|
||||
);
|
||||
});
|
||||
|
||||
describe("Boundary Values", () => {
|
||||
it("should handle minimum values", async () => {
|
||||
const input: Commitment = {
|
||||
value: BigInt(0),
|
||||
label: LABEL,
|
||||
nullifier: BigInt(0),
|
||||
secret: BigInt(0),
|
||||
};
|
||||
|
||||
const [commitmentHash, precommitmentHash, nullifierHash] = hashCommitment(input);
|
||||
|
||||
await circuit.expectPass(
|
||||
{
|
||||
value: input.value,
|
||||
label: LABEL,
|
||||
nullifier: input.nullifier,
|
||||
secret: input.secret,
|
||||
},
|
||||
{ commitment: commitmentHash, precommitmentHash, nullifierHash }
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle maximum field values", async () => {
|
||||
// P is the Goldilocks field modulus used by circom
|
||||
const P = BigInt("18446744073709551615");
|
||||
|
||||
const input: Commitment = {
|
||||
value: P - BigInt(1),
|
||||
label: LABEL,
|
||||
nullifier: P - BigInt(1),
|
||||
secret: P - BigInt(1),
|
||||
};
|
||||
|
||||
const [commitmentHash, precommitmentHash, nullifierHash] = hashCommitment(input);
|
||||
|
||||
await circuit.expectPass(
|
||||
{
|
||||
value: input.value,
|
||||
label: LABEL,
|
||||
nullifier: input.nullifier,
|
||||
secret: input.secret,
|
||||
},
|
||||
{ commitment: commitmentHash, precommitmentHash, nullifierHash }
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify nullifier hash depends only on nullifier", async () => {
|
||||
const base: Commitment = {
|
||||
value: parseEther("1"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
const modified = { ...base, value: parseEther("2"), label: randomBigInt(), secret: randomBigInt() };
|
||||
|
||||
const [_, __, nullifierHash1] = hashCommitment(base);
|
||||
const [___, ____, nullifierHash2] = hashCommitment(modified);
|
||||
|
||||
if (nullifierHash1 != nullifierHash2) {
|
||||
throw new Error("Nullifier hashes don't match");
|
||||
}
|
||||
});
|
||||
|
||||
it("should verify precommitment hash depends only on nullifier and secret", async () => {
|
||||
const base: Commitment = {
|
||||
value: parseEther("1"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
const modified = {
|
||||
...base,
|
||||
value: parseEther("2"),
|
||||
label: randomBigInt(),
|
||||
};
|
||||
|
||||
const [_, precommitmentHash1] = hashCommitment(base);
|
||||
const [__, precommitmentHash2] = hashCommitment(modified);
|
||||
|
||||
if (precommitmentHash1 != precommitmentHash2) {
|
||||
throw new Error("Precommitment hashes don't match");
|
||||
}
|
||||
});
|
||||
|
||||
it("should produce consistent outputs with same inputs", async () => {
|
||||
const input: Commitment = {
|
||||
value: parseEther("1"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
const [hash1, pre1, null1] = hashCommitment(input);
|
||||
const [hash2, pre2, null2] = hashCommitment(input);
|
||||
const [hash3, pre3, null3] = hashCommitment(input);
|
||||
|
||||
if (hash1 != hash2 || hash2 != hash3) {
|
||||
throw new Error("Commitment hashes don't match");
|
||||
}
|
||||
|
||||
if (pre1 != pre2 || pre2 != pre3) {
|
||||
throw new Error("Precommitment hashes don't match");
|
||||
}
|
||||
|
||||
if (null1 != null2 || null2 != null3) {
|
||||
throw new Error("Nullifier hashes don't match");
|
||||
}
|
||||
});
|
||||
|
||||
it("should produce different outputs when single input changes", async () => {
|
||||
const base: Commitment = {
|
||||
value: parseEther("1"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
const modifications: (keyof Commitment)[] = ["value", "label", "nullifier", "secret"];
|
||||
|
||||
const baseHash = hashCommitment(base)[0];
|
||||
|
||||
for (const field of modifications) {
|
||||
const modified = { ...base };
|
||||
if (field === "value") modified.value = parseEther("2");
|
||||
else if (field === "label") modified.label = randomBigInt();
|
||||
else modified[field] = randomBigInt();
|
||||
|
||||
const modifiedHash = hashCommitment(modified)[0];
|
||||
|
||||
if (modifiedHash == baseHash) {
|
||||
throw new Error("Hashes shouldn't match");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,34 @@
|
||||
import { Circomkit } from "circomkit";
|
||||
import { poseidon } from "../../../../node_modules/maci-crypto/build/ts/hashing.js";
|
||||
|
||||
export interface Commitment {
|
||||
value: bigint;
|
||||
label: bigint;
|
||||
nullifier: bigint;
|
||||
secret: bigint;
|
||||
}
|
||||
|
||||
export const circomkit = new Circomkit({
|
||||
verbose: false,
|
||||
protocol: "groth16",
|
||||
include: ["../../node_modules/circomlib/circuits", "../../node_modules/maci-circuits/circom"],
|
||||
});
|
||||
|
||||
export function hashCommitment(input: Commitment): [bigint, bigint, bigint] {
|
||||
const precommitment = poseidon([BigInt(input.nullifier), BigInt(input.secret)]);
|
||||
const nullifierHash = poseidon([BigInt(input.nullifier)]);
|
||||
const commitmentHash = poseidon([BigInt(input.value), BigInt(input.label), precommitment]);
|
||||
return [commitmentHash, precommitment, nullifierHash];
|
||||
}
|
||||
|
||||
export function randomBigInt(): bigint {
|
||||
return BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
||||
}
|
||||
|
||||
export function padSiblings(siblings: bigint[], targetDepth: number): bigint[] {
|
||||
const paddedSiblings = [...siblings];
|
||||
while (paddedSiblings.length < targetDepth) {
|
||||
paddedSiblings.push(BigInt(0));
|
||||
}
|
||||
return paddedSiblings;
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { WitnessTester } from "circomkit";
|
||||
import { circomkit } from "./common";
|
||||
|
||||
describe("fibonacci", () => {
|
||||
const N = 7;
|
||||
let circuit: WitnessTester<["in"], ["out"]>;
|
||||
|
||||
describe("vanilla", () => {
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`fibonacci_${N}`, {
|
||||
file: "fibonacci",
|
||||
template: "Fibonacci",
|
||||
params: [N],
|
||||
});
|
||||
console.log("#constraints:", await circuit.getConstraintCount());
|
||||
});
|
||||
|
||||
it("should compute correctly", async () => {
|
||||
await circuit.expectPass({ in: [1, 1] }, { out: fibonacci([1, 1], N) });
|
||||
});
|
||||
});
|
||||
|
||||
describe("recursive", () => {
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`fibonacci_${N}_recursive`, {
|
||||
file: "fibonacci",
|
||||
template: "FibonacciRecursive",
|
||||
params: [N],
|
||||
});
|
||||
console.log("#constraints:", await circuit.getConstraintCount());
|
||||
});
|
||||
|
||||
it("should compute correctly", async () => {
|
||||
await circuit.expectPass({ in: [1, 1] }, { out: fibonacci([1, 1], N) });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// simple fibonacci with 2 variables
|
||||
function fibonacci(init: [number, number], n: number): number {
|
||||
if (n < 0) {
|
||||
throw new Error("N must be positive");
|
||||
}
|
||||
|
||||
let [a, b] = init;
|
||||
for (let i = 2; i <= n; i++) {
|
||||
b = a + b;
|
||||
a = b - a;
|
||||
}
|
||||
return n === 0 ? a : b;
|
||||
}
|
||||
@@ -1,427 +0,0 @@
|
||||
import { WitnessTester } from "circomkit";
|
||||
import { circomkit } from "./common";
|
||||
|
||||
const expectedConstraints = {
|
||||
fp32: 401,
|
||||
fp64: 819,
|
||||
checkBitLength: (bits: number) => bits + 2,
|
||||
leftShift: (shiftBound: number) => shiftBound + 2,
|
||||
right: (bits: number) => bits + 2,
|
||||
normalize: (P: number) => 3 * P + 1 + 1, // MSNZB + 1
|
||||
msnzb: (bits: number) => 3 * bits + 1,
|
||||
};
|
||||
|
||||
// tests adapted from https://github.com/rdi-berkeley/zkp-mooc-lab
|
||||
describe("float_add 32-bit", () => {
|
||||
let circuit: WitnessTester<["e", "m"], ["e_out", "m_out"]>;
|
||||
|
||||
const k = 8;
|
||||
const p = 23;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester("fp32", {
|
||||
file: "float_add",
|
||||
template: "FloatAdd",
|
||||
params: [k, p],
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct number of constraints", async () => {
|
||||
await circuit.expectConstraintCount(expectedConstraints.fp32);
|
||||
});
|
||||
|
||||
it("case I test", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["43", "5"],
|
||||
m: ["11672136", "10566265"],
|
||||
},
|
||||
{ e_out: "43", m_out: "11672136" }
|
||||
);
|
||||
});
|
||||
|
||||
it("case II test 1", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["104", "106"],
|
||||
m: ["12444445", "14159003"],
|
||||
},
|
||||
{ e_out: "107", m_out: "8635057" }
|
||||
);
|
||||
});
|
||||
|
||||
it("case II test 2", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["176", "152"],
|
||||
m: ["16777215", "16777215"],
|
||||
},
|
||||
{ e_out: "177", m_out: "8388608" }
|
||||
);
|
||||
});
|
||||
|
||||
it("case II test 3", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["142", "142"],
|
||||
m: ["13291872", "13291872"],
|
||||
},
|
||||
{ e_out: "143", m_out: "13291872" }
|
||||
);
|
||||
});
|
||||
|
||||
it("one input zero test", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["0", "43"],
|
||||
m: ["0", "10566265"],
|
||||
},
|
||||
{ e_out: "43", m_out: "10566265" }
|
||||
);
|
||||
});
|
||||
|
||||
it("both inputs zero test", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["0", "0"],
|
||||
m: ["0", "0"],
|
||||
},
|
||||
{ e_out: "0", m_out: "0" }
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail - exponent zero but mantissa non-zero", async () => {
|
||||
await circuit.expectFail({
|
||||
e: ["0", "0"],
|
||||
m: ["0", "10566265"],
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail - mantissa ≥ 2^(p+1)", async () => {
|
||||
await circuit.expectFail({
|
||||
e: ["0", "43"],
|
||||
m: ["0", "16777216"],
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail - mantissa < 2^p", async () => {
|
||||
await circuit.expectFail({
|
||||
e: ["0", "43"],
|
||||
m: ["0", "6777216"],
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail - exponent ≥ 2^k", async () => {
|
||||
await circuit.expectFail({
|
||||
e: ["0", "256"],
|
||||
m: ["0", "10566265"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("float_add 64-bit", () => {
|
||||
const k = 11;
|
||||
const p = 52;
|
||||
let circuit: WitnessTester<["e", "m"], ["e_out", "m_out"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester("fp64", {
|
||||
file: "float_add",
|
||||
template: "FloatAdd",
|
||||
params: [k, p],
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct number of constraints", async () => {
|
||||
await circuit.expectConstraintCount(expectedConstraints.fp64);
|
||||
});
|
||||
|
||||
it("case I test", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["1122", "1024"],
|
||||
m: ["7807742059002284", "7045130465601185"],
|
||||
},
|
||||
{ e_out: "1122", m_out: "7807742059002284" }
|
||||
);
|
||||
});
|
||||
|
||||
it("case II test 1", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["1056", "1053"],
|
||||
m: ["8879495032259305", "5030141535601637"],
|
||||
},
|
||||
{ e_out: "1057", m_out: "4754131362104755" }
|
||||
);
|
||||
});
|
||||
|
||||
it("case II test 2", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["1035", "982"],
|
||||
m: ["4804509148660890", "8505192799372177"],
|
||||
},
|
||||
{ e_out: "1035", m_out: "4804509148660891" }
|
||||
);
|
||||
});
|
||||
|
||||
it("case II test 3", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["982", "982"],
|
||||
m: ["8505192799372177", "8505192799372177"],
|
||||
},
|
||||
{ e_out: "983", m_out: "8505192799372177" }
|
||||
);
|
||||
});
|
||||
|
||||
it("one input zero test", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["0", "982"],
|
||||
m: ["0", "8505192799372177"],
|
||||
},
|
||||
{ e_out: "982", m_out: "8505192799372177" }
|
||||
);
|
||||
});
|
||||
|
||||
it("both inputs zero test", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: ["0", "0"],
|
||||
m: ["0", "0"],
|
||||
},
|
||||
{ e_out: "0", m_out: "0" }
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail - exponent zero but mantissa non-zero", async () => {
|
||||
await circuit.expectFail({
|
||||
e: ["0", "0"],
|
||||
m: ["0", "8505192799372177"],
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail - mantissa < 2^p", async () => {
|
||||
await circuit.expectFail({
|
||||
e: ["0", "43"],
|
||||
m: ["0", "16777216"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("float_add utilities", () => {
|
||||
describe("check bit length", () => {
|
||||
const b = 23; // bit count
|
||||
let circuit: WitnessTester<["in"], ["out"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`cbl_${b}`, {
|
||||
file: "float_add",
|
||||
template: "CheckBitLength",
|
||||
params: [b],
|
||||
dir: "test/float_add",
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct number of constraints", async () => {
|
||||
await circuit.expectConstraintCount(expectedConstraints.checkBitLength(b));
|
||||
});
|
||||
|
||||
it("should give 1 for in ≤ b", async () => {
|
||||
await circuit.expectPass({ in: "4903265" }, { out: "1" });
|
||||
});
|
||||
|
||||
it("should give 0 for in > b", async () => {
|
||||
await circuit.expectPass({ in: "13291873" }, { out: "0" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("left shift", () => {
|
||||
const shift_bound = 25;
|
||||
let circuit: WitnessTester<["x", "shift", "skip_checks"], ["y"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`shl_${shift_bound}`, {
|
||||
file: "float_add",
|
||||
template: "LeftShift",
|
||||
dir: "test/float_add",
|
||||
params: [shift_bound],
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct number of constraints", async () => {
|
||||
await circuit.expectConstraintCount(expectedConstraints.leftShift(shift_bound));
|
||||
});
|
||||
|
||||
it("should pass test 1 - don't skip checks", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
x: "65",
|
||||
shift: "24",
|
||||
skip_checks: "0",
|
||||
},
|
||||
{ y: "1090519040" }
|
||||
);
|
||||
});
|
||||
|
||||
it("should pass test 2 - don't skip checks", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
x: "65",
|
||||
shift: "0",
|
||||
skip_checks: "0",
|
||||
},
|
||||
{ y: "65" }
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail - don't skip checks", async () => {
|
||||
await circuit.expectFail({
|
||||
x: "65",
|
||||
shift: "25",
|
||||
skip_checks: "0",
|
||||
});
|
||||
});
|
||||
|
||||
it("should pass when skip_checks = 1 and shift is ≥ shift_bound", async () => {
|
||||
await circuit.expectPass({
|
||||
x: "65",
|
||||
shift: "25",
|
||||
skip_checks: "1",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("right shift", () => {
|
||||
const b = 49;
|
||||
const shift = 24;
|
||||
let circuit: WitnessTester<["x"], ["y"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`shr_${b}`, {
|
||||
file: "float_add",
|
||||
template: "RightShift",
|
||||
dir: "test/float_add",
|
||||
params: [b, shift],
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct number of constraints", async () => {
|
||||
await circuit.expectConstraintCount(b);
|
||||
});
|
||||
|
||||
it("should pass - small bitwidth", async () => {
|
||||
await circuit.expectPass({ x: "82263136010365" }, { y: "4903265" });
|
||||
});
|
||||
|
||||
it("should fail - large bitwidth", async () => {
|
||||
await circuit.expectFail({ x: "15087340228765024367" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalize", () => {
|
||||
const k = 8;
|
||||
const p = 23;
|
||||
const P = 47;
|
||||
let circuit: WitnessTester<["e", "m", "skip_checks"], ["e_out", "m_out"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`normalize_${k}_${p}_${P}`, {
|
||||
file: "float_add",
|
||||
template: "Normalize",
|
||||
params: [k, p, P],
|
||||
dir: "test/float_add",
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct number of constraints", async () => {
|
||||
await circuit.expectConstraintCount(expectedConstraints.normalize(P));
|
||||
});
|
||||
|
||||
it("should pass - don't skip checks", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: "100",
|
||||
m: "20565784002591",
|
||||
skip_checks: "0",
|
||||
},
|
||||
{ e_out: "121", m_out: "164526272020728" }
|
||||
);
|
||||
});
|
||||
|
||||
it("should pass - already normalized and don't skip checks", async () => {
|
||||
await circuit.expectPass(
|
||||
{
|
||||
e: "100",
|
||||
m: "164526272020728",
|
||||
skip_checks: "0",
|
||||
},
|
||||
{ e_out: "124", m_out: "164526272020728" }
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail when m = 0 - don't skip checks", async () => {
|
||||
await circuit.expectFail({
|
||||
e: "100",
|
||||
m: "0",
|
||||
skip_checks: "0",
|
||||
});
|
||||
});
|
||||
|
||||
it("should pass when skip_checks = 1 and m is 0", async () => {
|
||||
await circuit.expectPass({
|
||||
e: "100",
|
||||
m: "0",
|
||||
skip_checks: "1",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("msnzb", () => {
|
||||
const b = 48;
|
||||
let circuit: WitnessTester<["in", "skip_checks"], ["one_hot"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`msnzb_${b}`, {
|
||||
file: "float_add",
|
||||
template: "MSNZB",
|
||||
dir: "test/float_add",
|
||||
params: [b],
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct number of constraints", async () => {
|
||||
await circuit.expectConstraintCount(expectedConstraints.msnzb(b));
|
||||
});
|
||||
|
||||
it("should pass test 1 - don't skip checks", async () => {
|
||||
await circuit.expectPass(
|
||||
{ in: "1", skip_checks: "0" },
|
||||
{
|
||||
// prettier-ignore
|
||||
one_hot: ['1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("should pass test 2 - don't skip checks", async () => {
|
||||
await circuit.expectPass(
|
||||
{ in: "281474976710655", skip_checks: "0" },
|
||||
{
|
||||
// prettier-ignore
|
||||
one_hot: ['0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1'],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("should fail when in = 0 - don't skip checks", async () => {
|
||||
await circuit.expectFail({ in: "0", skip_checks: "0" });
|
||||
});
|
||||
|
||||
it("should pass when skip_checks = 1 and in is 0", async () => {
|
||||
await circuit.expectPass({ in: "0", skip_checks: "1" });
|
||||
});
|
||||
});
|
||||
});
|
||||
56
packages/circuits/tests/lean-imt.test.ts
Normal file
56
packages/circuits/tests/lean-imt.test.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { WitnessTester } from "circomkit";
|
||||
import { circomkit, randomBigInt, padSiblings } from "./common";
|
||||
import { hashLeftRight } from "../../../node_modules/maci-crypto/build/ts/hashing.js"; // TODO: fix maci import
|
||||
import { LeanIMT } from "@zk-kit/lean-imt";
|
||||
|
||||
describe("LeanIMTInclusionProof Circuit", () => {
|
||||
let circuit: WitnessTester<["leaf", "leafIndex", "siblings", "actualDepth"], ["out"]>;
|
||||
|
||||
const hash = (a: bigint, b: bigint) => hashLeftRight(a, b);
|
||||
const maxDepth = 8;
|
||||
|
||||
// Lean Incrementral Merkle tree
|
||||
let tree: LeanIMT<bigint>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`merkleTree`, {
|
||||
file: "merkleTree",
|
||||
template: "LeanIMTInclusionProof",
|
||||
pubs: ["leaf", "leafIndex", "siblings", "actualDepth"],
|
||||
params: [maxDepth],
|
||||
});
|
||||
});
|
||||
|
||||
// Flush trees before each test
|
||||
beforeEach(async () => {
|
||||
tree = new LeanIMT(hash);
|
||||
});
|
||||
|
||||
it("Should compute roots correctly", async () => {
|
||||
const LEAVES = 16;
|
||||
|
||||
let leavesIndexes = [];
|
||||
|
||||
// insert leaves
|
||||
for (let i = 0; i < LEAVES; ++i) {
|
||||
let leafValue = randomBigInt();
|
||||
tree!.insert(leafValue);
|
||||
|
||||
leavesIndexes.push(tree.indexOf(leafValue));
|
||||
}
|
||||
|
||||
for (let i = 0; i < LEAVES; ++i) {
|
||||
let stateProof = tree.generateProof(i);
|
||||
|
||||
await circuit.expectPass(
|
||||
{
|
||||
leaf: stateProof.leaf,
|
||||
leafIndex: stateProof.index,
|
||||
siblings: padSiblings(stateProof.siblings, maxDepth),
|
||||
actualDepth: tree.depth,
|
||||
},
|
||||
{ out: tree.root }
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
import { WitnessTester } from "circomkit";
|
||||
import { circomkit } from "./common";
|
||||
|
||||
describe("multiplier", () => {
|
||||
const N = 3;
|
||||
let circuit: WitnessTester<["in"], ["out"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`multiplier_${N}`, {
|
||||
file: "multiplier",
|
||||
template: "Multiplier",
|
||||
params: [N],
|
||||
});
|
||||
});
|
||||
|
||||
it("should have correct number of constraints", async () => {
|
||||
await circuit.expectConstraintCount(N - 1);
|
||||
});
|
||||
|
||||
it("should multiply correctly", async () => {
|
||||
const randomNumbers = Array.from({ length: N }, () => Math.floor(Math.random() * 100 * N));
|
||||
await circuit.expectPass({ in: randomNumbers }, { out: randomNumbers.reduce((prev, acc) => acc * prev) });
|
||||
});
|
||||
});
|
||||
|
||||
describe("multiplier utilities", () => {
|
||||
describe("multiplication gate", () => {
|
||||
let circuit: WitnessTester<["in"], ["out"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester("mulgate", {
|
||||
file: "multiplier",
|
||||
template: "MultiplicationGate",
|
||||
dir: "test/multiplier",
|
||||
});
|
||||
});
|
||||
|
||||
it("should multiply correctly", async () => {
|
||||
await circuit.expectPass({ in: [7, 5] }, { out: 7 * 5 });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import { WitnessTester } from "circomkit";
|
||||
import { createHash } from "crypto";
|
||||
import { circomkit } from "./common";
|
||||
|
||||
describe("sha256", () => {
|
||||
let circuit: WitnessTester<["in"], ["out"]>;
|
||||
|
||||
// number of bytes for the sha256 input
|
||||
const NUM_BYTES = 36;
|
||||
|
||||
// preimage and its byte array
|
||||
const PREIMAGE = Buffer.from("today is a good day, not everyday is");
|
||||
const PREIMAGE_BYTES = PREIMAGE.toJSON().data;
|
||||
|
||||
// digest and its byte array
|
||||
const DIGEST = createHash("sha256").update(PREIMAGE).digest("hex");
|
||||
const DIGEST_BYTES = Buffer.from(DIGEST, "hex").toJSON().data;
|
||||
|
||||
// circuit signals
|
||||
const INPUT = {
|
||||
in: PREIMAGE_BYTES,
|
||||
};
|
||||
const OUTPUT = {
|
||||
out: DIGEST_BYTES,
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`sha256_${NUM_BYTES}`, {
|
||||
file: "sha256",
|
||||
template: "Sha256Bytes",
|
||||
params: [NUM_BYTES],
|
||||
});
|
||||
});
|
||||
|
||||
it("should compute hash correctly", async () => {
|
||||
await circuit.expectPass(INPUT, OUTPUT);
|
||||
});
|
||||
});
|
||||
@@ -1,200 +0,0 @@
|
||||
import { WitnessTester, CircuitSignals } from "circomkit";
|
||||
import { circomkit } from "./common";
|
||||
|
||||
const BOARD_SIZES = [4, 9] as const;
|
||||
const INPUTS: { [N in (typeof BOARD_SIZES)[number]]: CircuitSignals<["solution", "puzzle"]> } = {
|
||||
9: {
|
||||
solution: [
|
||||
[1, 9, 4, 8, 6, 5, 2, 3, 7],
|
||||
[7, 3, 5, 4, 1, 2, 9, 6, 8],
|
||||
[8, 6, 2, 3, 9, 7, 1, 4, 5],
|
||||
[9, 2, 1, 7, 4, 8, 3, 5, 6],
|
||||
[6, 7, 8, 5, 3, 1, 4, 2, 9],
|
||||
[4, 5, 3, 9, 2, 6, 8, 7, 1],
|
||||
[3, 8, 9, 6, 5, 4, 7, 1, 2],
|
||||
[2, 4, 6, 1, 7, 9, 5, 8, 3],
|
||||
[5, 1, 7, 2, 8, 3, 6, 9, 4],
|
||||
],
|
||||
puzzle: [
|
||||
[0, 0, 0, 8, 6, 0, 2, 3, 0],
|
||||
[7, 0, 5, 0, 0, 0, 9, 0, 8],
|
||||
[0, 6, 0, 3, 0, 7, 0, 4, 0],
|
||||
[0, 2, 0, 7, 0, 8, 0, 5, 0],
|
||||
[0, 7, 8, 5, 0, 0, 0, 0, 0],
|
||||
[4, 0, 0, 9, 0, 6, 0, 7, 0],
|
||||
[3, 0, 9, 0, 5, 0, 7, 0, 2],
|
||||
[0, 4, 0, 1, 0, 9, 0, 8, 0],
|
||||
[5, 0, 7, 0, 8, 0, 0, 9, 4],
|
||||
],
|
||||
},
|
||||
4: {
|
||||
solution: [
|
||||
[4, 1, 3, 2],
|
||||
[3, 2, 4, 1],
|
||||
[2, 4, 1, 3],
|
||||
[1, 3, 2, 4],
|
||||
],
|
||||
puzzle: [
|
||||
[0, 1, 0, 2],
|
||||
[3, 2, 0, 0],
|
||||
[0, 0, 1, 0],
|
||||
[1, 0, 0, 0],
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
BOARD_SIZES.map((N) =>
|
||||
describe(`sudoku (${N} by ${N})`, () => {
|
||||
const INPUT = INPUTS[N];
|
||||
let circuit: WitnessTester<["solution", "puzzle"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`sudoku_${N}x${N}`, {
|
||||
file: "sudoku",
|
||||
template: "Sudoku",
|
||||
pubs: ["puzzle"],
|
||||
params: [Math.sqrt(N)],
|
||||
});
|
||||
});
|
||||
|
||||
it("should compute correctly", async () => {
|
||||
await circuit.expectPass(INPUT);
|
||||
});
|
||||
|
||||
it("should NOT accept non-distinct rows", async () => {
|
||||
const badInput = JSON.parse(JSON.stringify(INPUT));
|
||||
badInput.solution[0][0] = badInput.solution[0][1];
|
||||
await circuit.expectFail(badInput);
|
||||
});
|
||||
|
||||
it("should NOT accept non-distinct columns", async () => {
|
||||
const badInput = JSON.parse(JSON.stringify(INPUT));
|
||||
badInput.solution[0][0] = badInput.solution[1][0];
|
||||
await circuit.expectFail(badInput);
|
||||
});
|
||||
|
||||
it("should NOT accept non-distinct square", async () => {
|
||||
const badInput = JSON.parse(JSON.stringify(INPUT));
|
||||
badInput.solution[0][0] = badInput.solution[1][1];
|
||||
await circuit.expectFail(badInput);
|
||||
});
|
||||
|
||||
it("should NOT accept empty value in solution", async () => {
|
||||
const badInput = JSON.parse(JSON.stringify(INPUT));
|
||||
badInput.solution[0][0] = 0;
|
||||
await circuit.expectFail(badInput);
|
||||
});
|
||||
|
||||
it("should NOT accept out-of-range values", async () => {
|
||||
const badInput = JSON.parse(JSON.stringify(INPUT));
|
||||
badInput.solution[0][0] = 99999;
|
||||
await circuit.expectFail(badInput);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
describe("sudoku utilities", () => {
|
||||
describe("assert bit length", () => {
|
||||
const b = 3; // bit count
|
||||
|
||||
let circuit: WitnessTester<["in"], []>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`bitlen_${b}`, {
|
||||
file: "sudoku",
|
||||
template: "AssertBitLength",
|
||||
dir: "test/sudoku",
|
||||
params: [b],
|
||||
});
|
||||
});
|
||||
|
||||
it("should pass for input < 2^b", async () => {
|
||||
await circuit.expectPass({
|
||||
in: 2 ** b - 1,
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail for input ≥ 2^b ", async () => {
|
||||
await circuit.expectFail({
|
||||
in: 2 ** b,
|
||||
});
|
||||
await circuit.expectFail({
|
||||
in: 2 ** b + 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("distinct", () => {
|
||||
const n = 3;
|
||||
let circuit: WitnessTester<["in"], []>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`distinct_${n}`, {
|
||||
file: "sudoku",
|
||||
template: "Distinct",
|
||||
dir: "test/sudoku",
|
||||
params: [n],
|
||||
});
|
||||
});
|
||||
|
||||
it("should pass if all inputs are unique", async () => {
|
||||
await circuit.expectPass({
|
||||
in: Array(n)
|
||||
.fill(0)
|
||||
.map((v, i) => v + i),
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail if there is a duplicate", async () => {
|
||||
const arr = Array(n)
|
||||
.fill(0)
|
||||
.map((v, i) => v + i);
|
||||
// make a duplicate
|
||||
arr[0] = arr[arr.length - 1];
|
||||
await circuit.expectFail({
|
||||
in: arr,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("in range", () => {
|
||||
const MIN = 1;
|
||||
const MAX = 9;
|
||||
let circuit: WitnessTester<["in"]>;
|
||||
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester(`inRange_${MIN}_${MAX}`, {
|
||||
file: "sudoku",
|
||||
template: "InRange",
|
||||
dir: "test/sudoku",
|
||||
params: [MIN, MAX],
|
||||
});
|
||||
});
|
||||
|
||||
it("should pass for in range", async () => {
|
||||
await circuit.expectPass({
|
||||
in: MAX,
|
||||
});
|
||||
await circuit.expectPass({
|
||||
in: MIN,
|
||||
});
|
||||
await circuit.expectPass({
|
||||
in: Math.floor((MIN + MAX) / 2),
|
||||
});
|
||||
});
|
||||
|
||||
it("should FAIL for out of range (upper bound)", async () => {
|
||||
await circuit.expectFail({
|
||||
in: MAX + 1,
|
||||
});
|
||||
});
|
||||
|
||||
it("should FAIL for out of range (lower bound)", async () => {
|
||||
if (MIN > 0) {
|
||||
await circuit.expectFail({
|
||||
in: MIN - 1,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
467
packages/circuits/tests/withdrawal.test.ts
Normal file
467
packages/circuits/tests/withdrawal.test.ts
Normal file
@@ -0,0 +1,467 @@
|
||||
import { WitnessTester } from "circomkit";
|
||||
import { circomkit, hashCommitment, randomBigInt, padSiblings, Commitment } from "./common";
|
||||
import { poseidon } from "../../../node_modules/maci-crypto/build/ts/hashing.js";
|
||||
import { parseEther, hexToBigInt, getAddress } from "viem";
|
||||
import { LeanIMT } from "@zk-kit/lean-imt";
|
||||
|
||||
// TODO: once the circuits are unchanged, add assertion on error messages
|
||||
|
||||
describe("Withdraw Circuit", () => {
|
||||
let circuit: WitnessTester<
|
||||
[
|
||||
"withdrawnValue",
|
||||
"stateRoot",
|
||||
"stateTreeDepth",
|
||||
"ASPRoot",
|
||||
"ASPTreeDepth",
|
||||
"context",
|
||||
"label",
|
||||
"existingValue",
|
||||
"existingNullifier",
|
||||
"existingSecret",
|
||||
"newNullifier",
|
||||
"newSecret",
|
||||
"stateSiblings",
|
||||
"stateIndex",
|
||||
"ASPSiblings",
|
||||
"ASPIndex"
|
||||
],
|
||||
["newCommitmentHash", "existingNullifierHash"]
|
||||
>;
|
||||
|
||||
// Test constants
|
||||
const maxTreeDepth = 32;
|
||||
const LABEL = randomBigInt(); // Random label for original deposit. Should be keccak256(SCOPE, nonce)
|
||||
const REMOVED_LEAF = poseidon([BigInt(0)]);
|
||||
|
||||
// Original deposit for 5 ETH
|
||||
const deposit = {
|
||||
value: parseEther("5"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
// Get deposit commitment hash
|
||||
const [depositHash, , depositNullifierHash] = hashCommitment(deposit);
|
||||
|
||||
// Using Poseidon for hashing the tree nodes
|
||||
const hash = (a: bigint, b: bigint) => poseidon([a, b]);
|
||||
|
||||
// Lean Incrementral Merkle trees
|
||||
let stateTree: LeanIMT<bigint>;
|
||||
let ASPTree: LeanIMT<bigint>;
|
||||
|
||||
// Instantiate circuit with public signals
|
||||
before(async () => {
|
||||
circuit = await circomkit.WitnessTester("withdraw", {
|
||||
file: "withdraw",
|
||||
template: "Withdraw",
|
||||
params: [maxTreeDepth],
|
||||
pubs: ["withdrawnValue", "stateRoot", "stateTreeDepth", "ASPRoot", "ASPTreeDepth", "context"],
|
||||
});
|
||||
});
|
||||
|
||||
// Flush trees before each test
|
||||
beforeEach(async () => {
|
||||
stateTree = new LeanIMT(hash);
|
||||
ASPTree = new LeanIMT(hash);
|
||||
});
|
||||
|
||||
it("Should allow withdrawing value from an approved deposit", async () => {
|
||||
// Insert deposit commitment in state tree
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(depositHash);
|
||||
|
||||
// Insert deposit label in ASP tree (deposit is now validated)
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(LABEL);
|
||||
|
||||
// Withrdaw 1 ETH from deposit
|
||||
const withdrawal = {
|
||||
value: parseEther("4"), // New value after withdrawal
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
// Hash the new commitment
|
||||
const [commitmentHash, ,] = hashCommitment(withdrawal);
|
||||
|
||||
// Generate merkle proofs for commitment and label
|
||||
let stateProof = stateTree.generateProof(3);
|
||||
let ASPProof = ASPTree.generateProof(3);
|
||||
|
||||
// Spend the deposit commitment and create a new commitment for the remaining value
|
||||
await circuit.expectPass(
|
||||
{
|
||||
withdrawnValue: parseEther("1"),
|
||||
stateRoot: stateProof.root,
|
||||
stateTreeDepth: stateTree.depth,
|
||||
ASPRoot: ASPProof.root,
|
||||
ASPTreeDepth: ASPTree.depth,
|
||||
context: randomBigInt(),
|
||||
label: LABEL,
|
||||
existingValue: deposit.value,
|
||||
existingNullifier: deposit.nullifier,
|
||||
existingSecret: deposit.secret,
|
||||
newNullifier: withdrawal.nullifier,
|
||||
newSecret: withdrawal.secret,
|
||||
stateSiblings: padSiblings(stateProof.siblings, maxTreeDepth),
|
||||
stateIndex: stateProof.index,
|
||||
ASPSiblings: padSiblings(ASPProof.siblings, maxTreeDepth),
|
||||
ASPIndex: ASPProof.index,
|
||||
},
|
||||
{
|
||||
newCommitmentHash: commitmentHash,
|
||||
existingNullifierHash: depositNullifierHash,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("Should allow withdrawing value from a child commitment", async () => {
|
||||
// Insert deposit in state tree
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(depositHash);
|
||||
|
||||
// Insert deposit label in ASP tree to mark as validated
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(LABEL);
|
||||
|
||||
// First withdrawal
|
||||
const firstChild = {
|
||||
value: parseEther("4"), // New value after withdrawal
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
// Hash first withdrawal commitment
|
||||
const [firstChildCommitmentHash, , firstChildNullifierHash] = hashCommitment(firstChild);
|
||||
|
||||
// Generate merkle proofs to spend deposit
|
||||
let stateProof = stateTree.generateProof(2);
|
||||
let ASPProof = ASPTree.generateProof(5);
|
||||
|
||||
// Spend the deposit commitment and create the new commitment for the remaining value
|
||||
await circuit.expectPass(
|
||||
{
|
||||
withdrawnValue: parseEther("1"),
|
||||
stateRoot: stateProof.root,
|
||||
stateTreeDepth: stateTree.depth,
|
||||
ASPRoot: ASPProof.root,
|
||||
ASPTreeDepth: ASPTree.depth,
|
||||
context: randomBigInt(),
|
||||
label: LABEL,
|
||||
existingValue: deposit.value,
|
||||
existingNullifier: deposit.nullifier,
|
||||
existingSecret: deposit.secret,
|
||||
newNullifier: firstChild.nullifier,
|
||||
newSecret: firstChild.secret,
|
||||
stateSiblings: padSiblings(stateProof.siblings, maxTreeDepth),
|
||||
stateIndex: stateProof.index,
|
||||
ASPSiblings: padSiblings(ASPProof.siblings, maxTreeDepth),
|
||||
ASPIndex: ASPProof.index,
|
||||
},
|
||||
{
|
||||
newCommitmentHash: firstChildCommitmentHash,
|
||||
existingNullifierHash: depositNullifierHash,
|
||||
}
|
||||
);
|
||||
|
||||
// Insert the new child commitment in the state tree
|
||||
stateTree!.insert(firstChildCommitmentHash);
|
||||
|
||||
// Second child product of spending the first child and withdrawing 2.5 ETH
|
||||
let secondChild = {
|
||||
value: parseEther("1.5"), // New value after withdrawal
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
// Hash the second child commitment
|
||||
const [secondChildComitmentHash, ,] = hashCommitment(secondChild);
|
||||
|
||||
// Regenerate state merkle proof
|
||||
let newStateProof = stateTree.generateProof(3);
|
||||
|
||||
// Spend the first child and create the second one for the remaining 1.5 ETH
|
||||
await circuit.expectPass(
|
||||
{
|
||||
withdrawnValue: parseEther("2.5"),
|
||||
stateRoot: newStateProof.root,
|
||||
stateTreeDepth: stateTree.depth,
|
||||
ASPRoot: ASPProof.root,
|
||||
ASPTreeDepth: ASPTree.depth,
|
||||
context: randomBigInt(),
|
||||
label: LABEL,
|
||||
existingValue: firstChild.value,
|
||||
existingNullifier: firstChild.nullifier,
|
||||
existingSecret: firstChild.secret,
|
||||
newNullifier: secondChild.nullifier,
|
||||
newSecret: secondChild.secret,
|
||||
stateSiblings: padSiblings(newStateProof.siblings, maxTreeDepth),
|
||||
stateIndex: newStateProof.index,
|
||||
ASPSiblings: padSiblings(ASPProof.siblings, maxTreeDepth),
|
||||
ASPIndex: ASPProof.index,
|
||||
},
|
||||
{
|
||||
newCommitmentHash: secondChildComitmentHash,
|
||||
existingNullifierHash: firstChildNullifierHash,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("Should allow for a full value withdrawal", async () => {
|
||||
// Insert commitment in state
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(depositHash);
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
|
||||
// Insert label in ASP to mark as validated
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(LABEL);
|
||||
ASPTree!.insert(randomBigInt());
|
||||
|
||||
// Withdraw the full amount of the existing commitment. New value = 0 wei
|
||||
let fullWithdrawal = {
|
||||
value: parseEther("0"), // Empty value after full withdrawal
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
// Hash new commitment of zero value
|
||||
const [commitmentHash, ,] = hashCommitment(fullWithdrawal);
|
||||
|
||||
// generate merkle proofs
|
||||
let stateProof = stateTree.generateProof(1);
|
||||
let ASPProof = ASPTree.generateProof(2);
|
||||
|
||||
// Fully spend the existing commitment
|
||||
await circuit.expectPass(
|
||||
{
|
||||
withdrawnValue: parseEther("5"),
|
||||
stateRoot: stateProof.root,
|
||||
stateTreeDepth: stateTree.depth,
|
||||
ASPRoot: ASPProof.root,
|
||||
ASPTreeDepth: ASPTree.depth,
|
||||
context: randomBigInt(),
|
||||
label: LABEL,
|
||||
existingValue: parseEther("5"),
|
||||
existingNullifier: deposit.nullifier,
|
||||
existingSecret: deposit.secret,
|
||||
newNullifier: fullWithdrawal.nullifier,
|
||||
newSecret: fullWithdrawal.secret,
|
||||
stateSiblings: padSiblings(stateProof.siblings, maxTreeDepth),
|
||||
stateIndex: stateProof.index,
|
||||
ASPSiblings: padSiblings(ASPProof.siblings, maxTreeDepth),
|
||||
ASPIndex: ASPProof.index,
|
||||
},
|
||||
{
|
||||
newCommitmentHash: commitmentHash,
|
||||
existingNullifierHash: depositNullifierHash,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("Withdrawal should fail if existing commitment is not in the state tree", async () => {
|
||||
// Insert commitment in state
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
|
||||
// Insert leaves in ASP tree
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
|
||||
// Withdraw any amount
|
||||
let withdrawal = {
|
||||
value: parseEther("1"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
// Generate ASP tree merkle proof for another commitment. Generating a proof for a commitment that is not in the tree will fail
|
||||
let stateProof = stateTree.generateProof(1);
|
||||
// Generate ASP tree merkle proof for another label. Generating a proof for a label that is not in the tree will fail
|
||||
let ASPProof = ASPTree.generateProof(1);
|
||||
|
||||
// Fail when trying to spend a commitment that is not in the state tree
|
||||
// TODO: add assertion on error message. Currently fails on line 72 => state tree inclusion check
|
||||
await circuit.expectFail({
|
||||
withdrawnValue: parseEther("4"),
|
||||
stateRoot: stateProof.root,
|
||||
stateTreeDepth: stateTree.depth,
|
||||
ASPRoot: ASPProof.root,
|
||||
ASPTreeDepth: ASPTree.depth,
|
||||
context: randomBigInt(),
|
||||
label: LABEL,
|
||||
existingValue: parseEther("5"),
|
||||
existingNullifier: deposit.nullifier, // User only knows the secrets of the deposit
|
||||
existingSecret: deposit.secret, // User only knows the secrets of the deposit
|
||||
newNullifier: withdrawal.nullifier,
|
||||
newSecret: withdrawal.secret,
|
||||
stateSiblings: padSiblings(stateProof.siblings, maxTreeDepth),
|
||||
stateIndex: stateProof.index,
|
||||
ASPSiblings: padSiblings(ASPProof.siblings, maxTreeDepth),
|
||||
ASPIndex: ASPProof.index,
|
||||
});
|
||||
});
|
||||
|
||||
it("Withdrawal should fail if label is not present in the ASP tree", async () => {
|
||||
// Insert commitment in state
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(depositHash);
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
|
||||
// Insert leaves in ASP tree
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
|
||||
// Withdraw any amount
|
||||
let withdrawal = {
|
||||
value: parseEther("2.33"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
// Generate state tree merkle proof
|
||||
let stateProof = stateTree.generateProof(4);
|
||||
// Generate ASP tree merkle proof for another label. Generating a proof for a label that is not in the tree will fail
|
||||
let ASPProof = ASPTree.generateProof(1);
|
||||
|
||||
// Fail when trying to spend the deposit with a label that is not included in the ASP tree
|
||||
// TODO: add assertion on error message. Currently fails on line 82 => ASP inclusion check
|
||||
await circuit.expectFail({
|
||||
withdrawnValue: parseEther("1.77"),
|
||||
stateRoot: stateProof.root,
|
||||
stateTreeDepth: stateTree.depth,
|
||||
ASPRoot: ASPProof.root,
|
||||
ASPTreeDepth: ASPTree.depth,
|
||||
context: randomBigInt(),
|
||||
label: LABEL,
|
||||
existingValue: parseEther("5"),
|
||||
existingNullifier: deposit.nullifier,
|
||||
existingSecret: deposit.secret,
|
||||
newNullifier: withdrawal.nullifier,
|
||||
newSecret: withdrawal.secret,
|
||||
stateSiblings: padSiblings(stateProof.siblings, maxTreeDepth),
|
||||
stateIndex: stateProof.index,
|
||||
ASPSiblings: padSiblings(ASPProof.siblings, maxTreeDepth),
|
||||
ASPIndex: ASPProof.index,
|
||||
});
|
||||
});
|
||||
|
||||
it("Withdrawal should fail if label is removed from the ASP tree", async () => {
|
||||
// Insert commitment in state
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(randomBigInt());
|
||||
stateTree!.insert(depositHash);
|
||||
|
||||
// Insert label in ASP to mark as validated
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(randomBigInt());
|
||||
ASPTree!.insert(LABEL);
|
||||
|
||||
// Withdraw a partial amount
|
||||
let firstWithdrawal: Commitment = {
|
||||
value: parseEther("4"),
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(),
|
||||
secret: randomBigInt(),
|
||||
};
|
||||
|
||||
// Hash the first withdrawal
|
||||
const [firstWithdrawalHash, ,] = hashCommitment(firstWithdrawal);
|
||||
|
||||
// Generate merkle proofs
|
||||
let stateProof = stateTree.generateProof(3);
|
||||
let ASPProof = ASPTree.generateProof(3);
|
||||
|
||||
// Partially spend the deposit commitment
|
||||
await circuit.expectPass(
|
||||
{
|
||||
withdrawnValue: parseEther("1"),
|
||||
stateRoot: stateProof.root,
|
||||
stateTreeDepth: stateTree.depth,
|
||||
ASPRoot: ASPProof.root,
|
||||
ASPTreeDepth: ASPTree.depth,
|
||||
context: randomBigInt(),
|
||||
label: LABEL,
|
||||
existingValue: parseEther("5"),
|
||||
existingNullifier: deposit.nullifier,
|
||||
existingSecret: deposit.secret,
|
||||
newNullifier: firstWithdrawal.nullifier,
|
||||
newSecret: firstWithdrawal.secret,
|
||||
stateSiblings: padSiblings(stateProof.siblings, maxTreeDepth),
|
||||
stateIndex: stateProof.index,
|
||||
ASPSiblings: padSiblings(ASPProof.siblings, maxTreeDepth),
|
||||
ASPIndex: ASPProof.index,
|
||||
},
|
||||
{
|
||||
newCommitmentHash: firstWithdrawalHash,
|
||||
existingNullifierHash: depositNullifierHash,
|
||||
}
|
||||
);
|
||||
|
||||
// Insert new commitment in tree
|
||||
stateTree!.insert(firstWithdrawalHash);
|
||||
|
||||
// Remove label from ASP tree
|
||||
ASPTree!.update(3, REMOVED_LEAF);
|
||||
|
||||
// Generate new state root. ASP proof should remain the same
|
||||
stateProof = stateTree.generateProof(4);
|
||||
|
||||
// Withdraw the full amount of the existing commitment
|
||||
let secondWithdrawal: Commitment = {
|
||||
value: parseEther("3"), // empty value after full withdrawal
|
||||
label: LABEL,
|
||||
nullifier: randomBigInt(), // new nullifier
|
||||
secret: randomBigInt(), // new secret
|
||||
};
|
||||
|
||||
// Fail when trying to spend the commitment with a removed label
|
||||
// TODO: add assertion on error message. Currently fails on line 82 => ASP inclusion check
|
||||
await circuit.expectFail({
|
||||
withdrawnValue: parseEther("1"),
|
||||
stateRoot: stateProof.root,
|
||||
stateTreeDepth: stateTree.depth,
|
||||
ASPRoot: ASPTree.root,
|
||||
ASPTreeDepth: ASPTree.depth,
|
||||
context: randomBigInt(),
|
||||
label: LABEL,
|
||||
existingValue: parseEther("4"),
|
||||
existingNullifier: firstWithdrawal.nullifier,
|
||||
existingSecret: firstWithdrawal.secret,
|
||||
newNullifier: secondWithdrawal.nullifier,
|
||||
newSecret: secondWithdrawal.secret,
|
||||
stateSiblings: padSiblings(stateProof.siblings, maxTreeDepth),
|
||||
stateIndex: stateProof.index,
|
||||
ASPSiblings: padSiblings(ASPProof.siblings, maxTreeDepth),
|
||||
ASPIndex: ASPProof.index,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,8 @@
|
||||
{
|
||||
"name": "solidity-foundry-boilerplate",
|
||||
"name": "@privacy-pool-core/contracts",
|
||||
"version": "1.0.0",
|
||||
"description": "Production ready Solidity boilerplate with Foundry",
|
||||
"homepage": "https://github.com/defi-wonderland/solidity-foundry-boilerplate#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/defi-wonderland/solidity-foundry-boilerplate.git"
|
||||
},
|
||||
"description": "Solidity smart contracts for the Privacy Pool protocol",
|
||||
"repository": "https://github.com/defi-wonderland/privacy-pool-core",
|
||||
"license": "MIT",
|
||||
"author": "Wonderland",
|
||||
"scripts": {
|
||||
|
||||
324
yarn.lock
324
yarn.lock
@@ -2,6 +2,16 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@adraffy/ens-normalize@1.10.1":
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069"
|
||||
integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==
|
||||
|
||||
"@adraffy/ens-normalize@^1.10.1":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33"
|
||||
integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==
|
||||
|
||||
"@babel/code-frame@^7.0.0":
|
||||
version "7.26.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
|
||||
@@ -226,6 +236,13 @@
|
||||
"@jridgewell/resolve-uri" "^3.0.3"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@noble/curves@1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35"
|
||||
integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==
|
||||
dependencies:
|
||||
"@noble/hashes" "1.3.2"
|
||||
|
||||
"@noble/curves@1.4.2", "@noble/curves@~1.4.0":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9"
|
||||
@@ -233,11 +250,33 @@
|
||||
dependencies:
|
||||
"@noble/hashes" "1.4.0"
|
||||
|
||||
"@noble/curves@1.7.0", "@noble/curves@^1.4.0", "@noble/curves@^1.6.0", "@noble/curves@~1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.7.0.tgz#0512360622439256df892f21d25b388f52505e45"
|
||||
integrity sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==
|
||||
dependencies:
|
||||
"@noble/hashes" "1.6.0"
|
||||
|
||||
"@noble/hashes@1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
|
||||
integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==
|
||||
|
||||
"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
|
||||
integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==
|
||||
|
||||
"@noble/hashes@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.0.tgz#d4bfb516ad6e7b5111c216a5cc7075f4cf19e6c5"
|
||||
integrity sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==
|
||||
|
||||
"@noble/hashes@1.6.1", "@noble/hashes@^1.4.0", "@noble/hashes@^1.5.0", "@noble/hashes@~1.6.0":
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.1.tgz#df6e5943edcea504bac61395926d6fd67869a0d5"
|
||||
integrity sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
@@ -269,6 +308,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1"
|
||||
integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==
|
||||
|
||||
"@scure/base@~1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.1.tgz#dd0b2a533063ca612c17aa9ad26424a2ff5aa865"
|
||||
integrity sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==
|
||||
|
||||
"@scure/bip32@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67"
|
||||
@@ -278,6 +322,15 @@
|
||||
"@noble/hashes" "~1.4.0"
|
||||
"@scure/base" "~1.1.6"
|
||||
|
||||
"@scure/bip32@1.6.0", "@scure/bip32@^1.5.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.6.0.tgz#6dbc6b4af7c9101b351f41231a879d8da47e0891"
|
||||
integrity sha512-82q1QfklrUUdXJzjuRU7iG7D7XiFx5PHYVS0+oeNKhyDLT7WPqs6pBcM2W5ZdwOwKCwoE1Vy1se+DHjcXwCYnA==
|
||||
dependencies:
|
||||
"@noble/curves" "~1.7.0"
|
||||
"@noble/hashes" "~1.6.0"
|
||||
"@scure/base" "~1.2.1"
|
||||
|
||||
"@scure/bip39@1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3"
|
||||
@@ -286,6 +339,14 @@
|
||||
"@noble/hashes" "~1.4.0"
|
||||
"@scure/base" "~1.1.6"
|
||||
|
||||
"@scure/bip39@1.5.0", "@scure/bip39@^1.4.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.5.0.tgz#c8f9533dbd787641b047984356531d84485f19be"
|
||||
integrity sha512-Dop+ASYhnrwm9+HA/HwXg7j2ZqM6yk2fyLWb5znexjctFY3+E+eU8cIWI0Pql0Qx4hPZCijlGq4OL71g+Uz30A==
|
||||
dependencies:
|
||||
"@noble/hashes" "~1.6.0"
|
||||
"@scure/base" "~1.2.1"
|
||||
|
||||
"@solidity-parser/parser@^0.16.0":
|
||||
version "0.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.2.tgz#42cb1e3d88b3e8029b0c9befff00b634cd92d2fa"
|
||||
@@ -332,6 +393,13 @@
|
||||
dependencies:
|
||||
undici-types "~6.20.0"
|
||||
|
||||
"@types/node@22.7.5":
|
||||
version "22.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b"
|
||||
integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==
|
||||
dependencies:
|
||||
undici-types "~6.19.2"
|
||||
|
||||
"@types/node@^20.3.0":
|
||||
version "20.17.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.11.tgz#2c05215fc37316b1596df7fbdba52151eaf83c50"
|
||||
@@ -339,6 +407,31 @@
|
||||
dependencies:
|
||||
undici-types "~6.19.2"
|
||||
|
||||
"@zk-kit/baby-jubjub@1.0.3", "@zk-kit/baby-jubjub@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@zk-kit/baby-jubjub/-/baby-jubjub-1.0.3.tgz#8d2eccd20d729f1dbd39203dbff9a245a61dea76"
|
||||
integrity sha512-Wl+QfV6XGOMk1yU2JTqHXeKWfJVXp83is0+dtqfj9wx4wsAPpb+qzYvwAxW5PBx5/Nu71Bh7jp/5vM+6QgHSwA==
|
||||
dependencies:
|
||||
"@zk-kit/utils" "1.2.1"
|
||||
|
||||
"@zk-kit/circuits@^0.4.0":
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@zk-kit/circuits/-/circuits-0.4.0.tgz#17a8333e8afe5a4e79260600a2dcefb2bc751a8f"
|
||||
integrity sha512-Di7mokhwBS3qxVeCfHxGeNIpDg1kTnr1JXmsWiQMZLkRTn3Hugh6Tl07J394rWD0pIWRwPQsinaMVL2sB4F8yQ==
|
||||
dependencies:
|
||||
circomlib "^2.0.5"
|
||||
|
||||
"@zk-kit/eddsa-poseidon@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@zk-kit/eddsa-poseidon/-/eddsa-poseidon-1.1.0.tgz#08ef95ccbb2fbb5260617b6b5120dfb25a25229b"
|
||||
integrity sha512-Djc+zOZjd73FpLLf32/XeVZi8GX4ShPQJGS4Iig1QMAR/2CggEi++6Jrkr9N2FM3M4MRCH1qxz2u22DjOLtASg==
|
||||
dependencies:
|
||||
"@zk-kit/baby-jubjub" "1.0.3"
|
||||
"@zk-kit/utils" "1.2.1"
|
||||
blakejs "^1.2.1"
|
||||
buffer "6.0.3"
|
||||
poseidon-lite "0.3.0"
|
||||
|
||||
"@zk-kit/lean-imt.sol@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@zk-kit/lean-imt.sol/-/lean-imt.sol-2.0.0.tgz#4b0aee47854b5844455f9361396062139416e12b"
|
||||
@@ -346,6 +439,28 @@
|
||||
dependencies:
|
||||
poseidon-solidity "0.0.5"
|
||||
|
||||
"@zk-kit/lean-imt@^2.2.2":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@zk-kit/lean-imt/-/lean-imt-2.2.2.tgz#79c8bd70fc0d444638328cb4781479b14c69a9dd"
|
||||
integrity sha512-rscIPEgBBcu9vP/DJ3J+3187G/ObKETl343G5enPawNT81oeQSdHx3e2ZapTC+GfrZ/AS2AHHUOpRS1FfdSwjg==
|
||||
dependencies:
|
||||
"@zk-kit/utils" "1.2.1"
|
||||
|
||||
"@zk-kit/poseidon-cipher@^0.3.2":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@zk-kit/poseidon-cipher/-/poseidon-cipher-0.3.2.tgz#a2afcc1e4fcfa9db3b245d584183cd7fb7fcd4ab"
|
||||
integrity sha512-Ezz1e0mj/GRDlHdU5m0uhj5iHY72zWJU0BP8DsCCvPubU7LPI2tVaPpxrAjT4JQqatbVRQHLIhixW7F7BPzaFg==
|
||||
dependencies:
|
||||
"@zk-kit/baby-jubjub" "1.0.3"
|
||||
"@zk-kit/utils" "1.2.1"
|
||||
|
||||
"@zk-kit/utils@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@zk-kit/utils/-/utils-1.2.1.tgz#6cb38120535c73ab68cd0f09684882af148f256d"
|
||||
integrity sha512-H2nTsyWdicVOyvqC5AjgU7tsTgmR6PDrruFJNmlmdhKp7RxEia/E1B1swMZjaasYa2QMp4Zc6oB7cWchty7B2Q==
|
||||
dependencies:
|
||||
buffer "^6.0.3"
|
||||
|
||||
JSONStream@^1.3.5:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
|
||||
@@ -359,6 +474,16 @@ abitype@0.7.1:
|
||||
resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745"
|
||||
integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==
|
||||
|
||||
abitype@1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.7.tgz#876a0005d211e1c9132825d45bcee7b46416b284"
|
||||
integrity sha512-ZfYYSktDQUwc2eduYu8C4wOs+RDPmnRYMh7zNfzeMtGGgb0U+6tLGjixUic6mXf5xKKCcgT5Qp6cv39tOARVFw==
|
||||
|
||||
abitype@^1.0.6:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.8.tgz#3554f28b2e9d6e9f35eb59878193eabd1b9f46ba"
|
||||
integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==
|
||||
|
||||
acorn-walk@^8.1.1:
|
||||
version "8.3.4"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7"
|
||||
@@ -371,6 +496,11 @@ acorn@^8.11.0, acorn@^8.4.1:
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
|
||||
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
|
||||
|
||||
aes-js@4.0.0-beta.5:
|
||||
version "4.0.0-beta.5"
|
||||
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873"
|
||||
integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==
|
||||
|
||||
ajv@^6.12.6:
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
@@ -463,6 +593,11 @@ assertion-error@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
|
||||
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
|
||||
|
||||
assertion-error@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
|
||||
integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
|
||||
|
||||
ast-parents@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3"
|
||||
@@ -509,6 +644,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
bfj@^7.0.2:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.1.0.tgz#c5177d522103f9040e1b12980fe8c38cf41d3f8b"
|
||||
@@ -533,6 +673,11 @@ blake2b-wasm@^2.4.0:
|
||||
b4a "^1.0.1"
|
||||
nanoassert "^2.0.0"
|
||||
|
||||
blakejs@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814"
|
||||
integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==
|
||||
|
||||
bluebird@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
@@ -565,6 +710,14 @@ browser-stdout@^1.3.1:
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
|
||||
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
|
||||
|
||||
buffer@6.0.3, buffer@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840"
|
||||
@@ -601,7 +754,7 @@ camelcase@^6.0.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
chai@^4.3.6, chai@^4.3.7:
|
||||
chai@^4.3.6:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8"
|
||||
integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==
|
||||
@@ -614,6 +767,17 @@ chai@^4.3.6, chai@^4.3.7:
|
||||
pathval "^1.1.1"
|
||||
type-detect "^4.1.0"
|
||||
|
||||
chai@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d"
|
||||
integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==
|
||||
dependencies:
|
||||
assertion-error "^2.0.1"
|
||||
check-error "^2.1.1"
|
||||
deep-eql "^5.0.1"
|
||||
loupe "^3.1.0"
|
||||
pathval "^2.0.0"
|
||||
|
||||
chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
@@ -634,6 +798,11 @@ check-error@^1.0.3:
|
||||
dependencies:
|
||||
get-func-name "^2.0.2"
|
||||
|
||||
check-error@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
|
||||
integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
|
||||
|
||||
check-types@^11.2.3:
|
||||
version "11.2.3"
|
||||
resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.3.tgz#1ffdf68faae4e941fce252840b1787b8edc93b71"
|
||||
@@ -687,15 +856,14 @@ circom_tester@^0.0.19:
|
||||
tmp-promise "^3.0.3"
|
||||
util "^0.12.4"
|
||||
|
||||
circomkit@^0.0.22:
|
||||
version "0.0.22"
|
||||
resolved "https://registry.yarnpkg.com/circomkit/-/circomkit-0.0.22.tgz#0d1425fdece61972b002fd69a5b636515289c65a"
|
||||
integrity sha512-rfNiDCBrg/c9JI4nbvpKa123eFyrBFHBHUUeJFjHpzuG7Zw3KMbIUs4dw9xoIeqtzLYL4Hs4jXTMKluStEMxKA==
|
||||
circomkit@^0.3.1, circomkit@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/circomkit/-/circomkit-0.3.2.tgz#9a0630cfc04595c494ffcfaf2be999fa77c85e3c"
|
||||
integrity sha512-/ek/xtVNaBFlp2g12TPAfn9kSFUnZuKOiCvZFFnPn3JtZwi40r4eTDozPaaJKH4ZZx3alEgCr9MhOP24+o4CvA==
|
||||
dependencies:
|
||||
chai "^4.3.7"
|
||||
circom_tester "^0.0.19"
|
||||
loglevel "^1.8.1"
|
||||
snarkjs "^0.7.0"
|
||||
commander "^12.1.0"
|
||||
loglevel "^1.9.2"
|
||||
|
||||
circomlib@^2.0.5:
|
||||
version "2.0.5"
|
||||
@@ -769,7 +937,7 @@ commander@^11.1.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906"
|
||||
integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==
|
||||
|
||||
commander@^12.0.0, commander@~12.1.0:
|
||||
commander@^12.0.0, commander@^12.1.0, commander@~12.1.0:
|
||||
version "12.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
|
||||
integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
|
||||
@@ -886,6 +1054,11 @@ deep-eql@^4.1.3:
|
||||
dependencies:
|
||||
type-detect "^4.0.0"
|
||||
|
||||
deep-eql@^5.0.1:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
|
||||
integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
|
||||
|
||||
deep-is@~0.1.3:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
|
||||
@@ -1056,7 +1229,20 @@ ethereum-cryptography@^2.0.0:
|
||||
"@scure/bip32" "1.4.0"
|
||||
"@scure/bip39" "1.3.0"
|
||||
|
||||
eventemitter3@^5.0.1:
|
||||
ethers@^6.13.4:
|
||||
version "6.13.4"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.4.tgz#bd3e1c3dc1e7dc8ce10f9ffb4ee40967a651b53c"
|
||||
integrity sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==
|
||||
dependencies:
|
||||
"@adraffy/ens-normalize" "1.10.1"
|
||||
"@noble/curves" "1.2.0"
|
||||
"@noble/hashes" "1.3.2"
|
||||
"@types/node" "22.7.5"
|
||||
aes-js "4.0.0-beta.5"
|
||||
tslib "2.7.0"
|
||||
ws "8.17.1"
|
||||
|
||||
eventemitter3@5.0.1, eventemitter3@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
|
||||
integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
|
||||
@@ -1450,6 +1636,11 @@ husky@>=9:
|
||||
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d"
|
||||
integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==
|
||||
|
||||
ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
ignore@^5.2.4:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||
@@ -1606,6 +1797,11 @@ isexe@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
isows@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.6.tgz#0da29d706fa51551c663c627ace42769850f86e7"
|
||||
integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==
|
||||
|
||||
jake@^10.8.5:
|
||||
version "10.9.2"
|
||||
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f"
|
||||
@@ -1815,7 +2011,7 @@ log-update@^6.1.0:
|
||||
strip-ansi "^7.1.0"
|
||||
wrap-ansi "^9.0.0"
|
||||
|
||||
loglevel@^1.8.1:
|
||||
loglevel@^1.9.2:
|
||||
version "1.9.2"
|
||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.9.2.tgz#c2e028d6c757720107df4e64508530db6621ba08"
|
||||
integrity sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==
|
||||
@@ -1832,6 +2028,49 @@ loupe@^2.3.6:
|
||||
dependencies:
|
||||
get-func-name "^2.0.1"
|
||||
|
||||
loupe@^3.1.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240"
|
||||
integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==
|
||||
|
||||
maci-circuits@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/maci-circuits/-/maci-circuits-2.5.0.tgz#b8684917b7acc449fc5641ace80ff8218870d712"
|
||||
integrity sha512-Jw7IupZ2IxMawgJeE9U4wqCalCNfKe6gcJIMTzAC2DzoDWI7Gy1Wr1VsbnsNcnBj0O9IwKnqU/y91z4W3ylfZg==
|
||||
dependencies:
|
||||
"@zk-kit/circuits" "^0.4.0"
|
||||
circomkit "^0.3.1"
|
||||
circomlib "^2.0.5"
|
||||
maci-core "^2.5.0"
|
||||
maci-crypto "^2.5.0"
|
||||
maci-domainobjs "^2.5.0"
|
||||
snarkjs "^0.7.5"
|
||||
|
||||
maci-core@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/maci-core/-/maci-core-2.5.0.tgz#18121bd7e66beb8e02203dcc2608f855788cbf50"
|
||||
integrity sha512-Ko+cd7pQUR/mx8UTLcTi6zTkRXwDcX+1CWu6i9i7xYPaSPTbRkC9z8ggtJNYEKdVdTG6nJbhTV2TfCa5YV17qg==
|
||||
dependencies:
|
||||
maci-crypto "^2.5.0"
|
||||
maci-domainobjs "^2.5.0"
|
||||
|
||||
maci-crypto@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/maci-crypto/-/maci-crypto-2.5.0.tgz#89a99921517b79564af8a356863c446b49e4cb0f"
|
||||
integrity sha512-ozrLDH6kaK62TomNr5tnVgrUs6szXHCwcRyPzsQy07Wg2ZX61nyY0EFgWKAuU8kXqvYRdTtQdgflw6qpVz/4+w==
|
||||
dependencies:
|
||||
"@zk-kit/baby-jubjub" "^1.0.3"
|
||||
"@zk-kit/eddsa-poseidon" "^1.1.0"
|
||||
"@zk-kit/poseidon-cipher" "^0.3.2"
|
||||
ethers "^6.13.4"
|
||||
|
||||
maci-domainobjs@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/maci-domainobjs/-/maci-domainobjs-2.5.0.tgz#f4710a1bdf4ffd613aaab109eae20b4db9da484e"
|
||||
integrity sha512-MKyksmw+ndPG5HS80Ka8L7dEFDCuEis8HplYb3S+GqPDFwQ+Cl4vpMxF1Mr0d1SWAM4RfD4FQgAFaum4FWr1EA==
|
||||
dependencies:
|
||||
maci-crypto "^2.5.0"
|
||||
|
||||
make-error@^1.1.1:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
@@ -1997,6 +2236,19 @@ os-tmpdir@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
|
||||
|
||||
ox@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ox/-/ox-0.1.2.tgz#0f791be2ccabeaf4928e6d423498fe1c8094e560"
|
||||
integrity sha512-ak/8K0Rtphg9vnRJlbOdaX9R7cmxD2MiSthjWGaQdMk3D7hrAlDoM+6Lxn7hN52Za3vrXfZ7enfke/5WjolDww==
|
||||
dependencies:
|
||||
"@adraffy/ens-normalize" "^1.10.1"
|
||||
"@noble/curves" "^1.6.0"
|
||||
"@noble/hashes" "^1.5.0"
|
||||
"@scure/bip32" "^1.5.0"
|
||||
"@scure/bip39" "^1.4.0"
|
||||
abitype "^1.0.6"
|
||||
eventemitter3 "5.0.1"
|
||||
|
||||
p-limit@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
|
||||
@@ -2077,6 +2329,11 @@ pathval@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
|
||||
integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==
|
||||
|
||||
pathval@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25"
|
||||
integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==
|
||||
|
||||
picocolors@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
@@ -2097,6 +2354,11 @@ pluralize@^8.0.0:
|
||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
|
||||
integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
|
||||
|
||||
poseidon-lite@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/poseidon-lite/-/poseidon-lite-0.3.0.tgz#93c42f6f9b870f154f2722dfd686b909c4285765"
|
||||
integrity sha512-ilJj4MIve4uBEG7SrtPqUUNkvpJ/pLVbndxa0WvebcQqeIhe+h72JR4g0EvwchUzm9sOQDlOjiDNmRAgxNZl4A==
|
||||
|
||||
poseidon-solidity@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/poseidon-solidity/-/poseidon-solidity-0.0.5.tgz#3f93e01cfe25f6d2f2fac49734fbb00961b84655"
|
||||
@@ -2321,7 +2583,7 @@ snarkjs@0.5.0:
|
||||
logplease "^1.2.15"
|
||||
r1csfile "0.0.41"
|
||||
|
||||
snarkjs@^0.7.0:
|
||||
snarkjs@^0.7.5:
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/snarkjs/-/snarkjs-0.7.5.tgz#334d83b61468bdffbbf922b20734ca47be50b8ab"
|
||||
integrity sha512-h+3c4rXZKLhLuHk4LHydZCk/h5GcNvk5GjVKRRkHmfb6Ntf8gHOA9zea3g656iclRuhqQ3iKDWFgiD9ypLrKiA==
|
||||
@@ -2587,6 +2849,11 @@ ts-node@^10.9.1:
|
||||
v8-compile-cache-lib "^3.0.1"
|
||||
yn "3.1.1"
|
||||
|
||||
tslib@2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
|
||||
integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
|
||||
|
||||
type-check@~0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
|
||||
@@ -2652,6 +2919,21 @@ v8-compile-cache-lib@^3.0.1:
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
|
||||
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
|
||||
|
||||
viem@^2.21.57:
|
||||
version "2.21.57"
|
||||
resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.57.tgz#bedbb444bb42e07ccc2264a9a0441903a113aab8"
|
||||
integrity sha512-Mw4f4Dw0+Y/wSHdynVmP4uh+Cw15HEoj8BOKvKH5nGA6oFZYRxSy9Ruu7ZG8jexeAVCZ57aIuXb0gNg6Vb1x0g==
|
||||
dependencies:
|
||||
"@noble/curves" "1.7.0"
|
||||
"@noble/hashes" "1.6.1"
|
||||
"@scure/bip32" "1.6.0"
|
||||
"@scure/bip39" "1.5.0"
|
||||
abitype "1.0.7"
|
||||
isows "1.0.6"
|
||||
ox "0.1.2"
|
||||
webauthn-p256 "0.0.10"
|
||||
ws "8.18.0"
|
||||
|
||||
wasmbuilder@0.0.16:
|
||||
version "0.0.16"
|
||||
resolved "https://registry.yarnpkg.com/wasmbuilder/-/wasmbuilder-0.0.16.tgz#f34c1f2c047d2f6e1065cbfec5603988f16d8549"
|
||||
@@ -2726,6 +3008,14 @@ web3-validator@^2.0.6:
|
||||
web3-types "^1.6.0"
|
||||
zod "^3.21.4"
|
||||
|
||||
webauthn-p256@0.0.10:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.10.tgz#877e75abe8348d3e14485932968edf3325fd2fdd"
|
||||
integrity sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA==
|
||||
dependencies:
|
||||
"@noble/curves" "^1.4.0"
|
||||
"@noble/hashes" "^1.4.0"
|
||||
|
||||
which-typed-array@^1.1.14, which-typed-array@^1.1.2:
|
||||
version "1.1.16"
|
||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.16.tgz#db4db429c4706feca2f01677a144278e4a8c216b"
|
||||
@@ -2784,6 +3074,16 @@ wrappy@1:
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
ws@8.17.1:
|
||||
version "8.17.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
|
||||
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
|
||||
|
||||
ws@8.18.0:
|
||||
version "8.18.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
|
||||
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
|
||||
|
||||
y18n@^5.0.5:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
|
||||
Reference in New Issue
Block a user