support any output count, some refactorings

This commit is contained in:
poma 2021-08-25 01:23:16 +03:00 committed by Alexey Pertsev
parent d93a6d6298
commit 98b2e238b9
4 changed files with 70 additions and 40 deletions

View 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;
}

View File

@ -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];
} }

View File

@ -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++) {

View File

@ -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;
} }