chain: initial add refactor.

This commit is contained in:
Christopher Jeffrey 2016-11-29 13:00:54 -08:00
parent 0f529d5f8b
commit 923364a70a
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -1200,6 +1200,40 @@ Chain.prototype.add = co(function* add(block) {
} }
}); });
Chain.prototype.storeOrphan = function storeOrphan(block) {
var hash = block.hash('hex');
var height = block.getCoinbaseHeight();
this.orphan.count++;
this.orphan.size += block.getSize();
this.orphan.map[block.prevBlock] = block;
this.orphan.bmap[hash] = block;
// Update the best height based on the coinbase.
// We do this even for orphans (peers will send
// us their highest block during the initial
// getblocks sync, making it an orphan).
if (height > this.bestHeight)
this.bestHeight = height;
this.emit('orphan', block, height);
};
Chain.prototype.resolveOrphan = function resolveOrphan(hash) {
var block = this.orphan.map[hash];
if (!block)
return;
delete this.orphan.bmap[block.hash('hex')];
delete this.orphan.map[hash];
this.orphan.count--;
this.orphan.size -= block.getSize();
return block;
};
/** /**
* Add a block to the chain without a lock. * Add a block to the chain without a lock.
* @private * @private
@ -1209,21 +1243,22 @@ Chain.prototype.add = co(function* add(block) {
Chain.prototype._add = co(function* add(block) { Chain.prototype._add = co(function* add(block) {
var ret = new VerifyResult(); var ret = new VerifyResult();
var now = this.network.now();
var initial = true; var initial = true;
var hash, prevBlock, height, checkpoint; var hash, height, checkpoint;
var orphan, entry, existing, prev; var orphan, entry, prev;
while (block) { while (block) {
hash = block.hash('hex'); hash = block.hash('hex');
prevBlock = block.prevBlock;
height = -1;
// Mark the start time. // Mark the start time.
this.mark(); this.mark();
// Special case for genesis block.
if (hash === this.network.genesis.hash)
break;
// Do not revalidate known invalid blocks. // Do not revalidate known invalid blocks.
if (this.invalid[hash] || this.invalid[prevBlock]) { if (this.invalid[hash] || this.invalid[block.prevBlock]) {
this.emit('invalid', block, block.getCoinbaseHeight()); this.emit('invalid', block, block.getCoinbaseHeight());
this.invalid[hash] = true; this.invalid[hash] = true;
throw new VerifyError(block, 'duplicate', 'duplicate', 100); throw new VerifyError(block, 'duplicate', 'duplicate', 100);
@ -1237,24 +1272,25 @@ Chain.prototype._add = co(function* add(block) {
// If the block is already known to be // If the block is already known to be
// an orphan, ignore it. // an orphan, ignore it.
orphan = this.orphan.map[prevBlock]; orphan = this.orphan.map[block.prevBlock];
if (orphan) { if (orphan) {
// The orphan chain forked. // The orphan chain forked.
if (orphan.hash('hex') !== hash) { if (orphan.hash('hex') !== hash) {
this.emit('fork', block, this.emit('fork', block,
block.getCoinbaseHeight(), block.getCoinbaseHeight(),
orphan.hash('hex')); orphan.hash('hex'));
this.resolveOrphan(block.prevBlock);
this.storeOrphan(block);
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
} }
this.emit('orphan', block, block.getCoinbaseHeight()); this.emit('orphan', block, block.getCoinbaseHeight());
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0); throw new VerifyError(block, 'duplicate', 'duplicate', 0);
} }
// Special case for genesis block.
if (hash === this.network.genesis.hash)
break;
// Validate the block we want to add. // Validate the block we want to add.
// This is only necessary for new // This is only necessary for new
// blocks coming in, not the resolving // blocks coming in, not the resolving
@ -1266,43 +1302,28 @@ Chain.prototype._add = co(function* add(block) {
throw new VerifyError(block, 'invalid', ret.reason, ret.score); throw new VerifyError(block, 'invalid', ret.reason, ret.score);
} }
existing = yield this.db.hasEntry(hash);
// Do we already have this block? // Do we already have this block?
if (existing) { if (yield this.db.hasEntry(hash)) {
this.emit('exists', block, block.getCoinbaseHeight()); this.emit('exists', block, block.getCoinbaseHeight());
throw new VerifyError(block, 'duplicate', 'duplicate', 0); throw new VerifyError(block, 'duplicate', 'duplicate', 0);
} }
// Find the previous block height/index. // Find the previous block height/index.
prev = yield this.db.getEntry(prevBlock); prev = yield this.db.getEntry(block.prevBlock);
if (prev)
height = prev.height + 1;
if (height > this.bestHeight)
this.bestHeight = height;
// If previous block wasn't ever seen, // If previous block wasn't ever seen,
// add it current to orphans and break. // add it current to orphans and break.
if (!prev) { if (!prev) {
this.orphan.count++; this.storeOrphan(block);
this.orphan.size += block.getSize();
this.orphan.map[prevBlock] = block;
this.orphan.bmap[hash] = block;
// Update the best height based on the coinbase.
// We do this even for orphans (peers will send
// us their highest block during the initial
// getblocks sync, making it an orphan).
if (block.getCoinbaseHeight() > this.bestHeight)
this.bestHeight = block.getCoinbaseHeight();
this.emit('orphan', block, block.getCoinbaseHeight());
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0); throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
} }
height = prev.height + 1;
// Update best height seen.
if (height > this.bestHeight)
this.bestHeight = height;
// Verify the checkpoint. // Verify the checkpoint.
if (this.options.useCheckpoints) { if (this.options.useCheckpoints) {
checkpoint = this.network.checkpoints[height]; checkpoint = this.network.checkpoints[height];
@ -1386,25 +1407,14 @@ Chain.prototype._add = co(function* add(block) {
// Keep track of stats. // Keep track of stats.
this.finish(block, entry); this.finish(block, entry);
// No orphan chain. // Try to resolve orphan chain.
if (!this.orphan.map[hash]) block = this.resolveOrphan(hash);
break;
// An orphan chain was found, start resolving.
initial = false; initial = false;
block = this.orphan.map[hash];
delete this.orphan.bmap[block.hash('hex')];
delete this.orphan.map[hash];
this.orphan.count--;
this.orphan.size -= block.getSize();
} }
// Failsafe for large orphan chains. Do not // Failsafe for large orphan chains. Do not
// allow more than 20mb stored in memory. // allow more than 20mb stored in memory.
if (this.orphan.size > this.orphanLimit) this.pruneOrphans();
this.pruneOrphans();
yield co.wait();
if (!this.synced && this.isFull()) { if (!this.synced && this.isFull()) {
this.synced = true; this.synced = true;
@ -1497,10 +1507,13 @@ Chain.prototype.purgeOrphans = function purgeOrphans() {
Chain.prototype.pruneOrphans = function pruneOrphans() { Chain.prototype.pruneOrphans = function pruneOrphans() {
var i, hashes, hash, orphan, height, best, last; var i, hashes, hash, orphan, height, best, last;
if (this.orphan.size <= this.orphanLimit)
return false;
hashes = Object.keys(this.orphan.map); hashes = Object.keys(this.orphan.map);
if (hashes.length === 0) if (hashes.length === 0)
return; return false;
for (i = 0; i < hashes.length; i++) { for (i = 0; i < hashes.length; i++) {
hash = hashes[i]; hash = hashes[i];
@ -1540,6 +1553,8 @@ Chain.prototype.pruneOrphans = function pruneOrphans() {
this.orphan.bmap[best.hash('hex')] = best; this.orphan.bmap[best.hash('hex')] = best;
this.orphan.count = 1; this.orphan.count = 1;
this.orphan.size = best.getSize(); this.orphan.size = best.getSize();
return true;
}; };
/** /**