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.
|
// Reset state.
|
||||||
this.tip = tip;
|
this.tip = tip;
|
||||||
this.height = tip.height;
|
this.height = tip.height;
|
||||||
this.synced = this.isFull(true);
|
this.synced = this.isFull();
|
||||||
|
|
||||||
state = yield this.getDeploymentState();
|
state = yield this.getDeploymentState();
|
||||||
|
|
||||||
@ -1669,8 +1669,8 @@ Chain.prototype.getCoinView = co(function* getCoinView(tx) {
|
|||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Chain.prototype.isFull = function isFull(force) {
|
Chain.prototype.isFull = function isFull() {
|
||||||
return !this.isInitial(force);
|
return !this.isInitial();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1681,10 +1681,7 @@ Chain.prototype.isFull = function isFull(force) {
|
|||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Chain.prototype.isInitial = function isInitial(force) {
|
Chain.prototype.isInitial = function isInitial() {
|
||||||
if (!force && this.synced)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (this.options.useCheckpoints) {
|
if (this.options.useCheckpoints) {
|
||||||
if (this.height < this.network.lastCheckpoint)
|
if (this.height < this.network.lastCheckpoint)
|
||||||
return true;
|
return true;
|
||||||
@ -1700,7 +1697,7 @@ Chain.prototype.isInitial = function isInitial(force) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Potentially emit a `sync` event.
|
* Potentially emit a `full` event.
|
||||||
* @private
|
* @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.
|
* Get the fill percentage.
|
||||||
* @returns {Number} percent - Ranges from 0.0 to 1.0.
|
* @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)
|
if (this.pool.peers.size() === 0)
|
||||||
throw new RPCError('Bitcoin is not connected!');
|
throw new RPCError('Bitcoin is not connected!');
|
||||||
|
|
||||||
if (!this.chain.isFull())
|
if (!this.chain.synced)
|
||||||
throw new RPCError('Bitcoin is downloading blocks...');
|
throw new RPCError('Bitcoin is downloading blocks...');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -164,7 +164,7 @@ Mempool.prototype._addBlock = function addBlock(block, txs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.fees)
|
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.
|
// We need to reset the rejects filter periodically.
|
||||||
// There may be a locktime in a TX that is now valid.
|
// 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);
|
this.emit('add entry', entry);
|
||||||
|
|
||||||
if (this.fees)
|
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());
|
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() {
|
Pool.prototype.resetChain = function resetChain() {
|
||||||
var tip = this.chain.tip;
|
var tip = this.chain.tip;
|
||||||
var watermark;
|
|
||||||
|
|
||||||
if (!this.options.headers)
|
if (!this.options.headers)
|
||||||
return;
|
return;
|
||||||
@ -242,16 +241,14 @@ Pool.prototype.resetChain = function resetChain() {
|
|||||||
this.headerChain.reset();
|
this.headerChain.reset();
|
||||||
this.headerNext = null;
|
this.headerNext = null;
|
||||||
|
|
||||||
watermark = this.getNextTip(tip.height);
|
if (!this.chain.hasChainwork()) {
|
||||||
|
|
||||||
if (watermark) {
|
|
||||||
this.headersFirst = true;
|
this.headersFirst = true;
|
||||||
this.headerTip = watermark;
|
this.headerTip = this.getNextTip(tip.height);
|
||||||
this.headerChain.push(new BlockNode(tip.hash, tip.height));
|
this.headerChain.push(new HeaderEntry(tip.hash, tip.height));
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
'Initialized header chain to height %d (watermark=%d).',
|
'Initialized header chain to height %d (watermark=%d).',
|
||||||
tip.height,
|
tip.height,
|
||||||
watermark.height);
|
this.headerTip.height);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -665,33 +662,25 @@ Pool.prototype.sendGetAddr = function sendGetAddr() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Request current header chain blocks.
|
* Request current header chain blocks.
|
||||||
|
* @private
|
||||||
* @param {Peer} peer
|
* @param {Peer} peer
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype.requestHeaders = co(function* requestHeaders(peer) {
|
Pool.prototype.resolveHeaders = function resolveHeaders(peer) {
|
||||||
var items = [];
|
var items = [];
|
||||||
var node;
|
var node;
|
||||||
|
|
||||||
if (!this.headerNext)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (node = this.headerNext; node; node = node.next) {
|
for (node = this.headerNext; node; node = node.next) {
|
||||||
this.headerNext = node.next;
|
this.headerNext = node.next;
|
||||||
|
|
||||||
if (yield this.hasBlock(node.hash))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
items.push(node.hash);
|
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);
|
this.getBlock(peer, items);
|
||||||
});
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the next highest watermark block.
|
* 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++) {
|
for (i = 0; i < this.network.checkpoints.length; i++) {
|
||||||
next = this.network.checkpoints[i];
|
next = this.network.checkpoints[i];
|
||||||
if (next.height > height)
|
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())
|
if (this.chain.hasChainwork())
|
||||||
return;
|
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;
|
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);
|
assert(this.headerChain.size > 0);
|
||||||
|
|
||||||
for (i = 0; i < headers.length; i++) {
|
for (i = 0; i < headers.length; i++) {
|
||||||
@ -1822,10 +1802,7 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = new BlockNode(hash, height);
|
node = new HeaderEntry(hash, height);
|
||||||
|
|
||||||
if (!this.headerNext)
|
|
||||||
this.headerNext = node;
|
|
||||||
|
|
||||||
if (node.height === this.headerTip.height) {
|
if (node.height === this.headerTip.height) {
|
||||||
if (this.options.useCheckpoints) {
|
if (this.options.useCheckpoints) {
|
||||||
@ -1841,6 +1818,9 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
|
|||||||
isWatermark = true;
|
isWatermark = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.headerNext)
|
||||||
|
this.headerNext = node;
|
||||||
|
|
||||||
this.headerChain.push(node);
|
this.headerChain.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1849,14 +1829,14 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
|
|||||||
headers.length,
|
headers.length,
|
||||||
peer.hostname());
|
peer.hostname());
|
||||||
|
|
||||||
// Request the hashes we just added.
|
// Request the blocks we just added.
|
||||||
if (isWatermark) {
|
if (isWatermark) {
|
||||||
this.headerChain.shift();
|
this.headerChain.shift();
|
||||||
yield this.requestHeaders(peer);
|
this.resolveHeaders(peer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart the getheaders process.
|
// Request more headers.
|
||||||
peer.sendGetHeaders([node.hash], this.headerTip.hash);
|
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) {
|
Pool.prototype._addBlock = co(function* addBlock(peer, block) {
|
||||||
var hash = block.hash('hex');
|
var hash = block.hash('hex');
|
||||||
var isWatermark = false;
|
|
||||||
var node, watermark;
|
|
||||||
|
|
||||||
if (!this.syncing)
|
if (!this.syncing)
|
||||||
return;
|
return;
|
||||||
@ -1933,22 +1911,6 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block) {
|
|||||||
return;
|
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();
|
peer.blockTime = util.ms();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1974,23 +1936,66 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block) {
|
|||||||
|
|
||||||
this.logStatus(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)
|
if (!this.headersFirst)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!isWatermark && !this.chain.isFull()) {
|
if (!peer.loader)
|
||||||
yield this.requestHeaders(peer);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = this.headerTip;
|
if (!this.chain.hasChainwork()) {
|
||||||
watermark = this.getNextTip(node.height);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.info(
|
||||||
|
'Chain hit target chainwork of %s. Switching to getblocks.',
|
||||||
|
this.chain.tip.chainwork.toString('hex', 64));
|
||||||
|
|
||||||
this.headersFirst = false;
|
this.headersFirst = false;
|
||||||
this.headerTip = null;
|
this.headerTip = null;
|
||||||
this.headerChain.reset();
|
this.headerChain.reset();
|
||||||
@ -3871,11 +3876,11 @@ NonceList.prototype.remove = function remove(hostname) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BlockNode
|
* HeaderEntry
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function BlockNode(hash, height) {
|
function HeaderEntry(hash, height) {
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.prev = null;
|
this.prev = null;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user