diff --git a/lib/crypto/crypto.js b/lib/crypto/crypto.js index aafa2962..7033ce3f 100644 --- a/lib/crypto/crypto.js +++ b/lib/crypto/crypto.js @@ -13,7 +13,7 @@ var scrypt = require('./scrypt'); var scryptAsync = require('./scrypt-async'); var utils = require('../utils/utils'); var native = require('../utils/native'); -var nativeCrypto, supersha, hash, aes; +var nativeCrypto, hash, aes; var isBrowser = (typeof process !== 'undefined' && process.browser) diff --git a/lib/env.js b/lib/env.js index 277024ac..721fea72 100644 --- a/lib/env.js +++ b/lib/env.js @@ -123,6 +123,7 @@ function Environment() { this.require('reader', './utils/reader'); this.require('writer', './utils/writer'); this.require('lru', './utils/lru'); + this.require('bloom', './utils/bloom'); this.require('uri', './utils/uri'); this.require('errors', './utils/errors'); @@ -147,7 +148,6 @@ function Environment() { this.require('sc', './script/sigcache'); // Primitives - this.require('bloom', './primitives/bloom'); this.require('address', './primitives/address'); this.require('outpoint', './primitives/outpoint'); this.require('input', './primitives/input'); @@ -162,6 +162,7 @@ function Environment() { this.require('merkleblock', './primitives/merkleblock'); this.require('headers', './primitives/headers'); this.require('keyring', './primitives/keyring'); + this.require('netaddress', './primitives/netaddress'); // HD this.require('hd', './hd/hd'); diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 4e43b489..d70ca235 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -11,7 +11,7 @@ var utils = require('../utils/utils'); var crypto = require('../crypto/crypto'); var assert = utils.assert; var constants = bcoin.constants; -var NetworkAddress = bcoin.packets.NetworkAddress; +var NetworkAddress = require('../primitives/netaddress'); var fs; try { diff --git a/lib/net/bip150.js b/lib/net/bip150.js index df8693cf..b414e9ed 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -12,12 +12,10 @@ var EventEmitter = require('events').EventEmitter; var bcoin = require('../env'); var utils = require('../utils/utils'); var crypto = require('../crypto/crypto'); +var packets = require('./packets'); var assert = utils.assert; var constants = bcoin.constants; -var ZERO_SIG = new Buffer(64); -ZERO_SIG.fill(0); - /** * Represents a BIP150 input and output stream. * @exports BIP150 @@ -76,9 +74,7 @@ BIP150.prototype.isAuthed = function isAuthed() { return this.challengeReceived && this.replyReceived; }; -BIP150.prototype.challenge = function challenge(payload) { - var p = bcoin.reader(payload); - var hash = p.readHash(); +BIP150.prototype.challenge = function challenge(hash) { var type = this.outbound ? 'r' : 'i'; var msg, sig; @@ -92,7 +88,7 @@ BIP150.prototype.challenge = function challenge(payload) { msg = this.hash(this.input.sid, type, this.publicKey); if (!crypto.ccmp(hash, msg)) - return ZERO_SIG; + return constants.ZERO_SIG64; if (this.isAuthed()) { this.auth = true; @@ -105,9 +101,7 @@ BIP150.prototype.challenge = function challenge(payload) { return bcoin.ec.fromDER(sig); }; -BIP150.prototype.reply = function reply(payload) { - var p = bcoin.reader(payload); - var data = p.readBytes(64); +BIP150.prototype.reply = function reply(data) { var type = this.outbound ? 'i' : 'r'; var sig, msg, result; @@ -115,7 +109,7 @@ BIP150.prototype.reply = function reply(payload) { assert(!this.replyReceived, 'Peer replied twice.'); this.replyReceived = true; - if (utils.equal(data, ZERO_SIG)) + if (utils.equal(data, constants.ZERO_SIG64)) throw new Error('Auth failure.'); if (!this.peerIdentity) @@ -141,9 +135,7 @@ BIP150.prototype.reply = function reply(payload) { return this.hash(this.input.sid, 'p', this.publicKey); }; -BIP150.prototype.propose = function propose(payload) { - var p = bcoin.reader(payload); - var hash = p.readHash(); +BIP150.prototype.propose = function propose(hash) { var match; assert(!this.outbound, 'Outbound peer tried to propose.'); @@ -167,8 +159,7 @@ BIP150.prototype.propose = function propose(payload) { return this.hash(this.output.sid, 'r', this.peerIdentity); }; -BIP150.prototype.toChallenge = function toChallenge(writer) { - var p = new bcoin.writer(writer); +BIP150.prototype.toChallenge = function toChallenge() { var msg; assert(this.bip151.handshake, 'No BIP151 handshake before challenge.'); @@ -180,12 +171,7 @@ BIP150.prototype.toChallenge = function toChallenge(writer) { assert(!this.challengeSent, 'Cannot initiate challenge twice.'); this.challengeSent = true; - p.writeBytes(msg); - - if (!writer) - p = p.render(); - - return p; + return new packets.AuthChallengePacket(msg); }; BIP150.prototype.rekey = function rekey(sid, key, req, res) { diff --git a/lib/net/bip151.js b/lib/net/bip151.js index a63818cb..8d10a2be 100644 --- a/lib/net/bip151.js +++ b/lib/net/bip151.js @@ -19,6 +19,7 @@ var crypto = require('../crypto/crypto'); var assert = utils.assert; var constants = bcoin.constants; var chachapoly = require('../crypto/chachapoly'); +var packets = require('./packets'); /* * Constants @@ -526,19 +527,10 @@ BIP151.prototype.isReady = function isReady() { * @returns {Buffer} */ -BIP151.prototype.toEncinit = function toEncinit(writer) { - var p = bcoin.writer(writer); - - p.writeBytes(this.input.getPublicKey()); - p.writeU8(this.input.cipher); - - if (!writer) - p = p.render(); - +BIP151.prototype.toEncinit = function toEncinit() { assert(!this.initSent, 'Cannot init twice.'); this.initSent = true; - - return p; + return new packets.EncinitPacket(this.input.getPublicKey(), this.input.cipher); }; /** @@ -547,16 +539,8 @@ BIP151.prototype.toEncinit = function toEncinit(writer) { * @returns {Buffer} */ -BIP151.prototype.toEncack = function toEncack(writer) { - var p = bcoin.writer(writer); - +BIP151.prototype.toEncack = function toEncack() { assert(this.output.prk, 'Cannot ack before init.'); - - p.writeBytes(this.output.getPublicKey()); - - if (!writer) - p = p.render(); - assert(!this.ackSent, 'Cannot ack twice.'); this.ackSent = true; @@ -565,7 +549,7 @@ BIP151.prototype.toEncack = function toEncack(writer) { this.emit('handshake'); } - return p; + return new packets.EncackPacket(this.output.getPublicKey()); }; /** @@ -575,17 +559,9 @@ BIP151.prototype.toEncack = function toEncack(writer) { * @returns {Buffer} */ -BIP151.prototype.toRekey = function toRekey(writer) { - var p = bcoin.writer(writer); - +BIP151.prototype.toRekey = function toRekey() { assert(this.handshake, 'Cannot rekey before handshake.'); - - p.writeBytes(constants.ZERO_KEY); - - if (!writer) - p = p.render(); - - return p; + return new packets.EncackPacket(constants.ZERO_KEY); }; /** @@ -593,16 +569,10 @@ BIP151.prototype.toRekey = function toRekey(writer) { * @param {Buffer} */ -BIP151.prototype.encinit = function encinit(data) { - var p = bcoin.reader(data); - var publicKey = p.readBytes(33); - var cipher = p.readU8(); - +BIP151.prototype.encinit = function encinit(publicKey, cipher) { + assert(cipher === this.output.cipher, 'Cipher mismatch.'); assert(!this.initReceived, 'Already initialized.'); this.initReceived = true; - - assert(cipher === this.output.cipher, 'Cipher mismatch.'); - this.output.init(publicKey); }; @@ -611,10 +581,7 @@ BIP151.prototype.encinit = function encinit(data) { * @param {Buffer} data */ -BIP151.prototype.encack = function encack(data) { - var p = bcoin.reader(data); - var publicKey = p.readBytes(33); - +BIP151.prototype.encack = function encack(publicKey) { assert(this.initSent, 'Unsolicited ACK.'); if (utils.equal(publicKey, constants.ZERO_KEY)) { diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 9f4eebd6..84614c40 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -552,43 +552,6 @@ TXResponse.prototype.toRaw = function toRaw(witness, writer) { return p; }; -/** - * Represents a SendCompact message (bip152): `sendcmpct` packet. - * @see https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki - * @constructor - */ - -function SendCompact(mode, version) { - if (!(this instanceof SendCompact)) - return new SendCompact(mode, version); - - this.mode = mode || 0; - this.version = version || 1; -} - -SendCompact.prototype.fromRaw = function fromRaw(data) { - var p = bcoin.reader(data); - this.mode = p.readU8(); - this.version = p.readU53(); - return this; -}; - -SendCompact.fromRaw = function fromRaw(data) { - return new SendCompact().fromRaw(data); -}; - -SendCompact.prototype.toRaw = function toRaw(writer) { - var p = bcoin.writer(writer); - - p.writeU8(this.mode); - p.writeU64(this.version); - - if (!writer) - p = p.render(); - - return p; -}; - /* * Expose */ @@ -596,4 +559,3 @@ SendCompact.prototype.toRaw = function toRaw(writer) { exports.CompactBlock = CompactBlock; exports.TXRequest = TXRequest; exports.TXResponse = TXResponse; -exports.SendCompact = SendCompact; diff --git a/lib/net/framer.js b/lib/net/framer.js index 5f8b73ed..a3d4edb1 100644 --- a/lib/net/framer.js +++ b/lib/net/framer.js @@ -11,8 +11,6 @@ var bcoin = require('../env'); var utils = require('../utils/utils'); var crypto = require('../crypto/crypto'); var assert = utils.assert; -var BufferWriter = require('../utils/writer'); -var DUMMY = new Buffer(0); /** * Protocol packet framer @@ -77,436 +75,6 @@ Framer.prototype.packet = function packet(cmd, payload, checksum) { return packet; }; -/** - * Create a version packet with a header. - * @param {VersionPacket} payload - * @returns {Buffer} version packet. - */ - -Framer.prototype.version = function version(payload) { - return this.packet('version', payload.toRaw()); -}; - -/** - * Create a verack packet with a header. - * @returns {Buffer} verack packet. - */ - -Framer.prototype.verack = function verack() { - return this.packet('verack', DUMMY); -}; - -/** - * Create a ping packet with a header. - * @param {BN} nonce - * @returns {Buffer} ping packet. - */ - -Framer.prototype.ping = function ping(nonce) { - if (!nonce) - return this.packet('ping', DUMMY); - return this.packet('ping', framePing(nonce)); -}; - -/** - * Create a pong packet with a header. - * @param {BN} nonce - * @returns {Buffer} pong packet. - */ - -Framer.prototype.pong = function pong(nonce) { - return this.packet('pong', framePing(nonce)); -}; - -/** - * Create an alert packet with a header. - * @param {AlertPacket} alert - * @returns {Buffer} alert packet. - */ - -Framer.prototype.alert = function _alert(alert) { - return this.packet('alert', alert.toRaw()); -}; - -/** - * Create a getaddr packet with a header. - * @returns {Buffer} getaddr packet. - */ - -Framer.prototype.getAddr = function getAddr() { - return this.packet('getaddr', DUMMY); -}; - -/** - * Create an addr packet with a header. - * @param {NetworkAddress[]} hosts - * @returns {Buffer} addr packet. - */ - -Framer.prototype.addr = function addr(hosts) { - return this.packet('addr', frameAddr(hosts)); -}; - -/** - * Create an inv packet with a header. - * @param {InvItem[]} items - * @returns {Buffer} inv packet. - */ - -Framer.prototype.inv = function inv(items) { - return this.packet('inv', frameItems(items)); -}; - -/** - * Create a getdata packet with a header. - * @param {InvItem[]} items - * @returns {Buffer} getdata packet. - */ - -Framer.prototype.getData = function getData(items) { - return this.packet('getdata', frameItems(items)); -}; - -/** - * Create a notfound packet with a header. - * @param {InvItem[]} items - * @returns {Buffer} notfound packet. - */ - -Framer.prototype.notFound = function notFound(items) { - return this.packet('notfound', frameItems(items)); -}; - -/** - * Create a getblocks packet with a header. - * @param {GetBlocksPacket} data - * @returns {Buffer} getblocks packet. - */ - -Framer.prototype.getBlocks = function getBlocks(data) { - return this.packet('getblocks', data.toRaw()); -}; - -/** - * Create a getheaders packet with a header. - * @param {GetBlocksPacket} data - * @returns {Buffer} getheaders packet. - */ - -Framer.prototype.getHeaders = function getHeaders(data) { - return this.packet('getheaders', data.toRaw()); -}; - -/** - * Create a headers packet with a header. - * @param {Headers[]} headers - * @returns {Buffer} headers packet. - */ - -Framer.prototype.headers = function _headers(headers) { - return this.packet('headers', frameItems(headers)); -}; - -/** - * Create a sendheaders packet with a header. - * @returns {Buffer} sendheaders packet. - */ - -Framer.prototype.sendHeaders = function sendHeaders() { - return this.packet('sendheaders', DUMMY); -}; - -/** - * Create a block packet with a header. - * @param {Block} block - * @returns {Buffer} block packet. - */ - -Framer.prototype.block = function _block(block) { - return this.packet('block', block.toNormal()); -}; - -/** - * Create a block packet with a header, - * using witness serialization. - * @param {Block} block - * @returns {Buffer} block packet. - */ - -Framer.prototype.witnessBlock = function witnessBlock(block) { - return this.packet('block', block.toRaw()); -}; - -/** - * Create a tx packet with a header. - * @param {TX} tx - * @returns {Buffer} tx packet. - */ - -Framer.prototype.tx = function _tx(tx) { - return this.packet('tx', tx.toNormal(), tx.hash()); -}; - -/** - * Create a tx packet with a header, - * using witness serialization. - * @param {TX} tx - * @returns {Buffer} tx packet. - */ - -Framer.prototype.witnessTX = function witnessTX(tx) { - var checksum; - - // Save some time by using the - // cached hash as our checksum. - if (tx.hasWitness()) { - // We can't use the coinbase - // hash since it is all zeroes. - // We really shouldn't be - // relaying coinbases in the - // first place, but oh well. - if (!tx.isCoinbase()) - checksum = tx.witnessHash(); - } else { - checksum = tx.hash(); - } - - return this.packet('tx', tx.toRaw(), checksum); -}; - -/** - * Create a reject packet with a header. - * @param {RejectPacket} details - * @returns {Buffer} reject packet. - */ - -Framer.prototype.reject = function reject(details) { - return this.packet('reject', details.toRaw()); -}; - -/** - * Create a mempool packet with a header. - * @returns {Buffer} mempool packet. - */ - -Framer.prototype.mempool = function mempool() { - return this.packet('mempool', DUMMY); -}; - -/** - * Create a filterload packet with a header. - * @param {Bloom} filter - * @returns {Buffer} filterload packet. - */ - -Framer.prototype.filterLoad = function filterLoad(filter) { - return this.packet('filterload', filter.toRaw()); -}; - -/** - * Create a filteradd packet with a header. - * @param {Buffer} data - * @returns {Buffer} filteradd packet. - */ - -Framer.prototype.filterAdd = function filterAdd(data) { - return this.packet('filteradd', frameFilterAdd(data)); -}; - -/** - * Create a filterclear packet with a header. - * @returns {Buffer} filterclear packet. - */ - -Framer.prototype.filterClear = function filterClear() { - return this.packet('filterclear', DUMMY); -}; - -/** - * Create a merkleblock packet with a header. - * @param {MerkleBlock} block - * @returns {Buffer} merkleblock packet. - */ - -Framer.prototype.merkleBlock = function merkleBlock(block) { - return this.packet('merkleblock', block.toRaw()); -}; - -/** - * Create a getutxos packet with a header. - * @param {GetUTXOsPacket} data - * @returns {Buffer} getutxos packet. - */ - -Framer.prototype.getUTXOs = function getUTXOs(data) { - return this.packet('getutxos', data.toRaw()); -}; - -/** - * Create a utxos packet with a header. - * @param {UTXOsPacket} utxos - * @returns {Buffer} utxos packet. - */ - -Framer.prototype.UTXOs = function UTXOs(utxos) { - return this.packet('utxos', utxos.toRaw()); -}; - -/** - * Create a havewitness packet with a header. - * @returns {Buffer} havewitness packet. - */ - -Framer.prototype.haveWitness = function haveWitness() { - return this.packet('havewitness', DUMMY); -}; - -/** - * Create a feefilter packet with a header. - * @param {Rate} rate - * @returns {Buffer} feefilter packet. - */ - -Framer.prototype.feeFilter = function feeFilter(rate) { - return this.packet('feefilter', frameFeeFilter(rate)); -}; - -/** - * Create a sendcmpct packet with a header. - * @param {SendCompact} data - * @returns {Buffer} sendcmpct packet. - */ - -Framer.prototype.sendCmpct = function sendCmpct(data) { - return this.packet('sendcmpct', data.toRaw()); -}; - -/** - * Create a cmpctblock packet with a header. - * @param {CompactBlock} block - * @returns {Buffer} cmpctblock packet. - */ - -Framer.prototype.cmpctBlock = function cmpctBlock(block) { - return this.packet('cmpctblock', block.toRaw(false)); -}; - -/** - * Create a getblocktxn packet with a header. - * @param {TXRequest} req - * @returns {Buffer} getblocktxn packet. - */ - -Framer.prototype.getBlockTxn = function getBlockTxn(req) { - return this.packet('getblocktxn', req.toRaw()); -}; - -/** - * Create a blocktxn packet with a header. - * @param {TXResponse} res - * @returns {Buffer} blocktxn packet. - */ - -Framer.prototype.blockTxn = function blockTxn(res) { - return this.packet('blocktxn', res.toRaw(false)); -}; - -/** - * Create an encinit packet with a header. - * @param {Buffer} data - * @returns {Buffer} encinit packet. - */ - -Framer.prototype.encinit = function encinit(data) { - return this.packet('encinit', data); -}; - -/** - * Create an encack packet with a header. - * @param {Buffer} data - * @returns {Buffer} encack packet. - */ - -Framer.prototype.encack = function encack(data) { - return this.packet('encack', data); -}; - -/** - * Create a authchallenge packet with a header. - * @param {Buffer} data - * @returns {Buffer} authchallenge packet. - */ - -Framer.prototype.authChallenge = function authChallenge(data) { - return this.packet('authchallenge', data); -}; - -/** - * Create a authreply packet with a header. - * @param {Buffer} data - * @returns {Buffer} authreply packet. - */ - -Framer.prototype.authReply = function authReply(data) { - return this.packet('authreply', data); -}; - -/** - * Create a authpropose packet with a header. - * @param {Buffer} data - * @returns {Buffer} authpropose packet. - */ - -Framer.prototype.authPropose = function authPropose(data) { - return this.packet('authpropose', data); -}; - -/* - * Helpers - */ - -function frameItems(items) { - var p = new BufferWriter(); - var i; - - p.writeVarint(items.length); - - for (i = 0; i < items.length; i++) - items[i].toRaw(p); - - return p.render(); -} - -function framePing(nonce) { - var p = new BufferWriter(); - p.writeU64(nonce); - return p.render(); -} - -function frameAddr(hosts) { - var p = new BufferWriter(); - var i; - - p.writeVarint(hosts.length); - - for (i = 0; i < hosts.length; i++) - hosts[i].toRaw(true, p); - - return p.render(); -} - -function frameFilterAdd(data) { - var p = new BufferWriter(); - p.writeVarBytes(data); - return p.render(); -} - -function frameFeeFilter(rate) { - var p = new BufferWriter(); - p.write64(rate); - return p.render(); -} - /* * Expose */ diff --git a/lib/net/packets.js b/lib/net/packets.js index 1b63f872..68785db2 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -12,8 +12,64 @@ var constants = require('../protocol/constants'); var utils = require('../utils/utils'); var crypto = require('../crypto/crypto'); var bn = require('bn.js'); -var IP = require('../utils/ip'); +var NetworkAddress = require('../primitives/netaddress'); var assert = utils.assert; +var DUMMY = new Buffer(0); + +/** + * Packet types. + * @enum {Number} + * @default + */ + +exports.types = { + VERSION: 0, + VERACK: 1, + PING: 1, + PONG: 2, + ALERT: 3, + GETADDR: 4, + ADDR: 5, + INV: 6, + GETDATA: 7, + NOTFOUND: 8, + GETBLOCKS: 9, + GETHEADERS: 10, + HEADERS: 11, + SENDHEADERS: 12, + BLOCK: 13, + TX: 14, + REJECT: 15, + MEMPOOL: 16, + FILTERLOAD: 17, + FILTERADD: 18, + FILTERCLEAR: 19, + MERKLEBLOCK: 20, + GETUTXOS: 21, + UTXOS: 22, + HAVEWITNESS: 23, + FEEFILTER: 24, + SENDCMPCT: 25, + CMPCTBLOCK: 26, + GETBLOCKTXN: 27, + BLOCKTXN: 28, + ENCINIT: 29, + ENCACK: 30, + AUTHCHALLENGE: 31, + AUTHREPLY: 32, + AUTHPROPOSE: 33, + UNKNOWN: 34 +}; + +/** + * Base Packet + * @constructor + */ + +function Packet() {} + +Packet.prototype.type = -1; +Packet.prototype.cmd = ''; /** * Version Packet @@ -46,6 +102,8 @@ function VersionPacket(options) { if (!(this instanceof VersionPacket)) return new VersionPacket(options); + Packet.call(this); + this.version = constants.VERSION; this.services = constants.LOCAL_SERVICES; this.ts = bcoin.now(); @@ -60,6 +118,11 @@ function VersionPacket(options) { this.fromOptions(options); } +utils.inherits(VersionPacket, Packet); + +VersionPacket.prototype.cmd = 'version'; +VersionPacket.prototype.type = exports.types.VERSION; + /** * Inject properties from options. * @private @@ -238,40 +301,84 @@ VersionPacket.fromRaw = function fromRaw(data, enc) { }; /** - * Represents a `getblocks` packet. - * @exports GetBlocksPacket + * Represents a `verack` packet. + * @exports VerackPacket * @constructor - * @param {Hash[]} locator - * @param {Hash?} stop - * @property {Hash[]} locator - * @property {Hash|null} stop */ -function GetBlocksPacket(locator, stop) { - if (!(this instanceof GetBlocksPacket)) - return new GetBlocksPacket(locator, stop); +function VerackPacket() { + if (!(this instanceof VerackPacket)) + return new VerackPacket(); - this.version = constants.VERSION; - this.locator = locator || []; - this.stop = stop || null; + Packet.call(this); } +VerackPacket.prototype.cmd = 'verack'; +VerackPacket.prototype.type = exports.types.VERACK; + /** - * Serialize getblocks packet. + * Serialize verack packet. * @returns {Buffer} */ -GetBlocksPacket.prototype.toRaw = function toRaw(writer) { +VerackPacket.prototype.toRaw = function toRaw(writer) { + return writer || DUMMY; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +VerackPacket.prototype.fromRaw = function fromRaw(data) { + return this; +}; + +/** + * Instantiate verack packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {VerackPacket} + */ + +VerackPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new VerackPacket().fromRaw(data); +}; + +/** + * Represents a `ping` packet. + * @exports PingPacket + * @constructor + * @param {BN?} nonce + * @property {BN} nonce + */ + +function PingPacket(nonce) { + if (!(this instanceof PingPacket)) + return new PingPacket(nonce); + + Packet.call(this); + + this.nonce = nonce || new bn(0); +} + +utils.inherits(PingPacket, Packet); + +PingPacket.prototype.cmd = 'ping'; +PingPacket.prototype.type = exports.types.PING; + +/** + * Serialize ping packet. + * @returns {Buffer} + */ + +PingPacket.prototype.toRaw = function toRaw(writer) { var p = bcoin.writer(writer); - var i; - p.writeU32(this.version); - p.writeVarint(this.locator.length); - - for (i = 0; i < this.locator.length; i++) - p.writeHash(this.locator[i]); - - p.writeHash(this.stop || constants.ZERO_HASH); + p.writeU64(this.nonce); if (!writer) p = p.render(); @@ -285,36 +392,86 @@ GetBlocksPacket.prototype.toRaw = function toRaw(writer) { * @param {Buffer} data */ -GetBlocksPacket.prototype.fromRaw = function fromRaw(data) { +PingPacket.prototype.fromRaw = function fromRaw(data) { var p = bcoin.reader(data); - var i, count; - - this.version = p.readU32(); - - count = p.readVarint(); - - for (i = 0; i < count; i++) - this.locator.push(p.readHash('hex')); - - this.stop = p.readHash('hex'); - - if (this.stop === constants.NULL_HASH) - this.stop = null; - + this.nonce = p.readU64(); return this; }; /** - * Instantiate getblocks packet from serialized data. + * Instantiate ping packet from serialized data. * @param {Buffer} data * @param {String?} enc - * @returns {GetBlocksPacket} + * @returns {PingPacket} */ -GetBlocksPacket.fromRaw = function fromRaw(data, enc) { +PingPacket.fromRaw = function fromRaw(data, enc) { if (typeof data === 'string') data = new Buffer(data, enc); - return new GetBlocksPacket().fromRaw(data); + return new PingPacket().fromRaw(data); +}; + +/** + * Represents a `pong` packet. + * @exports PongPacket + * @constructor + * @param {BN?} nonce + * @property {BN} nonce + */ + +function PongPacket(nonce) { + if (!(this instanceof PongPacket)) + return new PongPacket(nonce); + + Packet.call(this); + + this.nonce = nonce || new bn(0); +} + +utils.inherits(PongPacket, Packet); + +PongPacket.prototype.cmd = 'pong'; +PongPacket.prototype.type = exports.types.PONG; + +/** + * Serialize pong packet. + * @returns {Buffer} + */ + +PongPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeU64(this.nonce); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +PongPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.nonce = p.readU64(); + return this; +}; + +/** + * Instantiate pong packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {VerackPacket} + */ + +PongPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new PongPacket().fromRaw(data); }; /** @@ -344,6 +501,8 @@ function AlertPacket(options) { if (!(this instanceof AlertPacket)) return new AlertPacket(options); + Packet.call(this); + time = bcoin.now() + 7 * 86400; this.version = 1; @@ -368,6 +527,11 @@ function AlertPacket(options) { this.fromOptions(options); } +utils.inherits(AlertPacket, Packet); + +AlertPacket.prototype.cmd = 'alert'; +AlertPacket.prototype.type = exports.types.ALERT; + /** * Inject properties from options object. * @private @@ -580,6 +744,632 @@ AlertPacket.fromRaw = function fromRaw(data, enc) { return new AlertPacket().fromRaw(data, enc); }; +/** + * Represents a `getaddr` packet. + * @exports GetAddrPacket + * @constructor + */ + +function GetAddrPacket() { + if (!(this instanceof GetAddrPacket)) + return new GetAddrPacket(); + + Packet.call(this); +} + +utils.inherits(GetAddrPacket, Packet); + +GetAddrPacket.prototype.cmd = 'getaddr'; +GetAddrPacket.prototype.type = exports.types.GETADDR; + +/** + * Serialize getaddr packet. + * @returns {Buffer} + */ + +GetAddrPacket.prototype.toRaw = function toRaw(writer) { + return writer || DUMMY; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +GetAddrPacket.prototype.fromRaw = function fromRaw(data) { + return this; +}; + +/** + * Instantiate getaddr packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {GetAddrPacket} + */ + +GetAddrPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new GetAddrPacket().fromRaw(data); +}; + +/** + * Represents a `addr` packet. + * @exports AddrPacket + * @constructor + * @param {(NetworkAddress[])?} items + * @property {NetworkAddress[]} items + */ + +function AddrPacket(items) { + if (!(this instanceof AddrPacket)) + return new AddrPacket(items); + + Packet.call(this); + + this.items = items || []; +} + +utils.inherits(AddrPacket, Packet); + +AddrPacket.prototype.cmd = 'addr'; +AddrPacket.prototype.type = exports.types.ADDR; + +/** + * Serialize addr packet. + * @returns {Buffer} + */ + +AddrPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + var i; + + p.writeVarint(this.items.length); + + for (i = 0; i < this.items.length; i++) + this.items[i].toRaw(true, p); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +AddrPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + var i, count; + + count = p.readVarint(); + + for (i = 0; i < count; i++) + this.items.push(NetworkAddress.fromRaw(p, true)); + + return this; +}; + +/** + * Instantiate addr packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {AddrPacket} + */ + +AddrPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new AddrPacket().fromRaw(data); +}; + +/** + * Represents a `inv` packet. + * @exports InvPacket + * @constructor + * @param {(InvItem[])?} items + * @property {InvItem[]} items + */ + +function InvPacket(items) { + if (!(this instanceof InvPacket)) + return new InvPacket(items); + + Packet.call(this); + + this.items = items || []; +} + +utils.inherits(InvPacket, Packet); + +InvPacket.prototype.cmd = 'inv'; +InvPacket.prototype.type = exports.types.INV; + +/** + * Serialize inv packet. + * @returns {Buffer} + */ + +InvPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + var i; + + p.writeVarint(this.items.length); + + for (i = 0; i < this.items.length; i++) + this.items[i].toRaw(p); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +InvPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + var i, count; + + count = p.readVarint(); + + for (i = 0; i < count; i++) + this.items.push(bcoin.invitem.fromRaw(p)); + + return this; +}; + +/** + * Instantiate inv packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {InvPacket} + */ + +InvPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new InvPacket().fromRaw(data); +}; + +/** + * Represents a `getdata` packet. + * @exports GetDataPacket + * @extends InvPacket + * @constructor + * @param {(InvItem[])?} items + */ + +function GetDataPacket(items) { + if (!(this instanceof GetDataPacket)) + return new GetDataPacket(items); + + InvPacket.call(this, items); +} + +utils.inherits(GetDataPacket, InvPacket); + +GetDataPacket.prototype.cmd = 'getdata'; +GetDataPacket.prototype.type = exports.types.GETDATA; + +/** + * Instantiate getdata packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {GetDataPacket} + */ + +GetDataPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new GetDataPacket().fromRaw(data); +}; + +/** + * Represents a `notfound` packet. + * @exports NotFoundPacket + * @extends InvPacket + * @constructor + * @param {(InvItem[])?} items + */ + +function NotFoundPacket(items) { + if (!(this instanceof NotFoundPacket)) + return new NotFoundPacket(items); + + InvPacket.call(this, items); +} + +utils.inherits(NotFoundPacket, InvPacket); + +NotFoundPacket.prototype.cmd = 'notfound'; +NotFoundPacket.prototype.type = exports.types.NOTFOUND; + +/** + * Instantiate notfound packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {NotFoundPacket} + */ + +NotFoundPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new NotFoundPacket().fromRaw(data); +}; + +/** + * Represents a `getblocks` packet. + * @exports GetBlocksPacket + * @constructor + * @param {Hash[]} locator + * @param {Hash?} stop + * @property {Hash[]} locator + * @property {Hash|null} stop + */ + +function GetBlocksPacket(locator, stop) { + if (!(this instanceof GetBlocksPacket)) + return new GetBlocksPacket(locator, stop); + + Packet.call(this); + + this.version = constants.VERSION; + this.locator = locator || []; + this.stop = stop || null; +} + +utils.inherits(GetBlocksPacket, Packet); + +GetBlocksPacket.prototype.cmd = 'getblocks'; +GetBlocksPacket.prototype.type = exports.types.GETBLOCKS; + +/** + * Serialize getblocks packet. + * @returns {Buffer} + */ + +GetBlocksPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + var i; + + p.writeU32(this.version); + p.writeVarint(this.locator.length); + + for (i = 0; i < this.locator.length; i++) + p.writeHash(this.locator[i]); + + p.writeHash(this.stop || constants.ZERO_HASH); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +GetBlocksPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + var i, count; + + this.version = p.readU32(); + + count = p.readVarint(); + + for (i = 0; i < count; i++) + this.locator.push(p.readHash('hex')); + + this.stop = p.readHash('hex'); + + if (this.stop === constants.NULL_HASH) + this.stop = null; + + return this; +}; + +/** + * Instantiate getblocks packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {GetBlocksPacket} + */ + +GetBlocksPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new GetBlocksPacket().fromRaw(data); +}; + +/** + * Represents a `getheaders` packet. + * @exports GetHeadersPacket + * @extends GetBlocksPacket + * @constructor + * @param {Hash[]} locator + * @param {Hash?} stop + */ + +function GetHeadersPacket(locator, stop) { + if (!(this instanceof GetHeadersPacket)) + return new GetHeadersPacket(locator, stop); + + GetBlocksPacket.call(this, locator, stop); +} + +utils.inherits(GetHeadersPacket, GetBlocksPacket); + +GetHeadersPacket.prototype.cmd = 'getheaders'; +GetHeadersPacket.prototype.type = exports.types.GETHEADERS; + +/** + * Instantiate getheaders packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {GetHeadersPacket} + */ + +GetHeadersPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new GetHeadersPacket().fromRaw(data); +}; + +/** + * Represents a `headers` packet. + * @exports HeadersPacket + * @constructor + * @param {(Headers[])?} items + * @property {Headers[]} items + */ + +function HeadersPacket(items) { + if (!(this instanceof HeadersPacket)) + return new HeadersPacket(items); + + Packet.call(this); + + this.items = items || []; +} + +utils.inherits(HeadersPacket, Packet); + +HeadersPacket.prototype.cmd = 'headers'; +HeadersPacket.prototype.type = exports.types.HEADERS; + +/** + * Serialize headers packet. + * @returns {Buffer} + */ + +HeadersPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + var i; + + p.writeVarint(this.items.length); + + for (i = 0; i < this.items.length; i++) + this.items[i].toRaw(p); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +HeadersPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + var i, count; + + count = p.readVarint(); + + for (i = 0; i < count; i++) + this.items.push(bcoin.headers.fromRaw(p)); + + return this; +}; + +/** + * Instantiate headers packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {VerackPacket} + */ + +HeadersPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new HeadersPacket().fromRaw(data); +}; + +/** + * Represents a `sendheaders` packet. + * @exports SendHeadersPacket + * @constructor + */ + +function SendHeadersPacket() { + if (!(this instanceof SendHeadersPacket)) + return new SendHeadersPacket(); + + Packet.call(this); +} + +utils.inherits(SendHeadersPacket, Packet); + +SendHeadersPacket.prototype.cmd = 'sendheaders'; +SendHeadersPacket.prototype.type = exports.types.SENDHEADERS; + +/** + * Serialize sendheaders packet. + * @returns {Buffer} + */ + +SendHeadersPacket.prototype.toRaw = function toRaw(writer) { + return writer || DUMMY; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +SendHeadersPacket.prototype.fromRaw = function fromRaw(data) { + return this; +}; + +/** + * Instantiate sendheaders packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {SendHeadersPacket} + */ + +SendHeadersPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new SendHeadersPacket().fromRaw(data); +}; + +/** + * Represents a `block` packet. + * @exports BlockPacket + * @constructor + * @param {Block|null} block + * @param {Boolean?} witness + * @property {Block} block + * @property {Boolean} witness + */ + +function BlockPacket(block, witness) { + if (!(this instanceof BlockPacket)) + return new BlockPacket(block, witness); + + Packet.call(this); + + this.block = block || new bcoin.memblock(); + this.witness = witness || false; +} + +utils.inherits(BlockPacket, Packet); + +BlockPacket.prototype.cmd = 'block'; +BlockPacket.prototype.type = exports.types.BLOCK; + +/** + * Serialize block packet. + * @returns {Buffer} + */ + +BlockPacket.prototype.toRaw = function toRaw(writer) { + if (this.witness) + return this.block.toRaw(writer); + return this.block.toNormal(writer); +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +BlockPacket.prototype.fromRaw = function fromRaw(data) { + this.block.fromRaw(data); + return this; +}; + +/** + * Instantiate block packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {BlockPacket} + */ + +BlockPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new BlockPacket().fromRaw(data); +}; + +/** + * Represents a `tx` packet. + * @exports TXPacket + * @constructor + * @param {TX|null} tx + * @param {Boolean?} witness + * @property {TX} block + * @property {Boolean} witness + */ + +function TXPacket(tx, witness) { + if (!(this instanceof TXPacket)) + return new TXPacket(tx, witness); + + Packet.call(this); + + this.tx = tx || new bcoin.tx(); + this.witness = witness || false; +} + +utils.inherits(TXPacket, Packet); + +TXPacket.prototype.cmd = 'tx'; +TXPacket.prototype.type = exports.types.TX; + +/** + * Serialize tx packet. + * @returns {Buffer} + */ + +TXPacket.prototype.toRaw = function toRaw(writer) { + if (this.witness) + return this.tx.toRaw(writer); + return this.tx.toNormal(writer); +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +TXPacket.prototype.fromRaw = function fromRaw(data) { + this.tx.fromRaw(data); + return this; +}; + +/** + * Instantiate tx packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {TXPacket} + */ + +TXPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new TXPacket().fromRaw(data); +}; + /** * Reject Packet * @exports RejectPacket @@ -595,6 +1385,8 @@ function RejectPacket(options) { if (!(this instanceof RejectPacket)) return new RejectPacket(options); + Packet.call(this); + this.message = ''; this.code = constants.reject.INVALID; this.reason = ''; @@ -604,6 +1396,11 @@ function RejectPacket(options) { this.fromOptions(options); } +utils.inherits(RejectPacket, Packet); + +RejectPacket.prototype.cmd = 'reject'; +RejectPacket.prototype.type = exports.types.REJECT; + /** * Inject properties from options object. * @private @@ -771,286 +1568,98 @@ RejectPacket.prototype.inspect = function inspect() { }; /** - * Represents a network address. - * @exports NetworkAddress + * Represents a `mempool` packet. + * @exports MempoolPacket * @constructor - * @param {Object} options - * @param {Number?} options.ts - Timestamp. - * @param {Number?} options.services - Service bits. - * @param {String?} options.host - IP address (IPv6 or IPv4). - * @param {Number?} options.port - Port. - * @property {Number} id - * @property {Host} host - * @property {Number} port - * @property {Number} services - * @property {Number} ts */ -function NetworkAddress(options) { - if (!(this instanceof NetworkAddress)) - return new NetworkAddress(options); +function MempoolPacket() { + if (!(this instanceof MempoolPacket)) + return new MempoolPacket(); - this.id = NetworkAddress.uid++; - this.host = '0.0.0.0'; - this.port = 0; - this.services = 0; - this.ts = 0; - this.hostname = '0.0.0.0:0'; - - if (options) - this.fromOptions(options); + Packet.call(this); } -/** - * Globally incremented unique id. - * @private - * @type {Number} - */ +utils.inherits(MempoolPacket, Packet); -NetworkAddress.uid = 0; +MempoolPacket.prototype.cmd = 'mempool'; +MempoolPacket.prototype.type = exports.types.MEMPOOL; /** - * Inject properties from options object. - * @private - * @param {Object} options + * Serialize mempool packet. + * @returns {Buffer} */ -NetworkAddress.prototype.fromOptions = function fromOptions(options) { - var host = options.host; - - assert(typeof options.host === 'string'); - assert(typeof options.port === 'number'); - - if (IP.version(host) !== -1) - host = IP.normalize(host); - - this.host = host; - this.port = options.port; - - if (options.services) { - assert(typeof options.services === 'number'); - this.services = options.services; - } - - if (options.ts) { - assert(typeof options.ts === 'number'); - this.ts = options.ts; - } - - this.hostname = IP.hostname(this.host, this.port); - - return this; -}; - -/** - * Instantiate network address from options. - * @param {Object} options - * @returns {NetworkAddress} - */ - -NetworkAddress.fromOptions = function fromOptions(options) { - return new NetworkAddress().fromOptions(options); -}; - -/** - * Test whether the `host` field is an ip address. - * @returns {Boolean} - */ - -NetworkAddress.prototype.isIP = function isIP() { - return IP.version(this.host) !== -1; -}; - -/** - * Test whether the NETWORK service bit is set. - * @returns {Boolean} - */ - -NetworkAddress.prototype.hasNetwork = function hasNetwork() { - return (this.services & constants.services.NETWORK) !== 0; -}; - -/** - * Test whether the BLOOM service bit is set. - * @returns {Boolean} - */ - -NetworkAddress.prototype.hasBloom = function hasBloom() { - return (this.services & constants.services.BLOOM) !== 0; -}; - -/** - * Test whether the GETUTXO service bit is set. - * @returns {Boolean} - */ - -NetworkAddress.prototype.hasUTXO = function hasUTXO() { - return (this.services & constants.services.GETUTXO) !== 0; -}; - -/** - * Test whether the WITNESS service bit is set. - * @returns {Boolean} - */ - -NetworkAddress.prototype.hasWitness = function hasWitness() { - return (this.services & constants.services.WITNESS) !== 0; -}; - -/** - * Set host. - * @param {String} host - */ - -NetworkAddress.prototype.setHost = function setHost(host) { - this.host = host; - this.hostname = IP.hostname(host, this.port); -}; - -/** - * Set port. - * @param {Number} port - */ - -NetworkAddress.prototype.setPort = function setPort(port) { - this.port = port; - this.hostname = IP.hostname(this.host, port); -}; - -/** - * Inspect the network address. - * @returns {Object} - */ - -NetworkAddress.prototype.inspect = function inspect() { - return ''; -}; - -/** - * Inject properties from hostname and network. - * @private - * @param {String} hostname - * @param {(Network|NetworkType)?} network - */ - -NetworkAddress.prototype.fromHostname = function fromHostname(hostname, network) { - var address = IP.parseHost(hostname); - - network = bcoin.network.get(network); - - this.host = address.host; - this.port = address.port || network.port; - this.services = constants.services.NETWORK - | constants.services.BLOOM - | constants.services.WITNESS; - this.ts = bcoin.now(); - - this.hostname = IP.hostname(this.host, this.port); - - return this; -}; - -/** - * Instantiate a network address - * from a hostname (i.e. 127.0.0.1:8333). - * @param {String} hostname - * @param {(Network|NetworkType)?} network - * @returns {NetworkAddress} - */ - -NetworkAddress.fromHostname = function fromHostname(hostname, network) { - return new NetworkAddress().fromHostname(hostname, network); -}; - -/** - * Inject properties from socket. - * @private - * @param {net.Socket} socket - */ - -NetworkAddress.prototype.fromSocket = function fromSocket(socket) { - assert(typeof socket.remoteAddress === 'string'); - assert(typeof socket.remotePort === 'number'); - - this.host = IP.normalize(socket.remoteAddress); - this.port = socket.remotePort; - this.services = constants.services.NETWORK - | constants.services.BLOOM - | constants.services.WITNESS; - this.ts = bcoin.now(); - - this.hostname = IP.hostname(this.host, this.port); - - return this; -}; - -/** - * Instantiate a network address - * from a socket. - * @param {net.Socket} socket - * @returns {NetworkAddress} - */ - -NetworkAddress.fromSocket = function fromSocket(hostname) { - return new NetworkAddress().fromSocket(hostname); +MempoolPacket.prototype.toRaw = function toRaw(writer) { + return writer || DUMMY; }; /** * Inject properties from serialized data. * @private * @param {Buffer} data - * @param {Boolean?} full - Include timestamp. */ -NetworkAddress.prototype.fromRaw = function fromRaw(data, full) { - var p = bcoin.reader(data); - var now = bcoin.now(); - - // only version >= 31402 - this.ts = full ? p.readU32() : 0; - this.services = p.readU53(); - this.host = IP.toString(p.readBytes(16)); - this.port = p.readU16BE(); - - if (this.ts <= 100000000 || this.ts > now + 10 * 60) - this.ts = now - 5 * 24 * 60 * 60; - - this.hostname = IP.hostname(this.host, this.port); - +MempoolPacket.prototype.fromRaw = function fromRaw(data) { return this; }; /** - * Insantiate a network address from serialized data. + * Instantiate mempool packet from serialized data. * @param {Buffer} data - * @param {Boolean?} full - Include timestamp. - * @returns {NetworkAddress} + * @param {String?} enc + * @returns {VerackPacket} */ -NetworkAddress.fromRaw = function fromRaw(data, full) { - return new NetworkAddress().fromRaw(data, full); +MempoolPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new MempoolPacket().fromRaw(data); }; /** - * Serialize network address. - * @param {Boolean} full - Include timestamp. + * Represents a `filterload` packet. + * @exports FilterLoadPacket + * @constructor + * @param {Buffer|null} filter + * @param {Number|null} n + * @param {Number|null} tweak + * @param {Number|null} update + * @property {Buffer} filter + * @property {Number} n + * @property {Number} tweak + * @property {Number} update + */ + +function FilterLoadPacket(filter, n, tweak, update) { + if (!(this instanceof FilterLoadPacket)) + return new FilterLoadPacket(filter, n, tweak, update); + + Packet.call(this); + + this.filter = filter || DUMMY; + this.n = n || 0; + this.tweak = tweak || 0; + this.update = update || 0; +} + +utils.inherits(FilterLoadPacket, Packet); + +FilterLoadPacket.prototype.cmd = 'filterload'; +FilterLoadPacket.prototype.type = exports.types.FILTERLOAD; + +/** + * Serialize filterload packet. * @returns {Buffer} */ -NetworkAddress.prototype.toRaw = function toRaw(full, writer) { +FilterLoadPacket.prototype.toRaw = function toRaw(writer) { var p = bcoin.writer(writer); - if (full) - p.writeU32(this.ts); - - p.writeU64(this.services); - p.writeBytes(IP.toBuffer(this.host)); - p.writeU16BE(this.port); + p.writeVarBytes(this.filter); + p.writeU32(this.n); + p.writeU32(this.tweak); + p.writeU8(this.update); if (!writer) p = p.render(); @@ -1058,6 +1667,255 @@ NetworkAddress.prototype.toRaw = function toRaw(full, writer) { return p; }; +/** + * Serialize filterload packet. + * @returns {Buffer} + */ + +FilterLoadPacket.prototype.toFilter = function toFilter() { + return new bcoin.bloom(this.filter, this.n, this.tweak, this.update); +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +FilterLoadPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + + this.filter = p.readVarBytes(); + this.n = p.readU32(); + this.tweak = p.readU32(); + this.update = p.readU8(); + + assert(constants.filterFlagsByVal[this.update] != null, 'Bad filter flag.'); + + return this; +}; + +/** + * Inject properties from bloom filter. + * @private + * @param {Bloom} filter + */ + +FilterLoadPacket.prototype.fromFilter = function fromFilter(filter) { + this.filter = filter; + this.n = filter.n; + this.tweak = filter.tweak; + this.update = filter.update; + return this; +}; + +/** + * Instantiate filterload packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {FilterLoadPacket} + */ + +FilterLoadPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new FilterLoadPacket().fromRaw(data); +}; + +/** + * Instantiate filterload packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {FilterLoadPacket} + */ + +FilterLoadPacket.fromFilter = function fromFilter(filter) { + return new FilterLoadPacket().fromFilter(filter); +}; + +/** + * Ensure the filter is within the size limits. + * @returns {Boolean} + */ + +FilterLoadPacket.prototype.isWithinConstraints = function isWithinConstraints() { + if (this.filter.length > constants.bloom.MAX_BLOOM_FILTER_SIZE) + return false; + + if (this.n > constants.bloom.MAX_HASH_FUNCS) + return false; + + return true; +}; + +/** + * Represents a `filteradd` packet. + * @exports FilterAddPacket + * @constructor + * @param {Buffer?} data + * @property {Buffer} data + */ + +function FilterAddPacket(data) { + if (!(this instanceof FilterAddPacket)) + return new FilterAddPacket(data); + + Packet.call(this); + + this.data = data || DUMMY; +} + +utils.inherits(FilterAddPacket, Packet); + +FilterAddPacket.prototype.cmd = 'filteradd'; +FilterAddPacket.prototype.type = exports.types.FILTERADD; + +/** + * Serialize filteradd packet. + * @returns {Buffer} + */ + +FilterAddPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeVarBytes(this.data); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +FilterAddPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.data = p.readVarBytes(); + return this; +}; + +/** + * Instantiate filteradd packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {FilterAddPacket} + */ + +FilterAddPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new FilterAddPacket().fromRaw(data); +}; + +/** + * Represents a `filterclear` packet. + * @exports FilterClearPacket + * @constructor + */ + +function FilterClearPacket() { + if (!(this instanceof FilterClearPacket)) + return new FilterClearPacket(); + + Packet.call(this); +} + +utils.inherits(FilterClearPacket, Packet); + +FilterClearPacket.prototype.cmd = 'filterclear'; +FilterClearPacket.prototype.type = exports.types.FILTERCLEAR; + +/** + * Serialize filterclear packet. + * @returns {Buffer} + */ + +FilterClearPacket.prototype.toRaw = function toRaw(writer) { + return writer || DUMMY; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +FilterClearPacket.prototype.fromRaw = function fromRaw(data) { + return this; +}; + +/** + * Instantiate filterclear packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {FilterClearPacket} + */ + +FilterClearPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new FilterClearPacket().fromRaw(data); +}; + +/** + * Represents a `merkleblock` packet. + * @exports MerkleBlockPacket + * @constructor + * @param {MerkleBlock?} block + * @property {MerkleBlock} block + */ + +function MerkleBlockPacket(block) { + if (!(this instanceof MerkleBlockPacket)) + return new MerkleBlockPacket(block); + + Packet.call(this); + + this.block = block || new bcoin.merkleblock(); +} + +utils.inherits(MerkleBlockPacket, Packet); + +MerkleBlockPacket.prototype.cmd = 'merkleblock'; +MerkleBlockPacket.prototype.type = exports.types.MERKLEBLOCK; + +/** + * Serialize merkleblock packet. + * @returns {Buffer} + */ + +MerkleBlockPacket.prototype.toRaw = function toRaw(writer) { + return this.block.toRaw(writer); +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +MerkleBlockPacket.prototype.fromRaw = function fromRaw(data) { + this.block.fromRaw(data); + return this; +}; + +/** + * Instantiate merkleblock packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {MerkleBlockPacket} + */ + +MerkleBlockPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new MerkleBlockPacket().fromRaw(data); +}; + /** * Represents a `getutxos` packet. * @exports GetUTXOsPacket @@ -1072,10 +1930,17 @@ function GetUTXOsPacket(mempool, prevout) { if (!(this instanceof GetUTXOsPacket)) return new GetUTXOsPacket(mempool, prevout); + Packet.call(this); + this.mempool = mempool || false; this.prevout = prevout || []; } +utils.inherits(GetUTXOsPacket, Packet); + +GetUTXOsPacket.prototype.cmd = 'getutxos'; +GetUTXOsPacket.prototype.type = exports.types.GETUTXOS; + /** * Serialize getutxos packet. * @returns {Buffer} @@ -1145,6 +2010,8 @@ function UTXOsPacket(options) { if (!(this instanceof UTXOsPacket)) return new UTXOsPacket(options); + Packet.call(this); + this.height = -1; this.tip = constants.NULL_HASH; this.hits = []; @@ -1154,6 +2021,11 @@ function UTXOsPacket(options) { this.fromOptions(options); } +utils.inherits(UTXOsPacket, Packet); + +UTXOsPacket.prototype.cmd = 'utxos'; +UTXOsPacket.prototype.type = exports.types.UTXOS; + /** * Inject properties from options. * @private @@ -1283,7 +2155,7 @@ UTXOsPacket.prototype.fromRaw = function fromRaw(data) { * Instantiate utxos packet from serialized data. * @param {Buffer} data * @param {String?} enc - * @returns {GetBlocksPacket} + * @returns {UTXOsPacket} */ UTXOsPacket.fromRaw = function fromRaw(data, enc) { @@ -1292,14 +2164,866 @@ UTXOsPacket.fromRaw = function fromRaw(data, enc) { return new UTXOsPacket().fromRaw(data); }; +/** + * Represents a `havewitness` packet. + * @exports HaveWitnessPacket + * @constructor + */ + +function HaveWitnessPacket() { + if (!(this instanceof HaveWitnessPacket)) + return new HaveWitnessPacket(); + + Packet.call(this); +} + +utils.inherits(HaveWitnessPacket, Packet); + +HaveWitnessPacket.prototype.cmd = 'havewitness'; +HaveWitnessPacket.prototype.type = exports.types.HAVEWITNESS; + +/** + * Serialize havewitness packet. + * @returns {Buffer} + */ + +HaveWitnessPacket.prototype.toRaw = function toRaw(writer) { + return writer || DUMMY; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +HaveWitnessPacket.prototype.fromRaw = function fromRaw(data) { + return this; +}; + +/** + * Instantiate havewitness packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {HaveWitnessPacket} + */ + +HaveWitnessPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new HaveWitnessPacket().fromRaw(data); +}; + +/** + * Represents a `feefilter` packet. + * @exports FeeFilterPacket + * @constructor + * @param {Rate?} rate + * @property {Rate} rate + */ + +function FeeFilterPacket(rate) { + if (!(this instanceof FeeFilterPacket)) + return new FeeFilterPacket(rate); + + Packet.call(this); + + this.rate = rate || 0; +} + +utils.inherits(FeeFilterPacket, Packet); + +FeeFilterPacket.prototype.cmd = 'feefilter'; +FeeFilterPacket.prototype.type = exports.types.FEEFILTER; + +/** + * Serialize feefilter packet. + * @returns {Buffer} + */ + +FeeFilterPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.write64(this.rate); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +FeeFilterPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.rate = p.read64N(); + return this; +}; + +/** + * Instantiate feefilter packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {FeeFilterPacket} + */ + +FeeFilterPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new FeeFilterPacket().fromRaw(data); +}; + +/** + * Represents a `sendcmpct` packet. + * @exports SendCmpctPacket + * @constructor + * @param {Number|null} mode + * @param {Number|null} version + * @property {Number} mode + * @property {Number} version + */ + +function SendCmpctPacket(mode, version) { + if (!(this instanceof SendCmpctPacket)) + return new SendCmpctPacket(mode, version); + + Packet.call(this); + + this.mode = mode || 0; + this.version = version || 1; +} + +utils.inherits(SendCmpctPacket, Packet); + +SendCmpctPacket.prototype.cmd = 'sendcmpct'; +SendCmpctPacket.prototype.type = exports.types.SENDCMPCT; + +/** + * Serialize sendcmpct packet. + * @returns {Buffer} + */ + +SendCmpctPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeU8(this.mode); + p.writeU64(this.version); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +SendCmpctPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.mode = p.readU8(); + this.version = p.readU53(); + return this; +}; + +/** + * Instantiate sendcmpct packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {SendCmpctPacket} + */ + +SendCmpctPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new SendCmpctPacket().fromRaw(data); +}; + +/** + * Represents a `cmpctblock` packet. + * @exports CmpctBlockPacket + * @constructor + * @param {Block|null} block + * @param {Boolean|null} witness + * @property {Block} block + * @property {Boolean} witness + */ + +function CmpctBlockPacket(block, witness) { + if (!(this instanceof CmpctBlockPacket)) + return new CmpctBlockPacket(block, witness); + + Packet.call(this); + + this.block = block || new bcoin.bip152.CompactBlock(); + this.witness = witness || false; +} + +utils.inherits(CmpctBlockPacket, Packet); + +CmpctBlockPacket.prototype.cmd = 'cmpctblock'; +CmpctBlockPacket.prototype.type = exports.types.CMPCTBLOCK; + +/** + * Serialize cmpctblock packet. + * @returns {Buffer} + */ + +CmpctBlockPacket.prototype.toRaw = function toRaw(writer) { + return this.block.toRaw(this.witness, writer); +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +CmpctBlockPacket.prototype.fromRaw = function fromRaw(data) { + this.block.fromRaw(data); + return this; +}; + +/** + * Instantiate cmpctblock packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {CmpctBlockPacket} + */ + +CmpctBlockPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new CmpctBlockPacket().fromRaw(data); +}; + +/** + * Represents a `getblocktxn` packet. + * @exports GetBlockTxnPacket + * @constructor + * @param {TXRequest?} request + * @property {TXRequest} request + */ + +function GetBlockTxnPacket(request) { + if (!(this instanceof GetBlockTxnPacket)) + return new GetBlockTxnPacket(request); + + Packet.call(this); + + this.request = request || new bcoin.bip152.TXRequest(); +} + +utils.inherits(GetBlockTxnPacket, Packet); + +GetBlockTxnPacket.prototype.cmd = 'getblocktxn'; +GetBlockTxnPacket.prototype.type = exports.types.GETBLOCKTXN; + +/** + * Serialize getblocktxn packet. + * @returns {Buffer} + */ + +GetBlockTxnPacket.prototype.toRaw = function toRaw(writer) { + return this.request.toRaw(writer); +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +GetBlockTxnPacket.prototype.fromRaw = function fromRaw(data) { + this.request.fromRaw(data); + return this; +}; + +/** + * Instantiate getblocktxn packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {GetBlockTxnPacket} + */ + +GetBlockTxnPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new GetBlockTxnPacket().fromRaw(data); +}; + +/** + * Represents a `blocktxn` packet. + * @exports BlockTxnPacket + * @constructor + * @param {TXResponse?} response + * @param {Boolean?} witness + * @property {TXResponse} response + * @property {Boolean} witness + */ + +function BlockTxnPacket(response, witness) { + if (!(this instanceof BlockTxnPacket)) + return new BlockTxnPacket(response, witness); + + Packet.call(this); + + this.response = response || new bcoin.bip152.TXResponse(); + this.witness = witness || false; +} + +utils.inherits(BlockTxnPacket, Packet); + +BlockTxnPacket.prototype.cmd = 'blocktxn'; +BlockTxnPacket.prototype.type = exports.types.BLOCKTXN; + +/** + * Serialize blocktxn packet. + * @returns {Buffer} + */ + +BlockTxnPacket.prototype.toRaw = function toRaw(writer) { + return this.response.toRaw(this.witness, writer); +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +BlockTxnPacket.prototype.fromRaw = function fromRaw(data) { + this.response.fromRaw(data); + return this; +}; + +/** + * Instantiate blocktxn packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {BlockTxnPacket} + */ + +BlockTxnPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new BlockTxnPacket().fromRaw(data); +}; + +/** + * Represents a `encinit` packet. + * @exports EncinitPacket + * @constructor + * @param {Buffer|null} publicKey + * @param {Number|null} cipher + * @property {Buffer} publicKey + * @property {Number} cipher + */ + +function EncinitPacket(publicKey, cipher) { + if (!(this instanceof EncinitPacket)) + return new EncinitPacket(publicKey, cipher); + + Packet.call(this); + + this.publicKey = publicKey || constants.ZERO_KEY; + this.cipher = cipher || 0; +} + +utils.inherits(EncinitPacket, Packet); + +EncinitPacket.prototype.cmd = 'encinit'; +EncinitPacket.prototype.type = exports.types.ENCINIT; + +/** + * Serialize encinit packet. + * @returns {Buffer} + */ + +EncinitPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeBytes(this.publicKey); + p.writeU8(this.cipher); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +EncinitPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.publicKey = p.readBytes(33); + this.cipher = p.readU8(); + return this; +}; + +/** + * Instantiate getblocks packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {EncinitPacket} + */ + +EncinitPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new EncinitPacket().fromRaw(data); +}; + +/** + * Represents a `encack` packet. + * @exports EncackPacket + * @constructor + * @param {Buffer?} publicKey + * @property {Buffer} publicKey + */ + +function EncackPacket(publicKey) { + if (!(this instanceof EncackPacket)) + return new EncackPacket(publicKey); + + Packet.call(this); + + this.publicKey = publicKey || constants.ZERO_KEY; +} + +utils.inherits(EncackPacket, Packet); + +EncackPacket.prototype.cmd = 'encack'; +EncackPacket.prototype.type = exports.types.ENCACK; + +/** + * Serialize encack packet. + * @returns {Buffer} + */ + +EncackPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeBytes(this.publicKey); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +EncackPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.publicKey = p.readBytes(33); + return this; +}; + +/** + * Instantiate encack packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {EncackPacket} + */ + +EncackPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new EncackPacket().fromRaw(data); +}; + +/** + * Represents a `authchallenge` packet. + * @exports AuthChallengePacket + * @constructor + * @param {Buffer?} hash + * @property {Buffer} hash + */ + +function AuthChallengePacket(hash) { + if (!(this instanceof AuthChallengePacket)) + return new AuthChallengePacket(hash); + + Packet.call(this); + + this.hash = hash || constants.ZERO_HASH; +} + +utils.inherits(AuthChallengePacket, Packet); + +AuthChallengePacket.prototype.cmd = 'authchallenge'; +AuthChallengePacket.prototype.type = exports.types.AUTHCHALLENGE; + +/** + * Serialize authchallenge packet. + * @returns {Buffer} + */ + +AuthChallengePacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeBytes(this.hash); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +AuthChallengePacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.hash = p.readHash(); + return this; +}; + +/** + * Instantiate authchallenge packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {AuthChallengePacket} + */ + +AuthChallengePacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new AuthChallengePacket().fromRaw(data); +}; + +/** + * Represents a `authreply` packet. + * @exports AuthReplyPacket + * @constructor + * @param {Buffer?} signature + * @property {Buffer} signature + */ + +function AuthReplyPacket(signature) { + if (!(this instanceof AuthReplyPacket)) + return new AuthReplyPacket(signature); + + Packet.call(this); + + this.signature = signature || constants.ZERO_SIG64; +} + +utils.inherits(AuthReplyPacket, Packet); + +AuthReplyPacket.prototype.cmd = 'authreply'; +AuthReplyPacket.prototype.type = exports.types.AUTHREPLY; + +/** + * Serialize authreply packet. + * @returns {Buffer} + */ + +AuthReplyPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeBytes(this.signature); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +AuthReplyPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.signature = p.readBytes(64); + return this; +}; + +/** + * Instantiate authreply packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {AuthReplyPacket} + */ + +AuthReplyPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new AuthReplyPacket().fromRaw(data); +}; + +/** + * Represents a `authpropose` packet. + * @exports AuthProposePacket + * @constructor + * @param {Hash?} hash + * @property {Hash} hash + */ + +function AuthProposePacket(hash) { + if (!(this instanceof AuthProposePacket)) + return new AuthProposePacket(hash); + + Packet.call(this); + + this.hash = hash || constants.ZERO_HASH; +} + +utils.inherits(AuthProposePacket, Packet); + +AuthProposePacket.prototype.cmd = 'authpropose'; +AuthProposePacket.prototype.type = exports.types.AUTHPROPOSE; + +/** + * Serialize authpropose packet. + * @returns {Buffer} + */ + +AuthProposePacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeBytes(this.hash); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +AuthProposePacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + this.hash = p.readHash(); + return this; +}; + +/** + * Instantiate authpropose packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {AuthProposePacket} + */ + +AuthProposePacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new AuthProposePacket().fromRaw(data); +}; + +/** + * Represents an unknown packet. + * @exports UnknownPacket + * @constructor + * @param {String|null} cmd + * @param {Buffer|null} data + * @property {String} cmd + * @property {Buffer} data + */ + +function UnknownPacket(cmd, data) { + if (!(this instanceof UnknownPacket)) + return new UnknownPacket(cmd, data); + + Packet.call(this); + + this.cmd = cmd; + this.data = data; +} + +utils.inherits(UnknownPacket, Packet); + +UnknownPacket.prototype.type = exports.types.UNKNOWN; + +/** + * Serialize unknown packet. + * @returns {Buffer} + */ + +UnknownPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + + p.writeBytes(this.data); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +UnknownPacket.prototype.fromRaw = function fromRaw(cmd, data) { + assert(Buffer.isBuffer(data)); + this.cmd = cmd; + this.data = data; + return this; +}; + +/** + * Instantiate unknown packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {UnknownPacket} + */ + +UnknownPacket.fromRaw = function fromRaw(cmd, data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new UnknownPacket().fromRaw(cmd, data); +}; + +/** + * Parse a payload. + * @param {String} cmd + * @param {Buffer} data + * @returns {Packet} + */ + +exports.fromRaw = function fromRaw(cmd, data) { + switch (cmd) { + case 'version': + return VersionPacket.fromRaw(data); + case 'verack': + return VerackPacket.fromRaw(data); + case 'ping': + return PingPacket.fromRaw(data); + case 'pong': + return PongPacket.fromRaw(data); + case 'alert': + return AlertPacket.fromRaw(data); + case 'getaddr': + return GetAddrPacket.fromRaw(data); + case 'addr': + return AddrPacket.fromRaw(data); + case 'inv': + return InvPacket.fromRaw(data); + case 'getdata': + return GetDataPacket.fromRaw(data); + case 'notfound': + return NotFoundPacket.fromRaw(data); + case 'getblocks': + return GetBlocksPacket.fromRaw(data); + case 'getheaders': + return GetHeadersPacket.fromRaw(data); + case 'headers': + return HeadersPacket.fromRaw(data); + case 'sendheaders': + return SendHeadersPacket.fromRaw(data); + case 'block': + return BlockPacket.fromRaw(data); + case 'tx': + return TXPacket.fromRaw(data); + case 'reject': + return RejectPacket.fromRaw(data); + case 'mempool': + return MempoolPacket.fromRaw(data); + case 'filterload': + return FilterLoadPacket.fromRaw(data); + case 'filteradd': + return FilterAddPacket.fromRaw(data); + case 'filterclear': + return FilterClearPacket.fromRaw(data); + case 'merkleblock': + return MerkleBlockPacket.fromRaw(data); + case 'getutxos': + return GetUTXOsPacket.fromRaw(data); + case 'utxos': + return UTXOsPacket.fromRaw(data); + case 'havewitness': + return HaveWitnessPacket.fromRaw(data); + case 'feefilter': + return FeeFilterPacket.fromRaw(data); + case 'sendcmpct': + return SendCmpctPacket.fromRaw(data); + case 'cmpctblock': + return CmpctBlockPacket.fromRaw(data); + case 'getblocktxn': + return GetBlockTxnPacket.fromRaw(data); + case 'blocktxn': + return BlockTxnPacket.fromRaw(data); + case 'encinit': + return EncinitPacket.fromRaw(data); + case 'encack': + return EncackPacket.fromRaw(data); + case 'authchallenge': + return AuthChallengePacket.fromRaw(data); + case 'authreply': + return AuthReplyPacket.fromRaw(data); + case 'authpropose': + return AuthProposePacket.fromRaw(data); + default: + return UnknownPacket.fromRaw(cmd, data); + } +}; + /* * Expose */ +exports.Packet = Packet; exports.VersionPacket = VersionPacket; -exports.GetBlocksPacket = GetBlocksPacket; +exports.VerackPacket = VerackPacket; +exports.PingPacket = PingPacket; +exports.PongPacket = PongPacket; exports.AlertPacket = AlertPacket; +exports.GetAddrPacket = GetAddrPacket; +exports.AddrPacket = AddrPacket; +exports.InvPacket = InvPacket; +exports.GetDataPacket = GetDataPacket; +exports.NotFoundPacket = NotFoundPacket; +exports.GetBlocksPacket = GetBlocksPacket; +exports.GetHeadersPacket = GetHeadersPacket; +exports.HeadersPacket = HeadersPacket; +exports.SendHeadersPacket = SendHeadersPacket; +exports.BlockPacket = BlockPacket; +exports.TXPacket = TXPacket; exports.RejectPacket = RejectPacket; +exports.MempoolPacket = MempoolPacket; +exports.FilterLoadPacket = FilterLoadPacket; +exports.FilterAddPacket = FilterAddPacket; +exports.FilterClearPacket = FilterClearPacket; +exports.MerkleBlockPacket = MerkleBlockPacket; exports.GetUTXOsPacket = GetUTXOsPacket; exports.UTXOsPacket = UTXOsPacket; -exports.NetworkAddress = NetworkAddress; +exports.HaveWitnessPacket = HaveWitnessPacket; +exports.FeeFilterPacket = FeeFilterPacket; +exports.SendCmpctPacket = SendCmpctPacket; +exports.CmpctBlockPacket = CmpctBlockPacket; +exports.GetBlockTxnPacket = GetBlockTxnPacket; +exports.BlockTxnPacket = BlockTxnPacket; +exports.EncinitPacket = EncinitPacket; +exports.EncackPacket = EncackPacket; +exports.AuthChallengePacket = AuthChallengePacket; +exports.AuthReplyPacket = AuthReplyPacket; +exports.AuthProposePacket = AuthProposePacket; +exports.UnknownPacket = UnknownPacket; diff --git a/lib/net/parser.js b/lib/net/parser.js index 43338cee..ca9685fa 100644 --- a/lib/net/parser.js +++ b/lib/net/parser.js @@ -13,7 +13,6 @@ var utils = require('../utils/utils'); var crypto = require('../crypto/crypto'); var assert = utils.assert; var constants = require('../protocol/constants'); -var BufferReader = require('../utils/reader'); var packets = require('./packets'); /** @@ -40,7 +39,7 @@ function Parser(options) { this.pending = []; this.total = 0; this.waiting = 24; - this.packet = null; + this.header = null; this._init(); } @@ -60,13 +59,13 @@ Parser.prototype._init = function _init(str) { return; this.bip151.on('packet', function(cmd, body) { - var packet = new Packet(cmd, body.length); + var payload; try { - packet.payload = self.parsePayload(cmd, body); + payload = self.parsePayload(cmd, body); } catch (e) { - return self._error(e); + return self.error(e); } - self.emit('packet', packet); + self.emit('packet', payload); }); }; @@ -76,7 +75,7 @@ Parser.prototype._init = function _init(str) { * @param {String} str */ -Parser.prototype._error = function _error(str) { +Parser.prototype.error = function error(str) { this.emit('error', new Error(str)); }; @@ -120,42 +119,37 @@ Parser.prototype.feed = function feed(data) { * @param {Buffer} chunk */ -Parser.prototype.parse = function parse(chunk) { - var checksum; +Parser.prototype.parse = function parse(data) { + var payload, checksum; - if (chunk.length > constants.MAX_MESSAGE) { - this.waiting = 24; - this.packet = null; - return this._error('Packet too large: %dmb.', utils.mb(chunk.length)); - } + assert(data.length <= constants.MAX_MESSAGE); - if (!this.packet) { - this.packet = this.parseHeader(chunk); + if (!this.header) { + this.header = this.parseHeader(data); + this.waiting = this.header.size; return; } - this.packet.payload = chunk; + checksum = crypto.checksum(data).readUInt32LE(0, true); - checksum = crypto.checksum(this.packet.payload).readUInt32LE(0, true); - - if (checksum !== this.packet.checksum) { + if (checksum !== this.header.checksum) { this.waiting = 24; - this.packet = null; - return this._error('Invalid checksum'); + this.header = null; + return this.error('Invalid checksum'); } try { - this.packet.payload = this.parsePayload(this.packet.cmd, this.packet.payload); + payload = this.parsePayload(this.header.cmd, data); } catch (e) { this.emit('error', e); this.waiting = 24; - this.packet = null; + this.header = null; return; } - this.emit('packet', this.packet); + this.emit('packet', payload); this.waiting = 24; - this.packet = null; + this.header = null; }; /** @@ -166,31 +160,31 @@ Parser.prototype.parse = function parse(chunk) { */ Parser.prototype.parseHeader = function parseHeader(h) { - var i, magic, cmd, chk; + var i, magic, cmd, size, checksum; magic = h.readUInt32LE(0, true); if (magic !== this.network.magic) - return this._error('Invalid magic value: ' + magic.toString(16)); + return this.error('Invalid magic value: ' + magic.toString(16)); // Count length of the cmd for (i = 0; h[i + 4] !== 0 && i < 12; i++); if (i === 12) - return this._error('Not NULL-terminated cmd'); + return this.error('Not NULL-terminated cmd'); cmd = h.toString('ascii', 4, 4 + i); - this.waiting = h.readUInt32LE(16, true); + size = h.readUInt32LE(16, true); - if (this.waiting > constants.MAX_MESSAGE) { + if (size > constants.MAX_MESSAGE) { this.waiting = 24; - return this._error('Packet length too large: %dmb', utils.mb(this.waiting)); + return this.error('Packet length too large: %dmb', utils.mb(size)); } - chk = h.readUInt32LE(20, true); + checksum = h.readUInt32LE(20, true); - return new Packet(cmd, this.waiting, chk); + return new Header(cmd, size, checksum); }; /** @@ -201,156 +195,16 @@ Parser.prototype.parseHeader = function parseHeader(h) { */ Parser.prototype.parsePayload = function parsePayload(cmd, data) { - switch (cmd) { - case 'version': - return packets.VersionPacket.fromRaw(data); - case 'verack': - return null; - case 'ping': - return parsePing(data); - case 'pong': - return parsePing(data); - case 'alert': - return packets.AlertPacket.fromRaw(data); - case 'getaddr': - return null; - case 'addr': - return parseAddr(data); - case 'inv': - return parseInv(data); - case 'getdata': - return parseInv(data); - case 'notfound': - return parseInv(data); - case 'getblocks': - return packets.GetBlocksPacket.fromRaw(data); - case 'getheaders': - return packets.GetBlocksPacket.fromRaw(data); - case 'headers': - return parseHeaders(data); - case 'sendheaders': - return null; - case 'block': - return bcoin.memblock.fromRaw(data); - case 'tx': - return bcoin.tx.fromRaw(data); - case 'reject': - return packets.RejectPacket.fromRaw(data); - case 'mempool': - return null; - case 'filterload': - return bcoin.bloom.fromRaw(data); - case 'filteradd': - return parseFilterAdd(data); - case 'filterclear': - return null; - case 'merkleblock': - return bcoin.merkleblock.fromRaw(data); - case 'getutxos': - return packets.GetUTXOsPacket.fromRaw(data); - case 'utxos': - return packets.UTXOsPacket.fromRaw(data); - case 'havewitness': - return null; - case 'feefilter': - return parseFeeFilter(data); - case 'sendcmpct': - return bcoin.bip152.SendCompact.fromRaw(data); - case 'cmpctblock': - return bcoin.bip152.CompactBlock.fromRaw(data); - case 'getblocktxn': - return bcoin.bip152.TXRequest.fromRaw(data); - case 'blocktxn': - return bcoin.bip152.TXResponse.fromRaw(data); - case 'encinit': - assert(data.length >= 34); - return data; - case 'encack': - assert(data.length >= 33); - return data; - case 'authchallenge': - assert(data.length >= 32); - return data; - case 'authreply': - assert(data.length >= 64); - return data; - case 'authpropose': - assert(data.length >= 32); - return data; - default: - return data; - } + return packets.fromRaw(cmd, data); }; -/* - * Helpers - */ - -function parsePing(data) { - var p = new BufferReader(data); - return p.readU64(); -} - -function parseInv(data) { - var p = new BufferReader(data); - var items = []; - var i, count; - - count = p.readVarint(); - - assert(count <= 50000, 'Item count too high.'); - - for (i = 0; i < count; i++) - items.push(bcoin.invitem.fromRaw(p)); - - return items; -} - -function parseHeaders(data) { - var p = new BufferReader(data); - var headers = []; - var i, count; - - count = p.readVarint(); - - for (i = 0; i < count; i++) - headers.push(bcoin.headers.fromRaw(p)); - - return headers; -} - -function parseAddr(data) { - var p = new BufferReader(data); - var addrs = []; - var i, count; - - count = p.readVarint(); - - assert(count <= 10000, 'Too many addresses.'); - - for (i = 0; i < count; i++) - addrs.push(packets.NetworkAddress.fromRaw(p, true)); - - return addrs; -} - -function parseFeeFilter(data) { - var p = new BufferReader(data); - return p.read64N(); -} - -function parseFilterAdd(data) { - var p = new BufferReader(data); - return p.readVarBytes(); -} - /** * Packet * @constructor * @private */ -function Packet(cmd, size, checksum) { +function Header(cmd, size, checksum) { this.cmd = cmd; this.size = size; this.checksum = checksum; diff --git a/lib/net/peer.js b/lib/net/peer.js index 2afd387d..40542bff 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -12,14 +12,12 @@ var EventEmitter = require('events').EventEmitter; var utils = require('../utils/utils'); var Parser = require('./parser'); var Framer = require('./framer'); +var packets = require('./packets'); +var packetTypes = packets.types; +var NetworkAddress = require('../primitives/netaddress'); var assert = utils.assert; var constants = bcoin.constants; var InvItem = bcoin.invitem; -var VersionPacket = bcoin.packets.VersionPacket; -var GetBlocksPacket = bcoin.packets.GetBlocksPacket; -var RejectPacket = bcoin.packets.RejectPacket; -var NetworkAddress = bcoin.packets.NetworkAddress; -var GetUTXOsPacket = bcoin.packets.GetUTXOsPacket; /** * Represents a remote peer. @@ -225,7 +223,7 @@ Peer.prototype._init = function init() { }); this.bip151.on('rekey', function() { self.logger.debug('Rekeying with peer (%s).', self.hostname); - self.write(self.framer.encack(self.bip151.toRekey())); + self.send(self.bip151.toRekey()); }); } @@ -268,7 +266,7 @@ Peer.prototype._onConnect = function _onConnect() { if (this.bip151) { assert(!this.bip151.completed); this.logger.info('Attempting BIP151 handshake (%s).', this.hostname); - this.write(this.framer.encinit(this.bip151.toEncinit())); + this.send(this.bip151.toEncinit()); return this.bip151.wait(3000, function(err) { if (err) self.error(err, true); @@ -306,7 +304,7 @@ Peer.prototype._onBIP151 = function _onBIP151() { if (this.bip150.outbound) { if (!this.bip150.peerIdentity) return this.error('No known identity for peer.'); - this.write(this.framer.authChallenge(this.bip150.toChallenge())); + this.send(this.bip150.toChallenge()); } return this.bip150.wait(3000, function(err) { @@ -348,7 +346,7 @@ Peer.prototype._onHandshake = function _onHandshake() { if (this.pool.address.host !== '0.0.0.0' && !this.options.selfish && this.pool.server) { - this.write(this.framer.addr([this.pool.address])); + this.send(new packets.AddrPacket([this.pool.address])); } }; @@ -384,7 +382,7 @@ Peer.prototype._onAck = function _onAck(err) { // Ask for headers-only. if (this.options.headers) { if (this.version.version >= 70012) - this.write(this.framer.sendHeaders()); + this.send(new packets.SendHeadersPacket()); } // Let them know we support segwit (old @@ -392,7 +390,7 @@ Peer.prototype._onAck = function _onAck(err) { // of service bits). if (this.options.witness && this.network.oldWitness) { if (this.version.version >= 70012) - this.write(this.framer.haveWitness()); + this.send(new packets.HaveWitnessPacket()); } // We want compact blocks! @@ -402,7 +400,7 @@ Peer.prototype._onAck = function _onAck(err) { } // Find some more peers. - this.write(this.framer.getAddr()); + this.write(new packets.GetAddrPacket()); // Relay our spv filter if we have one. this.updateWatch(); @@ -564,7 +562,7 @@ Peer.prototype.sendInv = function sendInv(items) { for (i = 0; i < items.length; i += 50000) { chunk = items.slice(i, i + 50000); - this.write(this.framer.inv(chunk)); + this.send(new packets.InvPacket(chunk)); } }; @@ -593,16 +591,25 @@ Peer.prototype.sendHeaders = function sendHeaders(items) { for (i = 0; i < items.length; i += 2000) { chunk = items.slice(i, i + 2000); - this.write(this.framer.headers(chunk)); + this.send(new packets.HeadersPacket(chunk)); } }; +/** + * Send a packet. + * @param {Packet} packet + */ + +Peer.prototype.send = function send(packet) { + this.write(this.framer.packet(packet.cmd, packet.toRaw())); +}; + /** * Send a `version` packet. */ Peer.prototype.sendVersion = function sendVersion() { - var packet = new VersionPacket({ + var packet = new packets.VersionPacket({ version: constants.VERSION, services: this.pool.services, ts: bcoin.now(), @@ -613,8 +620,7 @@ Peer.prototype.sendVersion = function sendVersion() { height: this.chain.height, relay: this.options.relay }); - - this.write(this.framer.version(packet)); + this.send(packet); }; /** @@ -626,7 +632,7 @@ Peer.prototype.sendPing = function sendPing() { return; if (this.version.version <= 60000) { - this.write(this.framer.ping()); + this.send(new packets.PingPacket()); return; } @@ -638,7 +644,7 @@ Peer.prototype.sendPing = function sendPing() { this.lastPing = utils.ms(); this.challenge = utils.nonce(); - this.write(this.framer.ping(this.challenge)); + this.send(new packets.PingPacket(this.challenge)); }; /** @@ -671,7 +677,7 @@ Peer.prototype.updateWatch = function updateWatch() { if (!this.options.spv) return; - this.write(this.framer.filterLoad(this.pool.spvFilter)); + this.send(packets.FilterLoadPacket.fromFilter(this.pool.spvFilter)); }; /** @@ -680,7 +686,7 @@ Peer.prototype.updateWatch = function updateWatch() { */ Peer.prototype.sendFeeRate = function sendFeeRate(rate) { - this.write(this.framer.feeFilter(rate)); + this.send(new packets.FeeFilterPacket(rate)); }; /** @@ -847,7 +853,7 @@ Peer.prototype.getData = function getData(items) { data[i] = item; } - this.write(this.framer.getData(data)); + this.send(new packets.GetDataPacket(data)); }; /** @@ -857,123 +863,120 @@ Peer.prototype.getData = function getData(items) { */ Peer.prototype._onPacket = function onPacket(packet) { - var cmd = packet.cmd; - var payload = packet.payload; - if (this.bip151 && !this.bip151.completed - && cmd !== 'encinit' - && cmd !== 'encack') { + && packet.type !== packetTypes.ENCINIT + && packet.type !== packetTypes.ENCACK) { this.bip151.complete(new Error('Message before handshake.')); } if (this.bip150 && !this.bip150.completed - && cmd !== 'authchallenge' - && cmd !== 'authreply' - && cmd !== 'authpropose') { + && packet.type !== packetTypes.AUTHCHALLENGE + && packet.type !== packetTypes.AUTHREPLY + && packet.type !== packetTypes.AUTHPROPOSE) { this.bip150.complete(new Error('Message before auth.')); } - if (this.lastBlock && cmd !== 'tx') + if (this.lastBlock && packet.type !== packetTypes.TX) this._flushMerkle(); this.lastRecv = utils.ms(); - switch (cmd) { - case 'version': - return this._handleVersion(payload); - case 'verack': - this.fire(cmd); + switch (packet.type) { + case packetTypes.VERSION: + return this._handleVersion(packet); + case packetTypes.VERACK: + this.fire(packet.cmd); break; - case 'ping': - return this._handlePing(payload); - case 'pong': - return this._handlePong(payload); - case 'alert': - return this._handleAlert(payload); - case 'getaddr': + case packetTypes.PING: + return this._handlePing(packet); + case packetTypes.PONG: + return this._handlePong(packet); + case packetTypes.ALERT: + return this._handleAlert(packet); + case packetTypes.GETADDR: return this._handleGetAddr(); - case 'addr': - return this._handleAddr(payload); - case 'inv': - return this._handleInv(payload); - case 'getdata': - return this._handleGetData(payload); - case 'notfound': - this.fire(cmd, payload); + case packetTypes.ADDR: + return this._handleAddr(packet); + case packetTypes.INV: + return this._handleInv(packet); + case packetTypes.GETDATA: + return this._handleGetData(packet); + case packetTypes.NOTFOUND: + this.fire(packet.cmd, packet.items); break; - case 'getblocks': - return this._handleGetBlocks(payload); - case 'getheaders': - return this._handleGetHeaders(payload); - case 'headers': - return this._handleHeaders(payload); - case 'sendheaders': + case packetTypes.GETBLOCKS: + return this._handleGetBlocks(packet); + case packetTypes.GETHEADERS: + return this._handleGetHeaders(packet); + case packetTypes.HEADERS: + return this._handleHeaders(packet); + case packetTypes.SENDHEADERS: this.preferHeaders = true; - this.fire(cmd); + this.fire(packet.cmd); break; - case 'block': - this.fire(cmd, payload); + case packetTypes.BLOCK: + this.fire(packet.cmd, packet.block); break; - case 'tx': + case packetTypes.TX: if (this.lastBlock) { - if (this.lastBlock.hasTX(payload)) { - this.lastBlock.addTX(payload); + if (this.lastBlock.hasTX(packet.tx)) { + this.lastBlock.addTX(packet.tx); if (--this.waiting === 0) this._flushMerkle(); break; } } - this.fire(cmd, payload); + this.fire(packet.cmd, packet.tx); break; - case 'reject': - return this._handleReject(payload); - case 'mempool': + case packetTypes.REJECT: + return this._handleReject(packet); + case packetTypes.MEMPOOL: return this._handleMempool(); - case 'filterload': - return this._handleFilterLoad(payload); - case 'filteradd': - return this._handleFilterAdd(payload); - case 'filterclear': + case packetTypes.FILTERLOAD: + return this._handleFilterLoad(packet); + case packetTypes.FILTERADD: + return this._handleFilterAdd(packet); + case packetTypes.FILTERCLEAR: return this._handleFilterClear(); - case 'merkleblock': - payload.verifyPartial(); - this.lastBlock = payload; - this.waiting = payload.matches.length; + case packetTypes.MERKLEBLOCK: + packet.block.verifyPartial(); + this.lastBlock = packet.block; + this.waiting = packet.block.matches.length; if (this.waiting === 0) this._flushMerkle(); break; - case 'getutxos': - return this._handleGetUTXOs(payload); - case 'utxos': - return this._handleUTXOs(payload); - case 'havewitness': + case packetTypes.GETUTXOS: + return this._handleGetUTXOs(packet); + case packetTypes.UTXOS: + return this._handleUTXOs(packet); + case packetTypes.HAVEWITNESS: this.haveWitness = true; - this.fire(cmd); + this.fire(packet.cmd); break; - case 'feefilter': - return this._handleFeeFilter(payload); - case 'sendcmpct': - return this._handleSendCmpct(payload); - case 'cmpctblock': - return this._handleCmpctBlock(payload); - case 'getblocktxn': - return this._handleGetBlockTxn(payload); - case 'blocktxn': - return this._handleBlockTxn(payload); - case 'encinit': - return this._handleEncinit(payload); - case 'encack': - return this._handleEncack(payload); - case 'authchallenge': - return this._handleAuthChallenge(payload); - case 'authreply': - return this._handleAuthReply(payload); - case 'authpropose': - return this._handleAuthPropose(payload); - default: - this.logger.warning('Unknown packet: %s.', cmd); + case packetTypes.FEEFILTER: + return this._handleFeeFilter(packet); + case packetTypes.SENDCMPCT: + return this._handleSendCmpct(packet); + case packetTypes.CMPCTBLOCK: + return this._handleCmpctBlock(packet); + case packetTypes.GETBLOCKTXN: + return this._handleGetBlockTxn(packet); + case packetTypes.BLOCKTXN: + return this._handleBlockTxn(packet); + case packetTypes.ENCINIT: + return this._handleEncinit(packet); + case packetTypes.ENCACK: + return this._handleEncack(packet); + case packetTypes.AUTHCHALLENGE: + return this._handleAuthChallenge(packet); + case packetTypes.AUTHREPLY: + return this._handleAuthReply(packet); + case packetTypes.AUTHPROPOSE: + return this._handleAuthPropose(packet); + case packetTypes.UNKNOWN: + this.logger.warning('Unknown packet: %s.', packet.cmd); break; } }; @@ -1008,13 +1011,13 @@ Peer.prototype.fire = function fire(cmd, payload) { * @param {Object} */ -Peer.prototype._handleFilterLoad = function _handleFilterLoad(filter) { - if (!filter.isWithinConstraints()) { +Peer.prototype._handleFilterLoad = function _handleFilterLoad(packet) { + if (!packet.isWithinConstraints()) { this.setMisbehavior(100); return; } - this.spvFilter = filter; + this.spvFilter = packet.toFilter(); this.relay = true; }; @@ -1024,7 +1027,9 @@ Peer.prototype._handleFilterLoad = function _handleFilterLoad(filter) { * @param {Object} */ -Peer.prototype._handleFilterAdd = function _handleFilterAdd(data) { +Peer.prototype._handleFilterAdd = function _handleFilterAdd(packet) { + var data = packet.data; + if (data.length > constants.script.MAX_PUSH) { this.setMisbehavior(100); return; @@ -1055,7 +1060,9 @@ Peer.prototype._handleFilterClear = function _handleFilterClear() { * @param {Object} */ -Peer.prototype._handleFeeFilter = function _handleFeeFilter(rate) { +Peer.prototype._handleFeeFilter = function _handleFeeFilter(packet) { + var rate = packet.rate; + if (!(rate >= 0 && rate <= constants.MAX_MONEY)) { this.setMisbehavior(100); return; @@ -1111,7 +1118,7 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) { if (payload.prevout.length > 15) return done(); - utxos = new GetUTXOsPacket(); + utxos = new packets.GetUTXOsPacket(); utils.forEachSerial(payload.prevout, function(prevout, next) { var hash = prevout.hash; @@ -1154,7 +1161,7 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) { utxos.height = self.chain.height; utxos.tip = self.chain.tip.hash; - self.write(self.framer.UTXOs(utxos)); + self.send(new packets.UTXOsPacket(utxos)); done(); }); @@ -1378,8 +1385,7 @@ Peer.prototype._handleVersion = function _handleVersion(version) { if (!version.relay) this.relay = false; - // ACK - this.write(this.framer.verack()); + this.send(new packets.VerackPacket()); this.version = version; this.fire('version', version); }; @@ -1482,10 +1488,11 @@ Peer.prototype._getItem = function _getItem(item, callback) { * @param {Object} */ -Peer.prototype._handleGetData = function _handleGetData(items) { +Peer.prototype._handleGetData = function _handleGetData(packet) { var self = this; var notFound = []; - var unlock = this._lock(_handleGetData, [items, utils.nop]); + var items = packet.items; + var unlock = this._lock(_handleGetData, [packet, utils.nop]); if (!unlock) return; @@ -1528,10 +1535,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) { return next(); } - if (item.hasWitness()) - self.write(self.framer.witnessTX(tx)); - else - self.write(self.framer.tx(tx)); + self.send(new packets.TXPacket(tx, item.hasWitness())); return next(); } @@ -1541,10 +1545,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) { switch (item.type) { case constants.inv.BLOCK: case constants.inv.WITNESS_BLOCK: - if (item.hasWitness()) - self.write(self.framer.witnessBlock(block)); - else - self.write(self.framer.block(block)); + self.send(new packets.BlockPacket(block, item.hasWitness())); break; case constants.inv.FILTERED_BLOCK: case constants.inv.WITNESS_FILTERED_BLOCK: @@ -1555,22 +1556,18 @@ Peer.prototype._handleGetData = function _handleGetData(items) { block = block.toMerkle(self.spvFilter); - self.write(self.framer.merkleBlock(block)); + self.send(new packets.MerkleBlockPacket(block)); for (i = 0; i < block.txs.length; i++) { tx = block.txs[i]; - - if (item.hasWitness()) - self.write(self.framer.witnessTX(tx)); - else - self.write(self.framer.tx(tx)); + self.send(new packets.TXPacket(tx, item.hasWitness())); } break; case constants.inv.CMPCT_BLOCK: // Fallback to full block. if (block.height < self.chain.tip.height - 10) { - self.write(self.framer.block(block)); + self.send(new packets.BlockPacket(block, false)); break; } @@ -1585,7 +1582,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) { break; } - self.write(self.framer.cmpctBlock(block)); + self.send(new packets.CmpctBlockPacket(block, false)); break; default: self.logger.warning( @@ -1614,7 +1611,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) { self.hostname); if (notFound.length > 0) - self.write(self.framer.notFound(notFound)); + self.send(new packets.NotFoundPacket(notFound)); done(); }); @@ -1626,7 +1623,8 @@ Peer.prototype._handleGetData = function _handleGetData(items) { * @param {Object} */ -Peer.prototype._handleAddr = function _handleAddr(addrs) { +Peer.prototype._handleAddr = function _handleAddr(packet) { + var addrs = packet.items; var i; for (i = 0; i < addrs.length; i++) @@ -1648,8 +1646,8 @@ Peer.prototype._handleAddr = function _handleAddr(addrs) { * @param {Object} */ -Peer.prototype._handlePing = function _handlePing(nonce) { - this.write(this.framer.pong(nonce)); +Peer.prototype._handlePing = function _handlePing(packet) { + this.send(new packets.PongPacket(packet.nonce)); this.fire('ping', this.minPing); }; @@ -1659,7 +1657,8 @@ Peer.prototype._handlePing = function _handlePing(nonce) { * @param {Object} */ -Peer.prototype._handlePong = function _handlePong(nonce) { +Peer.prototype._handlePong = function _handlePong(packet) { + var nonce = packet.nonce; var now = utils.ms(); if (!this.challenge) { @@ -1734,7 +1733,7 @@ Peer.prototype._handleGetAddr = function _handleGetAddr() { items.length, this.hostname); - this.write(this.framer.addr(items)); + this.send(new packets.AddrPacket(items)); }; /** @@ -1743,10 +1742,12 @@ Peer.prototype._handleGetAddr = function _handleGetAddr() { * @param {Object} */ -Peer.prototype._handleInv = function _handleInv(items) { +Peer.prototype._handleInv = function _handleInv(packet) { + var items = packet.items; var blocks = []; var txs = []; - var i, item, unknown; + var unknown = -1; + var i, item; if (items.length > 50000) { this.setMisbehavior(100); @@ -1755,13 +1756,16 @@ Peer.prototype._handleInv = function _handleInv(items) { for (i = 0; i < items.length; i++) { item = items[i]; - if (item.type === constants.inv.TX) { - txs.push(item.hash); - } else if (item.type === constants.inv.BLOCK) { - blocks.push(item.hash); - } else { - unknown = item.type; - continue; + switch (item.type) { + case constants.inv.TX: + txs.push(item.hash); + break; + case constants.inv.BLOCK: + blocks.push(item.hash); + break; + default: + unknown = item.type; + continue; } this.invFilter.add(item.hash, 'hex'); } @@ -1774,7 +1778,7 @@ Peer.prototype._handleInv = function _handleInv(items) { if (txs.length > 0) this.emit('txs', txs); - if (unknown != null) { + if (unknown !== -1) { this.logger.warning( 'Peer sent an unknown inv type: %d (%s).', unknown, this.hostname); @@ -1787,7 +1791,8 @@ Peer.prototype._handleInv = function _handleInv(items) { * @param {Object} */ -Peer.prototype._handleHeaders = function _handleHeaders(headers) { +Peer.prototype._handleHeaders = function _handleHeaders(packet) { + var headers = packet.items; if (headers.length > 2000) { this.setMisbehavior(100); return; @@ -1835,20 +1840,20 @@ Peer.prototype._handleAlert = function _handleAlert(alert) { * @param {Object} */ -Peer.prototype._handleEncinit = function _handleEncinit(data) { +Peer.prototype._handleEncinit = function _handleEncinit(packet) { if (!this.bip151) return; try { - this.bip151.encinit(data); + this.bip151.encinit(packet.publicKey, packet.cipher); } catch (e) { this.error(e); return; } - this.write(this.framer.encack(this.bip151.toEncack())); + this.send(this.bip151.toEncack()); - this.fire('encinit', data); + this.fire('encinit', packet); }; /** @@ -1857,18 +1862,18 @@ Peer.prototype._handleEncinit = function _handleEncinit(data) { * @param {Object} */ -Peer.prototype._handleEncack = function _handleEncack(data) { +Peer.prototype._handleEncack = function _handleEncack(packet) { if (!this.bip151) return; try { - this.bip151.encack(data); + this.bip151.encack(packet.publicKey); } catch (e) { this.error(e); return; } - this.fire('encack', data); + this.fire('encack', packet); }; /** @@ -1877,22 +1882,22 @@ Peer.prototype._handleEncack = function _handleEncack(data) { * @param {Object} */ -Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(data) { - var result; +Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(packet) { + var sig; if (!this.bip150) return; try { - result = this.bip150.challenge(data); + sig = this.bip150.challenge(packet.hash); } catch (e) { this.error(e); return; } - this.write(this.framer.authReply(result)); + this.send(new packets.AuthReplyPacket(sig)); - this.fire('authchallenge', data); + this.fire('authchallenge', packet.hash); }; /** @@ -1901,23 +1906,23 @@ Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(data) { * @param {Object} */ -Peer.prototype._handleAuthReply = function _handleAuthReply(data) { - var result; +Peer.prototype._handleAuthReply = function _handleAuthReply(packet) { + var hash; if (!this.bip150) return; try { - result = this.bip150.reply(data); + hash = this.bip150.reply(packet.signature); } catch (e) { this.error(e); return; } - if (result) - this.write(this.framer.authPropose(result)); + if (hash) + this.send(new packets.AuthProposePacket(hash)); - this.fire('authreply', data); + this.fire('authreply', packet.signature); }; /** @@ -1926,22 +1931,22 @@ Peer.prototype._handleAuthReply = function _handleAuthReply(data) { * @param {Object} */ -Peer.prototype._handleAuthPropose = function _handleAuthPropose(data) { - var result; +Peer.prototype._handleAuthPropose = function _handleAuthPropose(packet) { + var hash; if (!this.bip150) return; try { - result = this.bip150.propose(data); + hash = this.bip150.propose(packet.hash); } catch (e) { this.error(e); return; } - this.write(this.framer.authChallenge(result)); + this.send(new packets.AuthChallengePacket(hash)); - this.fire('authpropose', data); + this.fire('authpropose', packet.hash); }; /** @@ -1950,25 +1955,25 @@ Peer.prototype._handleAuthPropose = function _handleAuthPropose(data) { * @param {Object} */ -Peer.prototype._handleSendCmpct = function _handleSendCmpct(cmpct) { - if (cmpct.version !== 1) { +Peer.prototype._handleSendCmpct = function _handleSendCmpct(packet) { + if (packet.version !== 1) { // Ignore this.logger.info('Peer request compact blocks version %d (%s).', - cmpct.version, this.hostname); + packet.version, this.hostname); return; } - if (cmpct.mode !== 0) { + if (packet.mode !== 0) { // Ignore (we can't do mode 1 yet). this.logger.info('Peer request compact blocks mode %d (%s).', - cmpct.mode, this.hostname); + packet.mode, this.hostname); return; } this.logger.info('Peer initialized compact blocks (%s).', this.hostname); - this.compactMode = cmpct; - this.fire('sendcmpct', cmpct); + this.compactMode = packet; + this.fire('sendcmpct', packet); }; /** @@ -1977,8 +1982,9 @@ Peer.prototype._handleSendCmpct = function _handleSendCmpct(cmpct) { * @param {Object} */ -Peer.prototype._handleCmpctBlock = function _handleCmpctBlock(block) { +Peer.prototype._handleCmpctBlock = function _handleCmpctBlock(packet) { var self = this; + var block = packet.block; var hash = block.hash('hex'); var result; @@ -2013,7 +2019,7 @@ Peer.prototype._handleCmpctBlock = function _handleCmpctBlock(block) { return; } - this.write(this.framer.getBlockTxn(block.toRequest())); + this.send(block.toRequest()); this.logger.debug( 'Received semi-full compact block %s (%s).', @@ -2033,8 +2039,9 @@ Peer.prototype._handleCmpctBlock = function _handleCmpctBlock(block) { * @param {Object} */ -Peer.prototype._handleGetBlockTxn = function _handleGetBlockTxn(req) { +Peer.prototype._handleGetBlockTxn = function _handleGetBlockTxn(packet) { var self = this; + var req = packet.request; var res, item; function done(err) { @@ -2077,7 +2084,7 @@ Peer.prototype._handleGetBlockTxn = function _handleGetBlockTxn(req) { res = bcoin.bip152.TXResponse.fromBlock(block, req); - self.write(self.framer.blockTxn(res)); + self.send(new packets.BlockTxnPacket(res, false)); done(); }); @@ -2089,7 +2096,8 @@ Peer.prototype._handleGetBlockTxn = function _handleGetBlockTxn(req) { * @param {Object} */ -Peer.prototype._handleBlockTxn = function _handleBlockTxn(res) { +Peer.prototype._handleBlockTxn = function _handleBlockTxn(packet) { + var res = packet.response; var block = this.compactBlocks[res.hash]; if (!block) { @@ -2124,7 +2132,7 @@ Peer.prototype.sendAlert = function sendAlert(alert) { if (!this.invFilter.added(alert.hash())) return; - this.write(this.framer.alert(alert)); + this.send(alert); }; /** @@ -2135,7 +2143,7 @@ Peer.prototype.sendAlert = function sendAlert(alert) { */ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) { - var packet = new GetBlocksPacket(locator, stop); + var packet = new packets.GetHeadersPacket(locator, stop); var height = -1; var hash = null; @@ -2153,7 +2161,7 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) { this.logger.debug('Height: %d, Hash: %s, Stop: %s', height, hash, stop); - this.write(this.framer.getHeaders(packet)); + this.send(packet); }; /** @@ -2163,7 +2171,7 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) { */ Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) { - var packet = new GetBlocksPacket(locator, stop); + var packet = new packets.GetBlocksPacket(locator, stop); var height = -1; var hash = null; @@ -2181,7 +2189,7 @@ Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) { this.logger.debug('Height: %d, Hash: %s, Stop: %s', height, hash, stop); - this.write(this.framer.getBlocks(packet)); + this.send(packet); }; /** @@ -2203,7 +2211,7 @@ Peer.prototype.sendMempool = function sendMempool() { 'Requesting inv packet from peer with mempool (%s).', this.hostname); - this.write(this.framer.mempool()); + this.send(new packets.MempoolPacket()); }; /** @@ -2212,7 +2220,7 @@ Peer.prototype.sendMempool = function sendMempool() { */ Peer.prototype.sendReject = function sendReject(code, reason, obj) { - var reject = RejectPacket.fromReason(code, reason, obj); + var reject = packets.RejectPacket.fromReason(code, reason, obj); if (obj) { this.logger.debug('Rejecting %s %s (%s): ccode=%s reason=%s.', @@ -2226,7 +2234,7 @@ Peer.prototype.sendReject = function sendReject(code, reason, obj) { 'Sending reject packet to peer (%s).', this.hostname); - this.write(this.framer.reject(reject)); + this.send(reject); }; /** @@ -2234,9 +2242,9 @@ Peer.prototype.sendReject = function sendReject(code, reason, obj) { */ Peer.prototype.sendCompact = function sendCompact() { - var cmpct = new bcoin.bip152.SendCompact(0, 1); + var cmpct = new packets.SendCmpctPacket(0, 1); this.logger.info('Initializing compact blocks (%s).', this.hostname); - this.write(this.framer.sendCmpct(cmpct)); + this.send(cmpct); }; /** diff --git a/lib/net/pool.js b/lib/net/pool.js index 2dbcac80..a62adfa6 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -15,7 +15,7 @@ var IP = require('../utils/ip'); var assert = utils.assert; var constants = bcoin.constants; var VerifyError = bcoin.errors.VerifyError; -var NetworkAddress = bcoin.packets.NetworkAddress; +var NetworkAddress = require('../primitives/netaddress'); var InvItem = bcoin.invitem; var VerifyResult = utils.VerifyResult; diff --git a/lib/primitives/netaddress.js b/lib/primitives/netaddress.js new file mode 100644 index 00000000..86af0cc8 --- /dev/null +++ b/lib/primitives/netaddress.js @@ -0,0 +1,304 @@ +/*! + * packets.js - packets for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var bcoin = require('../env'); +var constants = require('../protocol/constants'); +var utils = require('../utils/utils'); +var IP = require('../utils/ip'); +var assert = utils.assert; + +/** + * Represents a network address. + * @exports NetworkAddress + * @constructor + * @param {Object} options + * @param {Number?} options.ts - Timestamp. + * @param {Number?} options.services - Service bits. + * @param {String?} options.host - IP address (IPv6 or IPv4). + * @param {Number?} options.port - Port. + * @property {Number} id + * @property {Host} host + * @property {Number} port + * @property {Number} services + * @property {Number} ts + */ + +function NetworkAddress(options) { + if (!(this instanceof NetworkAddress)) + return new NetworkAddress(options); + + this.id = NetworkAddress.uid++; + this.host = '0.0.0.0'; + this.port = 0; + this.services = 0; + this.ts = 0; + this.hostname = '0.0.0.0:0'; + + if (options) + this.fromOptions(options); +} + +/** + * Globally incremented unique id. + * @private + * @type {Number} + */ + +NetworkAddress.uid = 0; + +/** + * Inject properties from options object. + * @private + * @param {Object} options + */ + +NetworkAddress.prototype.fromOptions = function fromOptions(options) { + var host = options.host; + + assert(typeof options.host === 'string'); + assert(typeof options.port === 'number'); + + if (IP.version(host) !== -1) + host = IP.normalize(host); + + this.host = host; + this.port = options.port; + + if (options.services) { + assert(typeof options.services === 'number'); + this.services = options.services; + } + + if (options.ts) { + assert(typeof options.ts === 'number'); + this.ts = options.ts; + } + + this.hostname = IP.hostname(this.host, this.port); + + return this; +}; + +/** + * Instantiate network address from options. + * @param {Object} options + * @returns {NetworkAddress} + */ + +NetworkAddress.fromOptions = function fromOptions(options) { + return new NetworkAddress().fromOptions(options); +}; + +/** + * Test whether the `host` field is an ip address. + * @returns {Boolean} + */ + +NetworkAddress.prototype.isIP = function isIP() { + return IP.version(this.host) !== -1; +}; + +/** + * Test whether the NETWORK service bit is set. + * @returns {Boolean} + */ + +NetworkAddress.prototype.hasNetwork = function hasNetwork() { + return (this.services & constants.services.NETWORK) !== 0; +}; + +/** + * Test whether the BLOOM service bit is set. + * @returns {Boolean} + */ + +NetworkAddress.prototype.hasBloom = function hasBloom() { + return (this.services & constants.services.BLOOM) !== 0; +}; + +/** + * Test whether the GETUTXO service bit is set. + * @returns {Boolean} + */ + +NetworkAddress.prototype.hasUTXO = function hasUTXO() { + return (this.services & constants.services.GETUTXO) !== 0; +}; + +/** + * Test whether the WITNESS service bit is set. + * @returns {Boolean} + */ + +NetworkAddress.prototype.hasWitness = function hasWitness() { + return (this.services & constants.services.WITNESS) !== 0; +}; + +/** + * Set host. + * @param {String} host + */ + +NetworkAddress.prototype.setHost = function setHost(host) { + this.host = host; + this.hostname = IP.hostname(host, this.port); +}; + +/** + * Set port. + * @param {Number} port + */ + +NetworkAddress.prototype.setPort = function setPort(port) { + this.port = port; + this.hostname = IP.hostname(this.host, port); +}; + +/** + * Inspect the network address. + * @returns {Object} + */ + +NetworkAddress.prototype.inspect = function inspect() { + return ''; +}; + +/** + * Inject properties from hostname and network. + * @private + * @param {String} hostname + * @param {(Network|NetworkType)?} network + */ + +NetworkAddress.prototype.fromHostname = function fromHostname(hostname, network) { + var address = IP.parseHost(hostname); + + network = bcoin.network.get(network); + + this.host = address.host; + this.port = address.port || network.port; + this.services = constants.services.NETWORK + | constants.services.BLOOM + | constants.services.WITNESS; + this.ts = bcoin.now(); + + this.hostname = IP.hostname(this.host, this.port); + + return this; +}; + +/** + * Instantiate a network address + * from a hostname (i.e. 127.0.0.1:8333). + * @param {String} hostname + * @param {(Network|NetworkType)?} network + * @returns {NetworkAddress} + */ + +NetworkAddress.fromHostname = function fromHostname(hostname, network) { + return new NetworkAddress().fromHostname(hostname, network); +}; + +/** + * Inject properties from socket. + * @private + * @param {net.Socket} socket + */ + +NetworkAddress.prototype.fromSocket = function fromSocket(socket) { + assert(typeof socket.remoteAddress === 'string'); + assert(typeof socket.remotePort === 'number'); + + this.host = IP.normalize(socket.remoteAddress); + this.port = socket.remotePort; + this.services = constants.services.NETWORK + | constants.services.BLOOM + | constants.services.WITNESS; + this.ts = bcoin.now(); + + this.hostname = IP.hostname(this.host, this.port); + + return this; +}; + +/** + * Instantiate a network address + * from a socket. + * @param {net.Socket} socket + * @returns {NetworkAddress} + */ + +NetworkAddress.fromSocket = function fromSocket(hostname) { + return new NetworkAddress().fromSocket(hostname); +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + * @param {Boolean?} full - Include timestamp. + */ + +NetworkAddress.prototype.fromRaw = function fromRaw(data, full) { + var p = bcoin.reader(data); + var now = bcoin.now(); + + // only version >= 31402 + this.ts = full ? p.readU32() : 0; + this.services = p.readU53(); + this.host = IP.toString(p.readBytes(16)); + this.port = p.readU16BE(); + + if (this.ts <= 100000000 || this.ts > now + 10 * 60) + this.ts = now - 5 * 24 * 60 * 60; + + this.hostname = IP.hostname(this.host, this.port); + + return this; +}; + +/** + * Insantiate a network address from serialized data. + * @param {Buffer} data + * @param {Boolean?} full - Include timestamp. + * @returns {NetworkAddress} + */ + +NetworkAddress.fromRaw = function fromRaw(data, full) { + return new NetworkAddress().fromRaw(data, full); +}; + +/** + * Serialize network address. + * @param {Boolean} full - Include timestamp. + * @returns {Buffer} + */ + +NetworkAddress.prototype.toRaw = function toRaw(full, writer) { + var p = bcoin.writer(writer); + + if (full) + p.writeU32(this.ts); + + p.writeU64(this.services); + p.writeBytes(IP.toBuffer(this.host)); + p.writeU16BE(this.port); + + if (!writer) + p = p.render(); + + return p; +}; + +module.exports = NetworkAddress; diff --git a/lib/protocol/constants.js b/lib/protocol/constants.js index 138f5053..053aab73 100644 --- a/lib/protocol/constants.js +++ b/lib/protocol/constants.js @@ -631,6 +631,17 @@ exports.ZERO_HASH160 = new Buffer( 'hex' ); +/** + * A hash of all 0xff. + * @const {String} + * @default + */ + +exports.MAX_HASH160 = new Buffer( + 'ffffffffffffffffffffffffffffffffffffffff', + 'hex' +); + /** * A hash of all zeroes. * @const {String} @@ -639,6 +650,14 @@ exports.ZERO_HASH160 = new Buffer( exports.NULL_HASH160 = '0000000000000000000000000000000000000000'; +/** + * A hash of all 0xff. + * @const {String} + * @default + */ + +exports.HIGH_HASH160 = 'ffffffffffffffffffffffffffffffffffffffff'; + /** * A compressed pubkey of all zeroes. * @const {Buffer} @@ -650,6 +669,31 @@ exports.ZERO_KEY = new Buffer( 'hex' ); +/** + * A 73 byte signature of all zeroes. + * @const {Buffer} + * @default + */ + +exports.ZERO_SIG = new Buffer('' + + '0000000000000000000000000000000000000000000000000000000000000000' + + '0000000000000000000000000000000000000000000000000000000000000000' + + '000000000000000000', + 'hex' +); + +/** + * A 64 byte signature of all zeroes. + * @const {Buffer} + * @default + */ + +exports.ZERO_SIG64 = new Buffer('' + + '0000000000000000000000000000000000000000000000000000000000000000' + + '0000000000000000000000000000000000000000000000000000000000000000', + 'hex' +); + /** * BCoin version. * @const {String} diff --git a/lib/primitives/bloom.js b/lib/utils/bloom.js similarity index 85% rename from lib/primitives/bloom.js rename to lib/utils/bloom.js index 4f58a5eb..b225ad7f 100644 --- a/lib/primitives/bloom.js +++ b/lib/utils/bloom.js @@ -8,10 +8,7 @@ 'use strict'; var constants = require('../protocol/constants'); -var assert = require('assert'); -var BufferReader = require('../utils/reader'); -var BufferWriter = require('../utils/writer'); -var murmur3 = require('../utils/murmur3'); +var murmur3 = require('./murmur3'); var sum32 = murmur3.sum32; var mul32 = murmur3.mul32; @@ -147,66 +144,6 @@ Bloom.prototype.added = function added(val, enc) { return ret; }; -/** - * Ensure the filter is within the size limits. - * @returns {Boolean} - */ - -Bloom.prototype.isWithinConstraints = function isWithinConstraints() { - if (this.filter.length > constants.bloom.MAX_BLOOM_FILTER_SIZE) - return false; - - if (this.n > constants.bloom.MAX_HASH_FUNCS) - return false; - - return true; -}; - -/** - * Serialize the filter in `filterload` packet format. - * @returns {Buffer} - */ - -Bloom.prototype.toRaw = function toRaw(writer) { - var p = BufferWriter(writer); - - p.writeVarBytes(this.filter); - p.writeU32(this.n); - p.writeU32(this.tweak); - p.writeU8(this.update); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Instantiate bloom filter from - * serialized data (filterload). - * @param {Buffer} - * @param {String?} enc - * @returns {Bloom} - */ - -Bloom.fromRaw = function fromRaw(data, enc) { - var p, filter, n, tweak, update; - - if (typeof data === 'string') - data = new Buffer(data, enc); - - p = BufferReader(data); - - filter = p.readVarBytes(); - n = p.readU32(); - tweak = p.readU32(); - update = p.readU8(); - - assert(constants.filterFlagsByVal[update] != null, 'Bad filter flag.'); - - return new Bloom(filter, n, tweak, update); -}; - /** * Create a filter from a false positive rate. * @param {Number} items - Expeected number of items. @@ -415,11 +352,15 @@ RollingFilter.prototype.added = function added(val, enc) { * Helpers */ +function U64(hi, lo) { + this.hi = hi; + this.lo = lo; +} + function read(data, off) { - return { - hi: data.readUInt32LE(off + 4, true), - lo: data.readUInt32LE(off, true) - }; + var hi = data.readUInt32LE(off + 4, true); + var lo = data.readUInt32LE(off, true); + return new U64(hi, lo); } function write(data, value, off) { diff --git a/test/bip150-test.js b/test/bip150-test.js index f0db4143..e428ba13 100644 --- a/test/bip150-test.js +++ b/test/bip150-test.js @@ -27,15 +27,17 @@ describe('BIP150', function() { } it('should do encinit', function() { - client.encinit(server.toEncinit()); - server.encinit(client.toEncinit()); + var init = server.toEncinit(); + client.encinit(init.publicKey, init.cipher); + var init = client.toEncinit(); + server.encinit(init.publicKey, init.cipher); assert(!client.handshake); assert(!server.handshake); }); it('should do encack', function() { - client.encack(server.toEncack()); - server.encack(client.toEncack()); + client.encack(server.toEncack().publicKey); + server.encack(client.toEncack().publicKey); assert(client.handshake); assert(server.handshake); }); @@ -49,7 +51,7 @@ describe('BIP150', function() { it('should do BIP150 handshake', function() { var challenge = client.bip150.toChallenge(); - var reply = server.bip150.challenge(challenge); + var reply = server.bip150.challenge(challenge.hash); var propose = client.bip150.reply(reply); var challenge = server.bip150.propose(propose); var reply = client.bip150.challenge(challenge); @@ -113,7 +115,7 @@ describe('BIP150', function() { client.once('rekey', function() { rekeyed = true; - var packet = client.packet('encack', client.toRekey()); + var packet = client.packet('encack', client.toRekey().toRaw()); var emitted = false; server.once('packet', function(cmd, body) { emitted = true; diff --git a/test/bip151-test.js b/test/bip151-test.js index b8589cb5..5557bb3a 100644 --- a/test/bip151-test.js +++ b/test/bip151-test.js @@ -17,15 +17,17 @@ describe('BIP151', function() { } it('should do encinit', function() { - client.encinit(server.toEncinit()); - server.encinit(client.toEncinit()); + var init = server.toEncinit(); + client.encinit(init.publicKey, init.cipher); + var init = client.toEncinit(); + server.encinit(init.publicKey, init.cipher); assert(!client.handshake); assert(!server.handshake); }); it('should do encack', function() { - client.encack(server.toEncack()); - server.encack(client.toEncack()); + client.encack(server.toEncack().publicKey); + server.encack(client.toEncack().publicKey); assert(client.handshake); assert(server.handshake); }); @@ -91,7 +93,7 @@ describe('BIP151', function() { client.once('rekey', function() { rekeyed = true; - var packet = client.packet('encack', client.toRekey()); + var packet = client.packet('encack', client.toRekey().toRaw()); var emitted = false; server.once('packet', function(cmd, body) { emitted = true; diff --git a/test/protocol-test.js b/test/protocol-test.js index a98aeab7..ff1b1dec 100644 --- a/test/protocol-test.js +++ b/test/protocol-test.js @@ -8,9 +8,10 @@ var utils = bcoin.utils; var crypto = require('../lib/crypto/crypto'); var fs = require('fs'); var alertData = fs.readFileSync(__dirname + '/data/alertTests.raw'); -var NetworkAddress = bcoin.packets.NetworkAddress; +var NetworkAddress = require('../lib/primitives/netaddress'); var Framer = require('../lib/net/framer'); var Parser = require('../lib/net/parser'); +var packets = require('../lib/net/packets'); describe('Protocol', function() { var version = require('../package.json').version; @@ -25,17 +26,17 @@ describe('Protocol', function() { function packetTest(command, payload, test) { it('should encode/decode ' + command, function(cb) { - var ver = new Buffer(framer[command](payload)); + var ver = new Buffer(framer.packet(command, payload.toRaw())); parser.once('packet', function(packet) { assert.equal(packet.cmd, command); - test(packet.payload); + test(packet); cb(); }); parser.feed(ver); }); } - var v1 = bcoin.packets.VersionPacket.fromOptions({ + var v1 = packets.VersionPacket.fromOptions({ version: constants.VERSION, services: constants.LOCAL_SERVICES, ts: bcoin.now(), @@ -54,7 +55,7 @@ describe('Protocol', function() { assert.equal(payload.relay, false); }); - var v2 = bcoin.packets.VersionPacket.fromOptions({ + var v2 = packets.VersionPacket.fromOptions({ version: constants.VERSION, services: constants.LOCAL_SERVICES, ts: bcoin.now(), @@ -73,7 +74,7 @@ describe('Protocol', function() { assert.equal(payload.relay, true); }); - packetTest('verack', {}, function(payload) { + packetTest('verack', new packets.VerackPacket(), function(payload) { }); var hosts = [ @@ -91,19 +92,19 @@ describe('Protocol', function() { }) ]; - packetTest('addr', hosts, function(payload) { - assert.equal(typeof payload.length, 'number'); - assert.equal(payload.length, 2); + packetTest('addr', new packets.AddrPacket(hosts), function(payload) { + assert.equal(typeof payload.items.length, 'number'); + assert.equal(payload.items.length, 2); - assert.equal(typeof payload[0].ts, 'number'); - assert.equal(payload[0].services, constants.LOCAL_SERVICES); - assert.equal(payload[0].host, hosts[0].host); - assert.equal(payload[0].port, hosts[0].port); + assert.equal(typeof payload.items[0].ts, 'number'); + assert.equal(payload.items[0].services, constants.LOCAL_SERVICES); + assert.equal(payload.items[0].host, hosts[0].host); + assert.equal(payload.items[0].port, hosts[0].port); - assert.equal(typeof payload[1].ts, 'number'); - assert.equal(payload[1].services, constants.LOCAL_SERVICES); - assert.equal(payload[1].host, hosts[1].host); - assert.equal(payload[1].port, hosts[1].port); + assert.equal(typeof payload.items[1].ts, 'number'); + assert.equal(payload.items[1].services, constants.LOCAL_SERVICES); + assert.equal(payload.items[1].host, hosts[1].host); + assert.equal(payload.items[1].port, hosts[1].port); }); it('should include the raw data of only one transaction in a ' + @@ -209,12 +210,12 @@ describe('Protocol', function() { var p = new bcoin.reader(alertData); p.start(); while (p.left()) { - var alert = bcoin.packets.AlertPacket.fromRaw(p); + var alert = packets.AlertPacket.fromRaw(p); assert(alert.verify(network.alertKey)); alert._payload = null; alert._hash = null; var data = alert.toRaw(); - alert = bcoin.packets.AlertPacket.fromRaw(data); + alert = packets.AlertPacket.fromRaw(data); assert(alert.verify(network.alertKey)); } p.end();