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.
* @private
@ -1209,21 +1243,22 @@ Chain.prototype.add = co(function* add(block) {
Chain.prototype._add = co(function* add(block) {
var ret = new VerifyResult();
var now = this.network.now();
var initial = true;
var hash, prevBlock, height, checkpoint;
var orphan, entry, existing, prev;
var hash, height, checkpoint;
var orphan, entry, prev;
while (block) {
hash = block.hash('hex');
prevBlock = block.prevBlock;
height = -1;
// Mark the start time.
this.mark();
// Special case for genesis block.
if (hash === this.network.genesis.hash)
break;
// 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.invalid[hash] = true;
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
// an orphan, ignore it.
orphan = this.orphan.map[prevBlock];
orphan = this.orphan.map[block.prevBlock];
if (orphan) {
// The orphan chain forked.
if (orphan.hash('hex') !== hash) {
this.emit('fork', block,
block.getCoinbaseHeight(),
orphan.hash('hex'));
this.resolveOrphan(block.prevBlock);
this.storeOrphan(block);
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
}
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.
// This is only necessary for new
// 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);
}
existing = yield this.db.hasEntry(hash);
// Do we already have this block?
if (existing) {
if (yield this.db.hasEntry(hash)) {
this.emit('exists', block, block.getCoinbaseHeight());
throw new VerifyError(block, 'duplicate', 'duplicate', 0);
}
// Find the previous block height/index.
prev = yield this.db.getEntry(prevBlock);
if (prev)
height = prev.height + 1;
if (height > this.bestHeight)
this.bestHeight = height;
prev = yield this.db.getEntry(block.prevBlock);
// If previous block wasn't ever seen,
// add it current to orphans and break.
if (!prev) {
this.orphan.count++;
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());
this.storeOrphan(block);
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.
if (this.options.useCheckpoints) {
checkpoint = this.network.checkpoints[height];
@ -1386,25 +1407,14 @@ Chain.prototype._add = co(function* add(block) {
// Keep track of stats.
this.finish(block, entry);
// No orphan chain.
if (!this.orphan.map[hash])
break;
// An orphan chain was found, start resolving.
// Try to resolve orphan chain.
block = this.resolveOrphan(hash);
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
// allow more than 20mb stored in memory.
if (this.orphan.size > this.orphanLimit)
this.pruneOrphans();
yield co.wait();
this.pruneOrphans();
if (!this.synced && this.isFull()) {
this.synced = true;
@ -1497,10 +1507,13 @@ Chain.prototype.purgeOrphans = function purgeOrphans() {
Chain.prototype.pruneOrphans = function pruneOrphans() {
var i, hashes, hash, orphan, height, best, last;
if (this.orphan.size <= this.orphanLimit)
return false;
hashes = Object.keys(this.orphan.map);
if (hashes.length === 0)
return;
return false;
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
@ -1540,6 +1553,8 @@ Chain.prototype.pruneOrphans = function pruneOrphans() {
this.orphan.bmap[best.hash('hex')] = best;
this.orphan.count = 1;
this.orphan.size = best.getSize();
return true;
};
/**