chain/pool: disable checkpoints on checkpoint failure. see #121.
This commit is contained in:
parent
77032f758d
commit
e7413aabb7
@ -53,7 +53,6 @@ replace-by-fee: false
|
|||||||
#
|
#
|
||||||
|
|
||||||
selfish: false
|
selfish: false
|
||||||
headers: true
|
|
||||||
compact: true
|
compact: true
|
||||||
bip151: true
|
bip151: true
|
||||||
listen: true
|
listen: true
|
||||||
|
|||||||
@ -1685,17 +1685,20 @@ Chain.prototype.maybeSync = function maybeSync() {
|
|||||||
if (this.synced)
|
if (this.synced)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this.hasChainwork())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.checkpoints) {
|
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;
|
this.checkpoints = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tip.ts < util.now() - this.network.block.maxTipAge)
|
if (this.tip.ts < util.now() - this.network.block.maxTipAge)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!this.hasChainwork())
|
||||||
|
return;
|
||||||
|
|
||||||
this.synced = true;
|
this.synced = true;
|
||||||
this.emit('full');
|
this.emit('full');
|
||||||
};
|
};
|
||||||
@ -2302,7 +2305,7 @@ function ChainOptions(options) {
|
|||||||
this.coinCache = 0;
|
this.coinCache = 0;
|
||||||
this.entryCache = (2016 + 1) * 2 + 100;
|
this.entryCache = (2016 + 1) * 2 + 100;
|
||||||
this.orphanLimit = 20 << 20;
|
this.orphanLimit = 20 << 20;
|
||||||
this.checkpoints = false;
|
this.checkpoints = true;
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(options);
|
this.fromOptions(options);
|
||||||
|
|||||||
114
lib/net/pool.js
114
lib/net/pool.js
@ -47,8 +47,6 @@ var packetTypes = packets.types;
|
|||||||
* @param {Boolean?} options.spv - Do an SPV sync.
|
* @param {Boolean?} options.spv - Do an SPV sync.
|
||||||
* @param {Boolean?} options.noRelay - Whether to ask
|
* @param {Boolean?} options.noRelay - Whether to ask
|
||||||
* for relayed transactions.
|
* for relayed transactions.
|
||||||
* @param {Boolean?} options.headers - Whether
|
|
||||||
* to use `getheaders` for sync.
|
|
||||||
* @param {Number?} [options.feeRate] - Fee filter rate.
|
* @param {Number?} [options.feeRate] - Fee filter rate.
|
||||||
* @param {Number?} [options.invTimeout=60000] - Timeout for broadcasted
|
* @param {Number?} [options.invTimeout=60000] - Timeout for broadcasted
|
||||||
* objects.
|
* objects.
|
||||||
@ -116,6 +114,7 @@ function Pool(options) {
|
|||||||
this.headerChain = new List();
|
this.headerChain = new List();
|
||||||
this.headerNext = null;
|
this.headerNext = null;
|
||||||
this.headerTip = null;
|
this.headerTip = null;
|
||||||
|
this.headerFails = 0;
|
||||||
|
|
||||||
this.peers = new PeerList();
|
this.peers = new PeerList();
|
||||||
this.authdb = new BIP150.AuthDB(this.options);
|
this.authdb = new BIP150.AuthDB(this.options);
|
||||||
@ -231,7 +230,7 @@ Pool.prototype._open = co(function* _open() {
|
|||||||
Pool.prototype.resetChain = function resetChain() {
|
Pool.prototype.resetChain = function resetChain() {
|
||||||
var tip = this.chain.tip;
|
var tip = this.chain.tip;
|
||||||
|
|
||||||
if (!this.options.headers)
|
if (!this.options.checkpoints)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.headersFirst = false;
|
this.headersFirst = false;
|
||||||
@ -239,14 +238,14 @@ Pool.prototype.resetChain = function resetChain() {
|
|||||||
this.headerChain.reset();
|
this.headerChain.reset();
|
||||||
this.headerNext = null;
|
this.headerNext = null;
|
||||||
|
|
||||||
if (!this.chain.hasChainwork()) {
|
if (tip.height < this.network.lastCheckpoint) {
|
||||||
this.headersFirst = true;
|
this.headersFirst = true;
|
||||||
this.headerTip = this.getNextTip(tip.height);
|
this.headerTip = this.getNextTip(tip.height);
|
||||||
this.headerChain.push(new HeaderEntry(tip.hash, tip.height));
|
this.headerChain.push(new HeaderEntry(tip.hash, tip.height));
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
'Initialized header chain to height %d (watermark=%d).',
|
'Initialized header chain to height %d (checkpoint=%s).',
|
||||||
tip.height,
|
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
|
* @private
|
||||||
* @param {Number} height
|
* @param {Number} height
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
@ -689,19 +688,13 @@ Pool.prototype.resolveHeaders = function resolveHeaders(peer) {
|
|||||||
Pool.prototype.getNextTip = function getNextTip(height) {
|
Pool.prototype.getNextTip = function getNextTip(height) {
|
||||||
var i, next;
|
var i, next;
|
||||||
|
|
||||||
if (this.options.checkpoints) {
|
for (i = 0; i < this.network.checkpoints.length; i++) {
|
||||||
for (i = 0; i < this.network.checkpoints.length; i++) {
|
next = this.network.checkpoints[i];
|
||||||
next = this.network.checkpoints[i];
|
if (next.height > height)
|
||||||
if (next.height > height)
|
return new HeaderEntry(next.hash, next.height);
|
||||||
return new HeaderEntry(next.hash, next.height);
|
|
||||||
}
|
|
||||||
throw new Error('Could not find next tip.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.chain.hasChainwork())
|
throw new Error('Next checkpoint not found.');
|
||||||
throw new Error('Could not find next tip.');
|
|
||||||
|
|
||||||
return new HeaderEntry(null, height + 20000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1755,7 +1748,7 @@ Pool.prototype.handleHeaders = co(function* handleHeaders(peer, packet) {
|
|||||||
|
|
||||||
Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
|
Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
|
||||||
var headers = packet.items;
|
var headers = packet.items;
|
||||||
var isWatermark = false;
|
var checkpoint = false;
|
||||||
var i, header, hash, height, last, node;
|
var i, header, hash, height, last, node;
|
||||||
|
|
||||||
if (!this.headersFirst)
|
if (!this.headersFirst)
|
||||||
@ -1783,14 +1776,6 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
|
|||||||
hash = header.hash('hex');
|
hash = header.hash('hex');
|
||||||
height = last.height + 1;
|
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()) {
|
if (!header.verify()) {
|
||||||
this.logger.warning(
|
this.logger.warning(
|
||||||
'Peer sent an invalid header (%s).',
|
'Peer sent an invalid header (%s).',
|
||||||
@ -1800,20 +1785,45 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
|
|||||||
return;
|
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);
|
node = new HeaderEntry(hash, height);
|
||||||
|
|
||||||
if (node.height === this.headerTip.height) {
|
if (node.height === this.headerTip.height) {
|
||||||
if (this.options.checkpoints) {
|
if (node.hash !== this.headerTip.hash) {
|
||||||
if (node.hash !== this.headerTip.hash) {
|
this.logger.warning(
|
||||||
this.logger.warning(
|
'Peer sent an invalid checkpoint (%s).',
|
||||||
'Peer sent an invalid checkpoint (%s).',
|
peer.hostname());
|
||||||
peer.hostname());
|
|
||||||
peer.increaseBan(100);
|
if (++this.headerFails < 4) {
|
||||||
peer.destroy();
|
peer.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.warning(
|
||||||
|
'Switching to getblocks (%s).',
|
||||||
|
peer.hostname());
|
||||||
|
|
||||||
|
yield this.switchSync(peer);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
isWatermark = true;
|
checkpoint = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.headerNext)
|
if (!this.headerNext)
|
||||||
@ -1828,7 +1838,7 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
|
|||||||
peer.hostname());
|
peer.hostname());
|
||||||
|
|
||||||
// Request the blocks we just added.
|
// Request the blocks we just added.
|
||||||
if (isWatermark) {
|
if (checkpoint) {
|
||||||
this.headerChain.shift();
|
this.headerChain.shift();
|
||||||
this.resolveHeaders(peer);
|
this.resolveHeaders(peer);
|
||||||
return;
|
return;
|
||||||
@ -1971,10 +1981,10 @@ Pool.prototype.resolveChain = co(function* resolveChain(peer, hash) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.chain.hasChainwork()) {
|
if (node.height < this.network.lastCheckpoint) {
|
||||||
if (node.height === this.headerTip.height) {
|
if (node.height === this.headerTip.height) {
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
'Hit high watermark %s (%d).',
|
'Received checkpoint %s (%d).',
|
||||||
util.revHex(node.hash), node.height);
|
util.revHex(node.hash), node.height);
|
||||||
|
|
||||||
this.headerTip = this.getNextTip(node.height);
|
this.headerTip = this.getNextTip(node.height);
|
||||||
@ -1991,9 +2001,24 @@ Pool.prototype.resolveChain = co(function* resolveChain(peer, hash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
'Chain hit target chainwork of %s. Switching to getblocks.',
|
'Switching to getblocks (%s).',
|
||||||
this.chain.tip.chainwork.toString('hex', 64));
|
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.headersFirst = false;
|
||||||
this.headerTip = null;
|
this.headerTip = null;
|
||||||
this.headerChain.reset();
|
this.headerChain.reset();
|
||||||
@ -3165,10 +3190,9 @@ function PoolOptions(options) {
|
|||||||
this.address.port = this.network.port;
|
this.address.port = this.network.port;
|
||||||
|
|
||||||
this.witness = true;
|
this.witness = true;
|
||||||
this.checkpoints = false;
|
this.checkpoints = true;
|
||||||
this.spv = false;
|
this.spv = false;
|
||||||
this.listen = false;
|
this.listen = false;
|
||||||
this.headers = false;
|
|
||||||
this.compact = false;
|
this.compact = false;
|
||||||
this.noRelay = false;
|
this.noRelay = false;
|
||||||
this.host = '0.0.0.0';
|
this.host = '0.0.0.0';
|
||||||
@ -3259,13 +3283,6 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
this.listen = options.listen;
|
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) {
|
if (options.compact != null) {
|
||||||
assert(typeof options.compact === 'boolean');
|
assert(typeof options.compact === 'boolean');
|
||||||
this.compact = options.compact;
|
this.compact = options.compact;
|
||||||
@ -3419,6 +3436,7 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
this.requiredServices |= common.services.BLOOM;
|
this.requiredServices |= common.services.BLOOM;
|
||||||
this.services &= ~common.services.NETWORK;
|
this.services &= ~common.services.NETWORK;
|
||||||
this.noRelay = true;
|
this.noRelay = true;
|
||||||
|
this.checkpoints = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selfish)
|
if (this.selfish)
|
||||||
|
|||||||
@ -102,7 +102,6 @@ function FullNode(options) {
|
|||||||
chain: this.chain,
|
chain: this.chain,
|
||||||
mempool: this.mempool,
|
mempool: this.mempool,
|
||||||
selfish: this.options.selfish,
|
selfish: this.options.selfish,
|
||||||
headers: this.options.headers,
|
|
||||||
compact: this.options.compact,
|
compact: this.options.compact,
|
||||||
bip151: this.options.bip151,
|
bip151: this.options.bip151,
|
||||||
bip150: this.options.bip150,
|
bip150: this.options.bip150,
|
||||||
|
|||||||
@ -71,7 +71,6 @@ function SPVNode(options) {
|
|||||||
knownPeers: this.options.knownPeers,
|
knownPeers: this.options.knownPeers,
|
||||||
identityKey: this.options.identityKey,
|
identityKey: this.options.identityKey,
|
||||||
maxOutbound: this.options.maxOutbound,
|
maxOutbound: this.options.maxOutbound,
|
||||||
headers: this.options.headers,
|
|
||||||
selfish: true,
|
selfish: true,
|
||||||
listen: false
|
listen: false
|
||||||
});
|
});
|
||||||
|
|||||||
@ -181,7 +181,7 @@ main.pow = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
chainwork: new BN(
|
chainwork: new BN(
|
||||||
'0000000000000000000000000000000000000000002fa4573e5f9cf6ca3e5e75',
|
'0000000000000000000000000000000000000000003a315fa3a5ef47f4384cf2',
|
||||||
'hex'
|
'hex'
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -543,7 +543,7 @@ testnet.pow = {
|
|||||||
),
|
),
|
||||||
bits: 486604799,
|
bits: 486604799,
|
||||||
chainwork: new BN(
|
chainwork: new BN(
|
||||||
'00000000000000000000000000000000000000000000001a461538dc48da1a06',
|
'00000000000000000000000000000000000000000000001e345893fa639796e9',
|
||||||
'hex'
|
'hex'
|
||||||
),
|
),
|
||||||
targetTimespan: 14 * 24 * 60 * 60,
|
targetTimespan: 14 * 24 * 60 * 60,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user