chain: return null on orphan blocks.

This commit is contained in:
Christopher Jeffrey 2017-03-04 15:37:27 -08:00
parent b265877b36
commit beefcfba67
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 68 additions and 32 deletions

View File

@ -1232,7 +1232,7 @@ Chain.prototype._add = co(function* add(block, flags) {
assert(initial); assert(initial);
assert(!result); assert(!result);
this.storeOrphan(block); this.storeOrphan(block);
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0); break;
} }
// Verify a checkpoint if there is one. // Verify a checkpoint if there is one.

View File

@ -1350,7 +1350,7 @@ RPC.prototype._submitwork = co(function* _submitwork(data) {
RPC.prototype.__submitwork = co(function* _submitwork(data) { RPC.prototype.__submitwork = co(function* _submitwork(data) {
var attempt = this.attempt; var attempt = this.attempt;
var block, header, cb, cur; var block, entry, header, cb, cur;
if (data.length !== 128) if (data.length !== 128)
throw new RPCError('Invalid parameter.'); throw new RPCError('Invalid parameter.');
@ -1393,7 +1393,7 @@ RPC.prototype.__submitwork = co(function* _submitwork(data) {
attempt.commit(header.nonce); attempt.commit(header.nonce);
try { try {
yield this.chain.add(block); entry = yield this.chain.add(block);
} catch (err) { } catch (err) {
if (err.type === 'VerifyError') { if (err.type === 'VerifyError') {
this.logger.warning('RPC block rejected: %s (%s).', this.logger.warning('RPC block rejected: %s (%s).',
@ -1403,6 +1403,12 @@ RPC.prototype.__submitwork = co(function* _submitwork(data) {
throw err; throw err;
} }
if (!entry) {
this.logger.warning('RPC block rejected: %s (bad-prevblk).',
block.rhash());
return false;
}
return true; return true;
}); });
@ -1497,10 +1503,12 @@ RPC.prototype._submitblock = co(function* submitblock(block) {
}); });
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()); this.logger.info('Handling submitted block: %s.', block.rhash());
try { try {
yield this.chain.add(block); entry = yield this.chain.add(block);
} catch (err) { } catch (err) {
if (err.type === 'VerifyError') { if (err.type === 'VerifyError') {
this.logger.warning('RPC block rejected: %s (%s).', this.logger.warning('RPC block rejected: %s (%s).',
@ -1510,6 +1518,12 @@ RPC.prototype.__submitblock = co(function* submitblock(block) {
throw err; throw err;
} }
if (!entry) {
this.logger.warning('RPC block rejected: %s (bad-prevblk).',
block.rhash());
return 'rejected: bad-prevblk';
}
return null; return null;
}); });
@ -2033,7 +2047,7 @@ RPC.prototype._generateBlocks = co(function* _generateBlocks(blocks, address) {
for (i = 0; i < blocks; i++) { for (i = 0; i < blocks; i++) {
block = yield this.miner.mineBlock(null, address); block = yield this.miner.mineBlock(null, address);
hashes.push(block.rhash()); hashes.push(block.rhash());
yield this.chain.add(block); assert(yield this.chain.add(block));
} }
return hashes; return hashes;

View File

@ -187,6 +187,11 @@ Miner.prototype.start = co(function* start() {
continue; continue;
} }
if (!entry) {
this.logger.warning('Mined a bad-prevblk (race condition?)');
continue;
}
if (this.stopping) if (this.stopping)
break; break;

View File

@ -1429,6 +1429,8 @@ Pool.prototype.handleBlockInv = co(function* handleBlockInv(peer, hashes) {
var items = []; var items = [];
var i, hash; var i, hash;
assert(hashes.length > 0);
if (!this.syncing) if (!this.syncing)
return; return;
@ -1489,6 +1491,8 @@ Pool.prototype.handleBlockInv = co(function* handleBlockInv(peer, hashes) {
*/ */
Pool.prototype.handleTXInv = co(function* handleTXInv(peer, hashes) { Pool.prototype.handleTXInv = co(function* handleTXInv(peer, hashes) {
assert(hashes.length > 0);
if (this.syncing && !this.chain.synced) if (this.syncing && !this.chain.synced)
return; return;
@ -1969,6 +1973,7 @@ Pool.prototype.addBlock = co(function* addBlock(peer, block, flags) {
Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) { Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) {
var hash = block.hash('hex'); var hash = block.hash('hex');
var entry;
if (!this.syncing) if (!this.syncing)
return; return;
@ -1984,19 +1989,9 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) {
peer.blockTime = util.ms(); peer.blockTime = util.ms();
try { try {
yield this.chain.add(block, flags); entry = yield this.chain.add(block, flags);
} catch (err) { } catch (err) {
if (err.type === 'VerifyError') { 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); peer.reject(block, err.code, err.reason, err.score);
this.emit('error', err, peer); this.emit('error', err, peer);
return; return;
@ -2004,6 +1999,22 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) {
throw err; 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); this.logStatus(block);
yield this.resolveChain(peer, hash); yield this.resolveChain(peer, hash);

View File

@ -28,12 +28,18 @@ describe('Chain', function() {
addBlock = co(function* addBlock(attempt) { addBlock = co(function* addBlock(attempt) {
var block = yield attempt.mineAsync(); var block = yield attempt.mineAsync();
var entry;
try { try {
yield chain.add(block); entry = yield chain.add(block);
} catch (e) { } catch (e) {
assert(e.type === 'VerifyError'); assert(e.type === 'VerifyError');
return e.reason; return e.reason;
} }
if (!entry)
return 'bad-prevblk';
return 'OK'; return 'OK';
}); });
@ -86,7 +92,7 @@ describe('Chain', function() {
for (i = 0; i < 200; i++) { for (i = 0; i < 200; i++) {
block = yield miner.mineBlock(); block = yield miner.mineBlock();
assert(block); assert(block);
yield chain.add(block); assert(yield chain.add(block));
} }
assert.equal(chain.height, 200); assert.equal(chain.height, 200);
@ -115,8 +121,8 @@ describe('Chain', function() {
hash1 = blk1.hash('hex'); hash1 = blk1.hash('hex');
hash2 = blk2.hash('hex'); hash2 = blk2.hash('hex');
yield chain.add(blk1); assert(yield chain.add(blk1));
yield chain.add(blk2); assert(yield chain.add(blk2));
assert(chain.tip.hash === hash1); assert(chain.tip.hash === hash1);
@ -157,7 +163,7 @@ describe('Chain', function() {
forked = true; forked = true;
}); });
yield chain.add(block); assert(yield chain.add(block));
assert(forked); assert(forked);
assert(chain.tip.hash === block.hash('hex')); assert(chain.tip.hash === block.hash('hex'));
@ -183,7 +189,7 @@ describe('Chain', function() {
var block = yield miner.mineBlock(); var block = yield miner.mineBlock();
var hash, entry, result; var hash, entry, result;
yield chain.add(block); assert(yield chain.add(block));
hash = block.hash('hex'); hash = block.hash('hex');
entry = yield chain.db.getEntry(hash); entry = yield chain.db.getEntry(hash);
@ -210,7 +216,7 @@ describe('Chain', function() {
block = yield attempt.mineAsync(); block = yield attempt.mineAsync();
yield chain.add(block); assert(yield chain.add(block));
attempt = yield miner.createBlock(); attempt = yield miner.createBlock();
@ -269,7 +275,7 @@ describe('Chain', function() {
attempt.addTX(mtx.toTX(), mtx.view); attempt.addTX(mtx.toTX(), mtx.view);
block = yield attempt.mineAsync(); block = yield attempt.mineAsync();
yield chain.add(block); assert(yield chain.add(block));
tx = block.txs[1]; tx = block.txs[1];
output = Coin.fromTX(tx, 2, chain.height); output = Coin.fromTX(tx, 2, chain.height);
@ -325,7 +331,7 @@ describe('Chain', function() {
for (i = 0; i < 417; i++) { for (i = 0; i < 417; i++) {
block = yield miner.mineBlock(); block = yield miner.mineBlock();
yield chain.add(block); assert(yield chain.add(block));
switch (chain.height) { switch (chain.height) {
case 288: case 288:
prev = yield chain.tip.getPrevious(); prev = yield chain.tip.getPrevious();
@ -367,7 +373,7 @@ describe('Chain', function() {
var block = yield mineCSV(tx); var block = yield mineCSV(tx);
var csv, attempt, rtx; var csv, attempt, rtx;
yield chain.add(block); assert(yield chain.add(block));
csv = block.txs[1]; csv = block.txs[1];
@ -390,7 +396,7 @@ describe('Chain', function() {
block = yield attempt.mineAsync(); block = yield attempt.mineAsync();
yield chain.add(block); assert(yield chain.add(block));
})); }));
it('should fail csv with bad sequence', co(function* () { it('should fail csv with bad sequence', co(function* () {
@ -418,7 +424,7 @@ describe('Chain', function() {
it('should mine a block', co(function* () { it('should mine a block', co(function* () {
var block = yield miner.mineBlock(); var block = yield miner.mineBlock();
assert(block); assert(block);
yield chain.add(block); assert(yield chain.add(block));
})); }));
it('should fail csv lock checks', co(function* () { it('should fail csv lock checks', co(function* () {
@ -426,7 +432,7 @@ describe('Chain', function() {
var block = yield mineCSV(tx); var block = yield mineCSV(tx);
var csv, attempt, rtx; var csv, attempt, rtx;
yield chain.add(block); assert(yield chain.add(block));
csv = block.txs[1]; csv = block.txs[1];
@ -556,7 +562,7 @@ describe('Chain', function() {
for (i = 0; i < 2001; i++) { for (i = 0; i < 2001; i++) {
block = yield miner.mineBlock(); block = yield miner.mineBlock();
assert(block); assert(block);
yield chain.add(block); assert(yield chain.add(block));
} }
assert.equal(chain.height, 2636); assert.equal(chain.height, 2636);
@ -578,7 +584,7 @@ describe('Chain', function() {
block = yield attempt.mineAsync(); block = yield attempt.mineAsync();
yield chain.add(block); assert(yield chain.add(block));
})); }));
it('should mine fail to connect too much weight', co(function* () { it('should mine fail to connect too much weight', co(function* () {
@ -768,7 +774,7 @@ describe('Chain', function() {
attempt.updateMerkle(); attempt.updateMerkle();
block = yield attempt.mineAsync(); block = yield attempt.mineAsync();
yield chain.add(block); assert(yield chain.add(block));
} }
assert.equal(chain.height, 2749); assert.equal(chain.height, 2749);