chain/pool: disable checkpoints on checkpoint failure. see #121.

This commit is contained in:
Christopher Jeffrey 2017-01-23 13:25:01 -08:00
parent 77032f758d
commit e7413aabb7
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
6 changed files with 76 additions and 58 deletions

View File

@ -53,7 +53,6 @@ replace-by-fee: false
#
selfish: false
headers: true
compact: true
bip151: true
listen: true

View File

@ -1685,17 +1685,20 @@ Chain.prototype.maybeSync = function maybeSync() {
if (this.synced)
return;
if (!this.hasChainwork())
return;
if (this.checkpoints) {
this.logger.info('Minimum chainwork reached. Disabling checkpoints.');
if (this.tip.height < this.network.lastCheckpoint)
return;
this.logger.info('Last checkpoint reached. Disabling checkpoints.');
this.checkpoints = false;
}
if (this.tip.ts < util.now() - this.network.block.maxTipAge)
return;
if (!this.hasChainwork())
return;
this.synced = true;
this.emit('full');
};
@ -2302,7 +2305,7 @@ function ChainOptions(options) {
this.coinCache = 0;
this.entryCache = (2016 + 1) * 2 + 100;
this.orphanLimit = 20 << 20;
this.checkpoints = false;
this.checkpoints = true;
if (options)
this.fromOptions(options);

View File

@ -47,8 +47,6 @@ var packetTypes = packets.types;
* @param {Boolean?} options.spv - Do an SPV sync.
* @param {Boolean?} options.noRelay - Whether to ask
* for relayed transactions.
* @param {Boolean?} options.headers - Whether
* to use `getheaders` for sync.
* @param {Number?} [options.feeRate] - Fee filter rate.
* @param {Number?} [options.invTimeout=60000] - Timeout for broadcasted
* objects.
@ -116,6 +114,7 @@ function Pool(options) {
this.headerChain = new List();
this.headerNext = null;
this.headerTip = null;
this.headerFails = 0;
this.peers = new PeerList();
this.authdb = new BIP150.AuthDB(this.options);
@ -231,7 +230,7 @@ Pool.prototype._open = co(function* _open() {
Pool.prototype.resetChain = function resetChain() {
var tip = this.chain.tip;
if (!this.options.headers)
if (!this.options.checkpoints)
return;
this.headersFirst = false;
@ -239,14 +238,14 @@ Pool.prototype.resetChain = function resetChain() {
this.headerChain.reset();
this.headerNext = null;
if (!this.chain.hasChainwork()) {
if (tip.height < this.network.lastCheckpoint) {
this.headersFirst = true;
this.headerTip = this.getNextTip(tip.height);
this.headerChain.push(new HeaderEntry(tip.hash, tip.height));
this.logger.info(
'Initialized header chain to height %d (watermark=%d).',
'Initialized header chain to height %d (checkpoint=%s).',
tip.height,
this.headerTip.height);
this.headerTip.hash);
}
};
@ -680,7 +679,7 @@ Pool.prototype.resolveHeaders = function resolveHeaders(peer) {
};
/**
* Find the next highest watermark block.
* Find the next checkpoint.
* @private
* @param {Number} height
* @returns {Object}
@ -689,19 +688,13 @@ Pool.prototype.resolveHeaders = function resolveHeaders(peer) {
Pool.prototype.getNextTip = function getNextTip(height) {
var i, next;
if (this.options.checkpoints) {
for (i = 0; i < this.network.checkpoints.length; i++) {
next = this.network.checkpoints[i];
if (next.height > height)
return new HeaderEntry(next.hash, next.height);
}
throw new Error('Could not find next tip.');
for (i = 0; i < this.network.checkpoints.length; i++) {
next = this.network.checkpoints[i];
if (next.height > height)
return new HeaderEntry(next.hash, next.height);
}
if (this.chain.hasChainwork())
throw new Error('Could not find next tip.');
return new HeaderEntry(null, height + 20000);
throw new Error('Next checkpoint not found.');
};
/**
@ -1755,7 +1748,7 @@ Pool.prototype.handleHeaders = co(function* handleHeaders(peer, packet) {
Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
var headers = packet.items;
var isWatermark = false;
var checkpoint = false;
var i, header, hash, height, last, node;
if (!this.headersFirst)
@ -1783,14 +1776,6 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
hash = header.hash('hex');
height = last.height + 1;
if (header.prevBlock !== last.hash) {
this.logger.warning(
'Peer sent a bad header chain (%s).',
peer.hostname());
peer.destroy();
return;
}
if (!header.verify()) {
this.logger.warning(
'Peer sent an invalid header (%s).',
@ -1800,20 +1785,45 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
return;
}
if (header.prevBlock !== last.hash) {
this.logger.warning(
'Peer sent a bad header chain (%s).',
peer.hostname());
if (++this.headerFails < 4) {
peer.destroy();
return;
}
this.logger.warning(
'Switching to getblocks (%s).',
peer.hostname());
yield this.switchSync(peer);
return;
}
node = new HeaderEntry(hash, height);
if (node.height === this.headerTip.height) {
if (this.options.checkpoints) {
if (node.hash !== this.headerTip.hash) {
this.logger.warning(
'Peer sent an invalid checkpoint (%s).',
peer.hostname());
peer.increaseBan(100);
if (node.hash !== this.headerTip.hash) {
this.logger.warning(
'Peer sent an invalid checkpoint (%s).',
peer.hostname());
if (++this.headerFails < 4) {
peer.destroy();
return;
}
this.logger.warning(
'Switching to getblocks (%s).',
peer.hostname());
yield this.switchSync(peer);
return;
}
isWatermark = true;
checkpoint = true;
}
if (!this.headerNext)
@ -1828,7 +1838,7 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
peer.hostname());
// Request the blocks we just added.
if (isWatermark) {
if (checkpoint) {
this.headerChain.shift();
this.resolveHeaders(peer);
return;
@ -1971,10 +1981,10 @@ Pool.prototype.resolveChain = co(function* resolveChain(peer, hash) {
return;
}
if (!this.chain.hasChainwork()) {
if (node.height < this.network.lastCheckpoint) {
if (node.height === this.headerTip.height) {
this.logger.info(
'Hit high watermark %s (%d).',
'Received checkpoint %s (%d).',
util.revHex(node.hash), node.height);
this.headerTip = this.getNextTip(node.height);
@ -1991,9 +2001,24 @@ Pool.prototype.resolveChain = co(function* resolveChain(peer, hash) {
}
this.logger.info(
'Chain hit target chainwork of %s. Switching to getblocks.',
this.chain.tip.chainwork.toString('hex', 64));
'Switching to getblocks (%s).',
peer.hostname());
yield this.switchSync(peer, hash);
});
/**
* Switch to getblocks.
* @private
* @param {Peer} peer
* @param {Hash} hash
* @returns {Promise}
*/
Pool.prototype.switchSync = co(function* switchSync(peer, hash) {
assert(this.headersFirst);
this.chain.checkpoints = false;
this.headersFirst = false;
this.headerTip = null;
this.headerChain.reset();
@ -3165,10 +3190,9 @@ function PoolOptions(options) {
this.address.port = this.network.port;
this.witness = true;
this.checkpoints = false;
this.checkpoints = true;
this.spv = false;
this.listen = false;
this.headers = false;
this.compact = false;
this.noRelay = false;
this.host = '0.0.0.0';
@ -3259,13 +3283,6 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
this.listen = options.listen;
}
if (options.headers != null) {
assert(typeof options.headers === 'boolean');
this.headers = options.headers;
} else {
this.headers = this.spv === true;
}
if (options.compact != null) {
assert(typeof options.compact === 'boolean');
this.compact = options.compact;
@ -3419,6 +3436,7 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
this.requiredServices |= common.services.BLOOM;
this.services &= ~common.services.NETWORK;
this.noRelay = true;
this.checkpoints = true;
}
if (this.selfish)

View File

@ -102,7 +102,6 @@ function FullNode(options) {
chain: this.chain,
mempool: this.mempool,
selfish: this.options.selfish,
headers: this.options.headers,
compact: this.options.compact,
bip151: this.options.bip151,
bip150: this.options.bip150,

View File

@ -71,7 +71,6 @@ function SPVNode(options) {
knownPeers: this.options.knownPeers,
identityKey: this.options.identityKey,
maxOutbound: this.options.maxOutbound,
headers: this.options.headers,
selfish: true,
listen: false
});

View File

@ -181,7 +181,7 @@ main.pow = {
*/
chainwork: new BN(
'0000000000000000000000000000000000000000002fa4573e5f9cf6ca3e5e75',
'0000000000000000000000000000000000000000003a315fa3a5ef47f4384cf2',
'hex'
),
@ -543,7 +543,7 @@ testnet.pow = {
),
bits: 486604799,
chainwork: new BN(
'00000000000000000000000000000000000000000000001a461538dc48da1a06',
'00000000000000000000000000000000000000000000001e345893fa639796e9',
'hex'
),
targetTimespan: 14 * 24 * 60 * 60,