UNPKG

@iden3/js-merkletree

Version:

javascript sparse merkle tree library

590 lines (589 loc) 25.6 kB
"use strict"; 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; Object.defineProperty(exports, "__esModule", { value: true }); exports.Merkletree = void 0; const hash_1 = require("../hash/hash"); const constants_1 = require("../../constants"); const node_1 = require("../node/node"); const utils_1 = require("../utils"); const crypto_1 = require("../utils/crypto"); const circom_1 = require("./circom"); const errors_1 = require("../errors"); const proof_1 = require("./proof"); const entry_1 = require("../entry"); 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 errors_1.ErrNotWritable; } __classPrivateFieldSet(this, _Merkletree_root, await this.root(), "f"); const kHash = hash_1.Hash.fromBigInt(k); const vHash = hash_1.Hash.fromBigInt(v); const newNodeLeaf = new node_1.NodeLeaf(kHash, vHash); const path = (0, utils_1.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 errors_1.ErrNotWritable; } if (n.type === constants_1.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 errors_1.ErrNotWritable; } if (n.type === constants_1.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 errors_1.ErrNotWritable; } if (!(0, entry_1.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 node_1.NodeLeaf(hIndex, hValue); const path = (0, utils_1.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(errors_1.ErrReachedMaxLevel); } let newNodeMiddle; if (pathNewLeaf[lvl] === pathOldLeaf[lvl]) { const nextKey = await this.pushLeaf(newLeaf, oldLeaf, lvl + 1, pathNewLeaf, pathOldLeaf); if (pathNewLeaf[lvl]) { newNodeMiddle = new node_1.NodeMiddle(new hash_1.Hash(), nextKey); } else { newNodeMiddle = new node_1.NodeMiddle(nextKey, new hash_1.Hash()); } return await this.addNode(newNodeMiddle); } const oldLeafKey = await oldLeaf.getKey(); const newLeafKey = await newLeaf.getKey(); if (pathNewLeaf[lvl]) { newNodeMiddle = new node_1.NodeMiddle(oldLeafKey, newLeafKey); } else { newNodeMiddle = new node_1.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(errors_1.ErrReachedMaxLevel); } const n = await this.getNode(key); if (typeof n === 'undefined') { throw errors_1.ErrNotFound; } switch (n.type) { case constants_1.NODE_TYPE_EMPTY: return this.addNode(newLeaf); case constants_1.NODE_TYPE_LEAF: { const nKey = n.entry[0]; const newLeafKey = newLeaf.entry[0]; if ((0, utils_1.bytesEqual)(nKey.value, newLeafKey.value)) { throw errors_1.ErrEntryIndexAlreadyExists; } const pathOldLeaf = (0, utils_1.getPath)(this.maxLevels, nKey.value); return this.pushLeaf(newLeaf, n, lvl, path, pathOldLeaf); } case constants_1.NODE_TYPE_MIDDLE: { n; let newNodeMiddle; if (path[lvl]) { const nextKey = await this.addLeaf(newLeaf, n.childR, lvl + 1, path); newNodeMiddle = new node_1.NodeMiddle(n.childL, nextKey); } else { const nextKey = await this.addLeaf(newLeaf, n.childL, lvl + 1, path); newNodeMiddle = new node_1.NodeMiddle(nextKey, n.childR); } return this.addNode(newNodeMiddle); } default: { throw errors_1.ErrInvalidNodeFound; } } } async get(k) { const kHash = hash_1.Hash.fromBigInt(k); const path = (0, utils_1.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 errors_1.ErrKeyNotFound; } switch (n.type) { case constants_1.NODE_TYPE_EMPTY: return { key: BigInt('0'), value: BigInt('0'), siblings }; case constants_1.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 constants_1.NODE_TYPE_MIDDLE: if (path[i]) { nextKey = n.childR; siblings.push(n.childL); } else { nextKey = n.childL; siblings.push(n.childR); } break; default: throw errors_1.ErrInvalidNodeFound; } } throw new Error(errors_1.ErrReachedMaxLevel); } async update(k, v) { if (!__classPrivateFieldGet(this, _Merkletree_writable, "f")) { throw errors_1.ErrNotWritable; } if (!(0, crypto_1.checkBigIntInField)(k)) { throw 'key not inside the finite field'; } if (!(0, crypto_1.checkBigIntInField)(v)) { throw 'key not inside the finite field'; } const kHash = hash_1.Hash.fromBigInt(k); const vHash = hash_1.Hash.fromBigInt(v); const path = (0, utils_1.getPath)(this.maxLevels, kHash.value); const cp = new circom_1.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 errors_1.ErrNotFound; } switch (n.type) { case constants_1.NODE_TYPE_EMPTY: throw errors_1.ErrKeyNotFound; case constants_1.NODE_TYPE_LEAF: if ((0, utils_1.bytesEqual)(kHash.value, n.entry[0].value)) { cp.oldValue = n.entry[1]; cp.siblings = (0, hash_1.circomSiblingsFromSiblings)([...siblings], this.maxLevels); const newNodeLeaf = new node_1.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 constants_1.NODE_TYPE_MIDDLE: if (path[i]) { nextKey = n.childR; siblings.push(n.childL); } else { nextKey = n.childL; siblings.push(n.childR); } break; default: throw errors_1.ErrInvalidNodeFound; } } throw errors_1.ErrKeyNotFound; } async getNode(k) { if ((0, utils_1.bytesEqual)(k.value, hash_1.ZERO_HASH.value)) { return new node_1.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 node_1.NodeMiddle(siblings[i], nodeKey); } else { node = new node_1.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 errors_1.ErrNotWritable; } const kHash = hash_1.Hash.fromBigInt(k); const path = (0, utils_1.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 errors_1.ErrNotFound; } switch (n.type) { case constants_1.NODE_TYPE_EMPTY: throw errors_1.ErrKeyNotFound; case constants_1.NODE_TYPE_LEAF: if ((0, utils_1.bytesEqual)(kHash.bytes, n.entry[0].value)) { await this.rmAndUpload(path, kHash, siblings); return; } throw errors_1.ErrKeyNotFound; case constants_1.NODE_TYPE_MIDDLE: if (path[i]) { nextKey = n.childR; siblings.push(n.childL); } else { nextKey = n.childL; siblings.push(n.childR); } break; default: throw errors_1.ErrInvalidNodeFound; } } throw errors_1.ErrKeyNotFound; } async rmAndUpload(path, kHash, siblings) { if (siblings.length === 0) { __classPrivateFieldSet(this, _Merkletree_root, hash_1.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 === constants_1.NODE_TYPE_MIDDLE) { let newNode; if (path[siblings.length - 1]) { newNode = new node_1.NodeMiddle(toUpload, hash_1.ZERO_HASH); } else { newNode = new node_1.NodeMiddle(hash_1.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 (!(0, utils_1.bytesEqual)(siblings[i].value, hash_1.ZERO_HASH.value)) { let newNode; if (path[i]) { newNode = new node_1.NodeMiddle(siblings[i], toUpload); } else { newNode = new node_1.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 errors_1.ErrNotFound; } switch (n.type) { case constants_1.NODE_TYPE_EMPTY: await f(n); break; case constants_1.NODE_TYPE_LEAF: await f(n); break; case constants_1.NODE_TYPE_MIDDLE: await f(n); await this.recWalk(n.childL, f); await this.recWalk(n.childR, f); break; default: throw errors_1.ErrInvalidNodeFound; } } async walk(rootKey, f) { if ((0, utils_1.bytesEqual)(rootKey.value, hash_1.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 = (0, hash_1.circomSiblingsFromSiblings)(cp.siblings, this.maxLevels); return cp; } async generateSCVerifierProof(k, rootKey) { if ((0, utils_1.bytesEqual)(rootKey.value, hash_1.ZERO_HASH.value)) { rootKey = await this.root(); } const { proof, value } = await this.generateProof(k, rootKey); const cp = new circom_1.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 = hash_1.ZERO_HASH; cp.oldValue = hash_1.ZERO_HASH; } cp.key = hash_1.Hash.fromBigInt(k); cp.value = hash_1.Hash.fromBigInt(value); if (proof.existence) { cp.fnc = 0; } else { cp.fnc = 1; } return cp; } async generateProof(k, rootKey) { let siblingKey; const kHash = hash_1.Hash.fromBigInt(k); const path = (0, utils_1.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 errors_1.ErrNotFound; } switch (n.type) { case constants_1.NODE_TYPE_EMPTY: return { proof: new proof_1.Proof({ existence, nodeAux, siblings }), value: BigInt('0') }; case constants_1.NODE_TYPE_LEAF: if ((0, utils_1.bytesEqual)(kHash.value, n.entry[0].value)) { existence = true; return { proof: new proof_1.Proof({ existence, nodeAux, siblings }), value: n.entry[1].bigInt() }; } nodeAux = { key: n.entry[0], value: n.entry[1] }; return { proof: new proof_1.Proof({ existence, nodeAux, siblings }), value: n.entry[1].bigInt() }; case constants_1.NODE_TYPE_MIDDLE: if (path[depth]) { nextKey = n.childR; siblingKey = n.childL; } else { nextKey = n.childL; siblingKey = n.childR; } break; default: throw errors_1.ErrInvalidNodeFound; } siblings.push(siblingKey); } throw errors_1.ErrKeyNotFound; } async addAndGetCircomProof(k, v) { const cp = new circom_1.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 !== errors_1.ErrKeyNotFound) { throw err; } } if (typeof key === 'undefined' || typeof value === 'undefined') { throw 'key/value undefined'; } cp.oldKey = hash_1.Hash.fromBigInt(key); cp.oldValue = hash_1.Hash.fromBigInt(value); if ((0, utils_1.bytesEqual)(cp.oldKey.value, hash_1.ZERO_HASH.value)) { cp.isOld0 = true; } cp.siblings = (0, hash_1.circomSiblingsFromSiblings)(siblings, this.maxLevels); await this.add(k, v); cp.newKey = hash_1.Hash.fromBigInt(k); cp.newValue = hash_1.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 constants_1.NODE_TYPE_EMPTY: break; case constants_1.NODE_TYPE_LEAF: // eslint-disable-next-line no-console console.log(`"${k.string()}" [style=filled]`); break; case constants_1.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 ((0, utils_1.bytesEqual)(rootKey.value, hash_1.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(hash_1.ZERO_HASH); // eslint-disable-next-line no-console console.log(`End of GraphViz of the MerkleTree with RootKey ${rootKey.bigInt().toString(10)}\n--------\n`); } } exports.Merkletree = Merkletree; _Merkletree_db = new WeakMap(), _Merkletree_root = new WeakMap(), _Merkletree_writable = new WeakMap(), _Merkletree_maxLevel = new WeakMap();