diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 0b413099..dcad8645 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -44,6 +44,7 @@ function Chain(options) { this.blockdb = options.blockdb; this.locked = false; this.pending = []; + this.pendingBlocks = {}; this.orphanLimit = options.orphanLimit || 20 * 1024 * 1024; this.invalid = {}; @@ -61,30 +62,6 @@ function Chain(options) { inherits(Chain, EventEmitter); -Chain.codes = { - okay: 0, - newOrphan: 1, - knownOrphan: 2, - forked: 3, - invalid: 4, - badCheckpoint: 4, - unchanged: 5 -}; - -Chain.messages = { - 0: 'Block was added successfully', - 1: 'Block is a new orphan', - 2: 'Block is a known orphan', - 3: 'Block is a greater fork', - 4: 'Block verification failed', - 5: 'Block does not match checkpoint', - 6: 'Chain is unchanged' -}; - -Chain.msg = function msg(code) { - return Chain.messages[code] || 'Unknown'; -}; - Chain.prototype._init = function _init() { var self = this; @@ -742,13 +719,13 @@ Chain.prototype._addEntry = function _addEntry(entry, block, callback) { // Already added if (this.heightLookup[entry.hash] != null) { assert(this.heightLookup[entry.hash] === entry.height); - return callback(null, Chain.codes.unchanged); + return callback(null, false); } // Duplicate height (do a sync call here since this is cached) existing = this.db.get(entry.height); if (existing && existing.hash === entry.hash) - return callback(null, Chain.codes.unchanged); + return callback(null, false); this._saveBlock(block, function(err) { if (err) @@ -758,7 +735,7 @@ Chain.prototype._addEntry = function _addEntry(entry, block, callback) { if (err) return callback(err); - return callback(null, Chain.codes.okay); + return callback(null, true); }); }); }; @@ -831,11 +808,11 @@ Chain.prototype.resetTime = function resetTime(ts) { Chain.prototype.add = function add(initial, peer, callback) { var self = this; var host = peer ? peer.host : 'unknown'; - var code = Chain.codes.unchanged; var total = 0; if (this.locked) { this.pending.push([initial, peer, callback]); + this.pendingBlocks[initial.hash('hex')] = true; return; } @@ -848,18 +825,17 @@ Chain.prototype.add = function add(initial, peer, callback) { // Special case for genesis block. if (block.isGenesis()) - return done(null, code); + return done(); // Do not revalidate known invalid blocks. if (self.invalid[hash] || self.invalid[prevHash]) { - code = Chain.codes.invalid; self.emit('invalid', { height: -1, hash: hash, seen: true, chain: self.invalid[prevHash] }, peer); - return done(null, code);; + return done(); } // Find the previous block height/index. @@ -870,7 +846,6 @@ Chain.prototype.add = function add(initial, peer, callback) { // blocks coming in, not the resolving // orphans. if (block === initial && !block.verify()) { - code = Chain.codes.invalid; self.invalid[hash] = true; self.emit('invalid', { height: prevHeight + 1, @@ -878,7 +853,7 @@ Chain.prototype.add = function add(initial, peer, callback) { seen: false, chain: false }, peer); - return done(null, code); + return done(); } // If the block is already known to be @@ -898,16 +873,14 @@ Chain.prototype.add = function add(initial, peer, callback) { received: hash, checkpoint: false }, peer); - code = Chain.codes.forked; - return done(null, code); + return done(); } self.emit('orphan', { height: -1, hash: hash, seen: true }, peer); - code = Chain.codes.knownOrphan; - return done(null, code); + return done(); } // If previous block wasn't ever seen, @@ -917,13 +890,12 @@ Chain.prototype.add = function add(initial, peer, callback) { self.orphan.size += block.getSize(); self.orphan.map[prevHash] = block; self.orphan.bmap[hash] = block; - code = Chain.codes.newOrphan; self.emit('orphan', { height: -1, hash: hash, seen: false }, peer); - return done(null, code); + return done(); } // Create a new chain entry. @@ -958,14 +930,13 @@ Chain.prototype.add = function add(initial, peer, callback) { // could be used if you want to be on the overly // safe (see: paranoid) side. // this.resetLastCheckpoint(entry.height); - code = Chain.codes.badCheckpoint; self.emit('fork', { height: entry.height, expected: network.checkpoints[entry.height], received: entry.hash, checkpoint: true }, peer); - return done(null, code); + return done(); } } @@ -997,10 +968,8 @@ Chain.prototype.add = function add(initial, peer, callback) { // The tip has more chainwork, it is a // higher height than the entry. This is // not an alternate tip. Ignore it. - if (self.tip.chainwork.cmp(entry.chainwork) > 0) { - code = Chain.codes.unchanged; - return done(null, code); - } + if (self.tip.chainwork.cmp(entry.chainwork) > 0) + return done(); // The block has equal chainwork (an // alternate tip). Reset the chain, find @@ -1018,9 +987,7 @@ Chain.prototype.add = function add(initial, peer, callback) { checkpoint: false }, peer); - code = Chain.codes.forked; - - return done(null, code); + return done(); }); } @@ -1039,7 +1006,6 @@ Chain.prototype.add = function add(initial, peer, callback) { return done(err); if (!verified) { - code = Chain.codes.invalid; self.invalid[entry.hash] = true; self.emit('invalid', { height: entry.height, @@ -1047,7 +1013,7 @@ Chain.prototype.add = function add(initial, peer, callback) { seen: false, chain: false }, peer); - return done(null, code); + return done(); } // Update the block height @@ -1057,19 +1023,14 @@ Chain.prototype.add = function add(initial, peer, callback) { }); // Attempt to add block to the chain index. - self._addEntry(entry, block, function(err, code_) { + self._addEntry(entry, block, function(err, success) { if (err) return done(err); - code = code_; - // Result should never be `unchanged` since // we already verified there were no // duplicate heights, etc. - assert(code !== Chain.codes.unchanged); - - // Should always be okay. - assert(code === Chain.codes.okay); + assert(success === true); // Keep track of the number of blocks we // added and the number of orphans resolved. @@ -1091,7 +1052,7 @@ Chain.prototype.add = function add(initial, peer, callback) { function handleOrphans() { if (!self.orphan.map[hash]) - return done(null, code); + return done(); // An orphan chain was found, start resolving. block = self.orphan.map[hash]; @@ -1104,12 +1065,13 @@ Chain.prototype.add = function add(initial, peer, callback) { } })(initial); - function done(err, code) { + function done(err) { var item; // Failsafe for large orphan chains. Do not // allow more than 20mb stored in memory. if (self.orphan.size > self.orphanLimit) { + self.emit('purge', self.orphan.count, self.orphan.size); Object.keys(self.orphan.bmap).forEach(function(hash) { self.emit('unresolved', self.orphan.bmap[hash], peer); }); @@ -1120,11 +1082,6 @@ Chain.prototype.add = function add(initial, peer, callback) { utils.debug('Warning: 20mb of orphans cleared!'); } - if (code !== Chain.codes.okay) { - if (!(self.options.multiplePeers && code === Chain.codes.newOrphan)) - utils.debug('Chain Error: %s', Chain.msg(code)); - } - // We intentionally did not asyncify the // callback so if it calls chain.add, it // still gets added to the queue. The @@ -1145,6 +1102,7 @@ Chain.prototype.add = function add(initial, peer, callback) { return; item = self.pending.shift(); + delete self.pendingBlocks[item[0].hash('hex')]; self.add(item[0], item[1], item[2]); }); @@ -1158,6 +1116,9 @@ Chain.prototype.has = function has(hash) { if (this.hasOrphan(hash)) return true; + if (this.hasPending(hash)) + return true; + return false; }; @@ -1213,6 +1174,15 @@ Chain.prototype.hasOrphan = function hasOrphan(hash) { return !!this.getOrphan(hash); }; +Chain.prototype.hasPending = function hasPending(hash) { + if (utils.isBuffer(hash)) + hash = utils.toHex(hash); + else if (hash.hash) + hash = hash.hash('hex'); + + return !!this.pendingBlocks[hash]; +}; + Chain.prototype.getBlock = function getBlock(hash) { if (typeof hash === 'number') return this.byHeight(hash);