chain: improve reset. fix versionbits checkpoints.

This commit is contained in:
Christopher Jeffrey 2016-10-20 22:15:22 -07:00
parent 96e6cee67d
commit 9e0542dba1
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 85 additions and 68 deletions

View File

@ -248,13 +248,12 @@ Chain.prototype._close = function close() {
*/
Chain.prototype.verifyContext = co(function* verifyContext(block, prev) {
var state, view;
var state = yield this.verify(block, prev);
var view;
state = yield this.verify(block, prev);
yield this.verifyDuplicates(block, prev, state);
yield this.checkDuplicates(block, prev);
view = yield this.checkInputs(block, prev, state);
view = yield this.verifyInputs(block, prev, state);
// Expose the state globally.
this.state = state;
@ -288,6 +287,10 @@ Chain.prototype.verify = co(function* verify(block, prev) {
var i, err, height, ts, tx, medianTime;
var commitmentHash, ancestors, state;
// Skip the genesis block.
if (this.isGenesis(block))
return this.state;
if (!block.verify(ret)) {
err = new VerifyError(block,
'invalid',
@ -308,24 +311,23 @@ Chain.prototype.verify = co(function* verify(block, prev) {
throw err;
}
// Skip the genesis block. Skip all blocks in spv mode.
if (this.options.spv || this.isGenesis(block))
// Skip all blocks in spv mode.
if (this.options.spv)
return this.state;
// Ensure it's not an orphan
if (!prev) {
throw new VerifyError(block,
'invalid',
'bad-prevblk',
0);
// Skip any blocks below the last checkpoint.
if (!this.options.witness) {
// We can't skip this with segwit
// enabled since the block may have
// been malleated: we don't know
// until we verify the witness
// merkle root.
if (prev.isHistorical())
return this.state;
}
if (prev.isHistorical())
return this.state;
ancestors = yield prev.getRetargetAncestors();
height = prev.height + 1;
ancestors = yield prev.getRetargetAncestors();
medianTime = prev.getMedianTime(ancestors);
// Ensure the timestamp is correct
@ -345,10 +347,6 @@ Chain.prototype.verify = co(function* verify(block, prev) {
state = yield this.getDeployments(block, prev, ancestors);
// Can't verify any further when merkleblock or headers.
if (this.options.spv)
return state;
// Make sure the height contained in the coinbase is correct.
if (state.hasBIP34()) {
if (block.getCoinbaseHeight() !== height) {
@ -549,9 +547,9 @@ Chain.prototype.getDeployments = co(function* getDeployments(block, prev, ancest
* @returns {Promise}
*/
Chain.prototype.checkDuplicates = co(function* checkDuplicates(block, prev) {
Chain.prototype.verifyDuplicates = co(function* verifyDuplicates(block, prev, state) {
var height = prev.height + 1;
var entry;
var i, tx, result;
if (this.options.spv)
return;
@ -562,39 +560,12 @@ Chain.prototype.checkDuplicates = co(function* checkDuplicates(block, prev) {
if (prev.isHistorical())
return;
if (this.network.block.bip34height === -1
|| height <= this.network.block.bip34height) {
yield this.findDuplicates(block, prev);
return;
}
// It was no longer possible to create duplicate
// TXs once bip34 went into effect. We can check
// for this to avoid a DB lookup.
entry = yield this.db.get(this.network.block.bip34height);
if (entry && entry.hash === this.network.block.bip34hash)
// BIP34 made it impossible to
// create duplicate txids.
if (state.hasBIP34())
return;
yield this.findDuplicates(block, prev);
});
/**
* Check block for duplicate txids in blockchain
* history (BIP30). Note that txids are only considered
* duplicate if they are not yet completely spent.
* @private
* @see https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki
* @param {Block|MerkleBlock} block
* @param {ChainEntry} prev
* @returns {Promise}
*/
Chain.prototype.findDuplicates = co(function* findDuplicates(block, prev) {
var height = prev.height + 1;
var i, tx, result;
// Check all transactions
// Check all transactions.
for (i = 0; i < block.txs.length; i++) {
tx = block.txs[i];
result = yield this.db.hasCoins(tx.hash());
@ -629,7 +600,7 @@ Chain.prototype.findDuplicates = co(function* findDuplicates(block, prev) {
* @returns {Promise}
*/
Chain.prototype.checkInputs = co(function* checkInputs(block, prev, state) {
Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
var height = prev.height + 1;
var historical = prev.isHistorical();
var sigops = 0;
@ -967,14 +938,14 @@ Chain.prototype.reset = co(function* reset(height) {
*/
Chain.prototype._reset = co(function* reset(height) {
var result = yield this.db.reset(height);
yield this.db.reset(height);
this.state = yield this.getDeploymentState();
// Reset the orphan map completely. There may
// have been some orphans on a forked chain we
// no longer need.
this.purgeOrphans();
return result;
});
/**
@ -1162,8 +1133,9 @@ Chain.prototype._add = co(function* add(block) {
if (this.options.useCheckpoints) {
checkpoint = this.network.checkpoints[height];
if (checkpoint) {
// Someone is either trying to fool us, or
// the consensus protocol is broken and
// Someone is either mining on top of
// an old block for no reason, or the
// consensus protocol is broken and
// there was a 20k+ block reorg.
if (hash !== checkpoint) {
this.logger.warning('Checkpoint mismatch!');
@ -1216,6 +1188,17 @@ Chain.prototype._add = co(function* add(block) {
// our tip's. Add the block but do _not_
// connect the inputs.
if (entry.chainwork.cmp(this.tip.chainwork) <= 0) {
try {
yield this.verify(block, prev);
} catch (e) {
if (e.type === 'VerifyError') {
if (!e.malleated)
this.invalid[entry.hash] = true;
this.emit('invalid', block, entry.height);
}
throw e;
}
yield this.db.save(entry, block);
this.emit('competitor', block, entry);
@ -1327,11 +1310,18 @@ Chain.prototype.finish = function finish(block, entry) {
*/
Chain.prototype.purgeOrphans = function purgeOrphans() {
this.emit('purge', this.orphan.count, this.orphan.size);
var count = this.orphan.count;
var size = this.orphan.size;
if (count === 0)
return;
this.orphan.map = {};
this.orphan.bmap = {};
this.orphan.count = 0;
this.orphan.size = 0;
this.emit('purge', count, size);
};
/**
@ -1783,8 +1773,10 @@ Chain.prototype.findLocator = co(function* findLocator(locator) {
Chain.prototype.isActive = co(function* isActive(prev, id) {
var state;
if (prev.isHistorical())
return false;
if (!this.options.witness) {
if (prev.isHistorical())
return false;
}
state = yield this.getState(prev, id);

View File

@ -1272,11 +1272,20 @@ ChainDB.prototype.reset = co(function* reset(block) {
var tip;
if (!entry)
return;
throw new Error('Block not found.');
if (!(yield entry.isMainChain()))
throw new Error('Cannot reset on alternate chain.');
if (this.prune)
throw new Error('Cannot reset when pruned.');
tip = yield this.getTip();
assert(tip);
while (tip && !tip.isGenesis()) {
this.logger.debug('Resetting main chain to: %s', entry.rhash);
while (!tip.isGenesis()) {
this.start();
if (tip.hash === entry.hash) {
@ -1300,7 +1309,11 @@ ChainDB.prototype.reset = co(function* reset(block) {
yield this.commit();
this.cacheHeight.remove(tip.height);
this.cacheHash.remove(tip.hash);
tip = yield this.get(tip.prevBlock);
assert(tip);
}
});
@ -1332,10 +1345,15 @@ ChainDB.prototype.saveBlock = co(function* saveBlock(block, view) {
*/
ChainDB.prototype.removeBlock = co(function* removeBlock(hash) {
var block = yield this.getBlock(hash);
var block;
if (this.options.spv)
return;
block = yield this.getBlock(hash);
if (!block)
return;
throw new Error('Block not found.');
this.del(layout.b(block.hash()));

View File

@ -2492,6 +2492,9 @@ Peer.prototype.sync = function sync() {
if (!this.version.hasNetwork())
return Promise.resolve();
if (this.options.witness && !this.version.hasWitness())
return;
if (!this.isLoader()) {
if (!this.chain.synced)
return Promise.resolve();

View File

@ -644,6 +644,7 @@ Pool.prototype.setLoader = function setLoader(peer) {
this.logger.info('Repurposing peer for loader (%s).', peer.hostname);
this.peers.repurpose(peer);
this.fillPeers();
peer.sync();
utils.nextTick(function() {
@ -892,6 +893,9 @@ Pool.prototype.__handleInv = co(function* _handleInv(hashes, peer) {
if (!this.chain.synced && !peer.isLoader())
return;
if (this.options.witness && !peer.version.hasWitness())
return;
if (!this.options.headers) {
yield this._handleBlocks(hashes, peer);
return;