diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 7a602bfa..9537f795 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -299,9 +299,14 @@ Chain.prototype.has = function has(hash, noProbe, cb) { return cb(!!this.orphan.map[hash]); }; -Chain.prototype.get = function get(hash, cb) { +Chain.prototype.get = function get(hash, force, cb) { + if (typeof force === 'function') { + cb = force; + force = false; + } + // Cached block found - if (this.block.bloom.test(hash, 'hex')) { + if (!force && this.block.bloom.test(hash, 'hex')) { for (var i = 0; i < this.block.list.length; i++) { if (this.block.list[i].hash('hex') === hash) { // NOTE: we return right after the statement - so `block` should be @@ -317,6 +322,7 @@ Chain.prototype.get = function get(hash, cb) { } if (this.request.add(hash, cb)) + false; this.emit('missing', hash, null, null); }; @@ -344,7 +350,21 @@ Chain.prototype.hashesInRange = function hashesInRange(start, end, cb) { start--; end = utils.binaryInsert(ts, end, compareTs, true); - return cb(this.index.hashes.slice(start, end)); + // Zip hashes and heights together and sort them by height + var hashes = this.index.hashes.slice(start, end); + var heights = this.index.heights.slice(start, end); + var zip = []; + for (var i = 0; i < hashes.length; i++) + zip.push({ hash: hashes[i], height: heights[i] }); + zip = zip.sort(function(a, b) { + return a.height - b.height; + }); + var hashes = zip.map(function(a) { + return a.hash; + }); + + var count = zip[zip.length - 1].height - zip[0].height + 1; + return cb(hashes, count); }; Chain.prototype.getLast = function getLast(cb) { diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index 6996dff8..8bfe1109 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -62,6 +62,7 @@ function Pool(options) { // getTX map map: {} }; + this.searching = false; // Currently broadcasted TXs this.tx = { @@ -355,7 +356,18 @@ Pool.prototype.unwatch = function unwatch(id) { this.peers.block[i].updateWatch(); }; -Pool.prototype.search = function search(id, range) { +Pool.prototype.search = function search(id, range, e) { + e = e || new EventEmitter(); + + // Serialize searches + if (this.searching) { + this.once('_searchEnd', function() { + this.search(id, range, e); + }); + return e; + } + this.searching = true; + // Optional id argument if (typeof id === 'object' && !Array.isArray(id) || typeof id === 'number') { @@ -378,33 +390,52 @@ Pool.prototype.search = function search(id, range) { range.start = +new Date() / 1000 - 432000; var self = this; - var e = new EventEmitter(); - this.chain.hashesInRange(range.start, range.end, function(hashes) { - var waiting = hashes.length; + this.chain.hashesInRange(range.start, range.end, function(hashes, count) { + var waiting = count; if (id) self.watch(id); self._loadRange(hashes, true); - hashes.slice().reverse().forEach(function(hash) { + hashes = hashes.slice().reverse(); + hashes.forEach(function(hash, i) { // Get the block that is in index - self.chain.get(hash, loadBlock); + self.chain.get(hash, true, function(block) { + loadBlock(block, hashes[i + 1]); + }); }); - function loadBlock(block) { + function loadBlock(block, stop) { + // Stop block reached + if (block.hash('hex') === stop) + return; + // Get block's prev and request it and all of it's parents up to // the next known block hash - self.chain.get(block.prevBlock, function() { - waiting--; - e.emit('progress', hashes.length - waiting, hashes.length); - if (waiting === 0) { - if (id) - self.unwatch(id); - e.emit('end'); - } + self.chain.get(block.prevBlock, block.prevBlock !== stop, function(prev) { + done(); + + // First hash loaded + if (!stop) + return; + + // Continue loading blocks + loadBlock(prev, stop); }); } + function done() { + waiting--; + e.emit('progress', count - waiting, count); + if (waiting === 0) { + if (id) + self.unwatch(id); + self.searching = false; + self.emit('_searchEnd'); + e.emit('end'); + } + } + // Empty search if (hashes.length === 0) { bcoin.utils.nextTick(function() {