pool: refactor header sync.

This commit is contained in:
Christopher Jeffrey 2017-01-21 16:07:02 -08:00
parent 219671c620
commit 6e13fe57c0
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 96 additions and 80 deletions

View File

@ -1012,7 +1012,7 @@ Chain.prototype._reset = co(function* reset(block, silent) {
// Reset state.
this.tip = tip;
this.height = tip.height;
this.synced = this.isFull(true);
this.synced = this.isFull();
state = yield this.getDeploymentState();
@ -1669,8 +1669,8 @@ Chain.prototype.getCoinView = co(function* getCoinView(tx) {
* @returns {Boolean}
*/
Chain.prototype.isFull = function isFull(force) {
return !this.isInitial(force);
Chain.prototype.isFull = function isFull() {
return !this.isInitial();
};
/**
@ -1681,10 +1681,7 @@ Chain.prototype.isFull = function isFull(force) {
* @returns {Boolean}
*/
Chain.prototype.isInitial = function isInitial(force) {
if (!force && this.synced)
return false;
Chain.prototype.isInitial = function isInitial() {
if (this.options.useCheckpoints) {
if (this.height < this.network.lastCheckpoint)
return true;
@ -1700,7 +1697,7 @@ Chain.prototype.isInitial = function isInitial(force) {
};
/**
* Potentially emit a `sync` event.
* Potentially emit a `full` event.
* @private
*/
@ -1711,6 +1708,20 @@ Chain.prototype.maybeSync = function maybeSync() {
}
};
/**
* Test the chain to see if it has the
* minimum required chainwork for the
* network.
* @returns {Boolean}
*/
Chain.prototype.hasChainwork = function hasChainwork() {
if (this.options.useCheckpoints)
return this.height >= this.network.lastCheckpoint;
return this.tip.chainwork.cmp(this.network.pow.chainwork) >= 0;
};
/**
* Get the fill percentage.
* @returns {Number} percent - Ranges from 0.0 to 1.0.

View File

@ -1571,7 +1571,7 @@ RPC.prototype.getblocktemplate = co(function* getblocktemplate(args) {
if (this.pool.peers.size() === 0)
throw new RPCError('Bitcoin is not connected!');
if (!this.chain.isFull())
if (!this.chain.synced)
throw new RPCError('Bitcoin is downloading blocks...');
}

View File

@ -164,7 +164,7 @@ Mempool.prototype._addBlock = function addBlock(block, txs) {
}
if (this.fees)
this.fees.processBlock(block.height, entries, this.chain.isFull());
this.fees.processBlock(block.height, entries, this.chain.synced);
// We need to reset the rejects filter periodically.
// There may be a locktime in a TX that is now valid.
@ -972,7 +972,7 @@ Mempool.prototype.addEntry = co(function* addEntry(entry, view) {
this.emit('add entry', entry);
if (this.fees)
this.fees.processTX(entry, this.chain.isFull());
this.fees.processTX(entry, this.chain.synced);
this.logger.debug('Added tx %s to mempool.', tx.txid());

View File

@ -232,7 +232,6 @@ Pool.prototype._open = co(function* _open() {
Pool.prototype.resetChain = function resetChain() {
var tip = this.chain.tip;
var watermark;
if (!this.options.headers)
return;
@ -242,16 +241,14 @@ Pool.prototype.resetChain = function resetChain() {
this.headerChain.reset();
this.headerNext = null;
watermark = this.getNextTip(tip.height);
if (watermark) {
if (!this.chain.hasChainwork()) {
this.headersFirst = true;
this.headerTip = watermark;
this.headerChain.push(new BlockNode(tip.hash, tip.height));
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).',
tip.height,
watermark.height);
this.headerTip.height);
}
};
@ -665,33 +662,25 @@ Pool.prototype.sendGetAddr = function sendGetAddr() {
/**
* Request current header chain blocks.
* @private
* @param {Peer} peer
* @returns {Promise}
*/
Pool.prototype.requestHeaders = co(function* requestHeaders(peer) {
Pool.prototype.resolveHeaders = function resolveHeaders(peer) {
var items = [];
var node;
if (!this.headerNext)
return;
for (node = this.headerNext; node; node = node.next) {
this.headerNext = node.next;
if (yield this.hasBlock(node.hash))
continue;
items.push(node.hash);
if (items.length === 50000)
break;
}
// Checkpoints should never be
// spaced more than 50k blocks
// apart from each other.
assert(items.length <= 50000);
this.getBlock(peer, items);
});
};
/**
* Find the next highest watermark block.
@ -707,15 +696,15 @@ Pool.prototype.getNextTip = function getNextTip(height) {
for (i = 0; i < this.network.checkpoints.length; i++) {
next = this.network.checkpoints[i];
if (next.height > height)
return next;
return new HeaderEntry(next.hash, next.height);
}
return;
throw new Error('Could not find next tip.');
}
if (this.chain.isFull())
return;
if (this.chain.hasChainwork())
throw new Error('Could not find next tip.');
return new BlockNode(null, height + 20000);
return new HeaderEntry(null, height + 20000);
};
/**
@ -1788,15 +1777,6 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
return;
}
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);
for (i = 0; i < headers.length; i++) {
@ -1822,10 +1802,7 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
return;
}
node = new BlockNode(hash, height);
if (!this.headerNext)
this.headerNext = node;
node = new HeaderEntry(hash, height);
if (node.height === this.headerTip.height) {
if (this.options.useCheckpoints) {
@ -1841,6 +1818,9 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
isWatermark = true;
}
if (!this.headerNext)
this.headerNext = node;
this.headerChain.push(node);
}
@ -1849,14 +1829,14 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
headers.length,
peer.hostname());
// Request the hashes we just added.
// Request the blocks we just added.
if (isWatermark) {
this.headerChain.shift();
yield this.requestHeaders(peer);
this.resolveHeaders(peer);
return;
}
// Restart the getheaders process.
// Request more headers.
peer.sendGetHeaders([node.hash], this.headerTip.hash);
});
@ -1919,8 +1899,6 @@ Pool.prototype.addBlock = co(function* addBlock(peer, block) {
Pool.prototype._addBlock = co(function* addBlock(peer, block) {
var hash = block.hash('hex');
var isWatermark = false;
var node, watermark;
if (!this.syncing)
return;
@ -1933,22 +1911,6 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block) {
return;
}
if (this.headersFirst) {
node = this.headerChain.head;
assert(node);
if (hash === node.hash) {
if (node.height === this.headerTip.height) {
this.logger.info(
'Received checkpoint block %s (%d).',
block.rhash(), node.height);
isWatermark = true;
} else {
this.headerChain.shift();
}
}
}
peer.blockTime = util.ms();
try {
@ -1974,23 +1936,66 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block) {
this.logStatus(block);
yield this.resolveChain(peer, hash);
});
/**
* Resolve header chain.
* @private
* @param {Peer} peer
* @param {Hash} hash
* @returns {Promise}
*/
Pool.prototype.resolveChain = co(function* resolveChain(peer, hash) {
var node = this.headerChain.head;
if (!this.headersFirst)
return;
if (!isWatermark && !this.chain.isFull()) {
yield this.requestHeaders(peer);
if (!peer.loader)
return;
if (peer.destroyed)
throw new Error('Peer was destroyed (header chain resolution).');
assert(node);
if (hash !== node.hash) {
this.logger.warning(
'Header hash mismatch %s != %s (%s).',
util.revHex(hash),
util.revHex(node.hash),
peer.hostname());
peer.destroy();
return;
}
node = this.headerTip;
watermark = this.getNextTip(node.height);
if (!this.chain.hasChainwork()) {
if (node.height === this.headerTip.height) {
this.logger.info(
'Hit high watermark %s (%d).',
util.revHex(node.hash), node.height);
this.headerTip = this.getNextTip(node.height);
peer.sendGetHeaders([hash], this.headerTip.hash);
return;
}
this.headerChain.shift();
this.resolveHeaders(peer);
if (watermark) {
this.headerTip = watermark;
peer.sendGetHeaders([hash], watermark.hash);
return;
}
this.logger.info(
'Chain hit target chainwork of %s. Switching to getblocks.',
this.chain.tip.chainwork.toString('hex', 64));
this.headersFirst = false;
this.headerTip = null;
this.headerChain.reset();
@ -3871,11 +3876,11 @@ NonceList.prototype.remove = function remove(hostname) {
};
/**
* BlockNode
* HeaderEntry
* @constructor
*/
function BlockNode(hash, height) {
function HeaderEntry(hash, height) {
this.hash = hash;
this.height = height;
this.prev = null;