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(!result);
this.storeOrphan(block);
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
break;
}
// 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) {
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;

View File

@ -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;

View File

@ -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);

View File

@ -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);