arnaucube
arnaucube/circomlibPublic Self-hosted repository
SMT Update
Jordi Baylina jordi@baylina.cat
Fri, 14 Dec 2018 14:24:30 +0100
commit

e02fd5edf8976bfebe381d5b37ec190b81c73257

parent

e636a4ac8334acfdf4c9a7e5363296eca649aac9

M circuits/smt/smtinsert.circom → circuits/smt/smtprocessor.circom

@@ -110,11 +110,11 @@ include "../bitify.circom";

include "../comparators.circom"; include "../switcher.circom"; include "smtlevins.circom"; -include "smtinsertlevel.circom"; -include "smtinsertsm.circom"; +include "smtprocessorlevel.circom"; +include "smtprocessorsm.circom"; include "smthash.circom"; -template SMTInsert(nLevels) { +template SMTProcessor(nLevels) { signal input oldRoot; signal input newRoot; signal input siblings[nLevels];

@@ -156,7 +156,7 @@ }

component sm[nLevels]; for (var i=0; i<nLevels; i++) { - sm[i] = SMTInsertSM(); + sm[i] = SMTProcessorSM(); if (i==0) { sm[i].prev_top <== enabled; sm[i].prev_old0 <== 0;

@@ -182,7 +182,7 @@ sm[nLevels-1].st_na === 1;

component levels[nLevels]; for (var i=nLevels-1; i != -1; i--) { - levels[i] = SMTInsertLevel(); + levels[i] = SMTProcessorLevel(); levels[i].st_top <== sm[i].st_top; levels[i].st_old0 <== sm[i].st_old0;
M circuits/smt/smtinsertlevel.circom → circuits/smt/smtprocessorlevel.circom

@@ -2,7 +2,7 @@

/****** -SMTInsertLevel +SMTProcessorLevel This circuit has 2 has

@@ -23,7 +23,7 @@

*****/ -template SMTInsertLevel() { +template SMTProcessorLevel() { signal input st_top; signal input st_old0; signal input st_bot;
M circuits/smt/smtinsertsm.circom → circuits/smt/smtprocessorsm.circom

@@ -1,5 +1,5 @@

/*************************************************************************************************** -Each level on a SMTInsert has a state. +Each level on a SMTProcessor has a state. The state of the level depends on the state of te botom level and on `xor` and `is0` signals.

@@ -9,18 +9,18 @@

`xor` signal is 0 if the index bit at the current level is the same in the old and the new index, and 1 if it is different. -`is0` signal, is 1 if we are inserting in an empty leaf and 0 if we are inserting -in a leaf that contains an element. +`is0` signal, is 1 if we are inserting/deleting in an empty leaf and 0 if we +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 insert level, we go to old0 and old1 states +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 -new1: This level is reached when xor=1. Here is where we insert 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. -na: Not appliable. After inserting it, we go to the na level. +na: Not appliable. After processing it, we go to the na level. Fnction

@@ -72,7 +72,7 @@ xor=0

***************************************************************************************************/ -template SMTInsertSM() { +template SMTProcessorSM() { signal input xor; signal input is0; signal input levIns;
M src/smt.js → src/smt.js

@@ -37,6 +37,55 @@

return res; } + async update(_key, _newValue) { + const key = bigInt(_key); + const newValue = bigInt(_newValue); + + + const resFind = await this.find(key); + const res = {}; + res.oldRoot = this.root; + res.oldKey = key; + res.oldValue = resFind.foundValue; + res.newKey = key; + res.newValue = newValue; + res.siblings = resFind.siblings; + + const ins = []; + const dels = []; + + let rtOld = smtHash([1, key, resFind.foundValue]); + let rtNew = smtHash([1, key, newValue]); + ins.push([rtNew, [1, key, newValue ]]); + dels.push(rtOld); + + const keyBits = this._splitBits(key); + for (let level = resFind.siblings.length-1; level >=0; level--) { + let oldNode, newNode; + const sibling = resFind.siblings[level]; + if (keyBits[level]) { + oldNode = [sibling, rtOld]; + newNode = [sibling, rtNew]; + } else { + oldNode = [rtOld, sibling, ]; + newNode = [rtNew, sibling, ]; + } + rtOld = smtHash(oldNode); + rtNew = smtHash(newNode); + dels.push(rtOld); + ins.push([rtNew, newNode]); + } + + res.newRoot = rtNew; + + await this.db.multiIns(ins); + await this.db.setRoot(rtNew); + this.root = rtNew; + await this.db.multiDel(dels); + + return res; + } + async delete(_key) { const key = bigInt(_key);

@@ -44,7 +93,7 @@ const resFind = await this.find(key);

if (!resFind.found) throw new Error("Key does not exists"); const res = { - sibblings: [], + siblings: [], delKey: key, delValue: resFind.foundValue };

@@ -55,16 +104,15 @@ let rtOld = smtHash([1, key, resFind.foundValue]);

let rtNew; dels.push(rtOld); - let mixed; - if (resFind.sibblings.length > 0) { - const record = await this.db.get(resFind.sibblings[resFind.sibblings.length - 1]); + if (resFind.siblings.length > 0) { + const record = await this.db.get(resFind.siblings[resFind.siblings.length - 1]); if ((record.length == 3)&&(record[0].equals(bigInt.one))) { mixed = false; res.oldKey = record[1]; res.oldValue = record[2]; res.isOld0 = false; - rtNew = resFind.sibblings[resFind.sibblings.length - 1]; + rtNew = resFind.siblings[resFind.siblings.length - 1]; } else if (record.length == 2) { mixed = true; res.oldKey = key;

@@ -83,12 +131,12 @@ }

const keyBits = this._splitBits(key); - for (let level = resFind.sibblings.length-1; level >=0; level--) { - let newSibling = resFind.sibblings[level]; - if ((level == resFind.sibblings.length-1)&&(!res.isOld0)) { + for (let level = resFind.siblings.length-1; level >=0; level--) { + let newSibling = resFind.siblings[level]; + if ((level == resFind.siblings.length-1)&&(!res.isOld0)) { newSibling = bigInt.zero; } - const oldSibling = resFind.sibblings[level]; + const oldSibling = resFind.siblings[level]; if (keyBits[level]) { rtOld = smtHash([oldSibling, rtOld]); } else {

@@ -100,7 +148,7 @@ mixed = true;

} if (mixed) { - res.sibblings.unshift(resFind.sibblings[level]); + res.siblings.unshift(resFind.siblings[level]); let newNode; if (keyBits[level]) { newNode = [newSibling, rtNew];

@@ -137,19 +185,19 @@ const resFind = await this.find(key);

if (resFind.found) throw new Error("Key already exists"); - res.sibblings = resFind.sibblings; + res.siblings = resFind.siblings; let mixed; if (!resFind.isOld0) { const oldKeyits = this._splitBits(resFind.notFoundKey); - for (let i= res.sibblings.length; oldKeyits[i] == newKeyBits[i]; i++) { - res.sibblings.push(bigInt.zero); + for (let i= res.siblings.length; oldKeyits[i] == newKeyBits[i]; i++) { + res.siblings.push(bigInt.zero); } rtOld = smtHash([1, resFind.notFoundKey, resFind.notFoundValue]); - res.sibblings.push(rtOld); + res.siblings.push(rtOld); addedOne = true; mixed = false; - } else if (res.sibblings.length >0) { + } else if (res.siblings.length >0) { mixed = true; rtOld = bigInt.zero; }

@@ -160,12 +208,12 @@

let rt = smtHash([1, key, value]); inserts.push([rt,[1, key, value]] ); - for (let i=res.sibblings.length-1; i>=0; i--) { - if ((i<res.sibblings.length-1)&&(!res.sibblings[i].isZero())) { + for (let i=res.siblings.length-1; i>=0; i--) { + if ((i<res.siblings.length-1)&&(!res.siblings[i].isZero())) { mixed = true; } if (mixed) { - const oldSibling = resFind.sibblings[i]; + const oldSibling = resFind.siblings[i]; if (newKeyBits[i]) { rtOld = smtHash([oldSibling, rtOld]); } else {

@@ -177,18 +225,18 @@

let newRt; if (newKeyBits[i]) { - newRt = smtHash([res.sibblings[i], rt]); - inserts.push([newRt,[res.sibblings[i], rt]] ); + newRt = smtHash([res.siblings[i], rt]); + inserts.push([newRt,[res.siblings[i], rt]] ); } else { - newRt = smtHash([rt, res.sibblings[i]]); - inserts.push([newRt,[rt, res.sibblings[i]]] ); + newRt = smtHash([rt, res.siblings[i]]); + inserts.push([newRt,[rt, res.siblings[i]]] ); } rt = newRt; } - if (addedOne) res.sibblings.pop(); - while ((res.sibblings.length>0) && (res.sibblings[res.sibblings.length-1].isZero())) { - res.sibblings.pop(); + if (addedOne) res.siblings.pop(); + while ((res.siblings.length>0) && (res.siblings[res.siblings.length-1].isZero())) { + res.siblings.pop(); } res.oldKey = resFind.notFoundKey; res.oldValue = resFind.notFoundValue;

@@ -216,7 +264,7 @@ let res;

if (root.isZero()) { res = { found: false, - sibblings: [], + siblings: [], notFoundKey: key, notFoundValue: bigInt.zero, isOld0: true

@@ -230,14 +278,14 @@ if ((record.length==3)&&(record[0].equals(bigInt.one))) {

if (record[1].equals(key)) { res = { found: true, - sibblings: [], + siblings: [], foundValue: record[2], isOld0: false }; } else { res = { found: false, - sibblings: [], + siblings: [], notFoundKey: record[1], notFoundValue: record[2], isOld0: false

@@ -246,10 +294,10 @@ }

} else { if (keyBits[level] == 0) { res = await this._find(key, keyBits, record[0], level+1); - res.sibblings.unshift(record[1]); + res.siblings.unshift(record[1]); } else { res = await this._find(key, keyBits, record[1], level+1); - res.sibblings.unshift(record[0]); + res.siblings.unshift(record[0]); } } return res;
D test/circuits/smtinsert10_test.circom

@@ -1,3 +0,0 @@

-include "../../circuits/smt/smtinsert.circom"; - -component main = SMTInsert(10);
A test/circuits/smtprocessor10_test.circom

@@ -0,0 +1,3 @@

+include "../../circuits/smt/smtprocessor.circom"; + +component main = SMTProcessor(10);
M test/smt.js → test/smt.js

@@ -16,7 +16,7 @@

async function testInsert(tree, key, value, circuit, log ) { const res = await tree.insert(key,value); - let siblings = res.sibblings; + let siblings = res.siblings; while (siblings.length<10) siblings.push(bigInt(0)); const w = circuit.calculateWitness({

@@ -38,7 +38,7 @@ }

async function testDelete(tree, key, circuit) { const res = await tree.delete(key); - let siblings = res.sibblings; + let siblings = res.siblings; while (siblings.length<10) siblings.push(bigInt(0)); const w = circuit.calculateWitness({

@@ -59,6 +59,29 @@ assert(circuit.checkWitness(w));

assert(root1.equals(res.newRoot)); } +async function testUpdate(tree, key, newValue, circuit) { + const res = await tree.update(key, newValue); + let siblings = res.siblings; + while (siblings.length<10) siblings.push(bigInt(0)); + + const w = circuit.calculateWitness({ + fnc: [0,1], + oldRoot: res.oldRoot, + newRoot: res.newRoot, + siblings: siblings, + oldKey: res.oldKey, + oldValue: res.oldValue, + isOld0: 0, + newKey: res.newKey, + newValue: res.newValue + }); + + const root1 = w[circuit.getSignalIdx("main.topSwitcher.outR")]; + + assert(circuit.checkWitness(w)); + assert(root1.equals(res.newRoot)); +} + describe("SMT test", function () { let circuit;

@@ -67,11 +90,11 @@

this.timeout(100000); before( async () => { - const cirDef = await compiler(path.join(__dirname, "circuits", "smtinsert10_test.circom")); + const cirDef = await compiler(path.join(__dirname, "circuits", "smtprocessor10_test.circom")); circuit = new snarkjs.Circuit(cirDef); - console.log("NConstrains SMTInsert: " + circuit.nConstraints); + console.log("NConstrains SMTProcessor: " + circuit.nConstraints); tree = await smt.newMemEmptyTrie(); });

@@ -175,7 +198,20 @@ assert(circuit.checkWitness(w));

}); it("Should update an element", async () => { + const tree1 = await smt.newMemEmptyTrie(); + const tree2 = await smt.newMemEmptyTrie(); + await testInsert(tree1,8,88, circuit); + await testInsert(tree1,9,99, circuit); + await testInsert(tree1,32,3232, circuit); + + await testInsert(tree2,8,888, circuit); + await testInsert(tree2,9,999, circuit); + await testInsert(tree2,32,323232, circuit); + + await testUpdate(tree1, 8, 888, circuit); + await testUpdate(tree1, 9, 999, circuit); + await testUpdate(tree1, 32, 323232, circuit); }); it("Should verify existance of an element", async () => {
M test/smtjs.js → test/smtjs.js

@@ -160,4 +160,23 @@ assert(tree.root.isZero());

assert.equal(Object.keys(tree.db.nodes).length, 0); }); + it("Should test update", async () => { + const tree1 = await smt.newMemEmptyTrie(); + const tree2 = await smt.newMemEmptyTrie(); + + await tree1.insert(8,88); + await tree1.insert(9,99,); + await tree1.insert(32,3232); + + await tree2.insert(8,888); + await tree2.insert(9,999); + await tree2.insert(32,323232); + + await tree1.update(8, 888); + await tree1.update(9, 999); + await tree1.update(32, 323232); + + assert(tree1.root.equals(tree2.root)); + }); + });