Verification not working

This commit is contained in:
Jordi Baylina 2018-12-15 09:00:35 +01:00
parent e02fd5edf8
commit 09f36d1e4d
No known key found for this signature in database
GPG Key ID: 7480C80C1BE43112
16 changed files with 61959 additions and 18 deletions

@ -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.

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

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

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

Binary file not shown.

BIN
doc/smt_diagram_1.monopic Normal file

Binary file not shown.

BIN
doc/smt_levins.monopic Normal file

Binary file not shown.

BIN
doc/smt_sm.monopic Normal file

Binary file not shown.

BIN
doc/smt_verifier_sm.monopic Normal file

Binary file not shown.

61546
err.sig Normal file

File diff suppressed because it is too large Load Diff

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

@ -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); */
});
});