diff --git a/lib/bcoin/ldb.js b/lib/bcoin/ldb.js index 4dacb10a..6d2e2087 100644 --- a/lib/bcoin/ldb.js +++ b/lib/bcoin/ldb.js @@ -12,12 +12,15 @@ var db = {}; module.exports = function ldb(name, options) { var file = bcoin.prefix + '/' + name + '-' + network.type + '.db'; - var backend = process.env.BCOIN_DB; + var backend = typeof options.db === 'string' + ? options.db + : process.env.BCOIN_DB; if (!db[file]) { - if (options.db) { + if (options.db && typeof options.db !== 'string') { + bcoin.ensurePrefix(); backend = options.db; - } else if (bcoin.isBrowser) { + } else if (bcoin.isBrowser && backend !== 'memdown') { backend = require('level-js'); } else { if (!backend || backend === 'leveldb') @@ -26,6 +29,11 @@ module.exports = function ldb(name, options) { backend = 'rocksdown'; else if (backend === 'lmdb') backend = 'lmdb'; + else if (backend === 'memory') + backend = 'memdown'; + + if (backend !== 'memdown') + bcoin.ensurePrefix(); backend = require(backend); } @@ -33,8 +41,6 @@ module.exports = function ldb(name, options) { if (!options) options = {}; - bcoin.ensurePrefix(); - db[file] = new LowlevelUp(file, { keyEncoding: 'ascii', valueEncoding: 'binary', diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 16f38f96..b6f7b8fd 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -29,6 +29,13 @@ function Mempool(node, options) { this.node = node; this.chain = node.chain; this.db = node.chain.db; + + if (this.options.memory) { + this.db = bcoin.ldb('mempool', { + db: 'memdown' + }); + } + this.tx = new bcoin.txdb('m', this.db, { indexSpent: true, indexExtra: false, @@ -100,7 +107,7 @@ Mempool.prototype._lock = function _lock(func, args, force) { self.busy = false; - if (func === Chain.prototype.add) { + if (func === Mempool.prototype.add) { if (self.pending.length === 0) self.emit('flush'); } diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index 71d24bfb..22a54cad 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -182,7 +182,7 @@ TXPool.prototype.mapAddresses = function mapAddresses(address, callback) { })(); }; -TXPool.prototype._addOrphan = function add(key, hash, index, callback) { +TXPool.prototype._addOrphan = function _addOrphan(key, hash, index, callback) { var prefix = this.prefix + '/'; var orphans; @@ -243,12 +243,12 @@ TXPool.prototype._getOrphans = function _getOrphans(key, callback) { }); }; -TXPool.prototype.add = function add(tx, callback) { +TXPool.prototype.add = function add(tx, callback, force) { var self = this; if (Array.isArray(tx)) { return utils.forEachSerial(tx, function(tx, next) { - self.add(tx, next); + self.add(tx, next, force); }, callback); } @@ -261,7 +261,7 @@ TXPool.prototype.add = function add(tx, callback) { return callback(null, false); } - return self._add(tx, map, callback); + return self._add(tx, map, callback, force); }); }; @@ -281,20 +281,16 @@ TXPool.prototype._add = function add(tx, map, callback, force) { if (!unlock) return; - function done(err, result) { - unlock(); - if (callback) - callback(err, result); - } + callback = utils.wrap(callback, unlock); // Attempt to confirm tx before adding it. this._confirm(tx, map, function(err, existing) { if (err) - return done(err); + return callback(err); // Ignore if we already have this tx. if (existing) - return done(null, true); + return callback(null, true); batch = self.db.batch(); @@ -353,7 +349,7 @@ TXPool.prototype._add = function add(tx, map, callback, force) { // Skip invalid transactions if (!tx.verify(i)) - return done(null, false); + return callback(null, false); updated = true; @@ -390,16 +386,16 @@ TXPool.prototype._add = function add(tx, map, callback, force) { self.isSpent(input.prevout.hash, input.prevout.index, function(err, result) { if (err) - return done(err); + return callback(err); // Are we double-spending? if (result) - return done(new Error('Transaction is double-spending.')); + return callback(new Error('Transaction is double-spending.')); // Add orphan, if no parent transaction is yet known self._addOrphan(key, hash, i, function(err, orphans) { if (err) - return done(err); + return callback(err); batch.put(prefix + 'o/' + key, orphans); @@ -409,7 +405,7 @@ TXPool.prototype._add = function add(tx, map, callback, force) { }); }, function(err) { if (err) - return done(err); + return callback(err); // Add unspent outputs or resolve orphans utils.forEachSerial(tx.outputs, function(output, next, i) { @@ -429,7 +425,7 @@ TXPool.prototype._add = function add(tx, map, callback, force) { var some = false; if (err) - return done(err); + return callback(err); if (!orphans) return finish(); @@ -459,7 +455,7 @@ TXPool.prototype._add = function add(tx, map, callback, force) { if (err) return next(err); return next(); - }); + }, true); }, function(err) { if (err) return next(err); @@ -493,11 +489,11 @@ TXPool.prototype._add = function add(tx, map, callback, force) { }); }, function(err) { if (err) - return done(err); + return callback(err); batch.write(function(err) { if (err) - return done(err); + return callback(err); self.emit('tx', tx, map); @@ -508,11 +504,11 @@ TXPool.prototype._add = function add(tx, map, callback, force) { self.emit('updated', tx, map); } - return done(null, true); + return callback(null, true); }); }); }); - }); + }, true); }; TXPool.prototype.isSpent = function isSpent(hash, index, callback, checkCoin) { @@ -548,12 +544,18 @@ TXPool.prototype.isSpent = function isSpent(hash, index, callback, checkCoin) { }); }; -TXPool.prototype._confirm = function _confirm(tx, map, callback) { +TXPool.prototype._confirm = function _confirm(tx, map, callback, force) { var self = this; var prefix = this.prefix + '/'; var hash = tx.hash('hex'); var batch; + var unlock = this._lock(_confirm, [tx, map, callback], force); + if (!unlock) + return; + + callback = utils.wrap(callback, unlock); + this.getTX(hash, function(err, existing) { if (err) return callback(err); @@ -634,16 +636,16 @@ TXPool.prototype._confirm = function _confirm(tx, map, callback) { }); }; -TXPool.prototype.remove = function remove(hash, callback) { +TXPool.prototype.remove = function remove(hash, callback, force) { var self = this; var first = Array.isArray(hash) ? hash[0] : hash; if (!this.options.indexExtra && (first instanceof bcoin.tx)) - return this.lazyRemove(hash, callback); + return this.lazyRemove(hash, callback, force); if (Array.isArray(hash)) { return utils.forEachSerial(hash, function(hash, next) { - self.remove(hash, next); + self.remove(hash, next, force); }, callback); } @@ -668,17 +670,17 @@ TXPool.prototype.remove = function remove(hash, callback) { return callback(null, false); } - return self._remove(tx, map, callback); + return self._remove(tx, map, callback, force); }); }); }; -TXPool.prototype.lazyRemove = function lazyRemove(tx, callback) { +TXPool.prototype.lazyRemove = function lazyRemove(tx, callback, force) { var self = this; if (Array.isArray(tx)) { return utils.forEachSerial(tx, function(tx, next) { - self.lazyRemove(tx, next); + self.lazyRemove(tx, next, force); }, callback); } @@ -691,15 +693,23 @@ TXPool.prototype.lazyRemove = function lazyRemove(tx, callback) { return callback(null, false); } - return self._remove(tx, map, callback); + return self._remove(tx, map, callback, force); }); }; -TXPool.prototype._remove = function remove(tx, map, callback) { +TXPool.prototype._remove = function remove(tx, map, callback, force) { var self = this; var prefix = this.prefix + '/'; var hash = tx.hash('hex'); - var batch = this.db.batch(); + var batch; + + var unlock = this._lock(remove, [tx, map, callback], force); + if (!unlock) + return; + + callback = utils.wrap(callback, unlock); + + batch = this.db.batch(); batch.del(prefix + 't/t/' + hash); @@ -795,12 +805,12 @@ TXPool.prototype._remove = function remove(tx, map, callback) { }); }; -TXPool.prototype.unconfirm = function unconfirm(hash, callback) { +TXPool.prototype.unconfirm = function unconfirm(hash, callback, force) { var self = this; if (Array.isArray(hash)) { return utils.forEachSerial(hash, function(hash, next) { - self.unconfirm(hash, next); + self.unconfirm(hash, next, force); }, callback); } @@ -827,18 +837,26 @@ TXPool.prototype.unconfirm = function unconfirm(hash, callback) { return callback(null, false); } - return self._unconfirm(tx, map, callback); + return self._unconfirm(tx, map, callback, force); }); }); }; -TXPool.prototype._unconfirm = function unconfirm(tx, map, callback) { +TXPool.prototype._unconfirm = function unconfirm(tx, map, callback, force) { var self = this; var prefix = this.prefix + '/'; - var hash = tx.hash('hex'); - var batch = this.db.batch(); - var height = tx.height; - var ts = tx.ts; + var hash, batch, height, ts; + + var unlock = this._lock(unconfirm, [tx, map, callback], force); + if (!unlock) + return; + + callback = utils.wrap(callback, unlock); + + hash = tx.hash('hex'); + batch = this.db.batch(); + height = tx.height; + ts = tx.ts; if (height !== -1) return callback(null, false);