UNPKG

icjs-mpt

Version:

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

1,728 lines (1,467 loc) 835 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Trie = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ const assert = require('assert') const levelup = require('levelup') const memdown = require('memdown') const async = require('async') const rlp = require('rlp') const ircUtil = require('icjs-util') const semaphore = require('semaphore') const TrieNode = require('./trieNode') const ReadStream = require('./readStream') const PrioritizedTaskExecutor = require('./prioritizedTaskExecutor') const matchingNibbleLength = require('./util').matchingNibbleLength const doKeysMatch = require('./util').doKeysMatch const callTogether = require('./util').callTogether const asyncFirstSeries = require('./util').asyncFirstSeries module.exports = Trie /** * Use `require('merkel-patricia-tree')` for the base interface. In IrChain applications stick with the Secure Trie Overlay `require('merkel-patricia-tree/secure')`. The API for the raw and the secure interface are about the same * @class Trie * @param {Object} [db] An instance of [levelup](https://github.com/rvagg/node-levelup/) or a compatible API. If the db is `null` or left undefined, then the trie will be stored in memory via [memdown](https://github.com/rvagg/memdown) * @param {Buffer|String} [root]` A hex `String` or `Buffer` for the root of a previously stored trie * @prop {Buffer} root The current root of the `trie` * @prop {Boolean} isCheckpoint determines if you are saving to a checkpoint or directly to the db * @prop {Buffer} EMPTY_TRIE_ROOT the Root for an empty trie */ function Trie (db, root) { var self = this this.EMPTY_TRIE_ROOT = ircUtil.SHA3_RLP this.sem = semaphore(1) // setup dbs this.db = db || levelup('', { db: memdown }) this._getDBs = [this.db] this._putDBs = [this.db] Object.defineProperty(this, 'root', { set: function (value) { if (value) { value = ircUtil.toBuffer(value) assert(value.length === 32, 'Invalid root length. Roots are 32 bytes') } else { value = self.EMPTY_TRIE_ROOT } this._root = value }, get: function () { return this._root } }) this.root = root } /** * Gets a value given a `key` * @method get * @param {Buffer|String} key - the key to search for * @param {Function} cb A callback `Function` which is given the arguments `err` - for errors that may have occured and `value` - the found value in a `Buffer` or if no value was found `null` */ Trie.prototype.get = function (key, cb) { var self = this key = ircUtil.toBuffer(key) self.findPath(key, function (err, node, remainder, stack) { var value = null if (node && remainder.length === 0) { value = node.value } cb(err, value) }) } /** * Stores a given `value` at the given `key` * @method put * @param {Buffer|String} key * @param {Buffer|String} Value * @param {Function} cb A callback `Function` which is given the argument `err` - for errors that may have occured */ Trie.prototype.put = function (key, value, cb) { var self = this key = ircUtil.toBuffer(key) value = ircUtil.toBuffer(value) if (!value || value.toString() === '') { self.del(key, cb) } else { cb = callTogether(cb, self.sem.leave) self.sem.take(function () { if (self.root.toString('hex') !== ircUtil.SHA3_RLP.toString('hex')) { // first try to find the give key or its nearst node self.findPath(key, function (err, foundValue, keyRemainder, stack) { if (err) { return cb(err) } // then update self._updateNode(key, value, keyRemainder, stack, cb) }) } else { self._createInitialNode(key, value, cb) // if no root initialize this trie } }) } } /** * deletes a value given a `key` * @method del * @param {Buffer|String} key * @param {Function} callback the callback `Function` */ Trie.prototype.del = function (key, cb) { var self = this key = ircUtil.toBuffer(key) cb = callTogether(cb, self.sem.leave) self.sem.take(function () { self.findPath(key, function (err, foundValue, keyRemainder, stack) { if (err) { return cb(err) } if (foundValue) { self._deleteNode(key, stack, cb) } else { cb() } }) }) } /** * Retrieves a raw value in the underlying db * @method getRaw * @param {Buffer} key * @param {Function} callback A callback `Function`, which is given the arguments `err` - for errors that may have occured and `value` - the found value in a `Buffer` or if no value was found `null`. */ Trie.prototype.getRaw = function (key, cb) { key = ircUtil.toBuffer(key) function dbGet (db, cb2) { db.get(key, { keyEncoding: 'binary', valueEncoding: 'binary' }, function (err, foundNode) { if (err || !foundNode) { cb2(null, null) } else { cb2(null, foundNode) } }) } asyncFirstSeries(this._getDBs, dbGet, cb) } // retrieves a node from dbs by hash Trie.prototype._lookupNode = function (node, cb) { if (TrieNode.isRawNode(node)) { cb(new TrieNode(node)) } else { this.getRaw(node, function (err, value) { if (err) { throw err } if (value) { value = new TrieNode(rlp.decode(value)) } cb(value) }) } } // TODO: remove the proxy method when changing the caching Trie.prototype._putRaw = function (key, val, cb) { function dbPut (db, cb2) { db.put(key, val, { keyEncoding: 'binary', valueEncoding: 'binary' }, cb2) } async.each(this._putDBs, dbPut, cb) } /** * Writes a value directly to the underlining db * @method putRaw * @param {Buffer|String} key The key as a `Buffer` or `String` * @param {Buffer} value The value to be stored * @param {Function} callback A callback `Function`, which is given the argument `err` - for errors that may have occured */ Trie.prototype.putRaw = Trie.prototype._putRaw /** * Removes a raw value in the underlying db * @method delRaw * @param {Buffer|String} key * @param {Function} callback A callback `Function`, which is given the argument `err` - for errors that may have occured */ Trie.prototype.delRaw = function (key, cb) { function del (db, cb2) { db.del(key, { keyEncoding: 'binary' }, cb2) } async.each(this._putDBs, del, cb) } // writes a single node to dbs Trie.prototype._putNode = function (node, cb) { var hash = node.hash() var serialized = node.serialize() this._putRaw(hash, serialized, cb) } // writes many nodes to db Trie.prototype._batchNodes = function (opStack, cb) { function dbBatch (db, cb) { db.batch(opStack, { keyEncoding: 'binary', valueEncoding: 'binary' }, cb) } async.each(this._putDBs, dbBatch, cb) } /** * Trys to find a path to the node for the given key * It returns a `stack` of nodes to the closet node * @method findPath * @param {String|Buffer} - key - the search key * @param {Function} - cb - the callback function. Its is given the following * arguments * - err - any errors encontered * - node - the last node found * - keyRemainder - the remaining key nibbles not accounted for * - stack - an array of nodes that forms the path to node we are searching for */ Trie.prototype.findPath = function (targetKey, cb) { var self = this var root = self.root var stack = [] targetKey = TrieNode.stringToNibbles(targetKey) this._walkTrie(root, processNode, cb) function processNode (nodeRef, node, keyProgress, walkController) { var nodeKey = node.key || [] var keyRemainder = targetKey.slice(matchingNibbleLength(keyProgress, targetKey)) var matchingLen = matchingNibbleLength(keyRemainder, nodeKey) stack.push(node) if (node.type === 'branch') { if (keyRemainder.length === 0) { walkController.return(null, node, [], stack) // we exhausted the key without finding a node } else { var branchIndex = keyRemainder[0] var branchNode = node.getValue(branchIndex) if (!branchNode) { // there are no more nodes to find and we didn't find the key walkController.return(null, null, keyRemainder, stack) } else { // node found, continuing search walkController.only(branchIndex) } } } else if (node.type === 'leaf') { if (doKeysMatch(keyRemainder, nodeKey)) { // keys match, return node with empty key walkController.return(null, node, [], stack) } else { // reached leaf but keys dont match walkController.return(null, null, keyRemainder, stack) } } else if (node.type === 'extention') { if (matchingLen !== nodeKey.length) { // keys dont match, fail walkController.return(null, null, keyRemainder, stack) } else { // keys match, continue search walkController.next() } } } } /* * Finds all nodes that store k,v values */ Trie.prototype._findNode = function (key, root, stack, cb) { this.findPath(key, function () { cb.apply(null, arguments) }) } /* * Finds all nodes that store k,v values */ Trie.prototype._findValueNodes = function (onFound, cb) { this._walkTrie(this.root, function (nodeRef, node, key, walkController) { var fullKey = key if (node.key) { fullKey = key.concat(node.key) } if (node.type === 'leaf') { // found leaf node! onFound(nodeRef, node, fullKey, walkController.next) } else if (node.type === 'branch' && node.value) { // found branch with value onFound(nodeRef, node, fullKey, walkController.next) } else { // keep looking for value nodes walkController.next() } }, cb) } /* * Finds all nodes that are stored directly in the db * (some nodes are stored raw inside other nodes) */ Trie.prototype._findDbNodes = function (onFound, cb) { this._walkTrie(this.root, function (nodeRef, node, key, walkController) { if (TrieNode.isRawNode(nodeRef)) { walkController.next() } else { onFound(nodeRef, node, key, walkController.next) } }, cb) } /** * Updates a node * @method _updateNode * @param {Buffer} key * @param {Buffer| String} value * @param {Array} keyRemainder * @param {Array} stack - * @param {Function} cb - the callback */ Trie.prototype._updateNode = function (key, value, keyRemainder, stack, cb) { var toSave = [] var lastNode = stack.pop() // add the new nodes key = TrieNode.stringToNibbles(key) // Check if the last node is a leaf and the key matches to this var matchLeaf = false if (lastNode.type === 'leaf') { var l = 0 for (var i = 0; i < stack.length; i++) { var n = stack[i] if (n.type === 'branch') { l++ } else { l += n.key.length } } if ((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.type === 'branch') { stack.push(lastNode) if (keyRemainder !== 0) { // add an extention to a branch node keyRemainder.shift() // create a new leaf var newLeaf = new TrieNode('leaf', keyRemainder, value) stack.push(newLeaf) } else { lastNode.value = value } } else { // create a branch node var lastKey = lastNode.key var matchingLength = matchingNibbleLength(lastKey, keyRemainder) var newBranchNode = new TrieNode('branch') // create a new extention node if (matchingLength !== 0) { var newKey = lastNode.key.slice(0, matchingLength) var newExtNode = new TrieNode('extention', newKey, value) stack.push(newExtNode) lastKey.splice(0, matchingLength) keyRemainder.splice(0, matchingLength) } stack.push(newBranchNode) if (lastKey.length !== 0) { var branchKey = lastKey.shift() if (lastKey.length !== 0 || lastNode.type === 'leaf') { // shriking extention or leaf lastNode.key = lastKey var formatedNode = this._formatNode(lastNode, false, toSave) newBranchNode.setValue(branchKey, formatedNode) } else { // remove extention or attaching this._formatNode(lastNode, false, true, toSave) newBranchNode.setValue(branchKey, lastNode.value) } } else { newBranchNode.value = lastNode.value } if (keyRemainder.length !== 0) { keyRemainder.shift() // add a leaf node to the new branch node var newLeafNode = new TrieNode('leaf', keyRemainder, value) stack.push(newLeafNode) } else { newBranchNode.value = value } } this._saveStack(key, stack, toSave, cb) } // walk tree Trie.prototype._walkTrie = function (root, onNode, onDone) { var self = this root = root || self.root onDone = onDone || function () {} var aborted = false var returnValues = [] if (root.toString('hex') === ircUtil.SHA3_RLP.toString('hex')) { return onDone() } self._lookupNode(root, function (node) { processNode(root, node, null, function (err) { if (err) { return onDone(err) } onDone.apply(null, returnValues) }) }) // the maximum pool size should be high enough to utilise the parallelizability of reading nodes from disk and // low enough to utilize the prioritisation of node lookup. var maxPoolSize = 500 var taskExecutor = new PrioritizedTaskExecutor(maxPoolSize) function processNode (nodeRef, node, key, cb) { if (!node) return cb() if (aborted) return cb() var stopped = false key = key || [] var walkController = { stop: function () { stopped = true cb() }, // end all traversal and return values to the onDone cb return: function () { aborted = true returnValues = arguments cb() }, next: function () { if (aborted) { return cb() } if (stopped) { return cb() } var children = node.getChildren() async.forEachOf(children, function (childData, index, cb) { var keyExtension = childData[0] var childRef = childData[1] var childKey = key.concat(keyExtension) var priority = childKey.length taskExecutor.execute(priority, function (taskCallback) { self._lookupNode(childRef, function (childNode) { taskCallback() processNode(childRef, childNode, childKey, cb) }) }) }, cb) }, only: function (childIndex) { var childRef = node.getValue(childIndex) var childKey = key.slice() childKey.push(childIndex) var priority = childKey.length taskExecutor.execute(priority, function (taskCallback) { self._lookupNode(childRef, function (childNode) { taskCallback() processNode(childRef, childNode, childKey, cb) }) }) } } onNode(nodeRef, node, key, walkController) } } /** * saves a stack * @method _saveStack * @param {Array} key - the key. Should follow the stack * @param {Array} stack - a stack of nodes to the value given by the key * @param {Array} opStack - a stack of levelup operations to commit at the end of this funciton * @param {Function} cb */ Trie.prototype._saveStack = function (key, stack, opStack, cb) { var lastRoot // update nodes while (stack.length) { var node = stack.pop() if (node.type === 'leaf') { key.splice(key.length - node.key.length) } else if (node.type === 'extention') { key.splice(key.length - node.key.length) if (lastRoot) { node.value = lastRoot } } else if (node.type === 'branch') { if (lastRoot) { var branchKey = key.pop() node.setValue(branchKey, lastRoot) } } lastRoot = this._formatNode(node, stack.length === 0, opStack) } if (lastRoot) { this.root = lastRoot } this._batchNodes(opStack, cb) } Trie.prototype._deleteNode = function (key, stack, cb) { function processBranchNode (key, branchKey, branchNode, parentNode, stack) { // branchNode is the node ON the branch node not THE branch node var branchNodeKey = branchNode.key if (!parentNode || parentNode.type === 'branch') { // branch->? if (parentNode) { stack.push(parentNode) } if (branchNode.type === 'branch') { // create an extention node // branch->extention->branch var extentionNode = new TrieNode('extention', [branchKey], null) stack.push(extentionNode) key.push(branchKey) } else { // branch key is an extention or a leaf // branch->(leaf or extention) branchNodeKey.unshift(branchKey) branchNode.key = branchNodeKey // hackery. This is equvilant to array.concat except we need keep the // rerfance to the `key` that was passed in. branchNodeKey.unshift(0) branchNodeKey.unshift(key.length) key.splice.apply(key, branchNodeKey) } stack.push(branchNode) } else { // parent is a extention var parentKey = parentNode.key if (branchNode.type === 'branch') { // ext->branch parentKey.push(branchKey) key.push(branchKey) parentNode.key = parentKey stack.push(parentNode) } else { // branch node is an leaf or extention 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 } var lastNode = stack.pop() var parentNode = stack.pop() var opStack = [] var self = this if (!Array.isArray(key)) { // convert key to nibbles key = TrieNode.stringToNibbles(key) } if (!parentNode) { // the root here has to be a leaf. this.root = this.EMPTY_TRIE_ROOT cb() } else { if (lastNode.type === 'branch') { lastNode.value = null } else { // the lastNode has to be a leaf if its not a branch. And a leaf's parent // if it has one must be a branch. var lastNodeKey = lastNode.key key.splice(key.length - lastNodeKey.length) // delete the value this._formatNode(lastNode, false, true, opStack) parentNode.setValue(key.pop(), null) lastNode = parentNode parentNode = stack.pop() } // nodes on the branch var branchNodes = [] // count the number of nodes on the branch lastNode.raw.forEach(function (node, i) { var val = lastNode.getValue(i) if (val) branchNodes.push([i, val]) }) // if there is only one branch node left, collapse the branch node if (branchNodes.length === 1) { // add the one remaing branch node to node above it var branchNode = branchNodes[0][1] var branchNodeKey = branchNodes[0][0] // look up node this._lookupNode(branchNode, function (foundNode) { key = processBranchNode(key, branchNodeKey, foundNode, parentNode, stack, opStack) self._saveStack(key, stack, opStack, cb) }) } else { // simple removing a leaf and recaluclation the stack if (parentNode) { stack.push(parentNode) } stack.push(lastNode) self._saveStack(key, stack, opStack, cb) } } } // Creates the initial node from an empty tree Trie.prototype._createInitialNode = function (key, value, cb) { var newNode = new TrieNode('leaf', key, value) this.root = newNode.hash() this._putNode(newNode, cb) } // formats node to be saved by levelup.batch. // returns either the hash that will be used key or the rawNode Trie.prototype._formatNode = function (node, topLevel, remove, opStack) { if (arguments.length === 3) { opStack = remove remove = false } var rlpNode = node.serialize() if (rlpNode.length >= 32 || topLevel) { var hashRoot = node.hash() if (remove && this.isCheckpoint) { opStack.push({ type: 'del', key: hashRoot }) } else { opStack.push({ type: 'put', key: hashRoot, value: rlpNode }) } return hashRoot } return node.raw } /** * The `data` event is given an `Object` hat has two properties; the `key` and the `value`. Both should be Buffers. * @method createReadStream * @return {stream.Readable} Returns a [stream](https://nodejs.org/dist/latest-v5.x/docs/api/stream.html#stream_class_stream_readable) of the contents of the `trie` */ Trie.prototype.createReadStream = function () { return new ReadStream(this) } // creates a new trie backed by the same db // and starting at the same root Trie.prototype.copy = function () { return new Trie(this.db, this.root) } /** * The given hash of operations (key additions or deletions) are executed on the DB * @method batch * @example * var ops = [ * { type: 'del', key: 'father' } * , { type: 'put', key: 'name', value: 'Yuri Irsenovich Kim' } * , { type: 'put', key: 'dob', value: '16 February 1941' } * , { type: 'put', key: 'spouse', value: 'Kim Young-sook' } * , { type: 'put', key: 'occupation', value: 'Clown' } * ] * trie.batch(ops) * @param {Array} ops * @param {Function} cb */ Trie.prototype.batch = function (ops, cb) { var self = this async.eachSeries(ops, function (op, cb2) { if (op.type === 'put') { self.put(op.key, op.value, cb2) } else if (op.type === 'del') { self.del(op.key, cb2) } else { cb2() } }, cb) } /** * Checks if a given root exists * @method checkRoot * @param {Buffer} root * @param {Function} cb */ Trie.prototype.checkRoot = function (root, cb) { root = ircUtil.toBuffer(root) this._lookupNode(root, function (value) { cb(null, !!value) }) } },{"./prioritizedTaskExecutor":160,"./readStream":162,"./trieNode":163,"./util":164,"assert":9,"async":13,"icjs-util":64,"levelup":109,"memdown":114,"rlp":135,"semaphore":143}],2:[function(require,module,exports){ const levelup = require('levelup') const memdown = require('memdown') const async = require('async') const inherits = require('util').inherits const Readable = require('readable-stream').Readable const levelws = require('level-ws') const callTogether = require('./util').callTogether module.exports = checkpointInterface function checkpointInterface (trie) { this._scratch = null trie._checkpoints = [] Object.defineProperty(trie, 'isCheckpoint', { get: function () { return !!trie._checkpoints.length } }) // new methods trie.checkpoint = checkpoint trie.commit = commit trie.revert = revert trie._enterCpMode = _enterCpMode trie._exitCpMode = _exitCpMode trie.createScratchReadStream = createScratchReadStream // overwrites trie.copy = copy.bind(trie, trie.copy.bind(trie)) } /** * Creates a checkpoint that can later be reverted to or committed. After this is called, no changes to the trie will be permanently saved until `commit` is called * @method checkpoint */ function checkpoint () { var self = this var wasCheckpoint = self.isCheckpoint self._checkpoints.push(self.root) if (!wasCheckpoint && self.isCheckpoint) { self._enterCpMode() } } /** * commits a checkpoint to disk * @method commit * @param {Function} cb the callback */ function commit (cb) { var self = this cb = callTogether(cb, self.sem.leave) self.sem.take(function () { if (self.isCheckpoint) { self._checkpoints.pop() if (!self.isCheckpoint) { self._exitCpMode(true, cb) } else { cb() } } else { throw new Error('trying to commit when not checkpointed') } }) } /** * Reverts the trie to the state it was at when `checkpoint` was first called. * @method revert * @param {Function} cb the callback */ function revert (cb) { var self = this cb = callTogether(cb, self.sem.leave) self.sem.take(function () { if (self.isCheckpoint) { self.root = self._checkpoints.pop() if (!self.isCheckpoint) { self._exitCpMode(false, cb) return } } cb() }) } // enter into checkpoint mode function _enterCpMode () { this._scratch = levelup('', { db: memdown }) this._getDBs = [this._scratch].concat(this._getDBs) this.__putDBs = this._putDBs this._putDBs = [this._scratch] this._putRaw = this.putRaw this.putRaw = putRaw } // exit from checkpoint mode function _exitCpMode (commitState, cb) { var self = this var scratch = this._scratch this._scratch = null this._getDBs = this._getDBs.slice(1) this._putDBs = this.__putDBs this.putRaw = this._putRaw function flushScratch (db, cb) { if (!db.createWriteStream) { db = levelws(db) } self.createScratchReadStream(scratch) .pipe(db.createWriteStream()) .on('close', cb) } if (commitState) { async.map(this._putDBs, flushScratch, cb) } else { cb() } } // adds the interface when copying the trie function copy (_super) { var trie = _super() checkpointInterface.call(trie, trie) trie._scratch = this._scratch // trie._checkpoints = this._checkpoints.slice() return trie } function putRaw (key, val, cb) { function dbPut (db, cb2) { db.put(key, val, { keyEncoding: 'binary', valueEncoding: 'binary' }, cb2) } async.each(this.__putDBs, dbPut, cb) } function createScratchReadStream (scratch) { var trie = this.copy() scratch = scratch || this._scratch // only read from the scratch trie._getDBs = [scratch] trie._scratch = scratch return new ScratchReadStream(trie) } // ScratchReadStream // this is used to minimally dump the scratch into the db inherits(ScratchReadStream, Readable) function ScratchReadStream (trie) { this.trie = trie this.next = null Readable.call(this, { objectMode: true }) } ScratchReadStream.prototype._read = function () { var self = this if (!self._started) { self._started = true self.trie._findDbNodes(function (nodeRef, node, key, next) { self.push({ key: nodeRef, value: node.serialize() }) next() }, function () { // close stream self.push(null) }) } } },{"./util":164,"async":13,"level-ws":94,"levelup":109,"memdown":114,"readable-stream":131,"util":158}],3:[function(require,module,exports){ const BaseTrie = require('./baseTrie') const checkpointInterface = require('./checkpoint-interface') const inherits = require('util').inherits const proof = require('./proof.js') module.exports = CheckpointTrie inherits(CheckpointTrie, BaseTrie) function CheckpointTrie () { BaseTrie.apply(this, arguments) checkpointInterface.call(this, this) } CheckpointTrie.prove = proof.prove CheckpointTrie.verifyProof = proof.verifyProof },{"./baseTrie":1,"./checkpoint-interface":2,"./proof.js":161,"util":158}],4:[function(require,module,exports){ (function (process){ /* Copyright (c) 2017 Rod Vagg, MIT License */ function AbstractChainedBatch (db) { this._db = db this._operations = [] this._written = false } AbstractChainedBatch.prototype._serializeKey = function (key) { return this._db._serializeKey(key) } AbstractChainedBatch.prototype._serializeValue = function (value) { return this._db._serializeValue(value) } AbstractChainedBatch.prototype._checkWritten = function () { if (this._written) throw new Error('write() already called on this batch') } AbstractChainedBatch.prototype.put = function (key, value) { this._checkWritten() var err = this._db._checkKey(key, 'key', this._db._isBuffer) if (err) throw err key = this._serializeKey(key) value = this._serializeValue(value) if (typeof this._put == 'function' ) this._put(key, value) else this._operations.push({ type: 'put', key: key, value: value }) return this } AbstractChainedBatch.prototype.del = function (key) { this._checkWritten() var err = this._db._checkKey(key, 'key', this._db._isBuffer) if (err) throw err key = this._serializeKey(key) if (typeof this._del == 'function' ) this._del(key) else this._operations.push({ type: 'del', key: key }) return this } AbstractChainedBatch.prototype.clear = function () { this._checkWritten() this._operations = [] if (typeof this._clear == 'function' ) this._clear() return this } AbstractChainedBatch.prototype.write = function (options, callback) { this._checkWritten() if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('write() requires a callback argument') if (typeof options != 'object') options = {} this._written = true if (typeof this._write == 'function' ) return this._write(callback) if (typeof this._db._batch == 'function') return this._db._batch(this._operations, options, callback) process.nextTick(callback) } module.exports = AbstractChainedBatch }).call(this,require('_process')) },{"_process":117}],5:[function(require,module,exports){ (function (process){ /* Copyright (c) 2017 Rod Vagg, MIT License */ function AbstractIterator (db) { this.db = db this._ended = false this._nexting = false } AbstractIterator.prototype.next = function (callback) { var self = this if (typeof callback != 'function') throw new Error('next() requires a callback argument') if (self._ended) return callback(new Error('cannot call next() after end()')) if (self._nexting) return callback(new Error('cannot call next() before previous next() has completed')) self._nexting = true if (typeof self._next == 'function') { return self._next(function () { self._nexting = false callback.apply(null, arguments) }) } process.nextTick(function () { self._nexting = false callback() }) } AbstractIterator.prototype.end = function (callback) { if (typeof callback != 'function') throw new Error('end() requires a callback argument') if (this._ended) return callback(new Error('end() already called on iterator')) this._ended = true if (typeof this._end == 'function') return this._end(callback) process.nextTick(callback) } module.exports = AbstractIterator }).call(this,require('_process')) },{"_process":117}],6:[function(require,module,exports){ (function (Buffer,process){ /* Copyright (c) 2017 Rod Vagg, MIT License */ var xtend = require('xtend') , AbstractIterator = require('./abstract-iterator') , AbstractChainedBatch = require('./abstract-chained-batch') function AbstractLevelDOWN (location) { if (!arguments.length || location === undefined) throw new Error('constructor requires at least a location argument') if (typeof location != 'string') throw new Error('constructor requires a location string argument') this.location = location this.status = 'new' } AbstractLevelDOWN.prototype.open = function (options, callback) { var self = this , oldStatus = this.status if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('open() requires a callback argument') if (typeof options != 'object') options = {} options.createIfMissing = options.createIfMissing != false options.errorIfExists = !!options.errorIfExists if (typeof this._open == 'function') { this.status = 'opening' this._open(options, function (err) { if (err) { self.status = oldStatus return callback(err) } self.status = 'open' callback() }) } else { this.status = 'open' process.nextTick(callback) } } AbstractLevelDOWN.prototype.close = function (callback) { var self = this , oldStatus = this.status if (typeof callback != 'function') throw new Error('close() requires a callback argument') if (typeof this._close == 'function') { this.status = 'closing' this._close(function (err) { if (err) { self.status = oldStatus return callback(err) } self.status = 'closed' callback() }) } else { this.status = 'closed' process.nextTick(callback) } } AbstractLevelDOWN.prototype.get = function (key, options, callback) { var err if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('get() requires a callback argument') if (err = this._checkKey(key, 'key')) return callback(err) key = this._serializeKey(key) if (typeof options != 'object') options = {} options.asBuffer = options.asBuffer != false if (typeof this._get == 'function') return this._get(key, options, callback) process.nextTick(function () { callback(new Error('NotFound')) }) } AbstractLevelDOWN.prototype.put = function (key, value, options, callback) { var err if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('put() requires a callback argument') if (err = this._checkKey(key, 'key')) return callback(err) key = this._serializeKey(key) value = this._serializeValue(value) if (typeof options != 'object') options = {} if (typeof this._put == 'function') return this._put(key, value, options, callback) process.nextTick(callback) } AbstractLevelDOWN.prototype.del = function (key, options, callback) { var err if (typeof options == 'function') callback = options if (typeof callback != 'function') throw new Error('del() requires a callback argument') if (err = this._checkKey(key, 'key')) return callback(err) key = this._serializeKey(key) if (typeof options != 'object') options = {} if (typeof this._del == 'function') return this._del(key, options, callback) process.nextTick(callback) } AbstractLevelDOWN.prototype.batch = function (array, options, callback) { if (!arguments.length) return this._chainedBatch() if (typeof options == 'function') callback = options if (typeof array == 'function') callback = array if (typeof callback != 'function') throw new Error('batch(array) requires a callback argument') if (!Array.isArray(array)) return callback(new Error('batch(array) requires an array argument')) if (!options || typeof options != 'object') options = {} var i = 0 , l = array.length , e , err for (; i < l; i++) { e = array[i] if (typeof e != 'object') continue if (err = this._checkKey(e.type, 'type')) return callback(err) if (err = this._checkKey(e.key, 'key')) return callback(err) } if (typeof this._batch == 'function') return this._batch(array, options, callback) process.nextTick(callback) } //TODO: remove from here, not a necessary primitive AbstractLevelDOWN.prototype.approximateSize = function (start, end, callback) { if ( start == null || end == null || typeof start == 'function' || typeof end == 'function') { throw new Error('approximateSize() requires valid `start`, `end` and `callback` arguments') } if (typeof callback != 'function') throw new Error('approximateSize() requires a callback argument') start = this._serializeKey(start) end = this._serializeKey(end) if (typeof this._approximateSize == 'function') return this._approximateSize(start, end, callback) process.nextTick(function () { callback(null, 0) }) } AbstractLevelDOWN.prototype._setupIteratorOptions = function (options) { var self = this options = xtend(options) ;[ 'start', 'end', 'gt', 'gte', 'lt', 'lte' ].forEach(function (o) { if (options[o] && self._isBuffer(options[o]) && options[o].length === 0) delete options[o] }) options.reverse = !!options.reverse options.keys = options.keys != false options.values = options.values != false options.limit = 'limit' in options ? options.limit : -1 options.keyAsBuffer = options.keyAsBuffer != false options.valueAsBuffer = options.valueAsBuffer != false return options } AbstractLevelDOWN.prototype.iterator = function (options) { if (typeof options != 'object') options = {} options = this._setupIteratorOptions(options) if (typeof this._iterator == 'function') return this._iterator(options) return new AbstractIterator(this) } AbstractLevelDOWN.prototype._chainedBatch = function () { return new AbstractChainedBatch(this) } AbstractLevelDOWN.prototype._isBuffer = function (obj) { return Buffer.isBuffer(obj) } AbstractLevelDOWN.prototype._serializeKey = function (key) { return this._isBuffer(key) ? key : String(key) } AbstractLevelDOWN.prototype._serializeValue = function (value) { if (value == null) return '' return this._isBuffer(value) || process.browser ? value : String(value) } AbstractLevelDOWN.prototype._checkKey = function (obj, type) { if (obj === null || obj === undefined) return new Error(type + ' cannot be `null` or `undefined`') if (this._isBuffer(obj) && obj.length === 0) return new Error(type + ' cannot be an empty Buffer') else if (String(obj) === '') return new Error(type + ' cannot be an empty String') } module.exports = AbstractLevelDOWN }).call(this,{"isBuffer":require("../is-buffer/index.js")},require('_process')) },{"../is-buffer/index.js":73,"./abstract-chained-batch":4,"./abstract-iterator":5,"_process":117,"xtend":159}],7:[function(require,module,exports){ exports.AbstractLevelDOWN = require('./abstract-leveldown') exports.AbstractIterator = require('./abstract-iterator') exports.AbstractChainedBatch = require('./abstract-chained-batch') exports.isLevelDOWN = require('./is-leveldown') },{"./abstract-chained-batch":4,"./abstract-iterator":5,"./abstract-leveldown":6,"./is-leveldown":8}],8:[function(require,module,exports){ var AbstractLevelDOWN = require('./abstract-leveldown') function isLevelDOWN (db) { if (!db || typeof db !== 'object') return false return Object.keys(AbstractLevelDOWN.prototype).filter(function (name) { // TODO remove approximateSize check when method is gone return name[0] != '_' && name != 'approximateSize' }).every(function (name) { return typeof db[name] == 'function' }) } module.exports = isLevelDOWN },{"./abstract-leveldown":6}],9:[function(require,module,exports){ (function (global){ 'use strict'; // compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js // original notice: /*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org> * @license MIT */ function compare(a, b) { if (a === b) { return 0; } var x = a.length; var y = b.length; for (var i = 0, len = Math.min(x, y); i < len; ++i) { if (a[i] !== b[i]) { x = a[i]; y = b[i]; break; } } if (x < y) { return -1; } if (y < x) { return 1; } return 0; } function isBuffer(b) { if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { return global.Buffer.isBuffer(b); } return !!(b != null && b._isBuffer); } // based on node assert, original notice: // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 // // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! // // Originally from narwhal.js (http://narwhaljs.org) // Copyright (c) 2009 Thomas Robinson <280north.com> // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the 'Software'), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var util = require('util/'); var hasOwn = Object.prototype.hasOwnProperty; var pSlice = Array.prototype.slice; var functionsHaveNames = (function () { return function foo() {}.name === 'foo'; }()); function pToString (obj) { return Object.prototype.toString.call(obj); } function isView(arrbuf) { if (isBuffer(arrbuf)) { return false; } if (typeof global.ArrayBuffer !== 'function') { return false; } if (typeof ArrayBuffer.isView === 'function') { return ArrayBuffer.isView(arrbuf); } if (!arrbuf) { return false; } if (arrbuf instanceof DataView) { return true; } if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { return true; } return false; } // 1. The assert module provides functions that throw // AssertionError's when particular conditions are not met. The // assert module must conform to the following interface. var assert = module.exports = ok; // 2. The AssertionError is defined in assert. // new assert.AssertionError({ message: message, // actual: actual, // expected: expected }) var regex = /\s*function\s+([^\(\s]*)\s*/; // based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js function getName(func) { if (!util.isFunction(func)) { return; } if (functionsHaveNames) { return func.name; } var str = func.toString(); var match = str.match(regex); return match && match[1]; } assert.AssertionError = function AssertionError(options) { this.name = 'AssertionError'; this.actual = options.actual; this.expected = options.expected; this.operator = options.operator; if (options.message) { this.message = options.message; this.generatedMessage = false; } else { this.message = getMessage(this); this.generatedMessage = true; } var stackStartFunction = options.stackStartFunction || fail; if (Error.captureStackTrace) { Error.captureStackTrace(this, stackStartFunction); } else { // non v8 browsers so we can have a stacktrace var err = new Error(); if (err.stack) { var out = err.stack; // try to strip useless frames var fn_name = getName(stackStartFunction); var idx = out.indexOf('\n' + fn_name); if (idx >= 0) { // once we have located the function frame // we need to strip out everything before it (and its line) var next_line = out.indexOf('\n', idx + 1); out = out.substring(next_line + 1); } this.stack = out; } } }; // assert.AssertionError instanceof Error util.inherits(assert.AssertionError, Error); function truncate(s, n) { if (typeof s === 'string') { return s.length < n ? s : s.slice(0, n); } else { return s; } } function inspect(something) { if (functionsHaveNames || !util.isFunction(something)) { return util.inspect(something); } var rawname = getName(something); var name = rawname ? ': ' + rawname : ''; return '[Function' + name + ']'; } function getMessage(self) { return truncate(inspect(self.actual), 128) + ' ' + self.operator + ' ' + truncate(inspect(self.expected), 128); } // At present only the three keys mentioned above are used and // understood by the spec. Implementations or sub modules can pass // other keys to the AssertionError's constructor - they will be // ignored. // 3. All of the following functions must throw an AssertionError // when a corresponding condition is not met, with a message that // may be undefined if not provided. All assertion methods provide // both the actual and expected values to the assertion error for // display purposes. function fail(actual, expected, message, operator, stackStartFunction) { throw new assert.AssertionError({ message: message, actual: actual, expected: expected, operator: operator, stackStartFunction: stackStartFunction }); } // EXTENSION! allows for well behaved errors defined elsewhere. assert.fail = fail; // 4. Pure assertion tests whether a value is truthy, as determined // by !!guard. // assert.ok(guard, message_opt); // This statement is equivalent to assert.equal(true, !!guard, // message_opt);. To test strictly for the value true, use // assert.strictEqual(true, guard, message_opt);. function ok(value, message) { if (!value) fail(value, true, message, '==', assert.ok); } assert.ok = ok; // 5. The equality assertion tests shallow, coercive equality with // ==. // assert.equal(actual, expected, message_opt); assert.equal = function equal(actual, expected, message) { if (actual != expected) fail(actual, expected, message, '==', assert.equal); }; // 6. The non-equality assertion tests for whether two objects are not equal // with != assert.notEqual(actual, expected, message_opt); assert.notEqual = function notEqual(actual, expected, message) { if (actual == expected) { fail(actual, expected, message, '!=', assert.notEqual); } }; // 7. The equivalence assertion tests a deep equality relation. // assert.deepEqual(actual, expected, message_opt); assert.deepEqual = function deepEqual(actual, expected, message) { if (!_deepEqual(actual, expected, false)) { fail(actual, expected, message, 'deepEqual', assert.deepEqual); } }; assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { if (!_deepEqual(actual, expected, true)) { fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); } }; function _deepEqual(actual, expected, strict, memos) { // 7.1. All identical values are equivalent, as determined by ===. if (actual === expected) { return true; } else if (isBuffer(actual) && isBuffer(expected)) { return compare(actual, expected) === 0; // 7.2. If the expected value is a Date object, the actual value is // equivalent if it is also a Date object that refers to the same time. } else if (util.isDate(actual) && util.isDate(expected)) { return actual.getTime() === expected.getTime(); // 7.3 If the expected value is a RegExp object, the actual value is // equivalent if it is also a RegExp object with the same source and // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). } else if (util.isRegExp(actual) && util.isRegExp(expected)) { return actual.source === expected.source && actual.global === expected.global && actual.multiline === expected.multiline && actual.lastIndex === expected.lastIndex && actual.ignoreCase === expected.ignoreCase; // 7.4. Other pairs that do not both pass typeof value == 'object', // equivalence is determined by ==. } else if ((actual === null || typeof actual !== 'object') && (expected === null || typeof expected !== 'object')) { return strict ? actual === expected : actual == expected; // If both values are instances of typed arrays, wrap their underlying // ArrayBuffers in a Buffer each to increase performance // This optimization requires the arrays to have the same type as checked by // Object.prototype.toString (aka pToString). Never perform binary // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their // bit patterns are not identical. } else if (isView(actual) && isView(expected) && pToString(actual) === pToString(expected) && !(actual instanceof Float32Array || actual instanceof Float64Array)) { return compare(new Uint8Array(actual.buffer), new Uint8Array(expected.buffer)) === 0; // 7.5 For all other Object pairs, including Array objects, equivalence is // determined by having the same number of owned properties (as verified // with Object.prototype.hasOwnProperty.call), the same set of keys // (although not necessarily the same order), equivalent values for every // corresponding key, and an identical 'prototype' property. Note: this // accounts for both named and indexed properties on Arrays. } else if (isBuffer(actual) !== isBuffer(expected)) { return false; } else { memos = memos || {actual: [], expected: []}; var actualIndex = memos.actual.indexOf(actual); if (actualIndex !== -1) { if (actualIndex === memos.expected.indexOf(expected)) { return true; } } memos.actual.push(actual); memos.expecte