Verification not working
This commit is contained in:
parent
e02fd5edf8
commit
09f36d1e4d
@ -64,4 +64,29 @@ template NOR() {
|
|||||||
out <== a*b + 1 - a - b;
|
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
|
Insert to an empty leaf
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
@ -213,4 +217,16 @@ template SMTProcessor(nLevels) {
|
|||||||
|
|
||||||
topSwitcher.outL === oldRoot*enabled;
|
topSwitcher.outL === oldRoot*enabled;
|
||||||
topSwitcher.outR === newRoot*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
|
SMTProcessorLevel
|
||||||
|
|
||||||
This circuit has 2 has
|
This circuit has 2 hash
|
||||||
|
|
||||||
Outputs according to the state.
|
Outputs according to the state.
|
||||||
|
|
||||||
|
@ -15,9 +15,9 @@ are inserting/deleting in a leaf that contains an element.
|
|||||||
The states are:
|
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.
|
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
|
old0: When the we reach insert level, we go to old0 state
|
||||||
according to `is0` signal.
|
if `is0`=1.
|
||||||
btn: Once in old1 we go to btn until xor=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
|
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.
|
old and the new trees with just one element.
|
||||||
na: Not appliable. After processing it, we go to the na level.
|
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,
|
oldRoot: res.oldRoot,
|
||||||
newRoot: res.newRoot,
|
newRoot: res.newRoot,
|
||||||
siblings: siblings,
|
siblings: siblings,
|
||||||
oldKey: res.oldKey,
|
oldKey: res.isOld0 ? 0 : res.oldKey,
|
||||||
oldValue: res.oldValue,
|
oldValue: res.isOld0 ? 0 : res.oldValue,
|
||||||
isOld0: res.isOld0 ? 1 : 0,
|
isOld0: res.isOld0 ? 1 : 0,
|
||||||
newKey: key,
|
newKey: key,
|
||||||
newValue: value
|
newValue: value
|
||||||
@ -46,8 +46,8 @@ async function testDelete(tree, key, circuit) {
|
|||||||
oldRoot: res.oldRoot,
|
oldRoot: res.oldRoot,
|
||||||
newRoot: res.newRoot,
|
newRoot: res.newRoot,
|
||||||
siblings: siblings,
|
siblings: siblings,
|
||||||
oldKey: res.oldKey,
|
oldKey: res.isOld0 ? 0 : res.oldKey,
|
||||||
oldValue: res.oldValue,
|
oldValue: res.isOld0 ? 0 : res.oldValue,
|
||||||
isOld0: res.isOld0 ? 1 : 0,
|
isOld0: res.isOld0 ? 1 : 0,
|
||||||
newKey: res.delKey,
|
newKey: res.delKey,
|
||||||
newValue: res.delValue
|
newValue: res.delValue
|
||||||
@ -214,12 +214,4 @@ describe("SMT test", function () {
|
|||||||
await testUpdate(tree1, 32, 323232, circuit);
|
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