From a96868ff8e195cafd319cebf6f5fe8a155b16ca7 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 4 Mar 2017 15:40:59 -0800 Subject: [PATCH] pool: track peer heights. --- lib/http/rpc.js | 2 ++ lib/net/peer.js | 3 +++ lib/net/pool.js | 61 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 3a44bb2c..2fbc8ebd 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -538,6 +538,8 @@ RPC.prototype.getpeerinfo = co(function* getpeerinfo(args, help) { subver: peer.agent, inbound: !peer.outbound, startingheight: peer.height, + besthash: peer.bestHash, + bestheight: peer.bestHeight, banscore: peer.banScore, inflight: peer.requestMap.keys().map(util.revHex), whitelisted: false diff --git a/lib/net/peer.js b/lib/net/peer.js index 527bc01f..15bf0b4b 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -125,6 +125,9 @@ function Peer(options) { this.minPing = -1; this.blockTime = -1; + this.bestHash = null; + this.bestHeight = -1; + this.connectTimeout = null; this.pingTimer = null; this.invTimer = null; diff --git a/lib/net/pool.js b/lib/net/pool.js index 9d5e926c..57c8cf5f 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -699,6 +699,30 @@ Pool.prototype.resolveHeaders = function resolveHeaders(peer) { this.getBlock(peer, items); }; +/** + * Update all peer heights by their best hash. + * @param {Hash} hash + * @param {Number} height + */ + +Pool.prototype.resolveHeight = function resolveHeight(hash, height) { + var total = 0; + var peer; + + for (peer = this.peers.head(); peer; peer = peer.next) { + if (peer.bestHash !== hash) + continue; + + if (peer.bestHeight !== height) { + peer.bestHeight = height; + total++; + } + } + + if (total > 0) + this.logger.debug('Resolved height for %d peers.', total); +}; + /** * Find the next checkpoint. * @private @@ -1427,13 +1451,19 @@ Pool.prototype._handleInv = co(function* handleInv(peer, packet) { Pool.prototype.handleBlockInv = co(function* handleBlockInv(peer, hashes) { var items = []; - var i, hash; + var i, hash, exists, height; assert(hashes.length > 0); if (!this.syncing) return; + // Always keep track of the peer's best hash. + if (!peer.loader || this.chain.synced) { + hash = hashes[hashes.length - 1]; + peer.bestHash = hash; + } + // Ignore for now if we're still syncing if (!this.chain.synced && !peer.loader) return; @@ -1466,6 +1496,8 @@ Pool.prototype.handleBlockInv = co(function* handleBlockInv(peer, hashes) { continue; } + exists = hash; + // Normally we request the hashContinue. // In the odd case where we already have // it, we can do one of two things: either @@ -1479,6 +1511,14 @@ Pool.prototype.handleBlockInv = co(function* handleBlockInv(peer, hashes) { } } + // Attempt to update the peer's best height + // with the last existing hash we know of. + if (exists && this.chain.synced) { + height = yield this.chain.db.getHeight(exists); + if (height !== -1) + peer.bestHeight = height; + } + this.getBlock(peer, items); }); @@ -1973,7 +2013,7 @@ Pool.prototype.addBlock = co(function* addBlock(peer, block, flags) { Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) { var hash = block.hash('hex'); - var entry; + var entry, height; if (!this.syncing) return; @@ -2008,6 +2048,17 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) { return; } + // During a getblocks sync, peers send + // their best tip frequently. We can grab + // the height commitment from the coinbase. + height = block.getCoinbaseHeight(); + + if (height !== -1) { + peer.bestHash = hash; + peer.bestHeight = height; + this.resolveHeight(hash, height); + } + this.logger.debug('Peer sent an orphan block. Resolving.'); yield this.resolveOrphan(peer, hash); @@ -2015,6 +2066,12 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) { return; } + if (this.chain.synced) { + peer.bestHash = entry.hash; + peer.bestHeight = entry.height; + this.resolveHeight(entry.hash, entry.height); + } + this.logStatus(block); yield this.resolveChain(peer, hash);