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) {
var i, item;
assert(this.items.length <= 2000, 'Too many headers.');
bw.writeVarint(this.items.length);
for (i = 0; i < this.items.length; i++) {
@ -1168,6 +1170,8 @@ HeadersPacket.prototype.fromReader = function fromReader(br) {
var count = br.readVarint();
var i;
assert(count <= 2000, 'Too many headers.');
for (i = 0; i < count; i++)
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() {
var locator, tip, checkpoint;
var locator, tip, watermark;
if (!this.pool.syncing)
return false;
@ -2220,8 +2220,8 @@ Peer.prototype.sync = co(function* sync() {
if (this.pool.headersFirst) {
tip = this.chain.tip;
checkpoint = this.pool.nextCheckpoint;
this.sendGetHeaders([tip.hash], checkpoint.hash);
watermark = this.pool.headerTip;
this.sendGetHeaders([tip.hash], watermark.hash);
return true;
}

View File

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