SMT Update
This commit is contained in:
parent
e636a4ac83
commit
e02fd5edf8
@ -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 @@ template SMTInsert(nLevels) {
|
||||
|
||||
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 @@ template SMTInsert(nLevels) {
|
||||
|
||||
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;
|
@ -2,7 +2,7 @@
|
||||
|
||||
/******
|
||||
|
||||
SMTInsertLevel
|
||||
SMTProcessorLevel
|
||||
|
||||
This circuit has 2 has
|
||||
|
||||
@ -23,7 +23,7 @@ H' is the Hash function with the inputs shifted acordingly.
|
||||
*****/
|
||||
|
||||
|
||||
template SMTInsertLevel() {
|
||||
template SMTProcessorLevel() {
|
||||
signal input st_top;
|
||||
signal input st_old0;
|
||||
signal input st_bot;
|
@ -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 @@ The state of the level depends on the state of te botom level and on `xor` and
|
||||
`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 @@ fnc[0] fnc[1]
|
||||
|
||||
***************************************************************************************************/
|
||||
|
||||
template SMTInsertSM() {
|
||||
template SMTProcessorSM() {
|
||||
signal input xor;
|
||||
signal input is0;
|
||||
signal input levIns;
|
108
src/smt.js
108
src/smt.js
@ -37,6 +37,55 @@ class SMT {
|
||||
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 @@ class SMT {
|
||||
if (!resFind.found) throw new Error("Key does not exists");
|
||||
|
||||
const res = {
|
||||
sibblings: [],
|
||||
siblings: [],
|
||||
delKey: key,
|
||||
delValue: resFind.foundValue
|
||||
};
|
||||
@ -55,16 +104,15 @@ class SMT {
|
||||
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 @@ class SMT {
|
||||
|
||||
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 @@ class SMT {
|
||||
}
|
||||
|
||||
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 @@ class SMT {
|
||||
|
||||
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 @@ class SMT {
|
||||
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 @@ class SMT {
|
||||
|
||||
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 @@ class SMT {
|
||||
if (root.isZero()) {
|
||||
res = {
|
||||
found: false,
|
||||
sibblings: [],
|
||||
siblings: [],
|
||||
notFoundKey: key,
|
||||
notFoundValue: bigInt.zero,
|
||||
isOld0: true
|
||||
@ -230,14 +278,14 @@ class SMT {
|
||||
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 @@ class SMT {
|
||||
} 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;
|
||||
|
@ -1,3 +0,0 @@
|
||||
include "../../circuits/smt/smtinsert.circom";
|
||||
|
||||
component main = SMTInsert(10);
|
3
test/circuits/smtprocessor10_test.circom
Normal file
3
test/circuits/smtprocessor10_test.circom
Normal file
@ -0,0 +1,3 @@
|
||||
include "../../circuits/smt/smtprocessor.circom";
|
||||
|
||||
component main = SMTProcessor(10);
|
44
test/smt.js
44
test/smt.js
@ -16,7 +16,7 @@ function print(circuit, w, s) {
|
||||
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 testInsert(tree, key, value, circuit, log ) {
|
||||
|
||||
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 @@ async function testDelete(tree, key, circuit) {
|
||||
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 @@ describe("SMT test", function () {
|
||||
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 @@ describe("SMT test", function () {
|
||||
|
||||
});
|
||||
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 () => {
|
||||
|
@ -160,4 +160,23 @@ describe("SMT Javascript test", function () {
|
||||
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));
|
||||
});
|
||||
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user