diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index addbb087..cf8c9aca 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -1210,8 +1210,7 @@ Chain.prototype.add = co(function* add(block) { Chain.prototype._add = co(function* add(block) { var ret = new VerifyResult(); var initial = true; - var hash, height, checkpoint; - var orphan, entry, prev; + var hash, height, orphan, entry, prev; while (block) { hash = block.hash('hex'); @@ -1236,11 +1235,21 @@ Chain.prototype._add = co(function* add(block) { throw new VerifyError(block, 'duplicate', 'duplicate', 0); } + // If the block is already known to be + // an orphan, ignore it. + // if (this.hasOrphan(hash)) { + // this.emit('orphan', block, block.getCoinbaseHeight()); + // throw new VerifyError(block, 'duplicate', 'duplicate', 0); + // } + + // The orphan chain may have forked. + // if (this.isOrphanFork(block)) + // throw new VerifyError(block, 'invalid', 'bad-prevblk', 0); + // If the block is already known to be // an orphan, ignore it. orphan = this.orphan.map[block.prevBlock]; if (orphan) { - // The orphan chain forked. if (orphan.hash('hex') !== hash) { this.emit('fork', block, block.getCoinbaseHeight(), @@ -1290,29 +1299,12 @@ Chain.prototype._add = co(function* add(block) { if (height > this.bestHeight) this.bestHeight = height; - // Verify the checkpoint. - if (this.options.useCheckpoints) { - checkpoint = this.network.checkpoints[height]; - if (checkpoint) { - // Someone is either mining on top of - // an old block for no reason, or the - // consensus protocol is broken and - // there was a 20k+ block reorg. - if (hash !== checkpoint) { - this.logger.warning('Checkpoint mismatch!'); - - this.purgeOrphans(); - - this.emit('fork', block, height, checkpoint); - - throw new VerifyError(block, - 'checkpoint', - 'checkpoint mismatch', - 100); - } - - this.emit('checkpoint', block, height); - } + // Verify a checkpoint if there is one. + if (!this.verifyCheckpoint(hash, height)) { + throw new VerifyError(block, + 'checkpoint', + 'checkpoint mismatch', + 100); } // Explanation: we try to keep as much data @@ -1446,6 +1438,68 @@ Chain.prototype.finish = function finish(block, entry) { time); }; +/** + * Test whether a block is a on a forked orphan chain. + * @private + * @param {Block} block + * @returns {Boolean} + */ + +Chain.prototype.isOrphanFork = function isOrphanFork(block) { + var orphan = this.orphan.map[block.prevBlock]; + + if (!orphan) + return false; + + assert(orphan.hash('hex') !== hash); + + this.emit('fork', block, + block.getCoinbaseHeight(), + orphan.hash('hex')); + + this.resolveOrphan(block.prevBlock); + this.storeOrphan(block); + + return true; +}; + +/** + * Verify a block hash and height against the checkpoints. + * @private + * @param {Hash} hash + * @param {Number} height + * @returns {Boolean} + */ + +Chain.prototype.verifyCheckpoint = function verifyCheckpoint(hash, height) { + var checkpoint; + + if (!this.options.useCheckpoints) + return true; + + checkpoint = this.network.checkpoints[height]; + + if (!checkpoint) + return true; + + if (hash === checkpoint) { + this.emit('checkpoint', block, height); + return true; + } + + // Someone is either mining on top of + // an old block for no reason, or the + // consensus protocol is broken and + // there was a 20k+ block reorg. + this.logger.warning('Checkpoint mismatch!'); + + this.purgeOrphans(); + + this.emit('fork', block, height, checkpoint); + + return false; +}; + /** * Store an orphan. * @private