UNPKG

merkle-patricia-tree

Version:

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

141 lines 4.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CheckpointDB = void 0; const db_1 = require("./db"); /** * DB is a thin wrapper around the underlying levelup db, * which validates inputs and sets encoding type. */ class CheckpointDB extends db_1.DB { /** * Initialize a DB instance. If `leveldb` is not provided, DB * defaults to an [in-memory store](https://github.com/Level/memdown). * @param leveldb - An abstract-leveldown compliant store */ constructor(leveldb) { super(leveldb); // Roots of trie at the moment of checkpoint this.checkpoints = []; } /** * Is the DB during a checkpoint phase? */ get isCheckpoint() { return this.checkpoints.length > 0; } /** * Adds a new checkpoint to the stack * @param root */ checkpoint(root) { this.checkpoints.push({ keyValueMap: new Map(), root }); } /** * Commits the latest checkpoint */ async commit() { const { keyValueMap } = this.checkpoints.pop(); if (!this.isCheckpoint) { // This was the final checkpoint, we should now commit and flush everything to disk const batchOp = []; keyValueMap.forEach(function (value, key) { if (value === null) { batchOp.push({ type: 'del', key: Buffer.from(key, 'binary'), }); } else { batchOp.push({ type: 'put', key: Buffer.from(key, 'binary'), value, }); } }); await this.batch(batchOp); } else { // dump everything into the current (higher level) cache const currentKeyValueMap = this.checkpoints[this.checkpoints.length - 1].keyValueMap; keyValueMap.forEach((value, key) => currentKeyValueMap.set(key, value)); } } /** * Reverts the latest checkpoint */ async revert() { const { root } = this.checkpoints.pop(); return root; } /** * Retrieves a raw value from leveldb. * @param key * @returns A Promise that resolves to `Buffer` if a value is found or `null` if no value is found. */ async get(key) { // Lookup the value in our cache. We return the latest checkpointed value (which should be the value on disk) for (let index = this.checkpoints.length - 1; index >= 0; index--) { const value = this.checkpoints[index].keyValueMap.get(key.toString('binary')); if (value !== undefined) { return value; } } // Nothing has been found in cache, look up from disk const value = await super.get(key); if (this.isCheckpoint) { // Since we are a checkpoint, put this value in cache, so future `get` calls will not look the key up again from disk. this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), value); } return value; } /** * Writes a value directly to leveldb. * @param key The key as a `Buffer` * @param value The value to be stored */ async put(key, val) { if (this.isCheckpoint) { // put value in cache this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), val); } else { await super.put(key, val); } } /** * Removes a raw value in the underlying leveldb. * @param keys */ async del(key) { if (this.isCheckpoint) { // delete the value in the current cache this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), null); } else { // delete the value on disk await this._leveldb.del(key, db_1.ENCODING_OPTS); } } /** * Performs a batch operation on db. * @param opStack A stack of levelup operations */ async batch(opStack) { if (this.isCheckpoint) { for (const op of opStack) { if (op.type === 'put') { await this.put(op.key, op.value); } else if (op.type === 'del') { await this.del(op.key); } } } else { await super.batch(opStack); } } } exports.CheckpointDB = CheckpointDB; //# sourceMappingURL=checkpointDb.js.map