diff --git a/etc/sample.conf b/etc/sample.conf index f2cc49cb..a1ffbeda 100644 --- a/etc/sample.conf +++ b/etc/sample.conf @@ -53,7 +53,6 @@ replace-by-fee: false # selfish: false -headers: true compact: true bip151: true listen: true diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 28d9c903..f895817d 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -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); diff --git a/lib/net/pool.js b/lib/net/pool.js index ed05cc67..1a065ba7 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -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) diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 44ee1ba9..88f5d9c8 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -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, diff --git a/lib/node/spvnode.js b/lib/node/spvnode.js index bd685c89..0f159212 100644 --- a/lib/node/spvnode.js +++ b/lib/node/spvnode.js @@ -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 }); diff --git a/lib/protocol/networks.js b/lib/protocol/networks.js index 89ed44d9..03cf5603 100644 --- a/lib/protocol/networks.js +++ b/lib/protocol/networks.js @@ -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,