Verification not working
This commit is contained in:
parent
e02fd5edf8
commit
09f36d1e4d
@ -64,4 +64,29 @@ template NOR() {
|
||||
out <== a*b + 1 - a - b;
|
||||
}
|
||||
|
||||
template MultiAND(n) {
|
||||
signal input in[n];
|
||||
signal output out;
|
||||
if (n==1) {
|
||||
out <== in[0];
|
||||
} else if (n==2) {
|
||||
component and1 = AND();
|
||||
and1.a <== in[0];
|
||||
and1.b <== in[1];
|
||||
out <== and1.out;
|
||||
} else {
|
||||
component and2 = AND();
|
||||
component ands[2];
|
||||
var n1 = n\2;
|
||||
var n2 = n-n\2;
|
||||
ands[0] = MultiAND(n1);
|
||||
ands[1] = MultiAND(n2);
|
||||
for (var i=0; i<n1; i++) ands[0].in[i] <== in[i];
|
||||
for (var i=0; i<n2; i++) ands[1].in[i] <== in[n1+i];
|
||||
and2.a <== ands[0].out;
|
||||
and2.b <== ands[1].out;
|
||||
out <== and2.out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
/***************************************************************************************************
|
||||
|
||||
SMTProcessor: Sparse Merkle Tree processor is a component to verify an insert/update/delete elements
|
||||
into the Sparse Merkle tree.
|
||||
|
||||
|
||||
Insert to an empty leaf
|
||||
=======================
|
||||
|
||||
@ -213,4 +217,16 @@ template SMTProcessor(nLevels) {
|
||||
|
||||
topSwitcher.outL === oldRoot*enabled;
|
||||
topSwitcher.outR === newRoot*enabled;
|
||||
|
||||
// Ckeck keys are equal if updating
|
||||
component areKeyEquals = IsEqual();
|
||||
areKeyEquals.in[0] <== oldKey;
|
||||
areKeyEquals.in[1] <== newKey;
|
||||
|
||||
component keysOk = MultiAND(3);
|
||||
keysOk.in[0] <== 1-fnc[0];
|
||||
keysOk.in[1] <== fnc[1];
|
||||
keysOk.in[2] <== 1-areKeyEquals.out;
|
||||
|
||||
keysOk.out === 0;
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
|
||||
|
||||
/******
|
||||
|
||||
SMTProcessorLevel
|
||||
|
||||
This circuit has 2 has
|
||||
This circuit has 2 hash
|
||||
|
||||
Outputs according to the state.
|
||||
|
||||
|
@ -15,9 +15,9 @@ are inserting/deleting in a leaf that contains an element.
|
||||
The states are:
|
||||
|
||||
top: While the index bits of the old and new insex in the top level is the same, whe are in the top state.
|
||||
old0 and old1: When the we reach processor level, we go to old0 and old1 states
|
||||
according to `is0` signal.
|
||||
btn: Once in old1 we go to btn until xor=1
|
||||
old0: When the we reach insert level, we go to old0 state
|
||||
if `is0`=1.
|
||||
btn: Once in insert level and `is0` =0 we go to btn or new1 level if xor=1
|
||||
new1: This level is reached when xor=1. Here is where we insert/delete the hash of the
|
||||
old and the new trees with just one element.
|
||||
na: Not appliable. After processing it, we go to the na level.
|
||||
|
117
circuits/smt/smtverifier.circom
Normal file
117
circuits/smt/smtverifier.circom
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
|
||||
SMTVerifier is a component to verify inclusion/exclusion of an element in the tree
|
||||
|
||||
|
||||
fnc: 0 -> VERIFY INCLUSION
|
||||
1 -> VERIFY NOT INCLUSION
|
||||
|
||||
*/
|
||||
|
||||
|
||||
include "../gates.circom";
|
||||
include "../bitify.circom";
|
||||
include "../comparators.circom";
|
||||
include "../switcher.circom";
|
||||
include "smtlevins.circom";
|
||||
include "smtverifierlevel.circom";
|
||||
include "smtverifiersm.circom";
|
||||
include "smthash.circom";
|
||||
|
||||
template SMTVerifier(nLevels) {
|
||||
signal input root;
|
||||
signal input siblings[nLevels];
|
||||
signal input oldKey;
|
||||
signal input oldValue;
|
||||
signal input isOld0;
|
||||
signal input key;
|
||||
signal input value;
|
||||
signal input fnc;
|
||||
|
||||
component hash1Old = SMTHash1();
|
||||
hash1Old.key <== oldKey;
|
||||
hash1Old.value <== oldValue;
|
||||
|
||||
component hash1New = SMTHash1();
|
||||
hash1New.key <== key;
|
||||
hash1New.value <== value;
|
||||
|
||||
component n2bOld = Num2Bits_strict();
|
||||
component n2bNew = Num2Bits_strict();
|
||||
|
||||
n2bOld.in <== oldKey;
|
||||
n2bNew.in <== key;
|
||||
|
||||
component smtLevIns = SMTLevIns(nLevels);
|
||||
for (var i=0; i<nLevels; i++) smtLevIns.siblings[i] <== siblings[i];
|
||||
smtLevIns.enabled <== 1;
|
||||
|
||||
component xors[nLevels];
|
||||
for (var i=0; i<nLevels; i++) {
|
||||
xors[i] = XOR();
|
||||
xors[i].a <== n2bOld.out[i];
|
||||
xors[i].b <== n2bNew.out[i];
|
||||
}
|
||||
|
||||
component sm[nLevels];
|
||||
for (var i=0; i<nLevels; i++) {
|
||||
sm[i] = SMTVerifierSM();
|
||||
if (i==0) {
|
||||
sm[i].prev_top <== 1;
|
||||
sm[i].prev_i0 <== 0;
|
||||
sm[i].prev_inew <== 0;
|
||||
sm[i].prev_iold <== 0;
|
||||
sm[i].prev_na <== 0;
|
||||
} else {
|
||||
sm[i].prev_top <== sm[i-1].st_top;
|
||||
sm[i].prev_i0 <== sm[i-1].st_i0;
|
||||
sm[i].prev_inew <== sm[i-1].st_inew;
|
||||
sm[i].prev_iold <== sm[i-1].st_iold;
|
||||
sm[i].prev_na <== sm[i-1].st_na;
|
||||
}
|
||||
sm[i].is0 <== isOld0;
|
||||
sm[i].xor <== xors[i].out;
|
||||
sm[i].fnc <== fnc;
|
||||
sm[i].levIns <== smtLevIns.levIns[i];
|
||||
}
|
||||
// sm[nLevels-1].st_na === 1;
|
||||
|
||||
component levels[nLevels];
|
||||
for (var i=nLevels-1; i != -1; i--) {
|
||||
levels[i] = SMTVerifierLevel();
|
||||
|
||||
levels[i].st_top <== sm[i].st_top;
|
||||
levels[i].st_i0 <== sm[i].st_i0;
|
||||
levels[i].st_inew <== sm[i].st_inew;
|
||||
levels[i].st_iold <== sm[i].st_iold;
|
||||
levels[i].st_na <== sm[i].st_na;
|
||||
|
||||
levels[i].sibling <== siblings[i];
|
||||
levels[i].old1leaf <== hash1Old.out;
|
||||
levels[i].new1leaf <== hash1New.out;
|
||||
|
||||
levels[i].lrbit <== n2bNew.out[i];
|
||||
if (i==nLevels-1) {
|
||||
levels[i].child <== 0;
|
||||
} else {
|
||||
levels[i].child <== levels[i+1].root;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check that if checking for non inclussuin and isOld0==0 then key!=old
|
||||
component areKeyEquals = IsEqual();
|
||||
areKeyEquals.in[0] <== oldKey;
|
||||
areKeyEquals.in[1] <== key;
|
||||
|
||||
component keysOk = MultiAND(3);
|
||||
keysOk.in[0] <== fnc;
|
||||
keysOk.in[1] <== 1-isOld0;
|
||||
keysOk.in[2] <== areKeyEquals.out;
|
||||
|
||||
keysOk.out === 0;
|
||||
|
||||
// Check the roots
|
||||
levels[0].root === root;
|
||||
|
||||
}
|
52
circuits/smt/smtverifierlevel.circom
Normal file
52
circuits/smt/smtverifierlevel.circom
Normal file
@ -0,0 +1,52 @@
|
||||
/******
|
||||
|
||||
SMTVerifierLevel
|
||||
|
||||
This circuit has 1 hash
|
||||
|
||||
Outputs according to the state.
|
||||
|
||||
State root
|
||||
===== =======
|
||||
top H'(child, sibling)
|
||||
i0 0
|
||||
iold old1leaf
|
||||
inew new1leaf
|
||||
na 0
|
||||
|
||||
H' is the Hash function with the inputs shifted acordingly.
|
||||
|
||||
*****/
|
||||
|
||||
|
||||
template SMTVerifierLevel() {
|
||||
signal input st_top;
|
||||
signal input st_i0;
|
||||
signal input st_iold;
|
||||
signal input st_inew;
|
||||
signal input st_na;
|
||||
|
||||
signal output root;
|
||||
signal input sibling;
|
||||
signal input old1leaf;
|
||||
signal input new1leaf;
|
||||
signal input lrbit;
|
||||
signal input child;
|
||||
|
||||
signal aux[2];
|
||||
|
||||
component proofHash = SMTHash2();
|
||||
component switcher = Switcher();
|
||||
|
||||
switcher.L <== child;
|
||||
switcher.R <== sibling;
|
||||
|
||||
switcher.sel <== lrbit;
|
||||
proofHash.L <== switcher.outL;
|
||||
proofHash.R <== switcher.outR;
|
||||
|
||||
aux[0] <== proofHash.out * st_top;
|
||||
aux[1] <== old1leaf*st_iold;
|
||||
|
||||
root <== aux[0] + aux[1] + new1leaf*st_inew;
|
||||
}
|
97
circuits/smt/smtverifiersm.circom
Normal file
97
circuits/smt/smtverifiersm.circom
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
Each level in the SMTVerifier has a state.
|
||||
|
||||
This is the state machine.
|
||||
|
||||
The signals are
|
||||
|
||||
levIns: 1 if we are in the level where the insertion should happen
|
||||
xor: 1 if the bitKey of the old and new keys are different in this level
|
||||
is0: Input that indicates that the oldKey is 0
|
||||
fnc: 0 -> VERIFY INCLUSION
|
||||
1 -> VERIFY NOT INCLUSION
|
||||
|
||||
err state is not a state itself. It's a lack of state.
|
||||
|
||||
The end of the last level will have to be `na`
|
||||
|
||||
levIns=0 ###########
|
||||
xor=1 # #
|
||||
fnc=1 ┌──────────▶# err #
|
||||
│ ## ##
|
||||
levIns=0 │ #########
|
||||
xor=0 || fnc=0 │ any
|
||||
┌────┐ │ ┌────┐
|
||||
│ │ │ │ │
|
||||
│ ▼ │ levIns=1 ▼ │
|
||||
│ ########### │ is0=1 ########### ########### │
|
||||
│ # # ───────────┘ fnc=1 # # any # # │
|
||||
└──# top # ─────────────────────▶# i0 #───────────────▶# na #──┘
|
||||
## ## ──────────┐ ## ## ┌───────▶## ##
|
||||
########─────────────┐│ ######### │┌────────▶#########
|
||||
││ levIns=1 ││
|
||||
││ is0=0 ########### ││
|
||||
││ fnc=1 # # any│
|
||||
│└──────────▶ # iold #────────┘│
|
||||
│ ## ## │
|
||||
│ ######### │
|
||||
│ │
|
||||
│ levIns=1 ########### │
|
||||
│ fnc=0 # # any
|
||||
└────────────▶# inew #─────────┘
|
||||
## ##
|
||||
#########
|
||||
|
||||
*/
|
||||
|
||||
|
||||
template SMTVerifierSM() {
|
||||
signal input xor;
|
||||
signal input is0;
|
||||
signal input levIns;
|
||||
signal input fnc;
|
||||
|
||||
signal input prev_top;
|
||||
signal input prev_i0;
|
||||
signal input prev_iold;
|
||||
signal input prev_inew;
|
||||
signal input prev_na;
|
||||
|
||||
signal output st_top;
|
||||
signal output st_i0;
|
||||
signal output st_iold;
|
||||
signal output st_inew;
|
||||
signal output st_na;
|
||||
|
||||
signal prev_top_lev_ins;
|
||||
signal prev_top_lev_ins_fnc;
|
||||
signal xor_fnc;
|
||||
|
||||
prev_top_lev_ins <== prev_top * levIns;
|
||||
prev_top_lev_ins_fnc <== prev_top_lev_ins*fnc; // prev_top * levIns * fnc
|
||||
xor_fnc <== xor*fnc;
|
||||
|
||||
|
||||
// st_top = prev_top * (1-levIns) * (1 - xor*fnc)
|
||||
// = + prev_top
|
||||
// - prev_top * levIns
|
||||
// - prev_top * xor * fnc
|
||||
// + prev_top * levIns * xor * fnc
|
||||
st_top <== (prev_top - prev_top_lev_ins)*(1-xor_fnc);
|
||||
|
||||
// st_inew = prev_top * levIns * (1-fnc)
|
||||
// = + prev_top * levIns
|
||||
// - prev_top * levIns * fnc
|
||||
st_inew <== prev_top_lev_ins - prev_top_lev_ins_fnc;
|
||||
|
||||
// st_iold = prev_top * levIns * (1-is0)*fnc
|
||||
// = + prev_top * levIns * fnc
|
||||
// - prev_top * levIns * fnc * is0
|
||||
st_iold <== prev_top_lev_ins_fnc * (1 - is0);
|
||||
|
||||
// st_i0 = prev_top * levIns * is0
|
||||
// = + prev_top * levIns * is0
|
||||
st_i0 <== prev_top_lev_ins * is0;
|
||||
|
||||
st_na <== prev_na + prev_inew + prev_iold + prev_i0;
|
||||
}
|
BIN
doc/smt_diagram_0.monopic
Normal file
BIN
doc/smt_diagram_0.monopic
Normal file
Binary file not shown.
BIN
doc/smt_diagram_1.monopic
Normal file
BIN
doc/smt_diagram_1.monopic
Normal file
Binary file not shown.
BIN
doc/smt_levins.monopic
Normal file
BIN
doc/smt_levins.monopic
Normal file
Binary file not shown.
BIN
doc/smt_sm.monopic
Normal file
BIN
doc/smt_sm.monopic
Normal file
Binary file not shown.
BIN
doc/smt_verifier_sm.monopic
Normal file
BIN
doc/smt_verifier_sm.monopic
Normal file
Binary file not shown.
3
test/circuits/smtverifier10_test.circom
Normal file
3
test/circuits/smtverifier10_test.circom
Normal file
@ -0,0 +1,3 @@
|
||||
include "../../circuits/smt/smtverifier.circom";
|
||||
|
||||
component main = SMTVerifier(10);
|
@ -24,8 +24,8 @@ async function testInsert(tree, key, value, circuit, log ) {
|
||||
oldRoot: res.oldRoot,
|
||||
newRoot: res.newRoot,
|
||||
siblings: siblings,
|
||||
oldKey: res.oldKey,
|
||||
oldValue: res.oldValue,
|
||||
oldKey: res.isOld0 ? 0 : res.oldKey,
|
||||
oldValue: res.isOld0 ? 0 : res.oldValue,
|
||||
isOld0: res.isOld0 ? 1 : 0,
|
||||
newKey: key,
|
||||
newValue: value
|
||||
@ -46,8 +46,8 @@ async function testDelete(tree, key, circuit) {
|
||||
oldRoot: res.oldRoot,
|
||||
newRoot: res.newRoot,
|
||||
siblings: siblings,
|
||||
oldKey: res.oldKey,
|
||||
oldValue: res.oldValue,
|
||||
oldKey: res.isOld0 ? 0 : res.oldKey,
|
||||
oldValue: res.isOld0 ? 0 : res.oldValue,
|
||||
isOld0: res.isOld0 ? 1 : 0,
|
||||
newKey: res.delKey,
|
||||
newValue: res.delValue
|
||||
@ -214,12 +214,4 @@ describe("SMT test", function () {
|
||||
await testUpdate(tree1, 32, 323232, circuit);
|
||||
});
|
||||
|
||||
it("Should verify existance of an element", async () => {
|
||||
|
||||
});
|
||||
|
||||
it("Should verify non existance of an element", async () => {
|
||||
|
||||
});
|
||||
|
||||
});
|
95
test/smtverifier.js
Normal file
95
test/smtverifier.js
Normal file
@ -0,0 +1,95 @@
|
||||
const chai = require("chai");
|
||||
const path = require("path");
|
||||
const snarkjs = require("snarkjs");
|
||||
const compiler = require("circom");
|
||||
|
||||
const smt = require("../src/smt.js");
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
const bigInt = snarkjs.bigInt;
|
||||
|
||||
function print(circuit, w, s) {
|
||||
console.log(s + ": " + w[circuit.getSignalIdx(s)]);
|
||||
}
|
||||
|
||||
async function testInclusion(tree, key, circuit) {
|
||||
|
||||
const res = await tree.find(key);
|
||||
|
||||
assert(res.found);
|
||||
let siblings = res.siblings;
|
||||
while (siblings.length<10) siblings.push(bigInt(0));
|
||||
|
||||
const w = circuit.calculateWitness({
|
||||
fnc: 0,
|
||||
root: tree.root,
|
||||
siblings: siblings,
|
||||
oldKey: 0,
|
||||
oldValue: 0,
|
||||
isOld0: 0,
|
||||
key: key,
|
||||
value: res.foundValue
|
||||
});
|
||||
|
||||
assert(circuit.checkWitness(w));
|
||||
}
|
||||
|
||||
async function testExclusion(tree, key, circuit) {
|
||||
const res = await tree.find(key);
|
||||
|
||||
assert(!res.found);
|
||||
let siblings = res.siblings;
|
||||
while (siblings.length<10) siblings.push(bigInt(0));
|
||||
|
||||
const w = circuit.calculateWitness({
|
||||
fnc: 1,
|
||||
root: tree.root,
|
||||
siblings: siblings,
|
||||
oldKey: res.isOld0 ? 0 : res.notFoundKey,
|
||||
oldValue: res.isOld0 ? 0 : res.notFoundValue,
|
||||
isOld0: res.isOld0 ? 1 : 0,
|
||||
key: key,
|
||||
value: 0
|
||||
}, console.log);
|
||||
|
||||
assert(circuit.checkWitness(w));
|
||||
}
|
||||
|
||||
describe("SMT test", function () {
|
||||
let circuit;
|
||||
let tree;
|
||||
|
||||
this.timeout(100000);
|
||||
|
||||
before( async () => {
|
||||
const cirDef = await compiler(path.join(__dirname, "circuits", "smtverifier10_test.circom"));
|
||||
|
||||
circuit = new snarkjs.Circuit(cirDef);
|
||||
|
||||
console.log("NConstrains SMTVerifier: " + circuit.nConstraints);
|
||||
|
||||
tree = await smt.newMemEmptyTrie();
|
||||
await tree.insert(7,77);
|
||||
await tree.insert(8,88);
|
||||
await tree.insert(32,3232);
|
||||
});
|
||||
|
||||
it("Check inclussion in a tree of 3", async () => {
|
||||
await testInclusion(tree, 7, circuit);
|
||||
await testInclusion(tree, 8, circuit);
|
||||
await testInclusion(tree, 32, circuit);
|
||||
});
|
||||
|
||||
it("Check exclussion in a tree of 3", async () => {
|
||||
// await testExclusion(tree, 0, circuit);
|
||||
await testExclusion(tree, 6, circuit);
|
||||
/* await testExclusion(tree, 9, circuit);
|
||||
await testExclusion(tree, 33, circuit);
|
||||
await testExclusion(tree, 31, circuit);
|
||||
await testExclusion(tree, 16, circuit);
|
||||
await testExclusion(tree, 64, circuit); */
|
||||
});
|
||||
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user