diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 70891e73..c28878d4 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -666,11 +666,9 @@ Chain.prototype.resetTime = function resetTime(ts) { return this.resetHeight(entry.height); }; -Chain.prototype.add = function add(block, peer, callback) { +Chain.prototype.add = function add(initial, peer, callback) { var self = this; - var initial = block; var code = Chain.codes.unchanged; - var hash, prevHash, prevHeight, entry, tip, existing, checkpoint, prev; var total = 0; callback = utils.asyncify(callback); @@ -678,10 +676,10 @@ Chain.prototype.add = function add(block, peer, callback) { if (this._locked) return callback(null, total); - // (function next(block) { - (function next() { - hash = block.hash('hex'); - prevHash = block.prevBlock; + (function next(block) { + var hash = block.hash('hex'); + var prevHash = block.prevBlock; + var prevHeight, entry, existing, checkpoint, prev; // Find the previous block height/index. prevHeight = self.heightLookup[prevHash]; @@ -746,84 +744,93 @@ Chain.prototype.add = function add(block, peer, callback) { height: prevHeight + 1 }); - // Add entry if we do not have it (or if - // there is another entry at its height) + // Fork at checkpoint + // Block did not match the checkpoint. The + // chain could be reset to the last sane + // checkpoint, but it really isn't necessary, + // so we don't do it. The misbehaving peer has + // been killed and hopefully we find a peer + // who isn't trying to fool us. + checkpoint = network.checkpoints[entry.height]; + if (checkpoint) { + self.emit('checkpoint', entry.height, entry.hash, checkpoint); + if (hash !== checkpoint) { + // Resetting to the last checkpoint _really_ isn't + // necessary (even bitcoind doesn't do it), but it + // 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 + }); + return done(null, code); + } + } + + // See if the entry already exists. existing = self.db.get(entry.height); - if (!existing || existing.hash !== hash) { - assert(self.heightLookup[entry.hash] == null); + + // Entry already exists. + if (existing) { + // We already have this block. Do regular + // orphan resolution (won't do anything). + if (existing.hash === hash) + return handleOrphans(); // A valid block with an already existing // height came in, that spells fork. We // don't store by hash so we can't compare // chainworks. We reset the chain, find a // new peer, and wait to see who wins. - if (existing) { - // 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); - } - // Get _our_ tip as opposed to - // the attempted alternate tip. - tip = existing; - // The block has equal chainwork (an - // alternate tip). Reset the chain, find - // a new peer, and wait to see who wins. - self._locked = true; - return self._removeBlock(tip.hash, function(err) { - self._locked = false; - if (err) - return done(err); - self.resetHeight(entry.height - 1); - self.emit('fork', { - height: prevHeight + 1, - expected: tip.hash, - received: hash, - checkpoint: false - }, peer); - code = Chain.codes.forked; - return done(null, code); - }); + assert(self.heightLookup[entry.hash] == null); + + // 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); } - // Fork at checkpoint - // Block did not match the checkpoint. The - // chain could be reset to the last sane - // checkpoint, but it really isn't necessary, - // so we don't do it. The misbehaving peer has - // been killed and hopefully we find a peer - // who isn't trying to fool us. - checkpoint = network.checkpoints[entry.height]; - if (checkpoint) { - self.emit('checkpoint', entry.height, entry.hash, checkpoint); - if (hash !== checkpoint) { - // Resetting to the last checkpoint _really_ isn't - // necessary (even bitcoind doesn't do it), but it - // 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 - }); - return done(null, code); - } - } + // The block has equal chainwork (an + // alternate tip). Reset the chain, find + // a new peer, and wait to see who wins. + self._locked = true; + return self._removeBlock(existing.hash, function(err) { + self._locked = false; + if (err) + return done(err); + self.resetHeight(entry.height - 1); + self.emit('fork', { + height: prevHeight + 1, + expected: existing.hash, + received: hash, + checkpoint: false + }, peer); + code = Chain.codes.forked; + return done(null, code); + }); + } - // Lookup previous entry. - prev = self.db.get(prevHeight); - assert(prev); + // Add entry if we do not have it. + assert(self.heightLookup[entry.hash] == null); - // Do "contextual" verification on our block - // now that we're certain its previous - // block is in the chain. - // self._verifyContext(block, prev, function(err, verified) { - if (!block.verifyContext(prev)) { + // Lookup previous entry. + prev = self.db.get(prevHeight); + assert(prev); + + // Do "contextual" verification on our block + // now that we're certain its previous + // block is in the chain. + self._verifyContext(block, prev, function(err, verified) { + if (err) + return done(err); + + if (!verified) { code = Chain.codes.invalid; self.emit('invalid', { height: prevHeight + 1, @@ -869,9 +876,7 @@ Chain.prototype.add = function add(block, peer, callback) { handleOrphans(); }); - } else { - handleOrphans(); - } + }); function handleOrphans() { if (!self.orphan.map[hash]) @@ -884,9 +889,9 @@ Chain.prototype.add = function add(block, peer, callback) { self.orphan.count--; self.orphan.size -= block.getSize(); - next(); + next(block); } - })(); + })(initial); function done(err, code) { // Failsafe for large orphan chains. Do not