Skip to content

Commit

Permalink
Added SMT comments and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
RuslanProgrammer committed Mar 11, 2024
1 parent a4ae9e7 commit 786b70d
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 25 deletions.
28 changes: 22 additions & 6 deletions circuits/Smt.circom
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ template Hash2() {

/*
* Hash2 = Poseidon(key | value | 1)
* The 1 is added to the end of the value to make the hash unique
*/
template Hash3() {
signal input a;
Expand Down Expand Up @@ -64,11 +65,15 @@ template DepthDeterminer(depth) {
isZero[i].in <== siblings[i];
}

// The last sibling is always zero due to the way the proof is constructed
isZero[depth - 1].out === 1;

// If a previous depth is zero, then the current depth is the desired one
desiredDepth[depth - 1] <== inverse(isZero[depth - 2].out);
// If the current depth is the desired one, then all the following depths are done
done[depth - 2] <== desiredDepth[depth - 1];

// If we didn't find the done depth, then check if the previous depth is zero
for (var i = depth - 2; i > 0; i--) {
desiredDepth[i] <== inverse(done[i]) * inverse(isZero[i - 1].out);
done[i - 1] <== desiredDepth[i] + done[i];
Expand All @@ -80,6 +85,7 @@ template DepthDeterminer(depth) {
// Determines the type of the node
template NodeTypeDeterminer() {
signal input auxIsEmpty;
// 1 if the node is at the desired depth, 0 otherwise
signal input isDesiredDepth;
signal input isExclusion;

Expand All @@ -88,14 +94,17 @@ template NodeTypeDeterminer() {
signal input previousAuxLeaf;
signal input previousLeaf;

// 1 if the node is a middle node, 0 otherwise
signal output middle;
// 1 if the node is a empty node, 0 otherwise
signal output empty;
// 1 if the node is a leaf node for the exclusion proof, 0 otherwise
signal output auxLeaf;
// 1 if the node is a leaf node, 0 otherwise
signal output leaf;

signal leafForExclusionCheck;

leafForExclusionCheck <== isDesiredDepth * isExclusion;
// 1 if the node is a leaf node and we are checking for exclusion, 0 otherwise
signal leafForExclusionCheck <== isDesiredDepth * isExclusion;

// Determine the node as a middle, until get to the desired depth
middle <== previousMiddle - isDesiredDepth;
Expand Down Expand Up @@ -151,16 +160,20 @@ template DepthHash() {
}

template SMTVerifier(depth) {
// The root of the full merkle tree
signal input root;
// The siblings for each depth
signal input siblings[depth];

signal input key;
signal input value;

signal input auxKey;
signal input auxValue;
// 1 if the aux node is empty, 0 otherwise
signal input auxIsEmpty;

signal input key;
signal input value;

// 1 if we are checking for exclusion, 0 if we are checking for inclusion
signal input isExclusion;

// Check that the auxIsEmpty is 0 if we are checking for inclusion
Expand Down Expand Up @@ -237,11 +250,14 @@ template SMTVerifier(depth) {
depthHash[i].currentKeyBit <== keyBits.out[i];

if (i == depth - 1) {
// The last depth has no child
depthHash[i].child <== 0;
} else {
// The child of the current depth is the root of the next depth
depthHash[i].child <== depthHash[i + 1].root;
}
}

// The root of the merkle tree is the root of the first depth
depthHash[0].root === root;
}
84 changes: 65 additions & 19 deletions test/Smt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,33 +63,29 @@ describe("SMT", () => {
expect(await smtVerifier.verifyProof(pA, pB, pC, publicSignals)).to.be.true;
});

it("should prove the tree inclusion for bamboo", async () => {
let leaves: string[] = [];

it("should prove the tree inclusion for each depth of bamboo", async () => {
for (let i = 0; i < 9; i++) {
const rand = parseInt("1".repeat(i + 1), 2).toString();

await smtMock.addElement(rand, rand);

leaves.push(rand);
}
const merkleProof = await smtMock.getProof(rand);

const merkleProof = await smtMock.getProof(leaves[8]);

const proofStruct = (await smtCircuit.genProof({
root: merkleProof.root,
siblings: merkleProof.siblings,
key: merkleProof.key,
value: merkleProof.value,
auxKey: 0,
auxValue: 0,
auxIsEmpty: 0,
isExclusion: 0,
})) as ProofStruct;
const proofStruct = (await smtCircuit.genProof({
root: merkleProof.root,
siblings: merkleProof.siblings,
key: merkleProof.key,
value: merkleProof.value,
auxKey: 0,
auxValue: 0,
auxIsEmpty: 0,
isExclusion: 0,
})) as ProofStruct;

const [pA, pB, pC, publicSignals] = await generateCalldata(proofStruct);
const [pA, pB, pC, publicSignals] = await generateCalldata(proofStruct);

expect(await smtVerifier.verifyProof(pA, pB, pC, publicSignals)).to.be.true;
expect(await smtVerifier.verifyProof(pA, pB, pC, publicSignals)).to.be.true;
}
});

it("should prove the tree inclusion for max depth", async () => {
Expand Down Expand Up @@ -143,6 +139,56 @@ describe("SMT", () => {
expect(await smtVerifier.verifyProof(pA, pB, pC, publicSignals)).to.be.true;
});

it("should prove the tree exclusion for each depth of bamboo", async () => {
for (let i = 0; i < 9; i++) {
const rand = parseInt("1".repeat(i + 1), 2).toString();

await smtMock.addElement(rand, rand);

const nonExistentLeaf = ethers.hexlify(ethers.randomBytes(30));

const merkleProof = await smtMock.getProof(nonExistentLeaf);

const auxIsEmpty = BigInt(merkleProof.auxKey) == 0n ? 1 : 0;

const proofStruct = (await smtCircuit.genProof({
root: merkleProof.root,
siblings: merkleProof.siblings,
key: merkleProof.key,
value: 0,
auxKey: merkleProof.auxKey,
auxValue: merkleProof.auxValue,
auxIsEmpty: auxIsEmpty,
isExclusion: 1,
})) as ProofStruct;

const [pA, pB, pC, publicSignals] = await generateCalldata(proofStruct);

expect(await smtVerifier.verifyProof(pA, pB, pC, publicSignals)).to.be.true;
}
});

it("should prove the tree exclusion for empty tree", async () => {
const nonExistentLeaf = ethers.hexlify(ethers.randomBytes(30));

const merkleProof = await smtMock.getProof(nonExistentLeaf);

const proofStruct = (await smtCircuit.genProof({
root: merkleProof.root,
siblings: merkleProof.siblings,
key: merkleProof.key,
value: 0,
auxKey: merkleProof.auxKey,
auxValue: merkleProof.auxValue,
auxIsEmpty: 1,
isExclusion: 1,
})) as ProofStruct;

const [pA, pB, pC, publicSignals] = await generateCalldata(proofStruct);

expect(await smtVerifier.verifyProof(pA, pB, pC, publicSignals)).to.be.true;
});

context("when data is incorrect", () => {
let _console = console;

Expand Down

0 comments on commit 786b70d

Please sign in to comment.