diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 60754a5f..5c520f5c 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -181,6 +181,62 @@ Chain.prototype.__defineGetter__('height', function() { return this.db.height; }); +// Maybe do this: +// Chain.prototype._lock = function _lock(func, args, callback, force) { +// And return wrapped callback with an unlock call in it + +Chain.prototype._lock = function _lock(func, args, force) { + var self = this; + var block; + + if (force) { + assert(this.busy); + return function() {}; + } + + if (this.busy) { + if (func === Chain.prototype.add) { + block = args[0]; + this.pending.push(block); + this.pendingBlocks[block.hash('hex')] = true; + this.pendingSize += block.getSize(); + if (this.pendingSize > this.pendingLimit) { + this.purgePending(); + return; + } + } + this.jobs.push([func, args]); + return; + } + + this.busy = true; + + return function unlock() { + var item, block; + + self.busy = false; + + if (func === Chain.prototype.add) { + if (self.pending.length === 0) + self.emit('flush'); + } + + if (self.jobs.length === 0) + return; + + item = self.jobs.shift(); + + if (item[0] === Chain.prototype.add) { + block = item[1][0]; + assert(block === self.pending.shift()); + delete self.pendingBlocks[block.hash('hex')]; + self.pendingSize -= block.getSize(); + } + + item[0].apply(self, item[1]); + }; +}; + Chain.prototype._ensureGenesis = function _ensureGenesis(callback) { var self = this; @@ -707,129 +763,6 @@ Chain.prototype.resetHeight = function resetHeight(height, force) { unlock(); }; -Chain.prototype.purgePending = function purgePending() { - var self = this; - this.pending.forEach(function(block) { - delete self.pendingBlocks[block.hash('hex')]; - self.pendingSize -= block.getSize(); - }); - this.pending.length = 0; - this.jobs = this.jobs.filter(function(item) { - return item[0] !== Chain.prototype.add; - }); -}; - -// Maybe do this: -// Chain.prototype._lock = function _lock(func, args, callback, force) { -// And return wrapped callback with an unlock call in it - -Chain.prototype._lock = function _lock(func, args, force) { - var self = this; - var block; - - if (force) { - assert(this.busy); - return function() {}; - } - - if (this.busy) { - if (func === Chain.prototype.add) { - block = args[0]; - this.pending.push(block); - this.pendingBlocks[block.hash('hex')] = true; - this.pendingSize += block.getSize(); - if (this.pendingSize > this.pendingLimit) { - utils.debug('Warning: %dmb of pending blocks. Purging.', - utils.mb(this.pendingSize)); - this.purgePending(); - return; - } - } - this.jobs.push([func, args]); - return; - } - - this.busy = true; - - return function unlock() { - var item, block; - - self.busy = false; - - if (func === Chain.prototype.add) { - if (self.pending.length === 0) - self.emit('flush'); - } - - if (self.jobs.length === 0) - return; - - item = self.jobs.shift(); - - if (item[0] === Chain.prototype.add) { - block = item[1][0]; - assert(block === self.pending.shift()); - delete self.pendingBlocks[block.hash('hex')]; - self.pendingSize -= block.getSize(); - } - - item[0].apply(self, item[1]); - }; -}; - -Chain.prototype.purgeOrphans = function purgeOrphans() { - this.emit('purge', this.orphan.count, this.orphan.size); - this.orphan.map = {}; - this.orphan.bmap = {}; - this.orphan.count = 0; - this.orphan.size = 0; -}; - -Chain.prototype.pruneOrphans = function pruneOrphans() { - var self = this; - var best, last; - - best = Object.keys(this.orphan.map).reduce(function(best, prevBlock, i) { - var orphan = self.orphan.map[prevBlock]; - var height = orphan.getCoinbaseHeight(); - - last = orphan; - - if (!best || height > best.getCoinbaseHeight()) - return orphan; - - return best; - }, null); - - // Save the best for last... or the - // last for the best in this case. - if (!best || best.getCoinbaseHeight() <= 0) - best = last; - - this.emit('purge', - this.orphan.count - (best ? 1 : 0), - this.orphan.size - (best ? best.getSize() : 0)); - - Object.keys(this.orphan.bmap).forEach(function(hash) { - var orphan = self.orphan.bmap[hash]; - if (orphan !== best) - self.emit('unresolved', orphan, peer); - }); - - this.orphan.map = {}; - this.orphan.bmap = {}; - this.orphan.count = 0; - this.orphan.size = 0; - - if (!best) - return; - - this.orphan.map[best.prevBlock] = best; - this.orphan.bmap[best.hash('hex')] = best; - this.orphan.count++; - this.orphan.size += best.getSize(); -}; - Chain.prototype.resetHeightAsync = function resetHeightAsync(height, callback, force) { var self = this; @@ -1349,6 +1282,77 @@ Chain.prototype.add = function add(initial, peer, callback, force) { } }; +Chain.prototype.purgeOrphans = function purgeOrphans() { + this.emit('purge', this.orphan.count, this.orphan.size); + this.orphan.map = {}; + this.orphan.bmap = {}; + this.orphan.count = 0; + this.orphan.size = 0; +}; + +Chain.prototype.pruneOrphans = function pruneOrphans() { + var self = this; + var best, last; + + best = Object.keys(this.orphan.map).reduce(function(best, prevBlock, i) { + var orphan = self.orphan.map[prevBlock]; + var height = orphan.getCoinbaseHeight(); + + last = orphan; + + if (!best || height > best.getCoinbaseHeight()) + return orphan; + + return best; + }, null); + + // Save the best for last... or the + // last for the best in this case. + if (!best || best.getCoinbaseHeight() <= 0) + best = last; + + this.emit('purge', + this.orphan.count - (best ? 1 : 0), + this.orphan.size - (best ? best.getSize() : 0)); + + Object.keys(this.orphan.bmap).forEach(function(hash) { + var orphan = self.orphan.bmap[hash]; + if (orphan !== best) + self.emit('unresolved', orphan, peer); + }); + + this.orphan.map = {}; + this.orphan.bmap = {}; + this.orphan.count = 0; + this.orphan.size = 0; + + if (!best) + return; + + this.orphan.map[best.prevBlock] = best; + this.orphan.bmap[best.hash('hex')] = best; + this.orphan.count++; + this.orphan.size += best.getSize(); +}; + +Chain.prototype.purgePending = function purgePending() { + var self = this; + + utils.debug('Warning: %dmb of pending blocks. Purging.', + utils.mb(this.pendingSize)); + + this.pending.forEach(function(block) { + delete self.pendingBlocks[block.hash('hex')]; + }); + + this.pending.length = 0; + this.pendingSize = 0; + + this.jobs = this.jobs.filter(function(item) { + return item[0] !== Chain.prototype.add; + }); +}; + Chain.prototype.has = function has(hash) { if (this.hasBlock(hash)) return true;