diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 818e8172..e1059b02 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -8,6 +8,7 @@ var inherits = require('inherits'); var EventEmitter = require('events').EventEmitter; var bcoin = require('../bcoin'); +var bn = require('bn.js'); var utils = bcoin.utils; var assert = utils.assert; var constants = bcoin.protocol.constants; @@ -46,6 +47,9 @@ function Peer(pool, createSocket, options) { this.host = null; this.port = 0; + this.challenge = null; + this.lastPong = 0; + if (this.options.backoff) { setTimeout(function() { self.socket = createSocket(self, pool); @@ -135,10 +139,10 @@ Peer.prototype._init = function init() { } this._ping.timer = setInterval(function() { - self._write(self.framer.ping([ - 0xde, 0xad, 0xbe, 0xef, - 0xde, 0xad, 0xbe, 0xef - ])); + self.challenge = self._nonce(); + self._write(self.framer.ping({ + nonce: self.challenge + })); }, this._ping.interval); // Send hello @@ -441,12 +445,20 @@ Peer.prototype._handleAddr = function handleAddr(addrs) { }, this); }; -Peer.prototype._handlePing = function handlePing() { - // No-op for now +Peer.prototype._handlePing = function handlePing(data) { + this._write(this.framer.pong({ + nonce: data.nonce + })); + this.emit('ping', data); }; -Peer.prototype._handlePong = function handlePong() { - // No-op for now +Peer.prototype._handlePong = function handlePong(data) { + if (!this.challenge || this.challenge.cmp(data.nonce) !== 0) + return this.emit('pong', false); + + this.lastPong = utils.now(); + + return this.emit('pong', true); }; Peer.prototype._handleGetAddr = function handleGetAddr() { @@ -586,6 +598,14 @@ Peer.prototype.reject = function reject(details) { this._write(this.framer.reject(details)); }; +Peer.nonce = new bn(0xffffffff).ushln(32).uor(new bn(0xffffffff)); + +Peer.prototype._nonce = function _nonce() { + var nonce = Peer.nonce.clone(); + nonce.imuln(Math.random()); + return nonce; +}; + /** * Expose */ diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index bd1c21cd..547948ec 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -667,10 +667,15 @@ Pool.prototype._createPeer = function _createPeer(backoff) { }); peer.on('reject', function(payload) { + payload.data = utils.toHex(payload.data); + self.emit('debug', 'Reject: msg=%s ccode=%s reason=%s data=%s', - payload.message, payload.ccode, payload.reason, - utils.toHex(payload.data)); + payload.message, + payload.ccode, + payload.reason, + payload.data); + self.emit('reject', payload, peer); }); diff --git a/lib/bcoin/protocol/framer.js b/lib/bcoin/protocol/framer.js index fa7faed9..0bc24bb9 100644 --- a/lib/bcoin/protocol/framer.js +++ b/lib/bcoin/protocol/framer.js @@ -158,12 +158,16 @@ Framer.prototype.notFound = function notFound(items) { return this._inv('notfound', items); }; -Framer.prototype.ping = function ping(nonce) { - return this.packet('ping', nonce); +Framer.prototype.ping = function ping(data) { + var p = []; + utils.writeU64(p, data.nonce, 0); + return this.packet('ping', p); }; -Framer.prototype.pong = function pong(nonce) { - return this.packet('pong', nonce.slice(0, 8)); +Framer.prototype.pong = function pong(data) { + var p = []; + utils.writeU64(p, data.nonce, 0); + return this.packet('pong', p); }; Framer.prototype.filterLoad = function filterLoad(bloom, update) { diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index 084abfe2..ff9138e4 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -139,9 +139,33 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) { if (cmd === 'addr') return this.parseAddr(p); + if (cmd === 'ping') + return this.parsePing(p); + + if (cmd === 'pong') + return this.parsePong(p); + return p; }; +Parser.prototype.parsePing = function parsePing(p) { + if (p.length < 8) + return this._error('pong packet is too small'); + + return { + nonce: readU64(p, 0) + }; +}; + +Parser.prototype.parsePong = function parsePong(p) { + if (p.length < 8) + return this._error('ping packet is too small'); + + return { + nonce: readU64(p, 0) + }; +}; + Parser.prototype.parseVersion = function parseVersion(p) { var v, services, ts, nonce, result, off, agent, height, relay; @@ -152,10 +176,10 @@ Parser.prototype.parseVersion = function parseVersion(p) { services = readU64(p, 4); // Timestamp - ts = readU64(p, 12); + ts = utils.read64(p, 12); // Nonce, very dramatic - nonce = { lo: readU32(p, 72), hi: readU32(p, 76) }; + nonce = readU64(p, 72); // User agent length result = utils.readIntv(p, 80); @@ -356,7 +380,7 @@ Parser.prototype.parseTXOut = function parseTXOut(p) { return { size: off + scriptLen, - value: new bn(utils.toArray(p.slice(0, 8)).reverse()), + value: utils.read64(p, 0), script: bcoin.script.decode(utils.toArray(p.slice(off, off + scriptLen))) }; }; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index aa3b118e..4a838c35 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -1288,7 +1288,7 @@ TX.prototype.toRaw = function toRaw(enc) { TX.fromRaw = function fromRaw(raw, enc) { if (enc === 'hex') raw = utils.toArray(raw, 'hex'); - return new bcoin.protocol.parser().parseTX(raw); + return new bcoin.tx(new bcoin.protocol.parser().parseTX(raw)); }; /** diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index e08edbe8..70167746 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -188,134 +188,6 @@ utils.dsha256 = function dsha256(data, enc) { return utils.sha256(utils.sha256(data, enc)); }; -utils.readU16 = function readU16(arr, off) { - if (!off) - off = 0; - return arr[off] | (arr[off + 1] << 8); -}; - -utils.readU32 = function readU32(arr, off) { - if (!off) - off = 0; - - var r = arr[off] - | (arr[off + 1] << 8) - | (arr[off + 2] << 16) - | (arr[off + 3] << 24); - - if (r < 0) - r += 0x100000000; - - return r; -}; - -utils.readU64 = function readU64(arr, off) { - if (!off) - off = 0; - return utils.readU32(arr, off) + utils.readU32(arr, off + 4) * 0x100000000; -}; - -utils.writeU16 = function writeU16(dst, num, off) { - if (!off) - off = 0; - dst[off] = num & 0xff; - dst[off + 1] = (num >>> 8) & 0xff; - return 2; -}; - -utils.writeU32 = function writeU32(dst, num, off) { - if (!off) - off = 0; - dst[off] = num & 0xff; - dst[off + 1] = (num >>> 8) & 0xff; - dst[off + 2] = (num >>> 16) & 0xff; - dst[off + 3] = (num >>> 24) & 0xff; - return 4; -}; - -utils.writeU64 = function writeU64(dst, num, off) { - var i = 0; - - if (!off) - off = 0; - - num = new bn(num).maskn(64).toArray(); - while (num.length < 8) - num.unshift(0); - - num.reverse().forEach(function(ch) { - dst[off++] = ch; - }); - - i = num.length; - - while (i--) - dst[off++] = num[i]; - - return 8; -}; - -utils.writeU16BE = function writeU16BE(dst, num, off) { - if (!off) - off = 0; - dst[off] = (num >>> 8) & 0xff; - dst[off + 1] = num & 0xff; - return 2; -}; - -utils.writeU32BE = function writeU32BE(dst, num, off) { - if (!off) - off = 0; - dst[off] = (num >>> 24) & 0xff; - dst[off + 1] = (num >>> 16) & 0xff; - dst[off + 2] = (num >>> 8) & 0xff; - dst[off + 3] = num & 0xff; - return 4; -}; - -utils.writeU64BE = function writeU64BE(dst, num, off) { - var i = 0; - - if (!off) - off = 0; - - num = new bn(num).maskn(64).toArray(); - while (num.length < 8) - num.unshift(0); - - for (; i < num.length; i++) - dst[off++] = num[i]; - - return 8; -}; - -utils.readU16BE = function readU16BE(arr, off) { - if (!off) - off = 0; - return (arr[off] << 8) | arr[off + 1]; -}; - -utils.readU32BE = function readU32BE(arr, off) { - if (!off) - off = 0; - - var r = (arr[off] << 24) - | (arr[off + 1] << 16) - | (arr[off + 2] << 8) - | arr[off + 3]; - - if (r < 0) - r += 0x100000000; - - return r; -}; - -utils.readU64BE = function readU64BE(arr, off) { - if (!off) - off = 0; - return utils.readU32BE(arr, off) * 0x100000000 + utils.readU32BE(arr, off + 4); -}; - utils.writeAscii = function writeAscii(dst, str, off) { var i = 0; var c; @@ -660,10 +532,10 @@ utils.isIP = function isIP(ip) { if (typeof ip !== 'string') return 0; - if (~ip.indexOf('.')) + if (ip.indexOf('.') !== -1) return 4; - if (~ip.indexOf(':')) + if (ip.indexOf(':') !== -1) return 6; return 0; @@ -759,65 +631,12 @@ utils.sortKeys = function sortKeys(keys) { }); }; -utils.readIntv = function readIntv(p, off) { - var r, bytes; - - if (!off) - off = 0; - - if (p[off] < 0xfd) { - r = p[off]; - bytes = 1; - } else if (p[off] === 0xfd) { - r = p[off + 1] | (p[off + 2] << 8); - bytes = 3; - } else if (p[off] === 0xfe) { - r = utils.readU32(p, off + 1); - bytes = 5; - } else { - r = 0; - bytes = 9; - } - - return { off: off + bytes, r: r }; -}; - -utils.writeIntv = function writeIntv(arr, value, off) { - if (!off) - off = 0; - - if (value < 0xfd) { - arr[off] = value; - return 1; - } - - if (value <= 0xffff) { - arr[off] = 0xfd; - arr[off + 1] = value & 0xff; - arr[off + 2] = value >>> 8; - return 3; - } - - if (value <= 0xffffffff) { - arr[off] = 0xfe; - arr[off + 1] = value & 0xff; - arr[off + 2] = (value >>> 8) & 0xff; - arr[off + 3] = (value >>> 16) & 0xff; - arr[off + 4] = value >>> 24; - return 5; - } - - arr[off] = 0xff; - utils.writeU64(arr, value, off + 1); - return 9; -}; - utils.uniq = function(obj) { var out = []; var i = 0; for (; i < obj.length; i++) { - if (!~out.indexOf(obj[i])) + if (out.indexOf(obj[i]) === -1) out.push(obj[i]); } @@ -917,3 +736,406 @@ utils.hash = function hash(obj, enc) { throw new Error('Cannot get hash of object'); }; + +// +// Integer Functions +// +// Non-64-bit functions originally taken from the node.js tree: +// +// Copyright Joyent, Inc. and other Node contributors. All rights reserved. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +utils.readU8 = function readU8(arr, off) { + off = off >>> 0; + return arr[off]; +}; + +utils.readU16 = function readU16(arr, off) { + off = off >>> 0; + return arr[off] | (arr[off + 1] << 8); +}; + +utils.readU16BE = function readU16BE(arr, off) { + off = off >>> 0; + return (arr[off] << 8) | arr[off + 1]; +}; + +utils.readU32 = function readU32(arr, off) { + off = off >>> 0; + + return ((arr[off]) + | (arr[off + 1] << 8) + | (arr[off + 2] << 16)) + + (arr[off + 3] * 0x1000000); +}; + +utils.readU32BE = function readU32BE(arr, off) { + off = off >>> 0; + + return (arr[off] * 0x1000000) + + ((arr[off + 1] << 16) + | (arr[off + 2] << 8) + | arr[off + 3]); +}; + +utils.readU64 = function readU64(arr, off) { + var num; + off = off >>> 0; + num = utils.toArray(arr.slice(off, off + 8)).reverse(); + return new bn(num); +}; + +utils.readU64BE = function readU64BE(arr, off) { + var num; + off = off >>> 0; + num = utils.toArray(arr.slice(off, off + 8)); + return new bn(num); +}; + +utils.read8 = function read8(arr, off) { + var num; + off = off >>> 0; + num = arr[off]; + return !(num & 0x80) ? num : (0xff - num + 1) * -1; +}; + +utils.read16 = function read16(arr, off) { + var num; + off = off >>> 0; + num = arr[off] | (arr[off + 1] << 8); + return (num & 0x8000) ? num | 0xffff0000 : num; +}; + +utils.read16BE = function read16BE(arr, off) { + var num; + off = off >>> 0; + num = arr[off + 1] | (arr[off] << 8); + return (num & 0x8000) ? num | 0xffff0000 : num; +}; + +utils.read32 = function read32(arr, off) { + off = off >>> 0; + + return (arr[off]) + | (arr[off + 1] << 8) + | (arr[off + 2] << 16) + | (arr[off + 3] << 24); +}; + +utils.read32BE = function read32BE(arr, off) { + off = off >>> 0; + + return (arr[off] << 24) + | (arr[off + 1] << 16) + | (arr[off + 2] << 8) + | (arr[off + 3]); +}; + +utils.read64 = function read64(arr, off) { + var num; + + off = off >>> 0; + + num = utils.toArray(arr.slice(off, off + 8)).reverse(); + + if (num[0] & 0x80) { + num[0] &= ~0x80; + num = new bn(num); + num = num.neg(); + return num; + } + + return new bn(num); +}; + +utils.read64BE = function read64BE(arr, off) { + var num; + + off = off >>> 0; + + num = utils.toArray(arr.slice(off, off + 8)); + + if (num[0] & 0x80) { + num[0] &= ~0x80; + num = new bn(num); + num = num.neg(); + return num; + } + + return new bn(num); +}; + +utils.writeU8 = function readU8(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = num & 0xff; + return 1; +}; + +utils.writeU16 = function readU16(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = num & 0xff; + dst[off + 1] = (num >>> 8) & 0xff; + return 2; +}; + +utils.writeU16BE = function read16BE(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = (num >>> 8) & 0xff; + dst[off + 1] = num & 0xff; + return 2; +}; + +utils.writeU32 = function writeU32(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off + 3] = (num >>> 24) & 0xff; + dst[off + 2] = (num >>> 16) & 0xff; + dst[off + 1] = (num >>> 8) & 0xff; + dst[off] = num & 0xff; + return 4; +}; + +utils.writeU32BE = function writeU32BE(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = (num >>> 24) & 0xff; + dst[off + 1] = (num >>> 16) & 0xff; + dst[off + 2] = (num >>> 8) & 0xff; + dst[off + 3] = num & 0xff; + return 4; +}; + +utils.writeU64 = function writeU64(dst, num, off) { + var i; + + if (!(num instanceof bn)) { + num = +num; + num = new bn(num); + } + + off = off >>> 0; + + num = num.maskn(64).toArray(); + + while (num.length < 8) + num.unshift(0); + + for (i = num.length - 1; i >= 0; i--) + dst[off++] = num[i] & 0xff; + + return 8; +}; + +utils.writeU64BE = function writeU64BE(dst, num, off) { + var i; + + if (!(num instanceof bn)) { + num = +num; + bn = new bn(num); + } + + off = off >>> 0; + + num = num.maskn(64).toArray(); + + while (num.length < 8) + num.unshift(0); + + for (i = 0; i < num.length; i++) + dst[off++] = num[i] & 0xff; + + return 8; +}; + +utils.write8 = function write8(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = num & 0xff; + return 1; +}; + +utils.write16 = function write16(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = num & 0xff; + dst[off + 1] = (num >>> 8) & 0xff; + return 2; +}; + +utils.write16BE = function write16BE(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = (num >>> 8) & 0xff; + dst[off + 1] = num & 0xff; + return 2; +}; + +utils.write32 = function write32(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = num & 0xff; + dst[off + 1] = (num >>> 8) & 0xff; + dst[off + 2] = (num >>> 16) & 0xff; + dst[off + 3] = (num >>> 24) & 0xff; + return 4; +}; + +utils.write32BE = function write32BE(dst, num, off) { + num = +num; + off = off >>> 0; + dst[off] = (num >>> 24) & 0xff; + dst[off + 1] = (num >>> 16) & 0xff; + dst[off + 2] = (num >>> 8) & 0xff; + dst[off + 3] = num & 0xff; + return 4; +}; + +utils.write64 = function write64(dst, num, off) { + var i; + + if (!(num instanceof bn)) { + num = +num; + if (num < 0) { + num &= ~0x80000000; + num = new bn(num); + num = num.neg(); + } else { + num = new bn(num); + } + } + + off = off >>> 0; + + num = num.maskn(64).toArray(); + + while (num.length < 8) + num.unshift(0); + + if (num.isNeg()) + num[0] |= 0x80; + + for (i = num.length - 1; i >= 0; i--) + dst[off++] = num[i] & 0xff; + + return 8; +}; + +utils.write64BE = function write64BE(dst, num, off) { + var i; + + if (!(num instanceof bn)) { + num = +num; + if (num < 0) { + num &= ~0x80000000; + num = new bn(num); + num = num.neg(); + } else { + num = new bn(num); + } + } + + off = off >>> 0; + + num = num.maskn(64).toArray(); + + while (num.length < 8) + num.unshift(0); + + if (num.isNeg()) + num[0] |= 0x80; + + for (i = 0; i < num.length; i++) + dst[off++] = num[i] & 0xff; + + return 8; +}; + +utils.readIntv = function readIntv(arr, off) { + var r, bytes; + + off = off >>> 0; + + if (arr[off] < 0xfd) { + r = arr[off]; + bytes = 1; + } else if (arr[off] === 0xfd) { + r = arr[off + 1] | (arr[off + 2] << 8); + bytes = 3; + } else if (arr[off] === 0xfe) { + r = utils.readU32(arr, off + 1); + bytes = 5; + } else if (arr[off] === 0xff) { + try { + r = utils.readU64(arr, off + 1).toNumber(); + } catch (e) { + r = 0; + } + bytes = 9; + } else { + // Malformed + r = arr[off]; + bytes = 1; + } + + return { off: off + bytes, r: r }; +}; + +utils.writeIntv = function writeIntv(dst, num, off) { + off = off >>> 0; + + if (num instanceof bn) { + if (num.cmpn(0xffffffff) > 0) { + dst[off] = 0xff; + utils.writeU64(dst, num, off + 1); + return 9; + } + num = num.toNumber(); + } + + num = +num; + + if (num < 0xfd) { + dst[off] = num & 0xff; + return 1; + } + + if (num <= 0xffff) { + dst[off] = 0xfd; + dst[off + 1] = num & 0xff; + dst[off + 2] = (num >>> 8) & 0xff; + return 3; + } + + if (num <= 0xffffffff) { + dst[off] = 0xfe; + dst[off + 1] = num & 0xff; + dst[off + 2] = (num >>> 8) & 0xff; + dst[off + 3] = (num >>> 16) & 0xff; + dst[off + 4] = (num >>> 24) & 0xff; + return 5; + } + + dst[off] = 0xff; + utils.writeU64(dst, num, off + 1); + return 9; +};