UNPKG

@iden3/js-merkletree

Version:

javascript sparse merkle tree library

586 lines (585 loc) 24.6 kB
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _Merkletree_db, _Merkletree_root, _Merkletree_writable, _Merkletree_maxLevel; import { Hash, ZERO_HASH, circomSiblingsFromSiblings } from '../hash/hash'; import { NODE_TYPE_EMPTY, NODE_TYPE_LEAF, NODE_TYPE_MIDDLE } from '../../constants'; import { NodeEmpty, NodeLeaf, NodeMiddle } from '../node/node'; import { bytesEqual, getPath } from '../utils'; import { checkBigIntInField } from '../utils/crypto'; import { CircomProcessorProof, CircomVerifierProof } from './circom'; import { ErrEntryIndexAlreadyExists, ErrInvalidNodeFound, ErrKeyNotFound, ErrNotFound, ErrNotWritable, ErrReachedMaxLevel } from '../errors'; import { Proof } from './proof'; import { checkEntryInField } from '../entry'; export class Merkletree { constructor(_db, _writable, _maxLevels) { _Merkletree_db.set(this, void 0); _Merkletree_root.set(this, void 0); _Merkletree_writable.set(this, void 0); _Merkletree_maxLevel.set(this, void 0); __classPrivateFieldSet(this, _Merkletree_db, _db, "f"); __classPrivateFieldSet(this, _Merkletree_writable, _writable, "f"); __classPrivateFieldSet(this, _Merkletree_maxLevel, _maxLevels, "f"); } async root() { if (!__classPrivateFieldGet(this, _Merkletree_root, "f")) { __classPrivateFieldSet(this, _Merkletree_root, await __classPrivateFieldGet(this, _Merkletree_db, "f").getRoot(), "f"); } return __classPrivateFieldGet(this, _Merkletree_root, "f"); } get maxLevels() { return __classPrivateFieldGet(this, _Merkletree_maxLevel, "f"); } async add(k, v) { if (!__classPrivateFieldGet(this, _Merkletree_writable, "f")) { throw ErrNotWritable; } __classPrivateFieldSet(this, _Merkletree_root, await this.root(), "f"); const kHash = Hash.fromBigInt(k); const vHash = Hash.fromBigInt(v); const newNodeLeaf = new NodeLeaf(kHash, vHash); const path = getPath(this.maxLevels, kHash.value); const newRootKey = await this.addLeaf(newNodeLeaf, __classPrivateFieldGet(this, _Merkletree_root, "f"), 0, path); __classPrivateFieldSet(this, _Merkletree_root, newRootKey, "f"); await __classPrivateFieldGet(this, _Merkletree_db, "f").setRoot(__classPrivateFieldGet(this, _Merkletree_root, "f")); } async updateNode(n) { if (!__classPrivateFieldGet(this, _Merkletree_writable, "f")) { throw ErrNotWritable; } if (n.type === NODE_TYPE_EMPTY) { return await n.getKey(); } const k = await n.getKey(); await __classPrivateFieldGet(this, _Merkletree_db, "f").put(k.value, n); return k; } async addNode(n) { if (!__classPrivateFieldGet(this, _Merkletree_writable, "f")) { throw ErrNotWritable; } if (n.type === NODE_TYPE_EMPTY) { return await n.getKey(); } const k = await n.getKey(); // if (typeof this.#db.get(k.value) !== 'undefined') { // throw ErrNodeKeyAlreadyExists; // } await __classPrivateFieldGet(this, _Merkletree_db, "f").put(k.value, n); return k; } async addEntry(e) { if (!__classPrivateFieldGet(this, _Merkletree_writable, "f")) { throw ErrNotWritable; } if (!checkEntryInField(e)) { throw 'elements not inside the finite field over r'; } __classPrivateFieldSet(this, _Merkletree_root, await __classPrivateFieldGet(this, _Merkletree_db, "f").getRoot(), "f"); const hIndex = await e.hIndex(); const hValue = await e.hValue(); const newNodeLeaf = new NodeLeaf(hIndex, hValue); const path = getPath(this.maxLevels, hIndex.value); const newRootKey = await this.addLeaf(newNodeLeaf, __classPrivateFieldGet(this, _Merkletree_root, "f"), 0, path); __classPrivateFieldSet(this, _Merkletree_root, newRootKey, "f"); await __classPrivateFieldGet(this, _Merkletree_db, "f").setRoot(__classPrivateFieldGet(this, _Merkletree_root, "f")); } async pushLeaf(newLeaf, oldLeaf, lvl, pathNewLeaf, pathOldLeaf) { if (lvl > __classPrivateFieldGet(this, _Merkletree_maxLevel, "f") - 2) { throw new Error(ErrReachedMaxLevel); } let newNodeMiddle; if (pathNewLeaf[lvl] === pathOldLeaf[lvl]) { const nextKey = await this.pushLeaf(newLeaf, oldLeaf, lvl + 1, pathNewLeaf, pathOldLeaf); if (pathNewLeaf[lvl]) { newNodeMiddle = new NodeMiddle(new Hash(), nextKey); } else { newNodeMiddle = new NodeMiddle(nextKey, new Hash()); } return await this.addNode(newNodeMiddle); } const oldLeafKey = await oldLeaf.getKey(); const newLeafKey = await newLeaf.getKey(); if (pathNewLeaf[lvl]) { newNodeMiddle = new NodeMiddle(oldLeafKey, newLeafKey); } else { newNodeMiddle = new NodeMiddle(newLeafKey, oldLeafKey); } await this.addNode(newLeaf); return await this.addNode(newNodeMiddle); } async addLeaf(newLeaf, key, lvl, path) { if (lvl > __classPrivateFieldGet(this, _Merkletree_maxLevel, "f") - 1) { throw new Error(ErrReachedMaxLevel); } const n = await this.getNode(key); if (typeof n === 'undefined') { throw ErrNotFound; } switch (n.type) { case NODE_TYPE_EMPTY: return this.addNode(newLeaf); case NODE_TYPE_LEAF: { const nKey = n.entry[0]; const newLeafKey = newLeaf.entry[0]; if (bytesEqual(nKey.value, newLeafKey.value)) { throw ErrEntryIndexAlreadyExists; } const pathOldLeaf = getPath(this.maxLevels, nKey.value); return this.pushLeaf(newLeaf, n, lvl, path, pathOldLeaf); } case NODE_TYPE_MIDDLE: { n; let newNodeMiddle; if (path[lvl]) { const nextKey = await this.addLeaf(newLeaf, n.childR, lvl + 1, path); newNodeMiddle = new NodeMiddle(n.childL, nextKey); } else { const nextKey = await this.addLeaf(newLeaf, n.childL, lvl + 1, path); newNodeMiddle = new NodeMiddle(nextKey, n.childR); } return this.addNode(newNodeMiddle); } default: { throw ErrInvalidNodeFound; } } } async get(k) { const kHash = Hash.fromBigInt(k); const path = getPath(this.maxLevels, kHash.value); let nextKey = await this.root(); const siblings = []; for (let i = 0; i < this.maxLevels; i++) { const n = await this.getNode(nextKey); if (typeof n === 'undefined') { throw ErrKeyNotFound; } switch (n.type) { case NODE_TYPE_EMPTY: return { key: BigInt('0'), value: BigInt('0'), siblings }; case NODE_TYPE_LEAF: // if (bytesEqual(kHash.value, (n as NodeLeaf).entry[0].value)) { // return { // key: (n as NodeLeaf).entry[0].BigInt(), // value: (n as NodeLeaf).entry[1].BigInt(), // siblings, // }; // } return { key: n.entry[0].bigInt(), value: n.entry[1].bigInt(), siblings }; case NODE_TYPE_MIDDLE: if (path[i]) { nextKey = n.childR; siblings.push(n.childL); } else { nextKey = n.childL; siblings.push(n.childR); } break; default: throw ErrInvalidNodeFound; } } throw new Error(ErrReachedMaxLevel); } async update(k, v) { if (!__classPrivateFieldGet(this, _Merkletree_writable, "f")) { throw ErrNotWritable; } if (!checkBigIntInField(k)) { throw 'key not inside the finite field'; } if (!checkBigIntInField(v)) { throw 'key not inside the finite field'; } const kHash = Hash.fromBigInt(k); const vHash = Hash.fromBigInt(v); const path = getPath(this.maxLevels, kHash.value); const cp = new CircomProcessorProof(); cp.fnc = 1; cp.oldRoot = await this.root(); cp.oldKey = kHash; cp.newKey = kHash; cp.newValue = vHash; let nextKey = await this.root(); const siblings = []; for (let i = 0; i < this.maxLevels; i += 1) { const n = await this.getNode(nextKey); if (typeof n === 'undefined') { throw ErrNotFound; } switch (n.type) { case NODE_TYPE_EMPTY: throw ErrKeyNotFound; case NODE_TYPE_LEAF: if (bytesEqual(kHash.value, n.entry[0].value)) { cp.oldValue = n.entry[1]; cp.siblings = circomSiblingsFromSiblings([...siblings], this.maxLevels); const newNodeLeaf = new NodeLeaf(kHash, vHash); await this.updateNode(newNodeLeaf); const newRootKey = await this.recalculatePathUntilRoot(path, newNodeLeaf, siblings); __classPrivateFieldSet(this, _Merkletree_root, newRootKey, "f"); await __classPrivateFieldGet(this, _Merkletree_db, "f").setRoot(newRootKey); cp.newRoot = newRootKey; return cp; } break; case NODE_TYPE_MIDDLE: if (path[i]) { nextKey = n.childR; siblings.push(n.childL); } else { nextKey = n.childL; siblings.push(n.childR); } break; default: throw ErrInvalidNodeFound; } } throw ErrKeyNotFound; } async getNode(k) { if (bytesEqual(k.value, ZERO_HASH.value)) { return new NodeEmpty(); } return await __classPrivateFieldGet(this, _Merkletree_db, "f").get(k.value); } async recalculatePathUntilRoot(path, node, siblings) { for (let i = siblings.length - 1; i >= 0; i -= 1) { const nodeKey = await node.getKey(); if (path[i]) { node = new NodeMiddle(siblings[i], nodeKey); } else { node = new NodeMiddle(nodeKey, siblings[i]); } await this.addNode(node); } const nodeKey = await node.getKey(); return nodeKey; } // Delete removes the specified Key from the MerkleTree and updates the path // from the deleted key to the Root with the new values. This method removes // the key from the MerkleTree, but does not remove the old nodes from the // key-value database; this means that if the tree is accessed by an old Root // where the key was not deleted yet, the key will still exist. If is desired // to remove the key-values from the database that are not under the current // Root, an option could be to dump all the leaves (using mt.DumpLeafs) and // import them in a new MerkleTree in a new database (using // mt.ImportDumpedLeafs), but this will loose all the Root history of the // MerkleTree async delete(k) { if (!__classPrivateFieldGet(this, _Merkletree_writable, "f")) { throw ErrNotWritable; } const kHash = Hash.fromBigInt(k); const path = getPath(this.maxLevels, kHash.value); let nextKey = __classPrivateFieldGet(this, _Merkletree_root, "f"); const siblings = []; for (let i = 0; i < __classPrivateFieldGet(this, _Merkletree_maxLevel, "f"); i += 1) { const n = await this.getNode(nextKey); if (typeof n === 'undefined') { throw ErrNotFound; } switch (n.type) { case NODE_TYPE_EMPTY: throw ErrKeyNotFound; case NODE_TYPE_LEAF: if (bytesEqual(kHash.bytes, n.entry[0].value)) { await this.rmAndUpload(path, kHash, siblings); return; } throw ErrKeyNotFound; case NODE_TYPE_MIDDLE: if (path[i]) { nextKey = n.childR; siblings.push(n.childL); } else { nextKey = n.childL; siblings.push(n.childR); } break; default: throw ErrInvalidNodeFound; } } throw ErrKeyNotFound; } async rmAndUpload(path, kHash, siblings) { if (siblings.length === 0) { __classPrivateFieldSet(this, _Merkletree_root, ZERO_HASH, "f"); await __classPrivateFieldGet(this, _Merkletree_db, "f").setRoot(__classPrivateFieldGet(this, _Merkletree_root, "f")); return; } const toUpload = siblings[siblings.length - 1]; if (siblings.length < 2) { __classPrivateFieldSet(this, _Merkletree_root, siblings[0], "f"); await __classPrivateFieldGet(this, _Merkletree_db, "f").setRoot(__classPrivateFieldGet(this, _Merkletree_root, "f")); } const nearestSibling = await __classPrivateFieldGet(this, _Merkletree_db, "f").get(toUpload.bytes); if (nearestSibling?.type === NODE_TYPE_MIDDLE) { let newNode; if (path[siblings.length - 1]) { newNode = new NodeMiddle(toUpload, ZERO_HASH); } else { newNode = new NodeMiddle(ZERO_HASH, toUpload); } await this.addNode(newNode); const newRootKey = await this.recalculatePathUntilRoot(path, newNode, siblings.slice(0, siblings.length - 1)); __classPrivateFieldSet(this, _Merkletree_root, newRootKey, "f"); await __classPrivateFieldGet(this, _Merkletree_db, "f").setRoot(__classPrivateFieldGet(this, _Merkletree_root, "f")); return; } for (let i = siblings.length - 2; i >= 0; i -= 1) { if (!bytesEqual(siblings[i].value, ZERO_HASH.value)) { let newNode; if (path[i]) { newNode = new NodeMiddle(siblings[i], toUpload); } else { newNode = new NodeMiddle(toUpload, siblings[i]); } await this.addNode(newNode); const newRootKey = await this.recalculatePathUntilRoot(path, newNode, siblings.slice(0, i)); __classPrivateFieldSet(this, _Merkletree_root, newRootKey, "f"); await __classPrivateFieldGet(this, _Merkletree_db, "f").setRoot(__classPrivateFieldGet(this, _Merkletree_root, "f")); break; } if (i === 0) { __classPrivateFieldSet(this, _Merkletree_root, toUpload, "f"); await __classPrivateFieldGet(this, _Merkletree_db, "f").setRoot(__classPrivateFieldGet(this, _Merkletree_root, "f")); break; } } } async recWalk(key, f) { const n = await this.getNode(key); if (typeof n === 'undefined') { throw ErrNotFound; } switch (n.type) { case NODE_TYPE_EMPTY: await f(n); break; case NODE_TYPE_LEAF: await f(n); break; case NODE_TYPE_MIDDLE: await f(n); await this.recWalk(n.childL, f); await this.recWalk(n.childR, f); break; default: throw ErrInvalidNodeFound; } } async walk(rootKey, f) { if (bytesEqual(rootKey.value, ZERO_HASH.value)) { rootKey = await this.root(); } await this.recWalk(rootKey, f); } async generateCircomVerifierProof(k, rootKey) { const cp = await this.generateSCVerifierProof(k, rootKey); cp.siblings = circomSiblingsFromSiblings(cp.siblings, this.maxLevels); return cp; } async generateSCVerifierProof(k, rootKey) { if (bytesEqual(rootKey.value, ZERO_HASH.value)) { rootKey = await this.root(); } const { proof, value } = await this.generateProof(k, rootKey); const cp = new CircomVerifierProof(); cp.root = rootKey; cp.siblings = proof.allSiblings(); if (typeof proof.nodeAux !== 'undefined') { cp.oldKey = proof.nodeAux.key; cp.oldValue = proof.nodeAux.value; } else { cp.oldKey = ZERO_HASH; cp.oldValue = ZERO_HASH; } cp.key = Hash.fromBigInt(k); cp.value = Hash.fromBigInt(value); if (proof.existence) { cp.fnc = 0; } else { cp.fnc = 1; } return cp; } async generateProof(k, rootKey) { let siblingKey; const kHash = Hash.fromBigInt(k); const path = getPath(this.maxLevels, kHash.value); if (!rootKey) { rootKey = await this.root(); } let nextKey = rootKey; let depth = 0; let existence = false; const siblings = []; let nodeAux; for (depth = 0; depth < this.maxLevels; depth += 1) { const n = await this.getNode(nextKey); if (typeof n === 'undefined') { throw ErrNotFound; } switch (n.type) { case NODE_TYPE_EMPTY: return { proof: new Proof({ existence, nodeAux, siblings }), value: BigInt('0') }; case NODE_TYPE_LEAF: if (bytesEqual(kHash.value, n.entry[0].value)) { existence = true; return { proof: new Proof({ existence, nodeAux, siblings }), value: n.entry[1].bigInt() }; } nodeAux = { key: n.entry[0], value: n.entry[1] }; return { proof: new Proof({ existence, nodeAux, siblings }), value: n.entry[1].bigInt() }; case NODE_TYPE_MIDDLE: if (path[depth]) { nextKey = n.childR; siblingKey = n.childL; } else { nextKey = n.childL; siblingKey = n.childR; } break; default: throw ErrInvalidNodeFound; } siblings.push(siblingKey); } throw ErrKeyNotFound; } async addAndGetCircomProof(k, v) { const cp = new CircomProcessorProof(); cp.fnc = 2; cp.oldRoot = await this.root(); let key = BigInt('0'); let value = BigInt('0'); let siblings = []; try { const res = await this.get(k); key = res.key; value = res.value; siblings = res.siblings; } catch (err) { if (err !== ErrKeyNotFound) { throw err; } } if (typeof key === 'undefined' || typeof value === 'undefined') { throw 'key/value undefined'; } cp.oldKey = Hash.fromBigInt(key); cp.oldValue = Hash.fromBigInt(value); if (bytesEqual(cp.oldKey.value, ZERO_HASH.value)) { cp.isOld0 = true; } cp.siblings = circomSiblingsFromSiblings(siblings, this.maxLevels); await this.add(k, v); cp.newKey = Hash.fromBigInt(k); cp.newValue = Hash.fromBigInt(v); cp.newRoot = await this.root(); return cp; } // NOTE: for now it only prints to console, will be updated in future async graphViz(rootKey) { let cnt = 0; await this.walk(rootKey, async (n) => { const k = await n.getKey(); let lr; let emptyNodes; switch (n.type) { case NODE_TYPE_EMPTY: break; case NODE_TYPE_LEAF: // eslint-disable-next-line no-console console.log(`"${k.string()}" [style=filled]`); break; case NODE_TYPE_MIDDLE: lr = [n.childL.string(), n.childR.string()]; emptyNodes = ''; lr.forEach((s, i) => { if (s === '0') { lr[i] = `empty${cnt}`; emptyNodes += `"${lr[i]}" [style=dashed,label=0];\n`; cnt += 1; } }); // eslint-disable-next-line no-console console.log(`"${k.string()}" -> {"${lr[1]}"}`); // eslint-disable-next-line no-console console.log(emptyNodes); break; default: break; } }); // eslint-disable-next-line no-console console.log(`}\n`); } async printGraphViz(rootKey) { if (bytesEqual(rootKey.value, ZERO_HASH.value)) { rootKey = await this.root(); } // eslint-disable-next-line no-console console.log(`--------\nGraphViz of the MerkleTree with RootKey ${rootKey.bigInt().toString(10)}\n`); await this.graphViz(ZERO_HASH); // eslint-disable-next-line no-console console.log(`End of GraphViz of the MerkleTree with RootKey ${rootKey.bigInt().toString(10)}\n--------\n`); } } _Merkletree_db = new WeakMap(), _Merkletree_root = new WeakMap(), _Merkletree_writable = new WeakMap(), _Merkletree_maxLevel = new WeakMap();