UNPKG

merkle-patricia-tree

Version:

This is an implementation of the modified merkle patricia tree as specified in Ethereum's yellow paper.

1,011 lines 47.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Trie = void 0; var semaphore_async_await_1 = __importDefault(require("semaphore-async-await")); var ethereumjs_util_1 = require("ethereumjs-util"); var db_1 = require("./db"); var readStream_1 = require("./readStream"); var nibbles_1 = require("./util/nibbles"); var walkController_1 = require("./util/walkController"); var trieNode_1 = require("./trieNode"); var verifyRangeProof_1 = require("./verifyRangeProof"); var assert = require('assert'); /** * The basic trie interface, use with `import { BaseTrie as Trie } from 'merkle-patricia-tree'`. * In Ethereum applications stick with the {@link SecureTrie} overlay. * The API for the base and the secure interface are about the same. */ var Trie = /** @class */ (function () { /** * test * @param db - A [levelup](https://github.com/Level/levelup) instance. By default (if the db is `null` or * left undefined) creates an in-memory [memdown](https://github.com/Level/memdown) instance. * @param root - A `Buffer` for the root of a previously stored trie * @param deleteFromDB - Delete nodes from DB on delete operations (disallows switching to an older state root) (default: `false`) */ function Trie(db, root, deleteFromDB) { if (deleteFromDB === void 0) { deleteFromDB = false; } this.EMPTY_TRIE_ROOT = ethereumjs_util_1.KECCAK256_RLP; this.lock = new semaphore_async_await_1.default(1); this.db = db ? new db_1.DB(db) : new db_1.DB(); this._root = this.EMPTY_TRIE_ROOT; this._deleteFromDB = deleteFromDB; if (root) { this.root = root; } } Object.defineProperty(Trie.prototype, "root", { /** * Gets the current root of the `trie` */ get: function () { return this._root; }, /** * Sets the current root of the `trie` */ set: function (value) { if (!value) { value = this.EMPTY_TRIE_ROOT; } assert(value.length === 32, 'Invalid root length. Roots are 32 bytes'); this._root = value; }, enumerable: false, configurable: true }); /** * This method is deprecated. * Please use {@link Trie.root} instead. * * @param value * @deprecated */ Trie.prototype.setRoot = function (value) { this.root = value !== null && value !== void 0 ? value : this.EMPTY_TRIE_ROOT; }; /** * Checks if a given root exists. */ Trie.prototype.checkRoot = function (root) { return __awaiter(this, void 0, void 0, function () { var value, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, this._lookupNode(root)]; case 1: value = _a.sent(); return [2 /*return*/, value !== null]; case 2: error_1 = _a.sent(); if (error_1.message == 'Missing node in DB') { return [2 /*return*/, false]; } else { throw error_1; } return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }; Object.defineProperty(Trie.prototype, "isCheckpoint", { /** * BaseTrie has no checkpointing so return false */ get: function () { return false; }, enumerable: false, configurable: true }); /** * Gets a value given a `key` * @param key - the key to search for * @param throwIfMissing - if true, throws if any nodes are missing. Used for verifying proofs. (default: false) * @returns A Promise that resolves to `Buffer` if a value was found or `null` if no value was found. */ Trie.prototype.get = function (key, throwIfMissing) { if (throwIfMissing === void 0) { throwIfMissing = false; } return __awaiter(this, void 0, void 0, function () { var _a, node, remaining, value; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.findPath(key, throwIfMissing)]; case 1: _a = _b.sent(), node = _a.node, remaining = _a.remaining; value = null; if (node && remaining.length === 0) { value = node.value; } return [2 /*return*/, value]; } }); }); }; /** * Stores a given `value` at the given `key` or do a delete if `value` is empty * (delete operations are only executed on DB with `deleteFromDB` set to `true`) * @param key * @param value * @returns A Promise that resolves once value is stored. */ Trie.prototype.put = function (key, value) { return __awaiter(this, void 0, void 0, function () { var _a, remaining, stack; return __generator(this, function (_b) { switch (_b.label) { case 0: if (!(!value || value.toString() === '')) return [3 /*break*/, 2]; return [4 /*yield*/, this.del(key)]; case 1: return [2 /*return*/, _b.sent()]; case 2: return [4 /*yield*/, this.lock.wait()]; case 3: _b.sent(); if (!this.root.equals(ethereumjs_util_1.KECCAK256_RLP)) return [3 /*break*/, 5]; // If no root, initialize this trie return [4 /*yield*/, this._createInitialNode(key, value)]; case 4: // If no root, initialize this trie _b.sent(); return [3 /*break*/, 8]; case 5: return [4 /*yield*/, this.findPath(key) // then update ]; case 6: _a = _b.sent(), remaining = _a.remaining, stack = _a.stack; // then update return [4 /*yield*/, this._updateNode(key, value, remaining, stack)]; case 7: // then update _b.sent(); _b.label = 8; case 8: this.lock.signal(); return [2 /*return*/]; } }); }); }; /** * Deletes a value given a `key` from the trie * (delete operations are only executed on DB with `deleteFromDB` set to `true`) * @param key * @returns A Promise that resolves once value is deleted. */ Trie.prototype.del = function (key) { return __awaiter(this, void 0, void 0, function () { var _a, node, stack; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.lock.wait()]; case 1: _b.sent(); return [4 /*yield*/, this.findPath(key)]; case 2: _a = _b.sent(), node = _a.node, stack = _a.stack; if (!node) return [3 /*break*/, 4]; return [4 /*yield*/, this._deleteNode(key, stack)]; case 3: _b.sent(); _b.label = 4; case 4: this.lock.signal(); return [2 /*return*/]; } }); }); }; /** * Tries to find a path to the node for the given key. * It returns a `stack` of nodes to the closest node. * @param key - the search key * @param throwIfMissing - if true, throws if any nodes are missing. Used for verifying proofs. (default: false) */ Trie.prototype.findPath = function (key, throwIfMissing) { if (throwIfMissing === void 0) { throwIfMissing = false; } return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { // eslint-disable-next-line no-async-promise-executor return [2 /*return*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () { var stack, targetKey, onFound, error_2; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: stack = []; targetKey = (0, nibbles_1.bufferToNibbles)(key); onFound = function (nodeRef, node, keyProgress, walkController) { return __awaiter(_this, void 0, void 0, function () { var keyRemainder, branchIndex, branchNode, matchingLen; return __generator(this, function (_a) { if (node === null) { return [2 /*return*/, reject(new Error('Path not found'))]; } keyRemainder = targetKey.slice((0, nibbles_1.matchingNibbleLength)(keyProgress, targetKey)); stack.push(node); if (node instanceof trieNode_1.BranchNode) { if (keyRemainder.length === 0) { // we exhausted the key without finding a node resolve({ node: node, remaining: [], stack: stack }); } else { branchIndex = keyRemainder[0]; branchNode = node.getBranch(branchIndex); if (!branchNode) { // there are no more nodes to find and we didn't find the key resolve({ node: null, remaining: keyRemainder, stack: stack }); } else { // node found, continuing search // this can be optimized as this calls getBranch again. walkController.onlyBranchIndex(node, keyProgress, branchIndex); } } } else if (node instanceof trieNode_1.LeafNode) { if ((0, nibbles_1.doKeysMatch)(keyRemainder, node.key)) { // keys match, return node with empty key resolve({ node: node, remaining: [], stack: stack }); } else { // reached leaf but keys dont match resolve({ node: null, remaining: keyRemainder, stack: stack }); } } else if (node instanceof trieNode_1.ExtensionNode) { matchingLen = (0, nibbles_1.matchingNibbleLength)(keyRemainder, node.key); if (matchingLen !== node.key.length) { // keys don't match, fail resolve({ node: null, remaining: keyRemainder, stack: stack }); } else { // keys match, continue search walkController.allChildren(node, keyProgress); } } return [2 /*return*/]; }); }); }; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, this.walkTrie(this.root, onFound)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_2 = _a.sent(); if (error_2.message == 'Missing node in DB' && !throwIfMissing) { // pass } else { reject(error_2); } return [3 /*break*/, 4]; case 4: // Resolve if _walkTrie finishes without finding any nodes resolve({ node: null, remaining: [], stack: stack }); return [2 /*return*/]; } }); }); })]; }); }); }; /** * Walks a trie until finished. * @param root * @param onFound - callback to call when a node is found. This schedules new tasks. If no tasks are available, the Promise resolves. * @returns Resolves when finished walking trie. */ Trie.prototype.walkTrie = function (root, onFound) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, walkController_1.WalkController.newWalk(onFound, this, root)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * @hidden * Backwards compatibility * @param root - * @param onFound - */ Trie.prototype._walkTrie = function (root, onFound) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.walkTrie(root, onFound)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * Creates the initial node from an empty tree. * @private */ Trie.prototype._createInitialNode = function (key, value) { return __awaiter(this, void 0, void 0, function () { var newNode; return __generator(this, function (_a) { switch (_a.label) { case 0: newNode = new trieNode_1.LeafNode((0, nibbles_1.bufferToNibbles)(key), value); this.root = newNode.hash(); return [4 /*yield*/, this.db.put(this.root, newNode.serialize())]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * Retrieves a node from db by hash. */ Trie.prototype.lookupNode = function (node) { return __awaiter(this, void 0, void 0, function () { var value, foundNode; return __generator(this, function (_a) { switch (_a.label) { case 0: if ((0, trieNode_1.isRawNode)(node)) { return [2 /*return*/, (0, trieNode_1.decodeRawNode)(node)]; } value = null; foundNode = null; return [4 /*yield*/, this.db.get(node)]; case 1: value = _a.sent(); if (value) { foundNode = (0, trieNode_1.decodeNode)(value); } else { // Dev note: this error message text is used for error checking in `checkRoot`, `verifyProof`, and `findPath` throw new Error('Missing node in DB'); } return [2 /*return*/, foundNode]; } }); }); }; /** * @hidden * Backwards compatibility * @param node The node hash to lookup from the DB */ Trie.prototype._lookupNode = function (node) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.lookupNode(node)]; }); }); }; /** * Updates a node. * @private * @param key * @param value * @param keyRemainder * @param stack */ Trie.prototype._updateNode = function (k, value, keyRemainder, stack) { return __awaiter(this, void 0, void 0, function () { var toSave, lastNode, key, matchLeaf, l, i, n, newLeaf, lastKey, matchingLength, newBranchNode, newKey, newExtNode, branchKey, formattedNode, newLeafNode; return __generator(this, function (_a) { switch (_a.label) { case 0: toSave = []; lastNode = stack.pop(); if (!lastNode) { throw new Error('Stack underflow'); } key = (0, nibbles_1.bufferToNibbles)(k); matchLeaf = false; if (lastNode instanceof trieNode_1.LeafNode) { l = 0; for (i = 0; i < stack.length; i++) { n = stack[i]; if (n instanceof trieNode_1.BranchNode) { l++; } else { l += n.key.length; } } if ((0, nibbles_1.matchingNibbleLength)(lastNode.key, key.slice(l)) === lastNode.key.length && keyRemainder.length === 0) { matchLeaf = true; } } if (matchLeaf) { // just updating a found value lastNode.value = value; stack.push(lastNode); } else if (lastNode instanceof trieNode_1.BranchNode) { stack.push(lastNode); if (keyRemainder.length !== 0) { // add an extension to a branch node keyRemainder.shift(); newLeaf = new trieNode_1.LeafNode(keyRemainder, value); stack.push(newLeaf); } else { lastNode.value = value; } } else { lastKey = lastNode.key; matchingLength = (0, nibbles_1.matchingNibbleLength)(lastKey, keyRemainder); newBranchNode = new trieNode_1.BranchNode(); // create a new extension node if (matchingLength !== 0) { newKey = lastNode.key.slice(0, matchingLength); newExtNode = new trieNode_1.ExtensionNode(newKey, value); stack.push(newExtNode); lastKey.splice(0, matchingLength); keyRemainder.splice(0, matchingLength); } stack.push(newBranchNode); if (lastKey.length !== 0) { branchKey = lastKey.shift(); if (lastKey.length !== 0 || lastNode instanceof trieNode_1.LeafNode) { // shrinking extension or leaf lastNode.key = lastKey; formattedNode = this._formatNode(lastNode, false, toSave); newBranchNode.setBranch(branchKey, formattedNode); } else { // remove extension or attaching this._formatNode(lastNode, false, toSave, true); newBranchNode.setBranch(branchKey, lastNode.value); } } else { newBranchNode.value = lastNode.value; } if (keyRemainder.length !== 0) { keyRemainder.shift(); newLeafNode = new trieNode_1.LeafNode(keyRemainder, value); stack.push(newLeafNode); } else { newBranchNode.value = value; } } return [4 /*yield*/, this._saveStack(key, stack, toSave)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * Deletes a node from the trie. * @private */ Trie.prototype._deleteNode = function (k, stack) { return __awaiter(this, void 0, void 0, function () { var processBranchNode, lastNode, parentNode, opStack, key, lastNodeKey, branchNodes, branchNode, branchNodeKey, foundNode; return __generator(this, function (_a) { switch (_a.label) { case 0: processBranchNode = function (key, branchKey, branchNode, parentNode, stack) { // branchNode is the node ON the branch node not THE branch node if (!parentNode || parentNode instanceof trieNode_1.BranchNode) { // branch->? if (parentNode) { stack.push(parentNode); } if (branchNode instanceof trieNode_1.BranchNode) { // create an extension node // branch->extension->branch // @ts-ignore var extensionNode = new trieNode_1.ExtensionNode([branchKey], null); stack.push(extensionNode); key.push(branchKey); } else { var branchNodeKey = branchNode.key; // branch key is an extension or a leaf // branch->(leaf or extension) branchNodeKey.unshift(branchKey); branchNode.key = branchNodeKey.slice(0); key = key.concat(branchNodeKey); } stack.push(branchNode); } else { // parent is an extension var parentKey = parentNode.key; if (branchNode instanceof trieNode_1.BranchNode) { // ext->branch parentKey.push(branchKey); key.push(branchKey); parentNode.key = parentKey; stack.push(parentNode); } else { var branchNodeKey = branchNode.key; // branch node is an leaf or extension and parent node is an exstention // add two keys together // dont push the parent node branchNodeKey.unshift(branchKey); key = key.concat(branchNodeKey); parentKey = parentKey.concat(branchNodeKey); branchNode.key = parentKey; } stack.push(branchNode); } return key; }; lastNode = stack.pop(); assert(lastNode); parentNode = stack.pop(); opStack = []; key = (0, nibbles_1.bufferToNibbles)(k); if (!parentNode) { // the root here has to be a leaf. this.root = this.EMPTY_TRIE_ROOT; return [2 /*return*/]; } if (lastNode instanceof trieNode_1.BranchNode) { lastNode.value = null; } else { // the lastNode has to be a leaf if it's not a branch. // And a leaf's parent, if it has one, must be a branch. if (!(parentNode instanceof trieNode_1.BranchNode)) { throw new Error('Expected branch node'); } lastNodeKey = lastNode.key; key.splice(key.length - lastNodeKey.length); // delete the value this._formatNode(lastNode, false, opStack, true); parentNode.setBranch(key.pop(), null); lastNode = parentNode; parentNode = stack.pop(); } branchNodes = lastNode.getChildren(); if (!(branchNodes.length === 1)) return [3 /*break*/, 4]; branchNode = branchNodes[0][1]; branchNodeKey = branchNodes[0][0]; return [4 /*yield*/, this._lookupNode(branchNode)]; case 1: foundNode = _a.sent(); if (!foundNode) return [3 /*break*/, 3]; key = processBranchNode(key, branchNodeKey, foundNode, parentNode, stack); return [4 /*yield*/, this._saveStack(key, stack, opStack)]; case 2: _a.sent(); _a.label = 3; case 3: return [3 /*break*/, 6]; case 4: // simple removing a leaf and recaluclation the stack if (parentNode) { stack.push(parentNode); } stack.push(lastNode); return [4 /*yield*/, this._saveStack(key, stack, opStack)]; case 5: _a.sent(); _a.label = 6; case 6: return [2 /*return*/]; } }); }); }; /** * Saves a stack of nodes to the database. * @private * @param key - the key. Should follow the stack * @param stack - a stack of nodes to the value given by the key * @param opStack - a stack of levelup operations to commit at the end of this funciton */ Trie.prototype._saveStack = function (key, stack, opStack) { return __awaiter(this, void 0, void 0, function () { var lastRoot, node, branchKey; return __generator(this, function (_a) { switch (_a.label) { case 0: // update nodes while (stack.length) { node = stack.pop(); if (node instanceof trieNode_1.LeafNode) { key.splice(key.length - node.key.length); } else if (node instanceof trieNode_1.ExtensionNode) { key.splice(key.length - node.key.length); if (lastRoot) { node.value = lastRoot; } } else if (node instanceof trieNode_1.BranchNode) { if (lastRoot) { branchKey = key.pop(); node.setBranch(branchKey, lastRoot); } } lastRoot = this._formatNode(node, stack.length === 0, opStack); } if (lastRoot) { this.root = lastRoot; } return [4 /*yield*/, this.db.batch(opStack)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * Formats node to be saved by `levelup.batch`. * @private * @param node - the node to format. * @param topLevel - if the node is at the top level. * @param opStack - the opStack to push the node's data. * @param remove - whether to remove the node (only used for CheckpointTrie). * @returns The node's hash used as the key or the rawNode. */ Trie.prototype._formatNode = function (node, topLevel, opStack, remove) { if (remove === void 0) { remove = false; } var rlpNode = node.serialize(); if (rlpNode.length >= 32 || topLevel) { // Do not use TrieNode.hash() here otherwise serialize() // is applied twice (performance) var hashRoot = (0, ethereumjs_util_1.keccak)(rlpNode); if (remove) { if (this._deleteFromDB) { opStack.push({ type: 'del', key: hashRoot, }); } } else { opStack.push({ type: 'put', key: hashRoot, value: rlpNode, }); } return hashRoot; } return node.raw(); }; /** * The given hash of operations (key additions or deletions) are executed on the trie * (delete operations are only executed on DB with `deleteFromDB` set to `true`) * @example * const ops = [ * { type: 'del', key: Buffer.from('father') } * , { type: 'put', key: Buffer.from('name'), value: Buffer.from('Yuri Irsenovich Kim') } * , { type: 'put', key: Buffer.from('dob'), value: Buffer.from('16 February 1941') } * , { type: 'put', key: Buffer.from('spouse'), value: Buffer.from('Kim Young-sook') } * , { type: 'put', key: Buffer.from('occupation'), value: Buffer.from('Clown') } * ] * await trie.batch(ops) * @param ops */ Trie.prototype.batch = function (ops) { return __awaiter(this, void 0, void 0, function () { var ops_1, ops_1_1, op, e_1_1; var e_1, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 7, 8, 9]); ops_1 = __values(ops), ops_1_1 = ops_1.next(); _b.label = 1; case 1: if (!!ops_1_1.done) return [3 /*break*/, 6]; op = ops_1_1.value; if (!(op.type === 'put')) return [3 /*break*/, 3]; if (!op.value) { throw new Error('Invalid batch db operation'); } return [4 /*yield*/, this.put(op.key, op.value)]; case 2: _b.sent(); return [3 /*break*/, 5]; case 3: if (!(op.type === 'del')) return [3 /*break*/, 5]; return [4 /*yield*/, this.del(op.key)]; case 4: _b.sent(); _b.label = 5; case 5: ops_1_1 = ops_1.next(); return [3 /*break*/, 1]; case 6: return [3 /*break*/, 9]; case 7: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; return [3 /*break*/, 9]; case 8: try { if (ops_1_1 && !ops_1_1.done && (_a = ops_1.return)) _a.call(ops_1); } finally { if (e_1) throw e_1.error; } return [7 /*endfinally*/]; case 9: return [2 /*return*/]; } }); }); }; /** * Saves the nodes from a proof into the trie. If no trie is provided a new one wil be instantiated. * @param proof * @param trie */ Trie.fromProof = function (proof, trie) { return __awaiter(this, void 0, void 0, function () { var opStack; return __generator(this, function (_a) { switch (_a.label) { case 0: opStack = proof.map(function (nodeValue) { return { type: 'put', key: (0, ethereumjs_util_1.keccak)(nodeValue), value: nodeValue, }; }); if (!trie) { trie = new Trie(); if (opStack[0]) { trie.root = opStack[0].key; } } return [4 /*yield*/, trie.db.batch(opStack)]; case 1: _a.sent(); return [2 /*return*/, trie]; } }); }); }; /** * prove has been renamed to {@link Trie.createProof}. * @deprecated * @param trie * @param key */ Trie.prove = function (trie, key) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.createProof(trie, key)]; }); }); }; /** * Creates a proof from a trie and key that can be verified using {@link Trie.verifyProof}. * @param trie * @param key */ Trie.createProof = function (trie, key) { return __awaiter(this, void 0, void 0, function () { var stack, p; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, trie.findPath(key)]; case 1: stack = (_a.sent()).stack; p = stack.map(function (stackElem) { return stackElem.serialize(); }); return [2 /*return*/, p]; } }); }); }; /** * Verifies a proof. * @param rootHash * @param key * @param proof * @throws If proof is found to be invalid. * @returns The value from the key, or null if valid proof of non-existence. */ Trie.verifyProof = function (rootHash, key, proof) { return __awaiter(this, void 0, void 0, function () { var proofTrie, e_2, value, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: proofTrie = new Trie(null, rootHash); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, Trie.fromProof(proof, proofTrie)]; case 2: proofTrie = _a.sent(); return [3 /*break*/, 4]; case 3: e_2 = _a.sent(); throw new Error('Invalid proof nodes given'); case 4: _a.trys.push([4, 6, , 7]); return [4 /*yield*/, proofTrie.get(key, true)]; case 5: value = _a.sent(); return [2 /*return*/, value]; case 6: err_1 = _a.sent(); if (err_1.message == 'Missing node in DB') { throw new Error('Invalid proof provided'); } else { throw err_1; } return [3 /*break*/, 7]; case 7: return [2 /*return*/]; } }); }); }; /** * {@link verifyRangeProof} */ Trie.verifyRangeProof = function (rootHash, firstKey, lastKey, keys, values, proof) { return (0, verifyRangeProof_1.verifyRangeProof)(rootHash, firstKey && (0, nibbles_1.bufferToNibbles)(firstKey), lastKey && (0, nibbles_1.bufferToNibbles)(lastKey), keys.map(nibbles_1.bufferToNibbles), values, proof); }; /** * The `data` event is given an `Object` that has two properties; the `key` and the `value`. Both should be Buffers. * @return Returns a [stream](https://nodejs.org/dist/latest-v12.x/docs/api/stream.html#stream_class_stream_readable) of the contents of the `trie` */ Trie.prototype.createReadStream = function () { return new readStream_1.TrieReadStream(this); }; /** * Creates a new trie backed by the same db. */ Trie.prototype.copy = function () { var db = this.db.copy(); return new Trie(db._leveldb, this.root); }; /** * Finds all nodes that are stored directly in the db * (some nodes are stored raw inside other nodes) * called by {@link ScratchReadStream} * @private */ Trie.prototype._findDbNodes = function (onFound) { return __awaiter(this, void 0, void 0, function () { var outerOnFound; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: outerOnFound = function (nodeRef, node, key, walkController) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { if ((0, trieNode_1.isRawNode)(nodeRef)) { if (node !== null) { walkController.allChildren(node, key); } } else { onFound(nodeRef, node, key, walkController); } return [2 /*return*/]; }); }); }; return [4 /*yield*/, this.walkTrie(this.root, outerOnFound)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * Finds all nodes that store k,v values * called by {@link TrieReadStream} * @private */ Trie.prototype._findValueNodes = function (onFound) { return __awaiter(this, void 0, void 0, function () { var outerOnFound; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: outerOnFound = function (nodeRef, node, key, walkController) { return __awaiter(_this, void 0, void 0, function () { var fullKey; return __generator(this, function (_a) { fullKey = key; if (node instanceof trieNode_1.LeafNode) { fullKey = key.concat(node.key); // found leaf node! onFound(nodeRef, node, fullKey, walkController); } else if (node instanceof trieNode_1.BranchNode && node.value) { // found branch with value onFound(nodeRef, node, fullKey, walkController); } else { // keep looking for value nodes if (node !== null) { walkController.allChildren(node, key); } } return [2 /*return*/]; }); }); }; return [4 /*yield*/, this.walkTrie(this.root, outerOnFound)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; return Trie; }()); exports.Trie = Trie; //# sourceMappingURL=baseTrie.js.map