pool: allow use of headersFirst without checkpoints.

This commit is contained in:
Christopher Jeffrey 2017-01-20 14:50:16 -08:00
parent fe52210c5e
commit 3fb2150dc4
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 79 additions and 45 deletions

View File

@ -1138,6 +1138,8 @@ HeadersPacket.prototype.getSize = function getSize() {
HeadersPacket.prototype.toWriter = function toWriter(bw) { HeadersPacket.prototype.toWriter = function toWriter(bw) {
var i, item; var i, item;
assert(this.items.length <= 2000, 'Too many headers.');
bw.writeVarint(this.items.length); bw.writeVarint(this.items.length);
for (i = 0; i < this.items.length; i++) { for (i = 0; i < this.items.length; i++) {
@ -1168,6 +1170,8 @@ HeadersPacket.prototype.fromReader = function fromReader(br) {
var count = br.readVarint(); var count = br.readVarint();
var i; var i;
assert(count <= 2000, 'Too many headers.');
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
this.items.push(Headers.fromReader(br)); this.items.push(Headers.fromReader(br));

View File

@ -2186,7 +2186,7 @@ Peer.prototype.reject = function reject(msg, code, reason, score) {
*/ */
Peer.prototype.sync = co(function* sync() { Peer.prototype.sync = co(function* sync() {
var locator, tip, checkpoint; var locator, tip, watermark;
if (!this.pool.syncing) if (!this.pool.syncing)
return false; return false;
@ -2220,8 +2220,8 @@ Peer.prototype.sync = co(function* sync() {
if (this.pool.headersFirst) { if (this.pool.headersFirst) {
tip = this.chain.tip; tip = this.chain.tip;
checkpoint = this.pool.nextCheckpoint; watermark = this.pool.headerTip;
this.sendGetHeaders([tip.hash], checkpoint.hash); this.sendGetHeaders([tip.hash], watermark.hash);
return true; return true;
} }

View File

@ -116,7 +116,7 @@ function Pool(options) {
this.headersFirst = false; this.headersFirst = false;
this.headerChain = new List(); this.headerChain = new List();
this.headerNext = null; this.headerNext = null;
this.nextCheckpoint = null; this.headerTip = null;
this.checkpoints = []; this.checkpoints = [];
this.peers = new PeerList(); this.peers = new PeerList();
@ -241,29 +241,26 @@ 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;
var checkpoint; var watermark;
if (!this.options.headers) if (!this.options.headers)
return; return;
if (!this.chain.options.useCheckpoints)
return;
this.headersFirst = false; this.headersFirst = false;
this.nextCheckpoint = null; this.headerTip = null;
this.headerChain.reset(); this.headerChain.reset();
this.headerNext = null; this.headerNext = null;
checkpoint = this.getNextCheckpoint(tip.height); watermark = this.getNextTip(tip.height);
if (checkpoint) { if (watermark) {
this.headersFirst = true; this.headersFirst = true;
this.nextCheckpoint = checkpoint; this.headerTip = watermark;
this.headerChain.push(new BlockNode(tip.hash, tip.height)); this.headerChain.push(new BlockNode(tip.hash, tip.height));
this.logger.info( this.logger.info(
'Initialized header chain to height %d (checkpoint=%d).', 'Initialized header chain to height %d (watermark=%d).',
tip.height, tip.height,
checkpoint.height); watermark.height);
} }
}; };
@ -383,7 +380,7 @@ Pool.prototype._disconnect = co(function* disconnect() {
} }
this.headersFirst = false; this.headersFirst = false;
this.nextCheckpoint = null; this.headerTip = null;
this.headerChain.reset(); this.headerChain.reset();
this.headerNext = null; this.headerNext = null;
@ -594,7 +591,7 @@ Pool.prototype.sendGetAddr = function sendGetAddr() {
* @returns {Promise} * @returns {Promise}
*/ */
Pool.prototype.resolveHeaders = co(function* resolveHeaders(peer) { Pool.prototype.requestHeaders = co(function* requestHeaders(peer) {
var items = []; var items = [];
var node; var node;
@ -619,15 +616,21 @@ Pool.prototype.resolveHeaders = co(function* resolveHeaders(peer) {
}); });
/** /**
* Find the next highest checkpoint. * Find the next highest watermark block.
* @private * @private
* @param {Number} height * @param {Number} height
* @returns {Object} * @returns {Object}
*/ */
Pool.prototype.getNextCheckpoint = function getNextCheckpoint(height) { Pool.prototype.getNextTip = function getNextTip(height) {
var i, next; var i, next;
if (!this.options.useCheckpoints) {
if (this.chain.isFull())
return;
return new BlockNode(null, height + 20000);
}
for (i = 0; i < this.checkpoints.length; i++) { for (i = 0; i < this.checkpoints.length; i++) {
next = this.checkpoints[i]; next = this.checkpoints[i];
if (next.height > height) if (next.height > height)
@ -1527,7 +1530,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 isCheckpoint = false; var isWatermark = false;
var i, header, hash, height, last, node; var i, header, hash, height, last, node;
if (!this.headersFirst) if (!this.headersFirst)
@ -1539,13 +1542,22 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
if (!peer.isLoader()) if (!peer.isLoader())
return; return;
if (headers.length === 0)
return;
if (headers.length > 2000) { if (headers.length > 2000) {
this.increaseBan(100); peer.increaseBan(100);
return; return;
} }
if (headers.length === 0) if (this.headerChain.size > 40000) {
this.logger.warning(
'Peer is sending too many headers (%s).',
peer.hostname);
peer.increaseBan(100);
peer.destroy();
return; return;
}
assert(this.headerChain.size > 0); assert(this.headerChain.size > 0);
@ -1556,13 +1568,16 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
height = last.height + 1; height = last.height + 1;
if (header.prevBlock !== last.hash) { if (header.prevBlock !== last.hash) {
peer.increaseBan(100); this.logger.warning('Peer sent a bad header chain (%s).', peer.hostname);
throw new Error('Bad header chain.'); peer.destroy();
return;
} }
if (!header.verify()) { if (!header.verify()) {
this.logger.warning('Peer sent an invalid header (%s).', peer.hostname);
peer.increaseBan(100); peer.increaseBan(100);
throw new Error('Invalid header.'); peer.destroy();
return;
} }
node = new BlockNode(hash, last.height + 1); node = new BlockNode(hash, last.height + 1);
@ -1570,12 +1585,18 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
if (!this.headerNext) if (!this.headerNext)
this.headerNext = node; this.headerNext = node;
if (node.height === this.nextCheckpoint.height) { if (node.height === this.headerTip.height) {
if (node.hash !== this.nextCheckpoint.hash) { if (this.options.useCheckpoints) {
peer.increaseBan(100); if (node.hash !== this.headerTip.hash) {
throw new Error('Bad checkpoint header.'); this.logger.warning(
'Peer sent an invalid checkpoint (%s).',
peer.hostname);
peer.increaseBan(100);
peer.destroy();
return;
}
} }
isCheckpoint = true; isWatermark = true;
} }
this.headerChain.push(node); this.headerChain.push(node);
@ -1589,14 +1610,14 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
this.emit('headers', packet, peer); this.emit('headers', packet, peer);
// Request the hashes we just added. // Request the hashes we just added.
if (isCheckpoint) { if (isWatermark) {
this.headerChain.shift(); this.headerChain.shift();
yield this.resolveHeaders(peer); yield this.requestHeaders(peer);
return; return;
} }
// Restart the getheaders process. // Restart the getheaders process.
peer.sendGetHeaders([node.hash], this.nextCheckpoint.hash); peer.sendGetHeaders([node.hash], this.headerTip.hash);
}); });
/** /**
@ -1658,8 +1679,8 @@ Pool.prototype.addBlock = co(function* addBlock(peer, block) {
Pool.prototype._addBlock = co(function* addBlock(peer, block) { Pool.prototype._addBlock = co(function* addBlock(peer, block) {
var hash = block.hash('hex'); var hash = block.hash('hex');
var isCheckpoint = false; var isWatermark = false;
var node, checkpoint; var node, watermark;
if (!this.syncing) if (!this.syncing)
return; return;
@ -1677,11 +1698,11 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block) {
assert(node); assert(node);
if (hash === node.hash) { if (hash === node.hash) {
if (hash === this.nextCheckpoint.hash) { if (node.height === this.headerTip.height) {
this.logger.info( this.logger.info(
'Received checkpoint block %s (%d).', 'Received checkpoint block %s (%d).',
hash, node.height); hash, node.height);
isCheckpoint = true; isWatermark = true;
} else { } else {
this.headerChain.shift(); this.headerChain.shift();
} }
@ -1716,26 +1737,26 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block) {
if (!this.headersFirst) if (!this.headersFirst)
return; return;
if (!isCheckpoint) { if (!isWatermark && !this.chain.isFull()) {
yield this.resolveHeaders(peer); yield this.requestHeaders(peer);
return; return;
} }
node = this.nextCheckpoint; node = this.headerTip;
checkpoint = this.getNextCheckpoint(node.height); watermark = this.getNextTip(node.height);
if (checkpoint) { if (watermark) {
this.nextCheckpoint = checkpoint; this.headerTip = watermark;
peer.sendGetHeaders([node.hash], checkpoint.hash); peer.sendGetHeaders([hash], watermark.hash);
return; return;
} }
this.headersFirst = false; this.headersFirst = false;
this.nextCheckpoint = null; this.headerTip = null;
this.headerChain.reset(); this.headerChain.reset();
this.headerNext = null; this.headerNext = null;
peer.sendGetBlocks([hash], null); yield this.getBlocks(peer, hash);
}); });
/** /**
@ -2884,6 +2905,7 @@ function PoolOptions(options) {
this.mempool = null; this.mempool = null;
this.witness = this.network.witness; this.witness = this.network.witness;
this.useCheckpoints = false;
this.spv = false; this.spv = false;
this.listen = false; this.listen = false;
this.headers = false; this.headers = false;
@ -2956,6 +2978,14 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
this.witness = this.chain.options.witness; this.witness = this.chain.options.witness;
} }
if (options.useCheckpoints != null) {
assert(typeof options.useCheckpoints === 'boolean');
assert(options.useCheckpoints === this.chain.options.useCheckpoints);
this.useCheckpoints = options.useCheckpoints;
} else {
this.useCheckpoints = this.chain.options.useCheckpoints;
}
if (options.spv != null) { if (options.spv != null) {
assert(typeof options.spv === 'boolean'); assert(typeof options.spv === 'boolean');
assert(options.spv === this.chain.options.spv); assert(options.spv === this.chain.options.spv);