chain: initial add refactor.
This commit is contained in:
parent
0f529d5f8b
commit
923364a70a
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user