pool: refactor header sync.
This commit is contained in:
parent
219671c620
commit
6e13fe57c0
@ -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.
|
||||
|
||||
@ -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...');
|
||||
}
|
||||
|
||||
|
||||
@ -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());
|
||||
|
||||
|
||||
143
lib/net/pool.js
143
lib/net/pool.js
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user