From beefcfba675e76ca0da2c896c92be8981c9e73de Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 4 Mar 2017 15:37:27 -0800 Subject: [PATCH] chain: return null on orphan blocks. --- lib/blockchain/chain.js | 2 +- lib/http/rpc.js | 22 ++++++++++++++++++---- lib/mining/miner.js | 5 +++++ lib/net/pool.js | 33 ++++++++++++++++++++++----------- test/chain-test.js | 38 ++++++++++++++++++++++---------------- 5 files changed, 68 insertions(+), 32 deletions(-) diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index be1f01e9..6dda4690 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -1232,7 +1232,7 @@ Chain.prototype._add = co(function* add(block, flags) { assert(initial); assert(!result); this.storeOrphan(block); - throw new VerifyError(block, 'invalid', 'bad-prevblk', 0); + break; } // Verify a checkpoint if there is one. diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 58abae91..3a44bb2c 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -1350,7 +1350,7 @@ RPC.prototype._submitwork = co(function* _submitwork(data) { RPC.prototype.__submitwork = co(function* _submitwork(data) { var attempt = this.attempt; - var block, header, cb, cur; + var block, entry, header, cb, cur; if (data.length !== 128) throw new RPCError('Invalid parameter.'); @@ -1393,7 +1393,7 @@ RPC.prototype.__submitwork = co(function* _submitwork(data) { attempt.commit(header.nonce); try { - yield this.chain.add(block); + entry = yield this.chain.add(block); } catch (err) { if (err.type === 'VerifyError') { this.logger.warning('RPC block rejected: %s (%s).', @@ -1403,6 +1403,12 @@ RPC.prototype.__submitwork = co(function* _submitwork(data) { throw err; } + if (!entry) { + this.logger.warning('RPC block rejected: %s (bad-prevblk).', + block.rhash()); + return false; + } + return true; }); @@ -1497,10 +1503,12 @@ RPC.prototype._submitblock = co(function* submitblock(block) { }); RPC.prototype.__submitblock = co(function* submitblock(block) { + var entry; + this.logger.info('Handling submitted block: %s.', block.rhash()); try { - yield this.chain.add(block); + entry = yield this.chain.add(block); } catch (err) { if (err.type === 'VerifyError') { this.logger.warning('RPC block rejected: %s (%s).', @@ -1510,6 +1518,12 @@ RPC.prototype.__submitblock = co(function* submitblock(block) { throw err; } + if (!entry) { + this.logger.warning('RPC block rejected: %s (bad-prevblk).', + block.rhash()); + return 'rejected: bad-prevblk'; + } + return null; }); @@ -2033,7 +2047,7 @@ RPC.prototype._generateBlocks = co(function* _generateBlocks(blocks, address) { for (i = 0; i < blocks; i++) { block = yield this.miner.mineBlock(null, address); hashes.push(block.rhash()); - yield this.chain.add(block); + assert(yield this.chain.add(block)); } return hashes; diff --git a/lib/mining/miner.js b/lib/mining/miner.js index cfd633da..3f411fed 100644 --- a/lib/mining/miner.js +++ b/lib/mining/miner.js @@ -187,6 +187,11 @@ Miner.prototype.start = co(function* start() { continue; } + if (!entry) { + this.logger.warning('Mined a bad-prevblk (race condition?)'); + continue; + } + if (this.stopping) break; diff --git a/lib/net/pool.js b/lib/net/pool.js index 60cbcb5b..9d5e926c 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -1429,6 +1429,8 @@ Pool.prototype.handleBlockInv = co(function* handleBlockInv(peer, hashes) { var items = []; var i, hash; + assert(hashes.length > 0); + if (!this.syncing) return; @@ -1489,6 +1491,8 @@ Pool.prototype.handleBlockInv = co(function* handleBlockInv(peer, hashes) { */ Pool.prototype.handleTXInv = co(function* handleTXInv(peer, hashes) { + assert(hashes.length > 0); + if (this.syncing && !this.chain.synced) return; @@ -1969,6 +1973,7 @@ Pool.prototype.addBlock = co(function* addBlock(peer, block, flags) { Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) { var hash = block.hash('hex'); + var entry; if (!this.syncing) return; @@ -1984,19 +1989,9 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) { peer.blockTime = util.ms(); try { - yield this.chain.add(block, flags); + entry = yield this.chain.add(block, flags); } catch (err) { if (err.type === 'VerifyError') { - if (err.reason === 'bad-prevblk') { - if (this.checkpoints) { - peer.increaseBan(10); - this.emit('error', err, peer); - return; - } - this.logger.debug('Peer sent an orphan block. Resolving.'); - yield this.resolveOrphan(peer, hash); - return; - } peer.reject(block, err.code, err.reason, err.score); this.emit('error', err, peer); return; @@ -2004,6 +1999,22 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) { throw err; } + // Block was orphaned. + if (!entry) { + if (this.checkpoints) { + this.logger.warning( + 'Peer sent orphan block with getheaders (%s).', + peer.hostname()); + return; + } + + this.logger.debug('Peer sent an orphan block. Resolving.'); + + yield this.resolveOrphan(peer, hash); + + return; + } + this.logStatus(block); yield this.resolveChain(peer, hash); diff --git a/test/chain-test.js b/test/chain-test.js index a1c49fc7..56b15c36 100644 --- a/test/chain-test.js +++ b/test/chain-test.js @@ -28,12 +28,18 @@ describe('Chain', function() { addBlock = co(function* addBlock(attempt) { var block = yield attempt.mineAsync(); + var entry; + try { - yield chain.add(block); + entry = yield chain.add(block); } catch (e) { assert(e.type === 'VerifyError'); return e.reason; } + + if (!entry) + return 'bad-prevblk'; + return 'OK'; }); @@ -86,7 +92,7 @@ describe('Chain', function() { for (i = 0; i < 200; i++) { block = yield miner.mineBlock(); assert(block); - yield chain.add(block); + assert(yield chain.add(block)); } assert.equal(chain.height, 200); @@ -115,8 +121,8 @@ describe('Chain', function() { hash1 = blk1.hash('hex'); hash2 = blk2.hash('hex'); - yield chain.add(blk1); - yield chain.add(blk2); + assert(yield chain.add(blk1)); + assert(yield chain.add(blk2)); assert(chain.tip.hash === hash1); @@ -157,7 +163,7 @@ describe('Chain', function() { forked = true; }); - yield chain.add(block); + assert(yield chain.add(block)); assert(forked); assert(chain.tip.hash === block.hash('hex')); @@ -183,7 +189,7 @@ describe('Chain', function() { var block = yield miner.mineBlock(); var hash, entry, result; - yield chain.add(block); + assert(yield chain.add(block)); hash = block.hash('hex'); entry = yield chain.db.getEntry(hash); @@ -210,7 +216,7 @@ describe('Chain', function() { block = yield attempt.mineAsync(); - yield chain.add(block); + assert(yield chain.add(block)); attempt = yield miner.createBlock(); @@ -269,7 +275,7 @@ describe('Chain', function() { attempt.addTX(mtx.toTX(), mtx.view); block = yield attempt.mineAsync(); - yield chain.add(block); + assert(yield chain.add(block)); tx = block.txs[1]; output = Coin.fromTX(tx, 2, chain.height); @@ -325,7 +331,7 @@ describe('Chain', function() { for (i = 0; i < 417; i++) { block = yield miner.mineBlock(); - yield chain.add(block); + assert(yield chain.add(block)); switch (chain.height) { case 288: prev = yield chain.tip.getPrevious(); @@ -367,7 +373,7 @@ describe('Chain', function() { var block = yield mineCSV(tx); var csv, attempt, rtx; - yield chain.add(block); + assert(yield chain.add(block)); csv = block.txs[1]; @@ -390,7 +396,7 @@ describe('Chain', function() { block = yield attempt.mineAsync(); - yield chain.add(block); + assert(yield chain.add(block)); })); it('should fail csv with bad sequence', co(function* () { @@ -418,7 +424,7 @@ describe('Chain', function() { it('should mine a block', co(function* () { var block = yield miner.mineBlock(); assert(block); - yield chain.add(block); + assert(yield chain.add(block)); })); it('should fail csv lock checks', co(function* () { @@ -426,7 +432,7 @@ describe('Chain', function() { var block = yield mineCSV(tx); var csv, attempt, rtx; - yield chain.add(block); + assert(yield chain.add(block)); csv = block.txs[1]; @@ -556,7 +562,7 @@ describe('Chain', function() { for (i = 0; i < 2001; i++) { block = yield miner.mineBlock(); assert(block); - yield chain.add(block); + assert(yield chain.add(block)); } assert.equal(chain.height, 2636); @@ -578,7 +584,7 @@ describe('Chain', function() { block = yield attempt.mineAsync(); - yield chain.add(block); + assert(yield chain.add(block)); })); it('should mine fail to connect too much weight', co(function* () { @@ -768,7 +774,7 @@ describe('Chain', function() { attempt.updateMerkle(); block = yield attempt.mineAsync(); - yield chain.add(block); + assert(yield chain.add(block)); } assert.equal(chain.height, 2749);