mirror of
https://github.com/tornadocash/tornado-nova
synced 2024-02-02 14:53:56 +01:00
support any output count, some refactorings
This commit is contained in:
parent
d93a6d6298
commit
98b2e238b9
30
circuits/merkleProof.circom
Normal file
30
circuits/merkleProof.circom
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||||
|
include "../node_modules/circomlib/circuits/switcher.circom";
|
||||||
|
|
||||||
|
// Verifies that merkle proof is correct for given merkle root and a leaf
|
||||||
|
// pathIndices bits is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path
|
||||||
|
template MerkleProof(levels) {
|
||||||
|
signal input leaf;
|
||||||
|
signal input pathElements[levels];
|
||||||
|
signal input pathIndices;
|
||||||
|
signal output root;
|
||||||
|
|
||||||
|
component switcher[levels];
|
||||||
|
component hasher[levels];
|
||||||
|
|
||||||
|
component indexBits = Num2Bits(levels);
|
||||||
|
indexBits.in <== pathIndices;
|
||||||
|
|
||||||
|
for (var i = 0; i < levels; i++) {
|
||||||
|
switcher[i] = Switcher();
|
||||||
|
switcher[i].L <== i == 0 ? leaf : hasher[i - 1].out;
|
||||||
|
switcher[i].R <== pathElements[i];
|
||||||
|
switcher[i].sel <== indexBits.out[i];
|
||||||
|
|
||||||
|
hasher[i] = Poseidon(2);
|
||||||
|
hasher[i].inputs[0] <== switcher[i].outL;
|
||||||
|
hasher[i].inputs[1] <== switcher[i].outR;
|
||||||
|
}
|
||||||
|
|
||||||
|
root <== hasher[levels - 1].out;
|
||||||
|
}
|
@ -1,30 +1,32 @@
|
|||||||
include "../node_modules/circomlib/circuits/poseidon.circom";
|
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||||
include "../node_modules/circomlib/circuits/switcher.circom";
|
|
||||||
|
|
||||||
// Verifies that merkle proof is correct for given merkle root and a leaf
|
// Helper template that computes hashes of the next tree layer
|
||||||
// pathIndices input is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path
|
template TreeLayer(height) {
|
||||||
|
var nItems = 1 << height;
|
||||||
|
signal input ins[nItems * 2];
|
||||||
|
signal output outs[nItems];
|
||||||
|
|
||||||
|
component hash[nItems];
|
||||||
|
for(var i = 0; i < nItems; i++) {
|
||||||
|
hash[i] = Poseidon(2);
|
||||||
|
hash[i].inputs[0] <== ins[i * 2];
|
||||||
|
hash[i].inputs[1] <== ins[i * 2 + 1];
|
||||||
|
hash[i].out ==> outs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a merkle tree from leaf array
|
||||||
template MerkleTree(levels) {
|
template MerkleTree(levels) {
|
||||||
signal input leaf;
|
signal input leaves[1 << levels];
|
||||||
signal input pathElements[levels];
|
|
||||||
signal input pathIndices;
|
|
||||||
signal output root;
|
signal output root;
|
||||||
|
|
||||||
component switcher[levels];
|
component layers[levels];
|
||||||
component hasher[levels];
|
for(var level = levels - 1; level >= 0; level--) {
|
||||||
|
layers[level] = TreeLayer(level);
|
||||||
component indexBits = Num2Bits(levels);
|
for(var i = 0; i < (1 << (level + 1)); i++) {
|
||||||
indexBits.in <== pathIndices;
|
layers[level].ins[i] <== level == levels - 1 ? leaves[i] : layers[level + 1].outs[i];
|
||||||
|
}
|
||||||
for (var i = 0; i < levels; i++) {
|
|
||||||
switcher[i] = Switcher();
|
|
||||||
switcher[i].L <== i == 0 ? leaf : hasher[i - 1].out;
|
|
||||||
switcher[i].R <== pathElements[i];
|
|
||||||
switcher[i].sel <== indexBits.out[i];
|
|
||||||
|
|
||||||
hasher[i] = Poseidon(2);
|
|
||||||
hasher[i].inputs[0] <== switcher[i].outL;
|
|
||||||
hasher[i].inputs[1] <== switcher[i].outR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
root <== hasher[levels - 1].out;
|
root <== levels > 0 ? layers[0].outs[0] : leaves[0];
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
include "./merkleTree.circom"
|
include "./merkleProof.circom"
|
||||||
include "./treeUpdater.circom"
|
include "./treeUpdater.circom"
|
||||||
include "./utils.circom"
|
include "./utils.circom"
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ template Transaction(levels, nIns, nOuts, zeroLeaf) {
|
|||||||
nullifierHasher[tx].privateKey <== inPrivateKey[tx];
|
nullifierHasher[tx].privateKey <== inPrivateKey[tx];
|
||||||
nullifierHasher[tx].nullifier === inputNullifier[tx];
|
nullifierHasher[tx].nullifier === inputNullifier[tx];
|
||||||
|
|
||||||
tree[tx] = MerkleTree(levels);
|
tree[tx] = MerkleProof(levels);
|
||||||
tree[tx].leaf <== inUtxoHasher[tx].commitment;
|
tree[tx].leaf <== inUtxoHasher[tx].commitment;
|
||||||
tree[tx].pathIndices <== inPathIndices[tx];
|
tree[tx].pathIndices <== inPathIndices[tx];
|
||||||
for (var i = 0; i < levels; i++) {
|
for (var i = 0; i < levels; i++) {
|
||||||
@ -124,7 +124,7 @@ template Transaction(levels, nIns, nOuts, zeroLeaf) {
|
|||||||
treeUpdater.oldRoot <== root;
|
treeUpdater.oldRoot <== root;
|
||||||
treeUpdater.newRoot <== newRoot;
|
treeUpdater.newRoot <== newRoot;
|
||||||
for (var i = 0; i < nOuts; i++) {
|
for (var i = 0; i < nOuts; i++) {
|
||||||
treeUpdater.leaf[i] <== outputCommitment[i];
|
treeUpdater.leaves[i] <== outputCommitment[i];
|
||||||
}
|
}
|
||||||
treeUpdater.pathIndices <== outPathIndices;
|
treeUpdater.pathIndices <== outPathIndices;
|
||||||
for (var i = 0; i < levels - 1; i++) {
|
for (var i = 0; i < levels - 1; i++) {
|
||||||
|
@ -1,27 +1,25 @@
|
|||||||
|
include "./merkleProof.circom";
|
||||||
include "./merkleTree.circom";
|
include "./merkleTree.circom";
|
||||||
|
|
||||||
// inserts a subtree into a merkle tree
|
// inserts a subtree into a merkle tree
|
||||||
// checks that tree previously contained zeroes is the same positions
|
// checks that tree previously contained zeroes is the same positions
|
||||||
// zeroSubtreeRoot is a root of a subtree that contains only zeroes
|
// zeroSubtreeRoot is a root of a subtree that contains only zeroes
|
||||||
template TreeUpdater(levels, subtreeLevels, zeroSubtreeRoot) {
|
template TreeUpdater(levels, subtreeLevels, zeroSubtreeRoot) {
|
||||||
// currently it works only with 1-level subtrees
|
|
||||||
assert(subtreeLevels == 1);
|
|
||||||
var remainingLevels = levels - subtreeLevels;
|
var remainingLevels = levels - subtreeLevels;
|
||||||
|
|
||||||
signal input oldRoot;
|
signal input oldRoot;
|
||||||
signal input newRoot;
|
signal input newRoot;
|
||||||
signal input leaf[1 << subtreeLevels];
|
signal input leaves[1 << subtreeLevels];
|
||||||
signal input pathIndices;
|
signal input pathIndices;
|
||||||
signal private input pathElements[remainingLevels];
|
signal private input pathElements[remainingLevels];
|
||||||
|
|
||||||
// calculate subtree root
|
// calculate subtree root
|
||||||
// todo: make it work with arbitrary subtree levels
|
component subtree = MerkleTree(subtreeLevels);
|
||||||
// currently it works only with 1-level subtrees
|
for(var i = 0; i < (1 << subtreeLevels); i++) {
|
||||||
component leafPair = Poseidon(2);
|
subtree.leaves[i] <== leaves[i];
|
||||||
leafPair.inputs[0] <== leaf[0];
|
}
|
||||||
leafPair.inputs[1] <== leaf[1];
|
|
||||||
|
|
||||||
component treeBefore = MerkleTree(remainingLevels);
|
component treeBefore = MerkleProof(remainingLevels);
|
||||||
for(var i = 0; i < remainingLevels; i++) {
|
for(var i = 0; i < remainingLevels; i++) {
|
||||||
treeBefore.pathElements[i] <== pathElements[i];
|
treeBefore.pathElements[i] <== pathElements[i];
|
||||||
}
|
}
|
||||||
@ -29,11 +27,11 @@ template TreeUpdater(levels, subtreeLevels, zeroSubtreeRoot) {
|
|||||||
treeBefore.leaf <== zeroSubtreeRoot;
|
treeBefore.leaf <== zeroSubtreeRoot;
|
||||||
treeBefore.root === oldRoot;
|
treeBefore.root === oldRoot;
|
||||||
|
|
||||||
component treeAfter = MerkleTree(remainingLevels);
|
component treeAfter = MerkleProof(remainingLevels);
|
||||||
for(var i = 0; i < remainingLevels; i++) {
|
for(var i = 0; i < remainingLevels; i++) {
|
||||||
treeAfter.pathElements[i] <== pathElements[i];
|
treeAfter.pathElements[i] <== pathElements[i];
|
||||||
}
|
}
|
||||||
treeAfter.pathIndices <== pathIndices;
|
treeAfter.pathIndices <== pathIndices;
|
||||||
treeAfter.leaf <== leafPair.out;
|
treeAfter.leaf <== subtree.root;
|
||||||
treeAfter.root === newRoot;
|
treeAfter.root === newRoot;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user