diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 923b6626..1048c472 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -55,6 +55,9 @@ var constants = bcoin.protocol.constants; * @property {BN} challenge - Local nonce. * @property {Number} lastPong - Timestamp for last `pong` * received (unix time). + * @property {Number} lastPing - Timestamp for last `ping` + * sent (unix time). + * @property {Number} minPing - Lowest ping time seen. * @property {String} id - Peer's uid. * @property {Number} banScore * @emits Peer#ack @@ -98,8 +101,10 @@ function Peer(pool, options) { this.localNonce = utils.nonce(); this.filterRate = -1; - this.challenge = utils.nonce(); - this.lastPong = 0; + this.challenge = null; + this.lastPong = -1; + this.lastPing = -1; + this.minPing = -1; this.banScore = 0; @@ -132,7 +137,7 @@ function Peer(pool, options) { this.ping = { timer: null, - interval: this.options.pingInterval || 30000 + interval: this.options.pingInterval || 120000 }; this.queue = { @@ -186,13 +191,6 @@ Peer.prototype._init = function init() { self._error(err); }); - this.ping.timer = setInterval(function() { - // self.challenge = utils.nonce(); - self.write(self.framer.ping({ - nonce: self.challenge - })); - }, this.ping.interval); - this.request('verack', function(err, payload) { if (err) { self._error(err); @@ -204,6 +202,10 @@ Peer.prototype._init = function init() { self.emit('ack'); self.ts = utils.now(); + self.ping.timer = setInterval(function() { + self.sendPing(); + }, self.ping.interval); + if (self.options.headers) { if (self.version && self.version.version > 70012) self.write(self.framer.sendHeaders()); @@ -259,12 +261,12 @@ Peer.prototype.createSocket = function createSocket(port, host) { } bcoin.debug( - 'Connecting to %s:%d (priority=%s).', + 'Connecting to %s (priority=%s).', hostname, this.priority); socket.once('connect', function() { bcoin.debug( - 'Connected to %s:%d (priority=%s).', + 'Connected to %s (priority=%s).', hostname, self.priority); }); @@ -314,6 +316,32 @@ Peer.prototype.sendInv = function sendInv(items) { this.write(this.framer.inv(items)); }; +/** + * Send a `ping` packet. + */ + +Peer.prototype.sendPing = function sendPing() { + if (!this.version) + return; + + if (this.version.version <= 60000) { + this.write(this.framer.packet('ping', new Buffer([]))); + return; + } + + if (this.challenge) { + bcoin.debug('Peer has not responded to ping (%s).', this.hostname); + return; + } + + this.lastPing = utils.ms(); + this.challenge = utils.nonce(); + + this.write(this.framer.ping({ + nonce: this.challenge + })); +}; + /** * Test whether an is being watched by the peer. * @param {BroadcastItem|TX} item @@ -367,8 +395,10 @@ Peer.prototype.destroy = function destroy() { this.socket = null; this.emit('close'); - clearInterval(this.ping.timer); - this.ping.timer = null; + if (this.ping.timer) { + clearInterval(this.ping.timer); + this.ping.timer = null; + } Object.keys(this.requests.map).forEach(function(cmd) { var queue = this.requests.map[cmd]; @@ -1220,19 +1250,40 @@ Peer.prototype._handleAddr = function handleAddr(addrs) { }; Peer.prototype._handlePing = function handlePing(data) { - this.write(this.framer.pong({ - nonce: data.nonce - })); - this.emit('ping', data); + this.write(this.framer.pong(data)); + this.emit('ping', this.minPing); }; Peer.prototype._handlePong = function handlePong(data) { - if (!this.challenge || this.challenge.cmp(data.nonce) !== 0) - return this.emit('pong', false); + var now = utils.ms(); - this.lastPong = utils.now(); + if (!this.challenge) { + bcoin.debug('Peer sent an unsolicited pong (%s).', this.hostname); + return; + } - return this.emit('pong', true); + if (data.nonce.cmp(this.challenge) !== 0) { + if (data.nonce.cmpn(0) === 0) { + bcoin.debug('Peer sent a zero nonce (%s).', this.hostname); + this.challenge = null; + return; + } + bcoin.debug('Peer sent the wrong nonce (%s).', this.hostname); + return; + } + + if (now >= this.lastPing) { + this.lastPong = now; + if (this.minPing === -1) + this.minPing = now - this.lastPing; + this.minPing = Math.min(this.minPing, now - this.lastPing); + } else { + bcoin.debug('Timing mismatch (what?) (%s).', this.hostname); + } + + this.challenge = null; + + this.emit('pong', this.minPing); }; Peer.prototype._handleGetAddr = function handleGetAddr() { @@ -1445,6 +1496,7 @@ Peer.prototype.inspect = function inspect() { + ' id=' + this.id + ' connected=' + this.connected + ' host=' + this.hostname + + ' ping=' + this.minPing + '>'; }; diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 063b950f..f75e9a37 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -868,17 +868,15 @@ utils.parseHost = function parseHost(addr) { /** * Concatenate a host and port. - * @param {Seed} seed + * @param {String} host + * @param {Number} port * @returns {String} */ -utils.hostname = function hostname(seed) { - var host = seed.host; - +utils.hostname = function hostname(host, port) { if (utils.isIP(host) === 6) host = '[' + host + ']'; - - return host + ':' + seed.port; + return host + ':' + port; }; /** @@ -1283,7 +1281,16 @@ utils.testTarget = function testTarget(hash, target) { */ utils.now = function now() { - return +new Date() / 1000 | 0; + return Math.floor(+new Date() / 1000); +}; + +/** + * Get current time in unix time (milliseconds). + * @returns {Number} + */ + +utils.ms = function ms() { + return +new Date(); }; /**