From 0d7d8073a2411bb08413098cdc9c668257f30b41 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 16 Apr 2016 05:55:00 -0700 Subject: [PATCH] refactor. --- lib/bcoin/chain.js | 8 +-- lib/bcoin/chaindb.js | 135 +++++++++++++++++++++------------------ lib/bcoin/coinview.js | 13 +++- lib/bcoin/http/client.js | 2 +- lib/bcoin/mempool.js | 55 ++++++++-------- lib/bcoin/peer.js | 8 ++- lib/bcoin/pool.js | 58 +++++++++-------- lib/bcoin/txdb.js | 31 +++------ 8 files changed, 163 insertions(+), 147 deletions(-) diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 3e6f9fdc..ff14b489 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -347,7 +347,7 @@ Chain.prototype._preload = function _preload(callback) { stream.on('data', function(data) { var blocks = []; var need = 80 - buf.size; - var lastEntry; + var lastEntry, block, entry, start; while (data.length >= need) { buf.data.push(data.slice(0, need)); @@ -367,8 +367,8 @@ Chain.prototype._preload = function _preload(callback) { if (blocks.length === 0) return; - blocks.forEach(function(data) { - var block, entry, start; + for (i = 0; i < blocks.length; i++) { + block = blocks[i]; try { data = parseHeader(data); @@ -423,7 +423,7 @@ Chain.prototype._preload = function _preload(callback) { lastEntry = entry; height++; - }); + } }); stream.on('end', function() { diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index c51bf12d..9439a2c2 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -19,9 +19,13 @@ var DUMMY = new Buffer([0]); * @exports ChainDB * @constructor * @param {Object} options - * @param {Boolean?} options.prune - * @param {Boolean?} options.spv - * @param {Number?} options.keepBlocks + * @param {Boolean?} options.prune - Whether to prune the chain. + * @param {Boolean?} options.spv - SPV-mode, will not save block + * data, only entries. + * @param {Number?} [options.keepBlocks=288] - Number of + * blocks to keep when pruning. + * @param {Boolean?} options.paranoid - Perform some paranoid checks + * against hashes. Will throw if corruption is detected. * @param {String?} options.name - Database name * @param {String?} options.location - Database location * @param {String?} options.db - Database backend name @@ -165,8 +169,8 @@ ChainDB.prototype.addCache = function addCache(entry) { }; /** - * Test the cache for a present transaction. - * @param {Hash} hash + * Test the cache for a present entry hash or height. + * @param {Hash|Number} hash - Hash or height. */ ChainDB.prototype.hasCache = function hasCache(hash) { @@ -180,10 +184,10 @@ ChainDB.prototype.hasCache = function hasCache(hash) { }; /** - * Get a transaction directly from the LRU cache. This is + * Get an entry directly from the LRU cache. This is * useful for optimization if we don't want to wait on a * nextTick during a `get()` call. - * @param {Hash} hash + * @param {Hash|Number} hash - Hash or height. */ ChainDB.prototype.getCache = function getCache(hash) { @@ -718,14 +722,17 @@ ChainDB.prototype.has = function has(height, callback) { */ ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback) { + var i, tx; + if (this.options.spv) return utils.nextTick(callback); batch.put('b/b/' + block.hash('hex'), block.toCompact()); - block.txs.forEach(function(tx) { + for (i = 0; i < block.txs.length; i++) { + tx = block.txs[i]; batch.put('t/t/' + tx.hash('hex'), tx.toExtended()); - }); + } if (!connect) return utils.nextTick(callback); @@ -743,6 +750,7 @@ ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) { var self = this; + var i, tx; if (this.options.spv) return utils.nextTick(callback); @@ -756,9 +764,10 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) { batch.del('b/b/' + block.hash('hex')); - block.txs.forEach(function(tx) { + for (i = 0; i < block.txs.length; i++) { + tx = block.txs[i]; batch.del('t/t/' + tx.hash('hex')); - }); + } self.disconnectBlock(block, batch, callback); }); @@ -773,6 +782,7 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) { ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) { var self = this; + var i, j, tx, input, output, key, address, hash, uniq; if (this.options.spv) { self.emit('add block', block); @@ -788,16 +798,17 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) { if (!block) return callback(); - block.txs.forEach(function(tx) { - var hash = tx.hash('hex'); - var uniq = {}; + for (i = 0; i < block.txs.length; i++) { + tx = block.txs[i]; + hash = tx.hash('hex'); + uniq = {}; - tx.inputs.forEach(function(input) { - var key = input.prevout.hash + '/' + input.prevout.index; - var address; + for (j = 0; j < tx.inputs.length; j++) { + input = tx.inputs[j]; + key = input.prevout.hash + '/' + input.prevout.index; if (tx.isCoinbase()) - return; + break; assert(input.coin); @@ -814,11 +825,11 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) { } batch.del('u/t/' + key); - }); + } - tx.outputs.forEach(function(output, i) { - var key = hash + '/' + i; - var address; + for (j = 0; j < tx.inputs.length; j++) { + output = tx.outputs[j]; + key = hash + '/' + j; if (self.options.indexAddress) { address = output.getAddress(); @@ -832,9 +843,9 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) { batch.put('u/a/' + address + '/' + key, DUMMY); } - batch.put('u/t/' + key, bcoin.coin(tx, i).toRaw()); - }); - }); + batch.put('u/t/' + key, bcoin.coin(tx, j).toRaw()); + } + } self.emit('add block', block); @@ -849,13 +860,14 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) { * @param {Function} callback - Returns [Error, {@link Block}]. */ -ChainDB.prototype.disconnectBlock = function disconnectBlock(hash, batch, callback) { +ChainDB.prototype.disconnectBlock = function disconnectBlock(block, batch, callback) { var self = this; + var i, j, tx, input, output, key, address, hash, uniq; if (this.options.spv) return utils.nextTick(callback); - this._ensureHistory(hash, function(err, block) { + this._ensureHistory(block, function(err, block) { if (err) return callback(err); @@ -863,20 +875,21 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(hash, batch, callba return callback(new Error('Block not found.')); if (self.options.paranoid) { - if (typeof hash === 'string') - assert(block.hash('hex') === hash, 'Database is corrupt.'); + if (typeof block === 'string') + assert(block.hash('hex') === block, 'Database is corrupt.'); } - block.txs.slice().reverse().forEach(function(tx) { - var hash = tx.hash('hex'); - var uniq = {}; + for (i = block.txs.length - 1; i >= 0; i--) { + tx = block.txs[i]; + hash = tx.hash('hex'); + uniq = {}; - tx.inputs.forEach(function(input) { - var key = input.prevout.hash + '/' + input.prevout.index; - var address; + for (j = 0; j < tx.inputs.length; j++) { + input = tx.inputs[j]; + key = input.prevout.hash + '/' + input.prevout.index; if (tx.isCoinbase()) - return; + break; assert(input.coin); @@ -893,11 +906,11 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(hash, batch, callba } batch.put('u/t/' + key, input.coin.toRaw()); - }); + } - tx.outputs.forEach(function(output, i) { - var key = hash + '/' + i; - var address; + for (j = 0; j < tx.inputs.length; j++) { + output = tx.outputs[j]; + key = hash + '/' + j; if (self.options.indexAddress) { address = output.getAddress(); @@ -912,8 +925,8 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(hash, batch, callba } batch.del('u/t/' + key); - }); - }); + } + } self.emit('remove block', block); @@ -1032,16 +1045,11 @@ ChainDB.prototype.fillTX = function fillTX(tx, callback) { * @param {Function} callback - Returns [Error, {@link Coin}[]]. */ -ChainDB.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, options, callback) { +ChainDB.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, callback) { var self = this; var ids = []; var coins = []; - if (!callback) { - callback = options; - options = {}; - } - if (typeof addresses === 'string') addresses = [addresses]; @@ -1141,17 +1149,12 @@ ChainDB.prototype.getCoin = function getCoin(hash, index, callback) { * @param {Function} callback - Returns [Error, {@link TX}[]]. */ -ChainDB.prototype.getTXByAddress = function getTXByAddress(addresses, options, callback) { +ChainDB.prototype.getTXByAddress = function getTXByAddress(addresses, callback) { var self = this; var hashes = []; var txs = []; var have = {}; - if (!callback) { - callback = options; - options = {}; - } - if (typeof addresses === 'string') addresses = [addresses]; @@ -1543,6 +1546,7 @@ ChainDB.prototype.isSpentTX = function isSpentTX(hash, callback) { ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) { var futureHeight; + var i, j, tx, input; if (this.options.spv) return callback(); @@ -1558,11 +1562,15 @@ ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) { batch.put('b/q/' + futureHeight, block.hash()); - block.txs.forEach(function(tx) { - if (tx.isCoinbase()) - return; + for (i = 0; i < block.txs.length; i++) { + tx = block.txs[i]; + + if (tx.isCoinbase()) + break; + + for (j = 0; j < tx.inputs.length; j++) { + input = tx.inputs[j]; - tx.inputs.forEach(function(input) { assert(input.coin); batch.put('u/x/' @@ -1575,8 +1583,8 @@ ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) { + '/' + input.prevout.hash + '/' + input.prevout.index, DUMMY); - }); - }); + } + } this._pruneQueue(block, batch, callback); }; @@ -1584,6 +1592,8 @@ ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) { ChainDB.prototype._pruneQueue = function _pruneQueue(block, batch, callback) { var self = this; var key = 'b/q/' + pad32(block.height); + var i; + this.db.get(key, function(err, hash) { if (err && err.type !== 'NotFoundError') return callback(err); @@ -1610,9 +1620,8 @@ ChainDB.prototype._pruneQueue = function _pruneQueue(block, batch, callback) { batch.del('b/b/' + hash); - compact.hashes.forEach(function(hash) { - batch.del('t/t/' + hash); - }); + for (i = 0; i < compact.hashes.length; i++) + batch.del('t/t/' + compact.hashes[i]); self._pruneCoinQueue(block, batch, callback); }); diff --git a/lib/bcoin/coinview.js b/lib/bcoin/coinview.js index 05757680..ec759081 100644 --- a/lib/bcoin/coinview.js +++ b/lib/bcoin/coinview.js @@ -150,9 +150,16 @@ CoinView.prototype.fill = function fill(obj, spend) { */ CoinView.prototype.toArray = function toArray() { - return Object.keys(this.coins).map(function(hash) { - return this.coins[hash]; - }, this); + var keys = Object.keys(this.coins); + var out = []; + var i, hash; + + for (i = 0; i < keys.length; i++) { + hash = keys[i]; + out = out.concat(this.coins[hash].toArray()); + } + + return out; }; return CoinView; diff --git a/lib/bcoin/http/client.js b/lib/bcoin/http/client.js index 66f89646..8eed9396 100644 --- a/lib/bcoin/http/client.js +++ b/lib/bcoin/http/client.js @@ -14,7 +14,7 @@ var assert = utils.assert; var request = require('./request'); /** - * Client + * BCoin HTTP client. * @exports Client * @constructor * @param {String} uri diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 3ed3c79e..a1563455 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -1145,9 +1145,9 @@ Mempool.prototype.removeOrphan = function removeOrphan(tx, callback) { } p = new BufferWriter(); - hashes.forEach(function(hash) { - p.writeHash(hash); - }); + + for (i = 0; i < hashes.length; i++) + p.writeHash(hashes[i]); batch.put('m/d/' + prev, p.render()); @@ -1410,7 +1410,7 @@ Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) { var self = this; var prefix = 'm/'; var hash = tx.hash('hex'); - var batch; + var i, addresses, address, input, output, key, coin, batch; var unlock = this.writeLock.lock(addUnchecked, [tx, callback], force); if (!unlock) @@ -1423,18 +1423,20 @@ Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) { batch.put(prefix + 't/t/' + hash, tx.toExtended()); batch.put(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash, DUMMY); - tx.getAddresses().forEach(function(address) { - batch.put(prefix + 't/a/' + address + '/' + hash, DUMMY); - }); + addresses = tx.getAddresses(); - tx.inputs.forEach(function(input) { - var key = input.prevout.hash + '/' + input.prevout.index; - var address; + for (i = 0; i < addresses.length; i++) + batch.put(prefix + 't/a/' + addresses[i] + '/' + hash, DUMMY); + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + key = input.prevout.hash + '/' + input.prevout.index; if (tx.isCoinbase()) - return; + break; assert(input.coin); + address = input.getAddress(); batch.del(prefix + 'u/t/' + key); @@ -1442,18 +1444,19 @@ Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) { if (address) batch.del(prefix + 'u/a/' + address + '/' + key); - }); + } - tx.outputs.forEach(function(output, i) { - var key = hash + '/' + i; - var address = output.getAddress(); - var coin = bcoin.coin(tx, i).toRaw(); + for (i = 0; i < tx.outputs.length; i++) { + output = tx.outputs[i]; + key = hash + '/' + i; + address = output.getAddress(); + coin = bcoin.coin(tx, i).toRaw(); batch.put(prefix + 'u/t/' + key, coin); if (address) batch.put(prefix + 'u/a/' + address + '/' + key, DUMMY); - }); + } return batch.write(callback); }; @@ -1469,7 +1472,7 @@ Mempool.prototype._addUnchecked = function addUnchecked(tx, callback, force) { Mempool.prototype._removeUnchecked = function removeUnchecked(hash, callback, force) { var self = this; var prefix = 'm/'; - var batch; + var batch, i, addresses, output; var unlock = this.writeLock.lock(removeUnchecked, [hash, callback], force); if (!unlock) @@ -1492,9 +1495,10 @@ Mempool.prototype._removeUnchecked = function removeUnchecked(hash, callback, fo batch.del(prefix + 't/t/' + hash); batch.del(prefix + 't/s/s/' + pad32(tx.ps) + '/' + hash); - tx.getAddresses().forEach(function(address) { - batch.del(prefix + 't/a/' + address + '/' + hash); - }); + addresses = tx.getAddresses(); + + for (i = 0; i < addresses.length; i++) + batch.del(prefix + 't/a/' + addresses[i] + '/' + hash); utils.forEachSerial(tx.inputs, function(input, next) { var key = input.prevout.hash + '/' + input.prevout.index; @@ -1530,15 +1534,16 @@ Mempool.prototype._removeUnchecked = function removeUnchecked(hash, callback, fo if (err) return callback(err); - tx.outputs.forEach(function(output, i) { - var key = hash + '/' + i; - var address = output.getAddress(); + for (i = 0; i < tx.outputs.length; i++) { + output = tx.outputs[i]; + key = hash + '/' + i; + address = output.getAddress(); batch.del(prefix + 'u/t/' + key); if (address) batch.del(prefix + 'u/a/' + address + '/' + key); - }); + } return batch.write(callback); }); diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 1adaad16..93a39e7e 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -1070,7 +1070,7 @@ Peer.prototype._handleGetData = function handleGetData(items) { var isWitness = item.type & constants.invWitnessMask; var type = item.type & ~constants.invWitnessMask; var hash = utils.toHex(item.hash); - var data; + var i, tx, data; if (type === constants.inv.tx) { if (!self.mempool) { @@ -1156,14 +1156,16 @@ Peer.prototype._handleGetData = function handleGetData(items) { self._write(self.framer.merkleBlock(block)); - block.txs.forEach(function(tx) { + for (i = 0; i < block.txs.length; i++) { + tx = block.txs[i]; + if (isWitness) tx = tx.renderWitness(); else tx = tx.renderNormal(); self._write(self.framer.packet('tx', tx)); - }); + } if (hash === self.hashContinue) { self._write(self.framer.inv([{ diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index ba59b60d..0338ba14 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -32,7 +32,6 @@ var constants = bcoin.protocol.constants; * send another getblocks request. * @param {Number?} [requestTimeout=120000] - Timeout for in-flight blocks. * @param {Number?} [invTimeout=60000] - Timeout for broadcasted objects. - * @param {Wallet[]?} wallets - Wallets to add to the bloom filter. * @param {Boolean?} listen - Whether to spin up a server socket * and listen for peers. * @param {Boolean?} selfish - A selfish pool. Will not serve blocks, @@ -293,10 +292,6 @@ Pool.prototype._init = function _init() { self.emit('full'); bcoin.debug('Chain is fully synced (height=%d).', self.chain.height); }); - - (this.options.wallets || []).forEach(function(wallet) { - self.addWallet(wallet); - }); }; /** @@ -893,12 +888,13 @@ Pool.prototype._load = function _load() { */ Pool.prototype.getMempool = function getMempool() { + var i; + if (this.peers.load) this.peers.load.getMempool(); - this.peers.regular.forEach(function(peer) { - peer.getMempool(); - }); + for (i = 0; i < this.peers.regular.length; i++) + this.peers.regular[i].getMempool(); }; Pool.prototype._createPeer = function _createPeer(options) { @@ -943,11 +939,14 @@ Pool.prototype._createPeer = function _createPeer(options) { }); peer.on('notfound', function(items) { - items.forEach(function(item) { - var req = self.request.map[utils.toHex(item.hash)]; + var i, item; + + for (i = 0; i < items.length; i++) { + item = items[i]; + req = self.request.map[utils.toHex(item.hash)]; if (req && req.peer === peer) item.finish(); - }); + } }); peer.on('tx', function(tx) { @@ -979,6 +978,8 @@ Pool.prototype._createPeer = function _createPeer(options) { }); peer.on('txs', function(txs) { + var i, hash; + self.emit('txs', txs, peer); if (!self.options.spv) { @@ -986,11 +987,11 @@ Pool.prototype._createPeer = function _createPeer(options) { return; } - txs.forEach(function(hash) { - hash = utils.toHex(hash); + for (i = 0; i < txs.length; i++) { + hash = utils.toHex(txs[i]); if (self.markTX(hash, 0)) self.getData(peer, self.tx.type, hash); - }); + } }); peer.on('version', function(version) { @@ -1356,6 +1357,7 @@ Pool.prototype.updateWatch = function updateWatch() { Pool.prototype.addWallet = function addWallet(wallet, callback) { var self = this; + var i; callback = utils.asyncify(callback); @@ -1366,9 +1368,8 @@ Pool.prototype.addWallet = function addWallet(wallet, callback) { if (err) return callback(err); - txs.forEach(function(tx) { - self.sendTX(tx); - }); + for (i = 0; i < txs.length; i++) + self.sendTX(txs[i]); if (!self.options.spv) return callback(); @@ -1713,7 +1714,7 @@ Pool.prototype._sendRequests = function _sendRequests(peer) { */ Pool.prototype.fulfill = function fulfill(hash) { - var item; + var i, item; if (Buffer.isBuffer(hash)) hash = utils.toHex(hash); @@ -1726,9 +1727,8 @@ Pool.prototype.fulfill = function fulfill(hash) { item.finish(); - item.callback.forEach(function(callback) { - callback(); - }); + for (i = 0; i < item.callback.length; i++) + item.callback[i](); return item; }; @@ -1811,10 +1811,12 @@ Pool.prototype.getTX = function getTX(hash, range, callback) { range = { start: utils.now() - delta, end: 0 }; function done(err, tx, range) { + var i; + delete self.validate.map[hash]; - cbs.forEach(function(callback) { - callback(err, tx, range); - }); + + for (i = 0; i < cbs.length; i++) + cbs[i](err, tx, range); } (function next() { @@ -2093,11 +2095,13 @@ Pool.prototype._getRandom = function _getRandom(seeds, uniq) { */ Pool.prototype.setSeeds = function setSeeds(seeds) { + var i, seed; + this.seeds = []; this.hosts = {}; - seeds.forEach(function(seed) { - this.addSeed(seed); - }, this); + + for (i = 0; i < seeds.length; i++) + this.addSeed(seeds[i]); }; /** diff --git a/lib/bcoin/txdb.js b/lib/bcoin/txdb.js index 1188025a..83f56637 100644 --- a/lib/bcoin/txdb.js +++ b/lib/bcoin/txdb.js @@ -547,14 +547,11 @@ TXPool.prototype._add = function add(tx, map, callback, force) { if (!orphans) { if (self.options.indexAddress && address) { map.table[address].forEach(function(id) { - batch.put( - prefix + 'u/a/' + id - + '/' + hash + '/' + i, - DUMMY); + batch.put(prefix + 'u/a/' + id + '/' + key, DUMMY); }); } - batch.put(prefix + 'u/t/' + hash + '/' + i, coin.toRaw()); + batch.put(prefix + 'u/t/' + key, coin.toRaw()); updated = true; } @@ -905,6 +902,7 @@ TXPool.prototype._remove = function remove(tx, map, callback, force) { return callback(err); tx.inputs.forEach(function(input) { + var key = input.prevout.hash + '/' + input.prevout.index; var address = input.getAddress(); if (tx.isCoinbase()) @@ -920,26 +918,17 @@ TXPool.prototype._remove = function remove(tx, map, callback, force) { if (self.options.indexAddress && address) { map.table[address].forEach(function(id) { - batch.put(prefix + 'u/a/' + id - + '/' + input.prevout.hash - + '/' + input.prevout.index, - DUMMY); + batch.put(prefix + 'u/a/' + id + '/' + key, DUMMY); }); } - batch.put(prefix + 'u/t/' - + input.prevout.hash - + '/' + input.prevout.index, - input.coin.toRaw()); - - batch.del(prefix + 's/t/' - + input.prevout.hash - + '/' + input.prevout.index); - - batch.del(prefix + 'o/' + input.prevout.hash + '/' + input.prevout.index); + batch.put(prefix + 'u/t/' + key, input.coin.toRaw()); + batch.del(prefix + 's/t/' + key); + batch.del(prefix + 'o/' + key); }); tx.outputs.forEach(function(output, i) { + var key = hash + '/' + i; var address = output.getAddress(); if (self.options.mapAddress) { @@ -949,11 +938,11 @@ TXPool.prototype._remove = function remove(tx, map, callback, force) { if (self.options.indexAddress && address) { map.table[address].forEach(function(id) { - batch.del(prefix + 'u/a/' + id + '/' + hash + '/' + i); + batch.del(prefix + 'u/a/' + id + '/' + key); }); } - batch.del(prefix + 'u/t/' + hash + '/' + i); + batch.del(prefix + 'u/t/' + key); }); batch.write(function(err) {