From 9efab84e65d923410ce4418794a5ca2ddcd74f37 Mon Sep 17 00:00:00 2001 From: poma Date: Sat, 2 Nov 2019 04:32:28 +0300 Subject: [PATCH 1/5] remove explicit constraints on inputs --- circuits/withdraw.circom | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/circuits/withdraw.circom b/circuits/withdraw.circom index cc1448d..36a4674 100644 --- a/circuits/withdraw.circom +++ b/circuits/withdraw.circom @@ -31,9 +31,9 @@ template Withdraw(levels, rounds) { signal input root; signal input nullifierHash; signal input receiver; // not taking part in any computations - signal input relayer; // not taking part in any computations - signal input fee; // not taking part in any computations - signal input refund; // not taking part in any computations + signal input relayer; // not taking part in any computations + signal input fee; // not taking part in any computations + signal input refund; // not taking part in any computations signal private input nullifier; signal private input secret; signal private input pathElements[levels]; @@ -52,18 +52,6 @@ template Withdraw(levels, rounds) { tree.pathElements[i] <== pathElements[i]; tree.pathIndex[i] <== pathIndex[i]; } - - // Add hidden signals to make sure that tampering with receiver or fee will invalidate the snark proof - // Most likely it is not required, but it's better to stay on the safe side and it only takes 2 constraints - // Squares are used to prevent optimizer from removing those constraints - signal receiverSquare; - signal feeSquare; - signal relayerSquare; - signal refundSquare; - receiverSquare <== receiver * receiver; - feeSquare <== fee * fee; - relayerSquare <== relayer * relayer; - refundSquare <== refund * refund; } component main = Withdraw(16, 220); From 7193655e4940476269426b80da40bc9a099598e4 Mon Sep 17 00:00:00 2001 From: poma Date: Sat, 2 Nov 2019 04:33:19 +0300 Subject: [PATCH 2/5] make rounds number a constant --- circuits/merkleTree.circom | 8 ++++---- circuits/withdraw.circom | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/circuits/merkleTree.circom b/circuits/merkleTree.circom index f7ffbb6..0d85605 100644 --- a/circuits/merkleTree.circom +++ b/circuits/merkleTree.circom @@ -1,13 +1,13 @@ include "../node_modules/circomlib/circuits/mimcsponge.circom"; // Computes MiMC(left + right) -template HashLeftRight(rounds) { +template HashLeftRight() { signal input left; signal input right; signal output hash; - component hasher = MiMCSponge(2, rounds, 1); + component hasher = MiMCSponge(2, 220, 1); hasher.ins[0] <== left; hasher.ins[1] <== right; hasher.k <== 0; @@ -43,7 +43,7 @@ template Selector() { // Verifies that merkle proof is correct for given merkle root and a leaf // pathIndex input is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path -template MerkleTree(levels, rounds) { +template MerkleTree(levels) { signal input leaf; signal input root; signal private input pathElements[levels]; @@ -54,7 +54,7 @@ template MerkleTree(levels, rounds) { for (var i = 0; i < levels; i++) { selectors[i] = Selector(); - hashers[i] = HashLeftRight(rounds); + hashers[i] = HashLeftRight(); selectors[i].pathElement <== pathElements[i]; selectors[i].pathIndex <== pathIndex[i]; diff --git a/circuits/withdraw.circom b/circuits/withdraw.circom index 36a4674..e060f4f 100644 --- a/circuits/withdraw.circom +++ b/circuits/withdraw.circom @@ -27,7 +27,7 @@ template CommitmentHasher() { } // Verifies that commitment that corresponds to given secret and nullifier is included in the merkle tree of deposits -template Withdraw(levels, rounds) { +template Withdraw(levels) { signal input root; signal input nullifierHash; signal input receiver; // not taking part in any computations @@ -45,7 +45,7 @@ template Withdraw(levels, rounds) { nullifierHash === hasher.nullifierHash; - component tree = MerkleTree(levels, rounds); + component tree = MerkleTree(levels); tree.leaf <== hasher.commitment; tree.root <== root; for (var i = 0; i < levels; i++) { @@ -54,4 +54,4 @@ template Withdraw(levels, rounds) { } } -component main = Withdraw(16, 220); +component main = Withdraw(16); From 07168f9816a6a78767f53aab25624bd1d739a66d Mon Sep 17 00:00:00 2001 From: poma Date: Sat, 2 Nov 2019 04:55:25 +0300 Subject: [PATCH 3/5] refactor select() into a generic multiplexer --- circuits/merkleTree.circom | 45 +++++++++++++------------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/circuits/merkleTree.circom b/circuits/merkleTree.circom index 0d85605..a8b2328 100644 --- a/circuits/merkleTree.circom +++ b/circuits/merkleTree.circom @@ -15,30 +15,15 @@ template HashLeftRight() { hash <== hasher.outs[0]; } -// if pathIndex == 0 returns (left = inputElement, right = pathElement) -// if pathIndex == 1 returns (left = pathElement, right = inputElement) -template Selector() { - signal input inputElement; - signal input pathElement; - signal input pathIndex; +// if s == 0 returns [in[0], in[1]] +// if s == 1 returns [in[1], in[0]] +template Mux() { + signal input in[2]; + signal input s; + signal output out[2]; - signal output left; - signal output right; - - signal leftSelector1; - signal leftSelector2; - signal rightSelector1; - signal rightSelector2; - - pathIndex * (1-pathIndex) === 0 - - leftSelector1 <== (1 - pathIndex) * inputElement; - leftSelector2 <== (pathIndex) * pathElement; - rightSelector1 <== (pathIndex) * inputElement; - rightSelector2 <== (1 - pathIndex) * pathElement; - - left <== leftSelector1 + leftSelector2; - right <== rightSelector1 + rightSelector2; + out[0] <== (in[1] - in[0])*s + in[0]; + out[1] <== (in[0] - in[1])*s + in[1]; } // Verifies that merkle proof is correct for given merkle root and a leaf @@ -53,20 +38,20 @@ template MerkleTree(levels) { component hashers[levels]; for (var i = 0; i < levels; i++) { - selectors[i] = Selector(); + selectors[i] = Mux(); hashers[i] = HashLeftRight(); - selectors[i].pathElement <== pathElements[i]; - selectors[i].pathIndex <== pathIndex[i]; + selectors[i].in[1] <== pathElements[i]; + selectors[i].s <== pathIndex[i]; - hashers[i].left <== selectors[i].left; - hashers[i].right <== selectors[i].right; + hashers[i].left <== selectors[i].out[0]; + hashers[i].right <== selectors[i].out[1]; } - selectors[0].inputElement <== leaf; + selectors[0].in[0] <== leaf; for (var i = 1; i < levels; i++) { - selectors[i].inputElement <== hashers[i-1].hash; + selectors[i].in[0] <== hashers[i-1].hash; } root === hashers[levels - 1].hash; From ec4508e81e92b03c8864eeff436f5e85ceae02f6 Mon Sep 17 00:00:00 2001 From: poma Date: Sat, 2 Nov 2019 05:05:25 +0300 Subject: [PATCH 4/5] rename pathIndex -> pathIndices --- circuits/merkleTree.circom | 9 +++------ circuits/withdraw.circom | 4 ++-- cli.js | 4 ++-- test/ERC20Mixer.test.js | 8 ++++---- test/ETHMixer.test.js | 16 ++++++++-------- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/circuits/merkleTree.circom b/circuits/merkleTree.circom index a8b2328..c93f131 100644 --- a/circuits/merkleTree.circom +++ b/circuits/merkleTree.circom @@ -4,14 +4,12 @@ include "../node_modules/circomlib/circuits/mimcsponge.circom"; template HashLeftRight() { signal input left; signal input right; - signal output hash; component hasher = MiMCSponge(2, 220, 1); hasher.ins[0] <== left; hasher.ins[1] <== right; hasher.k <== 0; - hash <== hasher.outs[0]; } @@ -27,12 +25,12 @@ template Mux() { } // Verifies that merkle proof is correct for given merkle root and a leaf -// pathIndex input is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path +// pathIndices input is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path template MerkleTree(levels) { signal input leaf; signal input root; signal private input pathElements[levels]; - signal private input pathIndex[levels]; + signal private input pathIndices[levels]; component selectors[levels]; component hashers[levels]; @@ -42,14 +40,13 @@ template MerkleTree(levels) { hashers[i] = HashLeftRight(); selectors[i].in[1] <== pathElements[i]; - selectors[i].s <== pathIndex[i]; + selectors[i].s <== pathIndices[i]; hashers[i].left <== selectors[i].out[0]; hashers[i].right <== selectors[i].out[1]; } selectors[0].in[0] <== leaf; - for (var i = 1; i < levels; i++) { selectors[i].in[0] <== hashers[i-1].hash; } diff --git a/circuits/withdraw.circom b/circuits/withdraw.circom index e060f4f..1aee5e1 100644 --- a/circuits/withdraw.circom +++ b/circuits/withdraw.circom @@ -37,7 +37,7 @@ template Withdraw(levels) { signal private input nullifier; signal private input secret; signal private input pathElements[levels]; - signal private input pathIndex[levels]; + signal private input pathIndices[levels]; component hasher = CommitmentHasher(); hasher.nullifier <== nullifier; @@ -50,7 +50,7 @@ template Withdraw(levels) { tree.root <== root; for (var i = 0; i < levels; i++) { tree.pathElements[i] <== pathElements[i]; - tree.pathIndex[i] <== pathIndex[i]; + tree.pathIndices[i] <== pathIndices[i]; } } diff --git a/cli.js b/cli.js index e2e9c2b..68843a2 100755 --- a/cli.js +++ b/cli.js @@ -107,7 +107,7 @@ async function withdrawErc20(note, receiver, relayer) { nullifier: deposit.nullifier, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, } console.log('Generating SNARK proof') @@ -182,7 +182,7 @@ async function withdraw(note, receiver) { nullifier: deposit.nullifier, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, } console.log('Generating SNARK proof') diff --git a/test/ERC20Mixer.test.js b/test/ERC20Mixer.test.js index 8e0d42d..88530be 100644 --- a/test/ERC20Mixer.test.js +++ b/test/ERC20Mixer.test.js @@ -136,7 +136,7 @@ contract('ERC20Mixer', accounts => { nullifier: deposit.nullifier, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) @@ -204,7 +204,7 @@ contract('ERC20Mixer', accounts => { nullifier: deposit.nullifier, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) @@ -263,7 +263,7 @@ contract('ERC20Mixer', accounts => { nullifier: deposit.nullifier, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) @@ -344,7 +344,7 @@ contract('ERC20Mixer', accounts => { nullifier: deposit.nullifier, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) diff --git a/test/ETHMixer.test.js b/test/ETHMixer.test.js index 2462b23..c1bf85a 100644 --- a/test/ETHMixer.test.js +++ b/test/ETHMixer.test.js @@ -148,7 +148,7 @@ contract('ETHMixer', accounts => { refund, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) let proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) @@ -208,7 +208,7 @@ contract('ETHMixer', accounts => { nullifier: deposit.nullifier, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) @@ -263,7 +263,7 @@ contract('ETHMixer', accounts => { refund, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) const { proof, publicSignals } = websnarkUtils.toSolidityInput(proofData) @@ -289,7 +289,7 @@ contract('ETHMixer', accounts => { refund, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) const { proof, publicSignals } = websnarkUtils.toSolidityInput(proofData) @@ -315,7 +315,7 @@ contract('ETHMixer', accounts => { refund, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) @@ -341,7 +341,7 @@ contract('ETHMixer', accounts => { refund, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) const dummyRoot = randomHex(32) @@ -370,7 +370,7 @@ contract('ETHMixer', accounts => { refund, secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) let { proof, publicSignals } = websnarkUtils.toSolidityInput(proofData) @@ -422,7 +422,7 @@ contract('ETHMixer', accounts => { refund: bigInt(1), secret: deposit.secret, pathElements: path_elements, - pathIndex: path_index, + pathIndices: path_index, }) const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) From f8cd3fea1eb5664dd0f8b3e4b83ac6e0c0ba3371 Mon Sep 17 00:00:00 2001 From: poma Date: Sun, 3 Nov 2019 11:41:05 +0300 Subject: [PATCH 5/5] refactor loop --- circuits/merkleTree.circom | 11 +++-------- circuits/withdraw.circom | 4 +--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/circuits/merkleTree.circom b/circuits/merkleTree.circom index c93f131..bfd6016 100644 --- a/circuits/merkleTree.circom +++ b/circuits/merkleTree.circom @@ -1,6 +1,6 @@ include "../node_modules/circomlib/circuits/mimcsponge.circom"; -// Computes MiMC(left + right) +// Computes MiMC([left, right]) template HashLeftRight() { signal input left; signal input right; @@ -37,19 +37,14 @@ template MerkleTree(levels) { for (var i = 0; i < levels; i++) { selectors[i] = Mux(); - hashers[i] = HashLeftRight(); - + selectors[i].in[0] <== i == 0 ? leaf : hashers[i - 1].hash; selectors[i].in[1] <== pathElements[i]; selectors[i].s <== pathIndices[i]; + hashers[i] = HashLeftRight(); hashers[i].left <== selectors[i].out[0]; hashers[i].right <== selectors[i].out[1]; } - selectors[0].in[0] <== leaf; - for (var i = 1; i < levels; i++) { - selectors[i].in[0] <== hashers[i-1].hash; - } - root === hashers[levels - 1].hash; } diff --git a/circuits/withdraw.circom b/circuits/withdraw.circom index 1aee5e1..a4c4848 100644 --- a/circuits/withdraw.circom +++ b/circuits/withdraw.circom @@ -6,7 +6,6 @@ include "merkleTree.circom"; template CommitmentHasher() { signal private input nullifier; signal private input secret; - signal output commitment; signal output nullifierHash; @@ -42,8 +41,7 @@ template Withdraw(levels) { component hasher = CommitmentHasher(); hasher.nullifier <== nullifier; hasher.secret <== secret; - - nullifierHash === hasher.nullifierHash; + hasher.nullifierHash === nullifierHash; component tree = MerkleTree(levels); tree.leaf <== hasher.commitment;