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
JavaScript
(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