chain: improve reset. fix versionbits checkpoints.
This commit is contained in:
parent
96e6cee67d
commit
9e0542dba1
@ -248,13 +248,12 @@ Chain.prototype._close = function close() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Chain.prototype.verifyContext = co(function* verifyContext(block, prev) {
|
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.verifyInputs(block, prev, state);
|
||||||
|
|
||||||
view = yield this.checkInputs(block, prev, state);
|
|
||||||
|
|
||||||
// Expose the state globally.
|
// Expose the state globally.
|
||||||
this.state = state;
|
this.state = state;
|
||||||
@ -288,6 +287,10 @@ Chain.prototype.verify = co(function* verify(block, prev) {
|
|||||||
var i, err, height, ts, tx, medianTime;
|
var i, err, height, ts, tx, medianTime;
|
||||||
var commitmentHash, ancestors, state;
|
var commitmentHash, ancestors, state;
|
||||||
|
|
||||||
|
// Skip the genesis block.
|
||||||
|
if (this.isGenesis(block))
|
||||||
|
return this.state;
|
||||||
|
|
||||||
if (!block.verify(ret)) {
|
if (!block.verify(ret)) {
|
||||||
err = new VerifyError(block,
|
err = new VerifyError(block,
|
||||||
'invalid',
|
'invalid',
|
||||||
@ -308,24 +311,23 @@ Chain.prototype.verify = co(function* verify(block, prev) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip the genesis block. Skip all blocks in spv mode.
|
// Skip all blocks in spv mode.
|
||||||
if (this.options.spv || this.isGenesis(block))
|
if (this.options.spv)
|
||||||
return this.state;
|
return this.state;
|
||||||
|
|
||||||
// Ensure it's not an orphan
|
// Skip any blocks below the last checkpoint.
|
||||||
if (!prev) {
|
if (!this.options.witness) {
|
||||||
throw new VerifyError(block,
|
// We can't skip this with segwit
|
||||||
'invalid',
|
// enabled since the block may have
|
||||||
'bad-prevblk',
|
// been malleated: we don't know
|
||||||
0);
|
// 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;
|
height = prev.height + 1;
|
||||||
|
ancestors = yield prev.getRetargetAncestors();
|
||||||
medianTime = prev.getMedianTime(ancestors);
|
medianTime = prev.getMedianTime(ancestors);
|
||||||
|
|
||||||
// Ensure the timestamp is correct
|
// Ensure the timestamp is correct
|
||||||
@ -345,10 +347,6 @@ Chain.prototype.verify = co(function* verify(block, prev) {
|
|||||||
|
|
||||||
state = yield this.getDeployments(block, prev, ancestors);
|
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.
|
// Make sure the height contained in the coinbase is correct.
|
||||||
if (state.hasBIP34()) {
|
if (state.hasBIP34()) {
|
||||||
if (block.getCoinbaseHeight() !== height) {
|
if (block.getCoinbaseHeight() !== height) {
|
||||||
@ -549,9 +547,9 @@ Chain.prototype.getDeployments = co(function* getDeployments(block, prev, ancest
|
|||||||
* @returns {Promise}
|
* @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 height = prev.height + 1;
|
||||||
var entry;
|
var i, tx, result;
|
||||||
|
|
||||||
if (this.options.spv)
|
if (this.options.spv)
|
||||||
return;
|
return;
|
||||||
@ -562,39 +560,12 @@ Chain.prototype.checkDuplicates = co(function* checkDuplicates(block, prev) {
|
|||||||
if (prev.isHistorical())
|
if (prev.isHistorical())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.network.block.bip34height === -1
|
// BIP34 made it impossible to
|
||||||
|| height <= this.network.block.bip34height) {
|
// create duplicate txids.
|
||||||
yield this.findDuplicates(block, prev);
|
if (state.hasBIP34())
|
||||||
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)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
yield this.findDuplicates(block, prev);
|
// Check all transactions.
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
for (i = 0; i < block.txs.length; i++) {
|
for (i = 0; i < block.txs.length; i++) {
|
||||||
tx = block.txs[i];
|
tx = block.txs[i];
|
||||||
result = yield this.db.hasCoins(tx.hash());
|
result = yield this.db.hasCoins(tx.hash());
|
||||||
@ -629,7 +600,7 @@ Chain.prototype.findDuplicates = co(function* findDuplicates(block, prev) {
|
|||||||
* @returns {Promise}
|
* @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 height = prev.height + 1;
|
||||||
var historical = prev.isHistorical();
|
var historical = prev.isHistorical();
|
||||||
var sigops = 0;
|
var sigops = 0;
|
||||||
@ -967,14 +938,14 @@ Chain.prototype.reset = co(function* reset(height) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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
|
// Reset the orphan map completely. There may
|
||||||
// have been some orphans on a forked chain we
|
// have been some orphans on a forked chain we
|
||||||
// no longer need.
|
// no longer need.
|
||||||
this.purgeOrphans();
|
this.purgeOrphans();
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1162,8 +1133,9 @@ Chain.prototype._add = co(function* add(block) {
|
|||||||
if (this.options.useCheckpoints) {
|
if (this.options.useCheckpoints) {
|
||||||
checkpoint = this.network.checkpoints[height];
|
checkpoint = this.network.checkpoints[height];
|
||||||
if (checkpoint) {
|
if (checkpoint) {
|
||||||
// Someone is either trying to fool us, or
|
// Someone is either mining on top of
|
||||||
// the consensus protocol is broken and
|
// an old block for no reason, or the
|
||||||
|
// consensus protocol is broken and
|
||||||
// there was a 20k+ block reorg.
|
// there was a 20k+ block reorg.
|
||||||
if (hash !== checkpoint) {
|
if (hash !== checkpoint) {
|
||||||
this.logger.warning('Checkpoint mismatch!');
|
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_
|
// our tip's. Add the block but do _not_
|
||||||
// connect the inputs.
|
// connect the inputs.
|
||||||
if (entry.chainwork.cmp(this.tip.chainwork) <= 0) {
|
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);
|
yield this.db.save(entry, block);
|
||||||
|
|
||||||
this.emit('competitor', block, entry);
|
this.emit('competitor', block, entry);
|
||||||
@ -1327,11 +1310,18 @@ Chain.prototype.finish = function finish(block, entry) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Chain.prototype.purgeOrphans = function purgeOrphans() {
|
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.map = {};
|
||||||
this.orphan.bmap = {};
|
this.orphan.bmap = {};
|
||||||
this.orphan.count = 0;
|
this.orphan.count = 0;
|
||||||
this.orphan.size = 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) {
|
Chain.prototype.isActive = co(function* isActive(prev, id) {
|
||||||
var state;
|
var state;
|
||||||
|
|
||||||
if (prev.isHistorical())
|
if (!this.options.witness) {
|
||||||
return false;
|
if (prev.isHistorical())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
state = yield this.getState(prev, id);
|
state = yield this.getState(prev, id);
|
||||||
|
|
||||||
|
|||||||
@ -1272,11 +1272,20 @@ ChainDB.prototype.reset = co(function* reset(block) {
|
|||||||
var tip;
|
var tip;
|
||||||
|
|
||||||
if (!entry)
|
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();
|
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();
|
this.start();
|
||||||
|
|
||||||
if (tip.hash === entry.hash) {
|
if (tip.hash === entry.hash) {
|
||||||
@ -1300,7 +1309,11 @@ ChainDB.prototype.reset = co(function* reset(block) {
|
|||||||
|
|
||||||
yield this.commit();
|
yield this.commit();
|
||||||
|
|
||||||
|
this.cacheHeight.remove(tip.height);
|
||||||
|
this.cacheHash.remove(tip.hash);
|
||||||
|
|
||||||
tip = yield this.get(tip.prevBlock);
|
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) {
|
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)
|
if (!block)
|
||||||
return;
|
throw new Error('Block not found.');
|
||||||
|
|
||||||
this.del(layout.b(block.hash()));
|
this.del(layout.b(block.hash()));
|
||||||
|
|
||||||
|
|||||||
@ -2492,6 +2492,9 @@ Peer.prototype.sync = function sync() {
|
|||||||
if (!this.version.hasNetwork())
|
if (!this.version.hasNetwork())
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|
||||||
|
if (this.options.witness && !this.version.hasWitness())
|
||||||
|
return;
|
||||||
|
|
||||||
if (!this.isLoader()) {
|
if (!this.isLoader()) {
|
||||||
if (!this.chain.synced)
|
if (!this.chain.synced)
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|||||||
@ -644,6 +644,7 @@ Pool.prototype.setLoader = function setLoader(peer) {
|
|||||||
this.logger.info('Repurposing peer for loader (%s).', peer.hostname);
|
this.logger.info('Repurposing peer for loader (%s).', peer.hostname);
|
||||||
this.peers.repurpose(peer);
|
this.peers.repurpose(peer);
|
||||||
this.fillPeers();
|
this.fillPeers();
|
||||||
|
|
||||||
peer.sync();
|
peer.sync();
|
||||||
|
|
||||||
utils.nextTick(function() {
|
utils.nextTick(function() {
|
||||||
@ -892,6 +893,9 @@ Pool.prototype.__handleInv = co(function* _handleInv(hashes, peer) {
|
|||||||
if (!this.chain.synced && !peer.isLoader())
|
if (!this.chain.synced && !peer.isLoader())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (this.options.witness && !peer.version.hasWitness())
|
||||||
|
return;
|
||||||
|
|
||||||
if (!this.options.headers) {
|
if (!this.options.headers) {
|
||||||
yield this._handleBlocks(hashes, peer);
|
yield this._handleBlocks(hashes, peer);
|
||||||
return;
|
return;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user