From d663eef37ea459d4526b5646eea243b8bbfd9820 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 25 Aug 2016 01:35:20 -0700 Subject: [PATCH] net: refactor framer and parser. --- lib/net/framer.js | 1044 ++++++++++---------------------------------- lib/net/packets.js | 236 ++++++++++ lib/net/parser.js | 573 ++++-------------------- lib/net/peer.js | 333 ++++++-------- lib/net/pool.js | 54 --- 5 files changed, 703 insertions(+), 1537 deletions(-) diff --git a/lib/net/framer.js b/lib/net/framer.js index 951ed324..ff9e67bc 100644 --- a/lib/net/framer.js +++ b/lib/net/framer.js @@ -11,7 +11,7 @@ var bcoin = require('../env'); var utils = require('../utils/utils'); var assert = utils.assert; var BufferWriter = require('../utils/writer'); -var DUMMY = new Buffer([]); +var DUMMY = new Buffer(0); /** * Protocol packet framer @@ -78,52 +78,51 @@ Framer.prototype.packet = function packet(cmd, payload, checksum) { /** * Create a version packet with a header. - * @param {Object} options - See {@link Framer.version}. + * @param {VersionPacket} payload * @returns {Buffer} version packet. */ -Framer.prototype.version = function version(options) { - return this.packet('version', Framer.version(options)); +Framer.prototype.version = function version(payload) { + return this.packet('version', payload.toRaw()); }; /** * Create a verack packet with a header. - * See {@link Framer.verack}. * @returns {Buffer} verack packet. */ Framer.prototype.verack = function verack() { - return this.packet('verack', Framer.verack()); + return this.packet('verack', DUMMY); }; /** - * Create a mempool packet with a header. - * See {@link Framer.mempool}. - * @returns {Buffer} mempool packet. + * Create a ping packet with a header. + * @param {BN} nonce + * @returns {Buffer} ping packet. */ -Framer.prototype.mempool = function mempool() { - return this.packet('mempool', Framer.mempool()); +Framer.prototype.ping = function ping(nonce) { + return this.packet('ping', framePing(nonce)); }; /** - * Create a getutxos packet with a header. - * @param {Object} data - See {@link Framer.getUTXOs}. - * @returns {Buffer} getutxos packet. + * Create a pong packet with a header. + * @param {BN} nonce + * @returns {Buffer} pong packet. */ -Framer.prototype.getUTXOs = function getUTXOs(data) { - return this.packet('getutxos', Framer.getUTXOs(data)); +Framer.prototype.pong = function pong(nonce) { + return this.packet('pong', framePing(nonce)); }; /** - * Create a utxos packet with a header. - * @param {Object} data - See {@link Framer.utxos}. - * @returns {Buffer} utxos packet. + * Create an alert packet with a header. + * @param {AlertPacket} alert + * @returns {Buffer} alert packet. */ -Framer.prototype.UTXOs = function UTXOs(data) { - return this.packet('utxos', Framer.UTXOs(data)); +Framer.prototype.alert = function _alert(alert) { + return this.packet('alert', alert.toRaw()); }; /** @@ -132,170 +131,123 @@ Framer.prototype.UTXOs = function UTXOs(data) { */ Framer.prototype.getAddr = function getAddr() { - return this.packet('getaddr', Framer.getAddr()); + return this.packet('getaddr', DUMMY); }; /** - * Create a submitorder packet with a header. - * @param {Object} order - See {@link Framer.submitOrder}. - * @returns {Buffer} submitorder packet. + * Create an addr packet with a header. + * @param {NetworkAddress[]} hosts + * @returns {Buffer} addr packet. */ -Framer.prototype.submitOrder = function submitOrder(order) { - return this.packet('submitorder', Framer.submitOrder(order)); -}; - -/** - * Create a checkorder packet with a header. - * @param {Object} order - See {@link Framer.checkOrder}. - * @returns {Buffer} checkorder packet. - */ - -Framer.prototype.checkOrder = function checkOrder(order) { - return this.packet('checkorder', Framer.checkOrder(order)); -}; - -/** - * Create a reply packet with a header. - * @param {Object} data - See {@link Framer.reply}. - * @returns {Buffer} reply packet. - */ - -Framer.prototype.reply = function reply(data) { - return this.packet('reply', Framer.reply(data)); -}; - -/** - * Create a sendheaders packet with a header. - * @param {Object} options - See {@link Framer.sendHeaders}. - * @returns {Buffer} sendheaders packet. - */ - -Framer.prototype.sendHeaders = function sendHeaders() { - return this.packet('sendheaders', Framer.sendHeaders()); -}; - -/** - * Create a havewitness packet with a header. - * @returns {Buffer} havewitness packet. - */ - -Framer.prototype.haveWitness = function haveWitness() { - return this.packet('havewitness', Framer.haveWitness()); -}; - -/** - * Create a filteradd packet with a header. - * @param {Object} data - See {@link Framer.filteradd}. - * @returns {Buffer} filteradd packet. - */ - -Framer.prototype.filterAdd = function filterAdd(data) { - return this.packet('filteradd', Framer.filterAdd(data)); -}; - -/** - * Create a filterclear packet with a header. - * @returns {Buffer} filterclear packet. - */ - -Framer.prototype.filterClear = function filterClear() { - return this.packet('filterclear', Framer.filterClear()); +Framer.prototype.addr = function addr(hosts) { + return this.packet('addr', frameAddr(hosts)); }; /** * Create an inv packet with a header. - * @param {Object} items - See {@link Framer.inv}. + * @param {InvItem[]} items * @returns {Buffer} inv packet. */ Framer.prototype.inv = function inv(items) { - return this.packet('inv', Framer.inv(items)); + return this.packet('inv', frameItems(items)); }; /** * Create a getdata packet with a header. - * @param {Object} items - See {@link Framer.getData}. + * @param {InvItem[]} items * @returns {Buffer} getdata packet. */ Framer.prototype.getData = function getData(items) { - return this.packet('getdata', Framer.getData(items)); + return this.packet('getdata', frameItems(items)); }; /** * Create a notfound packet with a header. - * @param {Object} items - See {@link Framer.notFound}. + * @param {InvItem[]} items * @returns {Buffer} notfound packet. */ Framer.prototype.notFound = function notFound(items) { - return this.packet('notfound', Framer.notFound(items)); -}; - -/** - * Create a ping packet with a header. - * @param {Object} data - See {@link Framer.ping}. - * @returns {Buffer} ping packet. - */ - -Framer.prototype.ping = function ping(data) { - return this.packet('ping', Framer.ping(data)); -}; - -/** - * Create a pong packet with a header. - * @param {Object} data - See {@link Framer.pong}. - * @returns {Buffer} pong packet. - */ - -Framer.prototype.pong = function pong(data) { - return this.packet('pong', Framer.pong(data)); -}; - -/** - * Create a filterload packet with a header. - * @param {Object} data - See {@link Framer.filterLoad}. - * @returns {Buffer} filterload packet. - */ - -Framer.prototype.filterLoad = function filterLoad(data) { - return this.packet('filterload', Framer.filterLoad(data)); -}; - -/** - * Create a getheaders packet with a header. - * @param {Object} options - See {@link Framer.getHeaders}. - * @returns {Buffer} getheaders packet. - */ - -Framer.prototype.getHeaders = function getHeaders(data) { - return this.packet('getheaders', Framer.getHeaders(data)); + return this.packet('notfound', frameItems(items)); }; /** * Create a getblocks packet with a header. - * @param {Object} data - See {@link Framer.getBlocks}. + * @param {GetBlocksPacket} data * @returns {Buffer} getblocks packet. */ Framer.prototype.getBlocks = function getBlocks(data) { - return this.packet('getblocks', Framer.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 - See {@link Framer.tx}. + * @param {TX} tx * @returns {Buffer} tx packet. */ Framer.prototype.tx = function _tx(tx) { - return this.packet('tx', Framer.tx(tx), tx.hash()); + return this.packet('tx', tx.toNormal(), tx.hash()); }; /** - * Create a tx packet with a header, using witness serialization. - * @param {TX} tx - See {@link Framer.witnessTX}. + * Create a tx packet with a header, + * using witness serialization. + * @param {TX} tx * @returns {Buffer} tx packet. */ @@ -307,420 +259,229 @@ Framer.prototype.witnessTX = function witnessTX(tx) { 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', Framer.witnessTX(tx), checksum); -}; - -/** - * Create a block packet with a header. - * @param {Block} block - See {@link Framer.block}. - * @returns {Buffer} block packet. - */ - -Framer.prototype.block = function _block(block) { - return this.packet('block', Framer.block(block)); -}; - -/** - * Create a block packet with a header, using witness serialization. - * @param {Block} block - See {@link Framer.witnessBlock}. - * @returns {Buffer} block packet. - */ - -Framer.prototype.witnessBlock = function witnessBlock(block) { - return this.packet('block', Framer.witnessBlock(block)); -}; - -/** - * Create a merkleblock packet with a header. - * @param {MerkleBlock} block - See {@link Framer.merkleBlock}. - * @returns {Buffer} merkleblock packet. - */ - -Framer.prototype.merkleBlock = function merkleBlock(block) { - return this.packet('merkleblock', Framer.merkleBlock(block)); -}; - -/** - * Create a headers packet with a header. - * @param {Headers[]} headers - See {@link Framer.headers}. - * @returns {Buffer} headers packet. - */ - -Framer.prototype.headers = function _headers(headers) { - return this.packet('headers', Framer.headers(headers)); + return this.packet('tx', tx.toRaw(), checksum); }; /** * Create a reject packet with a header. - * @param {Object} details - See {@link Framer.reject}. + * @param {RejectPacket} details * @returns {Buffer} reject packet. */ Framer.prototype.reject = function reject(details) { - return this.packet('reject', Framer.reject(details)); + return this.packet('reject', details.toRaw()); }; /** - * Create an addr packet with a header. - * @param {Object} peers - See {@link Framer.addr}. - * @returns {Buffer} addr packet. + * Create a mempool packet with a header. + * @returns {Buffer} mempool packet. */ -Framer.prototype.addr = function addr(peers) { - return this.packet('addr', Framer.addr(peers)); +Framer.prototype.mempool = function mempool() { + return this.packet('mempool', DUMMY); }; /** - * Create an alert packet with a header. - * @param {Object} options - See {@link Framer.alert}. - * @returns {Buffer} alert packet. + * Create a filterload packet with a header. + * @param {Bloom} filter + * @returns {Buffer} filterload packet. */ -Framer.prototype.alert = function _alert(alert) { - return this.packet('alert', Framer.alert(alert)); +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 {Object} options - See {@link Framer.feefilter}. + * @param {Rate} rate * @returns {Buffer} feefilter packet. */ -Framer.prototype.feeFilter = function feeFilter(options) { - return this.packet('feefilter', Framer.feeFilter(options)); -}; - -/** - * Create an encinit packet with a header. - * @param {Object} options - See {@link Framer.encinit}. - * @returns {Buffer} encinit packet. - */ - -Framer.prototype.encinit = function encinit(options) { - return this.packet('encinit', Framer.encinit(options)); -}; - -/** - * Create an encack packet with a header. - * @param {Object} options - See {@link Framer.encack}. - * @returns {Buffer} encack packet. - */ - -Framer.prototype.encack = function encack(options) { - return this.packet('encack', Framer.encack(options)); +Framer.prototype.feeFilter = function feeFilter(rate) { + return this.packet('feefilter', frameFeeFilter(rate)); }; /** * Create a sendcmpct packet with a header. - * @param {Object} options - See {@link Framer.sendCmpct}. - * @returns {Buffer} sendCmpct packet. + * @param {SendCompact} data + * @returns {Buffer} sendcmpct packet. */ -Framer.prototype.sendCmpct = function sendCmpct(options) { - return this.packet('sendcmpct', Framer.sendCmpct(options)); +Framer.prototype.sendCmpct = function sendCmpct(data) { + return this.packet('sendcmpct', data.toRaw()); }; /** * Create a cmpctblock packet with a header. - * @param {Object} options - See {@link Framer.cmpctBlock}. - * @returns {Buffer} cmpctBlock packet. + * @param {CompactBlock} block + * @returns {Buffer} cmpctblock packet. */ -Framer.prototype.cmpctBlock = function cmpctBlock(options) { - return this.packet('cmpctblock', Framer.cmpctBlock(options)); +Framer.prototype.cmpctBlock = function cmpctBlock(block) { + return this.packet('cmpctblock', block.toRaw(false)); }; /** * Create a getblocktxn packet with a header. - * @param {Object} options - See {@link Framer.getBlockTxn}. - * @returns {Buffer} getBlockTxn packet. + * @param {TXRequest} req + * @returns {Buffer} getblocktxn packet. */ -Framer.prototype.getBlockTxn = function getBlockTxn(options) { - return this.packet('getblocktxn', Framer.getBlockTxn(options)); +Framer.prototype.getBlockTxn = function getBlockTxn(req) { + return this.packet('getblocktxn', req.toRaw()); }; /** * Create a blocktxn packet with a header. - * @param {Object} options - See {@link Framer.blockTxn}. - * @returns {Buffer} blockTxn packet. + * @param {TXResponse} res + * @returns {Buffer} blocktxn packet. */ -Framer.prototype.blockTxn = function blockTxn(options) { - return this.packet('blocktxn', Framer.blockTxn(options)); +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 {Object} options - See {@link Framer.authChallenge}. - * @returns {Buffer} authChallenge packet. + * @param {Buffer} data + * @returns {Buffer} authchallenge packet. */ -Framer.prototype.authChallenge = function authChallenge(options) { - return this.packet('authchallenge', Framer.authChallenge(options)); +Framer.prototype.authChallenge = function authChallenge(data) { + return this.packet('authchallenge', data); }; /** * Create a authreply packet with a header. - * @param {Object} options - See {@link Framer.authReply}. - * @returns {Buffer} authReply packet. + * @param {Buffer} data + * @returns {Buffer} authreply packet. */ -Framer.prototype.authReply = function authReply(options) { - return this.packet('authreply', Framer.authReply(options)); +Framer.prototype.authReply = function authReply(data) { + return this.packet('authreply', data); }; /** * Create a authpropose packet with a header. - * @param {Object} options - See {@link Framer.authPropose}. - * @returns {Buffer} authPropose packet. + * @param {Buffer} data + * @returns {Buffer} authpropose packet. */ -Framer.prototype.authPropose = function authPropose(options) { - return this.packet('authpropose', Framer.authPropose(options)); +Framer.prototype.authPropose = function authPropose(data) { + return this.packet('authpropose', data); }; -/** - * Create a version packet (without a header). - * @param {VersionPacket} options - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. +/* + * Helpers */ -Framer.version = function _version(version, writer) { - return version.toRaw(writer); -}; - -/** - * Create a verack packet (without a header). - * @returns {Buffer} - */ - -Framer.verack = function verack() { - return DUMMY; -}; - -/** - * Create an inv, getdata, or notfound packet. - * @private - * @param {InvItem[]} items - * @returns {Buffer} - */ - -Framer._inv = function _inv(items, writer) { - var p = new BufferWriter(writer); +function frameItems(items) { + var p = new BufferWriter(); var i; - assert(items.length <= 50000); - p.writeVarint(items.length); for (i = 0; i < items.length; i++) items[i].toRaw(p); - if (!writer) - p = p.render(); + return p.render(); +} - return p; -}; +function framePing(nonce) { + var p = new BufferWriter(); + p.writeU64(nonce); + return p.render(); +} -/** - * Create an inv packet (without a header). - * @param {InvItem[]} items - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.inv = function inv(items, writer) { - return Framer._inv(items, writer); -}; - -/** - * Create a getdata packet (without a header). - * @param {InvItem[]} items - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.getData = function getData(items, writer) { - return Framer._inv(items, writer); -}; - -/** - * Create a notfound packet (without a header). - * @param {InvItem[]} items - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.notFound = function notFound(items, writer) { - return Framer._inv(items, writer); -}; - -/** - * Create a ping packet (without a header). - * @param {PingPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.ping = function ping(data) { - var p = new Buffer(8); - utils.writeU64(p, data.nonce, 0); - return p; -}; - -/** - * Create a pong packet (without a header). - * @param {PingPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.pong = function pong(data) { - var p = new Buffer(8); - utils.writeU64(p, data.nonce, 0); - return p; -}; - -/** - * Create a filterload packet (without a header). - * @param {Bloom} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.filterLoad = function filterLoad(filter, writer) { - return filter.toRaw(writer); -}; - -/** - * Create a getheaders packet (without a header). - * @param {GetBlocksPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.getHeaders = function getHeaders(data, writer) { - return data.toRaw(writer); -}; - -/** - * Create a getblocks packet (without a header). - * @param {GetBlocksPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.getBlocks = function getBlocks(data, writer) { - return data.toRaw(writer); -}; - -/** - * Serialize transaction without witness. - * @param {TX} tx - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.tx = function _tx(tx, writer) { - return tx.toNormal(writer); -}; - -/** - * Serialize transaction with witness. Calculates the witness - * size as it is framing (exposed on return value as `_witnessSize`). - * @param {TX} tx - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.witnessTX = function _witnessTX(tx, writer) { - return tx.toRaw(writer); -}; - -/** - * Serialize a block without witness data. - * @param {Block} block - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.block = function _block(block, writer) { - return block.toNormal(writer); -}; - -/** - * Serialize a block with witness data. Calculates the witness - * size as it is framing (exposed on return value as `_witnessSize`). - * @param {Block} block - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.witnessBlock = function _witnessBlock(block, writer) { - return block.toRaw(writer); -}; - -/** - * Serialize a merkle block. - * @param {MerkleBlock} block - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.merkleBlock = function _merkleBlock(block, writer) { - return block.toRaw(writer); -}; - -/** - * Serialize headers. - * @param {Headers[]} headers - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.headers = function _headers(headers, writer) { - var p = new BufferWriter(writer); - var i; - - p.writeVarint(headers.length); - - for (i = 0; i < headers.length; i++) - headers[i].toRaw(p); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Create a reject packet (without a header). - * @param {RejectPacket} details - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.reject = function _reject(reject, writer) { - return reject.toRaw(writer); -}; - -/** - * Create an addr packet (without a header). - * @param {NetworkAddress[]} hosts - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.addr = function addr(hosts, writer) { - var p = new BufferWriter(writer); +function frameAddr(hosts) { + var p = new BufferWriter(); var i; p.writeVarint(hosts.length); @@ -728,345 +489,20 @@ Framer.addr = function addr(hosts, writer) { for (i = 0; i < hosts.length; i++) hosts[i].toRaw(true, p); - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Create an alert packet (without a header). - * @param {AlertPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.alert = function _alert(alert, writer) { - return alert.toRaw(writer); -}; - -/** - * Create a mempool packet (without a header). - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.mempool = function mempool() { - return DUMMY; -}; - -/** - * Create a getaddr packet (without a header). - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.getAddr = function getAddr() { - return DUMMY; -}; - -/** - * Create a getutxos packet (without a header). - * @param {GetUTXOsPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.getUTXOs = function getUTXOs(data, writer) { - var p = new BufferWriter(writer); - var i; - - p.writeU8(data.mempool ? 1 : 0); - p.writeVarint(data.prevout.length); - - for (i = 0; i < data.prevout.length; i++) - data.prevout[i].toRaw(p); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Create a utxos packet (without a header). - * @param {UTXOsPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.UTXOs = function UTXOs(data, writer) { - var p = new BufferWriter(writer); - var i, coin, height, map; - - if (!data.map) { - assert(data.hits); - map = new Buffer((data.hits.length + 7) / 8 | 0); - for (i = 0; i < data.hits.length; i++) - map[i / 8 | 0] |= +data.hits[i] << (i % 8); - } else { - map = data.map; - } - - p.writeU32(data.height); - p.writeHash(data.tip); - p.writeVarBytes(map); - p.writeVarInt(data.coins.length); - - for (i = 0; i < data.coins.length; i++) { - coin = data.coins[i]; - height = coin.height; - - if (height === -1) - height = 0x7fffffff; - - p.writeU32(coin.version); - p.writeU32(height); - p.write64(coin.value); - p.writeVarBytes(coin.script.toRaw()); - } - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Create a submitorder packet (without a header). - * @param {SubmitOrderPacket} order - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.submitOrder = function submitOrder(order, writer) { - var p = new BufferWriter(writer); - - p.writeHash(order.hash); - order.tx.toRaw(p); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Create a checkorder packet (without a header). - * @param {SubmitOrderPacket} order - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.checkOrder = function checkOrder(order, writer) { - return Framer.submitOrder(order, writer); -}; - -/** - * Create a reply packet (without a header). - * @param {ReplyPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.reply = function reply(data, writer) { - var p = new BufferWriter(writer); - - p.writeHash(data.hash); - p.writeU32(data.code || 0); - - if (data.publicKey) - p.writeVarBytes(data.publicKey); - else - p.writeVarInt(0); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Create a sendheaders packet (without a header). - * @returns {Buffer} - */ - -Framer.sendHeaders = function sendHeaders() { - return DUMMY; -}; - -/** - * Create a havewitness packet (without a header). - * @returns {Buffer} - */ - -Framer.haveWitness = function haveWitness() { - return DUMMY; -}; - -/** - * Create a filteradd packet (without a header). - * @param {Object|Buffer} data - Data to be added to bloom filter. - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.filterAdd = function filterAdd(data, writer) { - var p = new BufferWriter(writer); - - p.writeVarBytes(data.data || data); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Create a filterclear packet (without a header). - * @returns {Buffer} - */ - -Framer.filterClear = function filterClear() { - return DUMMY; -}; - -/** - * Create a feefilter packet (without a header). - * @param {FeeFilterPacket} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.feeFilter = function feeFilter(data, writer) { - var p = new BufferWriter(writer); - - p.write64(data.rate); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Create an encinit packet (without a header). - * @param {BIP151} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.encinit = function encinit(data, writer) { - if (writer) { - writer.writeBytes(data); - return writer; - } - return data; -}; - -/** - * Create an encinit packet (without a header). - * @param {BIP151} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.encack = function encack(data, writer) { - if (writer) { - writer.writeBytes(data); - return writer; - } - return data; -}; - -/** - * Create a sendcmpct packet (without a header). - * @param {SendCompact} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.sendCmpct = function sendCmpct(data, writer) { - return data.toRaw(writer); -}; - -/** - * Create a cmpctblock packet (without a header). - * @param {CompactBlock} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.cmpctBlock = function cmpctBlock(data, writer) { - return data.toRaw(false, writer); -}; - -/** - * Create a getblocktxn packet (without a header). - * @param {BlockTXRequest} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.getBlockTxn = function getBlockTxn(data, writer) { - return data.toRaw(writer); -}; - -/** - * Create a blocktxn packet (without a header). - * @param {BlockTX} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.blockTxn = function blockTxn(data, writer) { - return data.toRaw(false, writer); -}; - -/** - * Create an authchallenge packet (without a header). - * @param {Buffer} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.authChallenge = function authChallenge(data, writer) { - if (writer) { - writer.writeBytes(data); - return writer; - } - return data; -}; - -/** - * Create an authreply packet (without a header). - * @param {Buffer} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.authReply = function authReply(data, writer) { - if (writer) { - writer.writeBytes(data); - return writer; - } - return data; -}; - -/** - * Create an authPropose packet (without a header). - * @param {Buffer} data - * @param {BufferWriter?} writer - A buffer writer to continue writing from. - * @returns {Buffer} Returns a BufferWriter if `writer` was passed in. - */ - -Framer.authPropose = function authPropose(data, writer) { - if (writer) { - writer.writeBytes(data); - return writer; - } - return data; -}; + 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 0b0924b0..4222ea45 100644 --- a/lib/net/packets.js +++ b/lib/net/packets.js @@ -997,6 +997,240 @@ NetworkAddress.prototype.toRaw = function toRaw(full, writer) { return p; }; +/** + * Represents a `getutxos` packet. + * @exports GetUTXOsPacket + * @constructor + * @param {Boolean} mempool + * @param {Outpoint[]} prevout + * @property {Boolean} mempool + * @property {Outpoint[]} prevout + */ + +function GetUTXOsPacket(mempool, prevout) { + if (!(this instanceof GetUTXOsPacket)) + return new GetUTXOsPacket(mempool, prevout); + + this.mempool = mempool || false; + this.prevout = prevout || []; +} + +/** + * Serialize getutxos packet. + * @returns {Buffer} + */ + +GetUTXOsPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + var i; + + p.writeU8(this.mempool ? 1 : 0); + p.writeVarint(this.prevout.length); + + for (i = 0; i < this.prevout.length; i++) + this.prevout[i].toRaw(p); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +GetUTXOsPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + var i, count; + + this.mempool = p.readU8() === 1; + + count = p.readVarint(); + + for (i = 0; i < count; i++) + this.prevout.push(bcoin.outpoint.fromRaw(p)); + + return this; +}; + +/** + * Instantiate getutxos packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {GetBlocksPacket} + */ + +GetUTXOsPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new GetUTXOsPacket().fromRaw(data); +}; + +/** + * Represents a `utxos` packet. + * @exports UTXOsPacket + * @constructor + * @param {Object} options + * @property {Number} height + * @property {Hash} tip + * @property {Boolean[]} hits + * @property {Coin[]} coins + */ + +function UTXOsPacket(options) { + if (!(this instanceof UTXOsPacket)) + return new UTXOsPacket(options); + + this.height = -1; + this.tip = constants.NULL_HASH; + this.hits = []; + this.coins = []; + + if (options) + this.fromOptions(options); +} + +/** + * Inject properties from options. + * @private + * @param {Buffer} data + */ + +UTXOsPacket.prototype.fromOptions = function fromOptions(options) { + if (options.height != null) { + assert(utils.isNumber(options.height)); + this.height = options.height; + } + + if (options.tip) { + assert(typeof options.tip === 'string'); + this.tip = options.tip; + } + + if (options.hits) { + assert(Array.isArray(options.hits)); + this.hits = options.hits; + } + + if (options.coins) { + assert(Array.isArray(options.coins)); + this.coins = options.coins; + } + + return this; +}; + +/** + * Instantiate utxos packet from options. + * @param {Buffer} data + * @param {String?} enc + * @returns {GetBlocksPacket} + */ + +UTXOsPacket.fromOptions = function fromOptions(options) { + return new UTXOsPacket().fromOptions(options); +}; + +/** + * Serialize utxos packet. + * @returns {Buffer} + */ + +UTXOsPacket.prototype.toRaw = function toRaw(writer) { + var p = bcoin.writer(writer); + var map = new Buffer((this.hits.length + 7) / 8 | 0); + var i, bit, oct, coin, height; + + for (i = 0; i < this.hits.length; i++) { + bit = i % 8; + oct = (i - bit) / 8; + map[oct] |= +this.hits[i] << (7 - bit); + } + + p.writeU32(this.height); + p.writeHash(this.tip); + p.writeVarBytes(map); + p.writeVarInt(this.coins.length); + + for (i = 0; i < this.coins.length; i++) { + coin = this.coins[i]; + height = coin.height; + + if (height === -1) + height = 0x7fffffff; + + p.writeU32(coin.version); + p.writeU32(height); + p.write64(coin.value); + p.writeVarBytes(coin.script.toRaw()); + } + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +UTXOsPacket.prototype.fromRaw = function fromRaw(data) { + var p = bcoin.reader(data); + var i, bit, oct, coin, output; + var version, height, map, count; + + this.height = p.readU32(); + this.tip = p.readHash('hex'); + + map = p.readVarBytes(); + count = p.readVarint(); + + for (i = 0; i < map.length * 8; i++) { + bit = i % 8; + oct = (i - bit) / 8; + this.hits.push((map[oct] >> (7 - bit)) & 1); + } + + for (i = 0; i < count; i++) { + version = p.readU32(); + height = p.readU32(); + coin = new bcoin.coin(); + + if (height === 0x7fffffff) + height = -1; + + output = bcoin.output.fromRaw(p); + + coin.version = version; + coin.height = height; + coin.script = output.script; + coin.value = output.value; + + this.coins.push(coin); + } + + return this; +}; + +/** + * Instantiate utxos packet from serialized data. + * @param {Buffer} data + * @param {String?} enc + * @returns {GetBlocksPacket} + */ + +UTXOsPacket.fromRaw = function fromRaw(data, enc) { + if (typeof data === 'string') + data = new Buffer(data, enc); + return new UTXOsPacket().fromRaw(data); +}; + /* * Expose */ @@ -1005,4 +1239,6 @@ exports.VersionPacket = VersionPacket; exports.GetBlocksPacket = GetBlocksPacket; exports.AlertPacket = AlertPacket; exports.RejectPacket = RejectPacket; +exports.GetUTXOsPacket = GetUTXOsPacket; +exports.UTXOsPacket = UTXOsPacket; exports.NetworkAddress = NetworkAddress; diff --git a/lib/net/parser.js b/lib/net/parser.js index 82e4dfff..7b3f57e5 100644 --- a/lib/net/parser.js +++ b/lib/net/parser.js @@ -13,6 +13,7 @@ var utils = require('../utils/utils'); var assert = utils.assert; var constants = require('../protocol/constants'); var BufferReader = require('../utils/reader'); +var packets = require('./packets'); /** * Protocol packet parser @@ -191,378 +192,109 @@ Parser.prototype.parseHeader = function parseHeader(h) { return new Packet(cmd, this.waiting, chk); }; -/** - * Parse mempool packet. - * @param {Buffer|BufferReader} p - * @returns {Object} - */ - -Parser.parseMempool = function parseMempool(p) { - return {}; -}; - -/** - * Parse submitorder packet. - * @param {Buffer|BufferReader} p - * @returns {SubmitOrderPacket} - */ - -Parser.parseSubmitOrder = function parseSubmitOrder(p) { - p = new BufferReader(p); - return { - hash: p.readHash('hex'), - tx: Parser.parseTX(p) - }; -}; - -/** - * Parse checkorder packet. - * @param {Buffer|BufferReader} p - * @returns {SubmitOrderPacket} - */ - -Parser.parseCheckOrder = function parseCheckOrder(p) { - return Parser.parseSubmitOrder(p); -}; - -/** - * Parse reply packet. - * @param {Buffer|BufferReader} p - * @returns {ReplyPacket} - */ - -Parser.parseReply = function parseReply(p) { - p = new BufferReader(p); - return { - hash: p.readHash('hex'), - code: p.readU32(), - publicKey: p.readVarBytes() - }; -}; - -/** - * Parse sendheaders packet. - * @param {Buffer|BufferReader} p - * @returns {Object} - */ - -Parser.parseSendHeaders = function parseSendHeaders(p) { - return {}; -}; - -/** - * Parse havewitness packet. - * @param {Buffer|BufferReader} p - * @returns {Object} - */ - -Parser.parseHaveWitness = function parseHaveWitness(p) { - return {}; -}; - -/** - * Parse getaddr packet. - * @param {Buffer|BufferReader} p - * @returns {Object} - */ - -Parser.parseGetAddr = function parseGetAddr(p) { - return {}; -}; - -/** - * Parse filterload packet. - * @param {Buffer|BufferReader} p - * @returns {Bloom} - */ - -Parser.parseFilterLoad = function parseFilterLoad(p) { - return bcoin.bloom.fromRaw(p); -}; - -/** - * Parse filteradd packet. - * @param {Buffer|BufferReader} p - * @returns {FilterAddPacket} - */ - -Parser.parseFilterAdd = function parseFilterAdd(p) { - p = new BufferReader(p); - return { - data: p.readVarBytes() - }; -}; - -/** - * Parse filterclear packet. - * @param {Buffer|BufferReader} p - * @returns {Object} - */ - -Parser.parseFilterClear = function parseFilterClear(p) { - return {}; -}; - /** * Parse a payload. * @param {String} cmd - Packet type. - * @param {Buffer} p - Payload. + * @param {Buffer} data - Payload. * @returns {Object} */ -Parser.prototype.parsePayload = function parsePayload(cmd, p) { +Parser.prototype.parsePayload = function parsePayload(cmd, data) { switch (cmd) { case 'version': - return Parser.parseVersion(p); + return packets.VersionPacket.fromRaw(data); case 'verack': - return Parser.parseVerack(p); - case 'mempool': - return Parser.parseMempool(p); - case 'getaddr': - return Parser.parseGetAddr(p); - case 'submitorder': - return Parser.parseSubmitOrder(p); - case 'checkorder': - return Parser.parseCheckOrder(p); - case 'reply': - return Parser.parseReply(p); - case 'sendheaders': - return Parser.parseSendHeaders(p); - case 'havewitness': - return Parser.parseHaveWitness(p); - case 'filterload': - return Parser.parseFilterLoad(p); - case 'filteradd': - return Parser.parseFilterAdd(p); - case 'filterclear': - return Parser.parseFilterClear(p); - case 'inv': - return Parser.parseInv(p); - case 'getdata': - return Parser.parseGetData(p); - case 'notfound': - return Parser.parseNotFound(p); - case 'getheaders': - return Parser.parseGetHeaders(p); - case 'getblocks': - return Parser.parseGetBlocks(p); - case 'merkleblock': - return Parser.parseMerkleBlock(p); - case 'headers': - return Parser.parseHeaders(p); - case 'block': - return Parser.parseMemBlock(p); - case 'tx': - return Parser.parseTX(p); - case 'reject': - return Parser.parseReject(p); - case 'addr': - return Parser.parseAddr(p); + return null; case 'ping': - return Parser.parsePing(p); + return parsePing(data); case 'pong': - return Parser.parsePong(p); + return parsePing(data); case 'alert': - return Parser.parseAlert(p); + 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 Parser.parseGetUTXOs(p); + return packets.GetUTXOsPacket.fromRaw(data); case 'utxos': - return Parser.parseUTXOs(p); + return packets.UTXOsPacket.fromRaw(data); + case 'havewitness': + return null; case 'feefilter': - return Parser.parseFeeFilter(p); - case 'encinit': - return Parser.parseEncinit(p); - case 'encack': - return Parser.parseEncack(p); + return parseFeeFilter(data); case 'sendcmpct': - return Parser.parseSendCmpct(p); + return bcoin.bip152.SendCompact.fromRaw(data); case 'cmpctblock': - return Parser.parseCmpctBlock(p); + return bcoin.bip152.CompactBlock.fromRaw(data); case 'getblocktxn': - return Parser.parseGetBlockTxn(p); + return bcoin.bip152.TXRequest.fromRaw(data); case 'blocktxn': - return Parser.parseBlockTxn(p); + return bcoin.bip152.TXResponse.fromRaw(data); + case 'encinit': + assert(data.length >= 34); + return data; + case 'encack': + assert(data.length >= 33); + return data; case 'authchallenge': - return Parser.parseAuthChallenge(p); + assert(data.length >= 32); + return data; case 'authreply': - return Parser.parseAuthReply(p); + assert(data.length >= 64); + return data; case 'authpropose': - return Parser.parseAuthPropose(p); + assert(data.length >= 32); + return data; default: - return p; + return data; } }; -/** - * Parse getutxos packet. - * @param {Buffer|BufferReader} p - * @returns {GetUTXOsPacket} +/* + * Helpers */ -Parser.parseGetUTXOs = function parseGetUTXOs(p) { - var mempool, prevout, count, i; +function parsePing(data) { + var p = new BufferReader(data); + return p.readU64(); +} - p = new BufferReader(p); - - mempool = p.readU8() === 1; - prevout = []; - count = p.readVarint(); - - for (i = 0; i < count; i++) - prevout.push(bcoin.outpoint.fromRaw(p)); - - return { - mempool: mempool, - prevout: prevout - }; -}; - -/** - * Parse utxos packet. - * @param {Buffer|BufferReader} p - * @returns {UTXOsPacket} - */ - -Parser.parseUTXOs = function parseUTXOs(p) { - var chainHeight, tip, map, count, coins; - var coin, version, height, i, hits, j; - - p = new BufferReader(p); - - chainHeight = p.readU32(); - tip = p.readHash('hex'); - map = p.readVarBytes(); - count = p.readVarint(); - coins = []; - hits = []; - - for (i = 0; i < map.length; i++) { - for (j = 0; j < 8; j++) - hits.push((map[i] >> j) & 1); - } - - for (i = 0; i < count; i++) { - version = p.readU32(); - height = p.readU32(); - - if (height === 0x7fffffff) - height = -1; - - coin = bcoin.output.fromRaw(p); - coin.version = version; - coin.height = height; - coins.push(new bcoin.coin(coin)); - } - - return { - height: chainHeight, - tip: tip, - map: map, - coins: coins, - hits: hits - }; -}; - -/** - * Parse ping packet. - * @param {Buffer|BufferReader} p - * @returns {PingPacket} - */ - -Parser.parsePing = function parsePing(p) { - p = new BufferReader(p); - - return { - nonce: p.readU64() - }; -}; - -/** - * Parse pong packet. - * @param {Buffer|BufferReader} p - * @returns {PingPacket} - */ - -Parser.parsePong = function parsePong(p) { - p = new BufferReader(p); - - return { - nonce: p.readU64() - }; -}; - -/** - * Parse version packet. - * @param {Buffer|BufferReader} p - * @returns {VersionPacket} - */ - -Parser.parseVersion = function parseVersion(p) { - return bcoin.packets.VersionPacket.fromRaw(p); -}; - -/** - * Parse verack packet. - * @param {Buffer|BufferReader} p - * @returns {Object} - */ - -Parser.parseVerack = function parseVerack(p) { - return {}; -}; - -/** - * Parse notfound packet. - * @param {Buffer|BufferReader} p - * @returns {InvItem[]} - */ - -Parser.parseNotFound = function parseNotFound(p) { - return Parser.parseInv(p); -}; - -/** - * Parse getdata packet. - * @param {Buffer|BufferReader} p - * @returns {InvItem[]} - */ - -Parser.parseGetData = function parseGetData(p) { - return Parser.parseInv(p); -}; - -/** - * Parse getblocks packet. - * @param {Buffer|BufferReader} p - * @returns {GetBlocksPacket} - */ - -Parser.parseGetBlocks = function parseGetBlocks(p) { - return bcoin.packets.GetBlocksPacket.fromRaw(p); -}; - -/** - * Parse getheaders packet. - * @param {Buffer|BufferReader} p - * @returns {GetBlocksPacket} - */ - -Parser.parseGetHeaders = function parseGetHeaders(p) { - return bcoin.packets.GetBlocksPacket.fromRaw(p); -}; - -/** - * Parse inv packet. - * @param {Buffer|BufferReader} p - * @returns {InvItem[]} - */ - -Parser.parseInv = function parseInv(p) { +function parseInv(data) { + var p = new BufferReader(data); var items = []; var i, count; - p = new BufferReader(p); - count = p.readVarint(); assert(count <= 50000, 'Item count too high.'); @@ -571,174 +303,45 @@ Parser.parseInv = function parseInv(p) { items.push(bcoin.invitem.fromRaw(p)); return items; -}; +} -/** - * Parse merkleblock packet. - * @param {Buffer|BufferReader} p - * @returns {Block} - */ - -Parser.parseMerkleBlock = function parseMerkleBlock(p) { - return bcoin.merkleblock.fromRaw(p); -}; - -/** - * Parse headers packet. - * @param {Buffer|BufferReader} p - * @returns {Headers[]} - */ - -Parser.parseHeaders = function parseHeaders(p) { +function parseHeaders(data) { + var p = new BufferReader(data); var headers = []; var i, count; - p = new BufferReader(p); - count = p.readVarint(); for (i = 0; i < count; i++) headers.push(bcoin.headers.fromRaw(p)); return headers; -}; +} -/** - * Parse block packet. - * @param {Buffer|BufferReader} p - * @returns {Block} - */ - -Parser.parseBlock = function parseBlock(p) { - return bcoin.block.fromRaw(p); -}; - -/** - * Parse block packet. - * @param {Buffer|BufferReader} p - * @returns {Block} - */ - -Parser.parseMemBlock = function parseMemBlock(p) { - return bcoin.memblock.fromRaw(p); -}; - -/** - * Parse tx packet (will automatically switch to witness - * parsing if a witness transaction is detected). - * @param {Buffer|BufferReader} p - * @returns {TX} - */ - -Parser.parseTX = function parseTX(p) { - return bcoin.tx.fromRaw(p); -}; - -/** - * Parse reject packet. - * @param {Buffer|BufferReader} p - * @returns {RejectPacket} - */ - -Parser.parseReject = function parseReject(p) { - return bcoin.packets.RejectPacket.fromRaw(p); -}; - -/** - * Parse addr packet. - * @param {Buffer|BufferReader} p - * @returns {NetworkAddress[]} - */ - -Parser.parseAddr = function parseAddr(p) { +function parseAddr(data) { + var p = new BufferReader(data); var addrs = []; var i, count; - p = new BufferReader(p); - count = p.readVarint(); assert(count <= 10000, 'Too many addresses.'); for (i = 0; i < count; i++) - addrs.push(bcoin.packets.NetworkAddress.fromRaw(p, true)); + addrs.push(packets.NetworkAddress.fromRaw(p, true)); return addrs; -}; +} -/** - * Parse mempool packet. - * @param {Buffer|BufferReader} p - * @returns {Object} - */ +function parseFeeFilter(data) { + var p = new BufferReader(data); + return p.read64N(); +} -Parser.parseMempool = function parseMempool(p) { - return {}; -}; - -/** - * Parse alert packet. - * @param {Buffer|BufferReader} p - * @returns {AlertPacket} - */ - -Parser.parseAlert = function parseAlert(p) { - return bcoin.packets.AlertPacket.fromRaw(p); -}; - -/** - * Parse feefilter packet. - * @param {Buffer|BufferReader} p - * @returns {FeeFilterPacket} - */ - -Parser.parseFeeFilter = function parseFeeFilter(p) { - p = new BufferReader(p); - return { - rate: p.read64N() - }; -}; - -Parser.parseEncinit = function parseEncinit(p) { - // Handled elsewhere. - return p; -}; - -Parser.parseEncack = function parseEncack(p) { - // Handled elsewhere. - return p; -}; - -Parser.parseSendCmpct = function parseSendCmpct(p) { - return bcoin.bip152.SendCompact.fromRaw(p); -}; - -Parser.parseCmpctBlock = function parseCmpctBlock(p) { - return bcoin.bip152.CompactBlock.fromRaw(p); -}; - -Parser.parseGetBlockTxn = function parseGetBlockTxn(p) { - return bcoin.bip152.TXRequest.fromRaw(p); -}; - -Parser.parseBlockTxn = function parseBlockTxn(p) { - return bcoin.bip152.TXResponse.fromRaw(p); -}; - -Parser.parseAuthChallenge = function parseAuthChallenge(p) { - // Handled elsewhere. - return p; -}; - -Parser.parseAuthReply = function parseAuthReply(p) { - // Handled elsewhere. - return p; -}; - -Parser.parseAuthPropose = function parseAuthPropose(p) { - // Handled elsewhere. - return p; -}; +function parseFilterAdd(data) { + var p = new BufferReader(data); + return p.readVarBytes(); +} /** * Packet diff --git a/lib/net/peer.js b/lib/net/peer.js index ba244185..771619af 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -20,6 +20,7 @@ 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. @@ -648,9 +649,7 @@ Peer.prototype.sendPing = function sendPing() { this.lastPing = utils.ms(); this.challenge = utils.nonce(); - this.write(this.framer.ping({ - nonce: this.challenge - })); + this.write(this.framer.ping(this.challenge)); }; /** @@ -692,9 +691,7 @@ Peer.prototype.updateWatch = function updateWatch() { */ Peer.prototype.sendFeeRate = function sendFeeRate(rate) { - this.write(this.framer.feeFilter({ - rate: rate - })); + this.write(this.framer.feeFilter(rate)); }; /** @@ -892,52 +889,39 @@ Peer.prototype._onPacket = function onPacket(packet) { switch (cmd) { case 'version': return this._handleVersion(payload); - case 'inv': - return this._handleInv(payload); - case 'headers': - return this._handleHeaders(payload); - case 'getdata': - return this._handleGetData(payload); - case 'addr': - return this._handleAddr(payload); + case 'verack': + this.fire(cmd); + break; case 'ping': return this._handlePing(payload); case 'pong': return this._handlePong(payload); - case 'getaddr': - return this._handleGetAddr(payload); - case 'reject': - return this._handleReject(payload); case 'alert': return this._handleAlert(payload); - case 'getutxos': - return this._handleGetUTXOs(payload); - case 'utxos': - return this._handleUTXOs(payload); - case 'feefilter': - return this._handleFeeFilter(payload); + case '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); + break; case 'getblocks': return this._handleGetBlocks(payload); case 'getheaders': return this._handleGetHeaders(payload); - case 'mempool': - return this._handleMempool(payload); - case 'filterload': - return this._handleFilterLoad(payload); - case 'filteradd': - return this._handleFilterAdd(payload); - case 'filterclear': - return this._handleFilterClear(payload); + case 'headers': + return this._handleHeaders(payload); + case 'sendheaders': + this.preferHeaders = true; + this.fire(cmd); + break; case 'block': this.fire(cmd, payload); break; - case 'merkleblock': - payload.verifyPartial(); - this.lastBlock = payload; - this.waiting = payload.matches.length; - if (this.waiting === 0) - this._flushMerkle(); - break; case 'tx': if (this.lastBlock) { if (this.lastBlock.hasTX(payload)) { @@ -949,20 +933,41 @@ Peer.prototype._onPacket = function onPacket(packet) { } this.fire(cmd, payload); break; - case 'sendheaders': - this.preferHeaders = true; - this.fire(cmd, payload); + case 'reject': + return this._handleReject(payload); + case 'mempool': + return this._handleMempool(); + case 'filterload': + return this._handleFilterLoad(payload); + case 'filteradd': + return this._handleFilterAdd(payload); + case 'filterclear': + return this._handleFilterClear(); + case 'merkleblock': + payload.verifyPartial(); + this.lastBlock = payload; + this.waiting = payload.matches.length; + if (this.waiting === 0) + this._flushMerkle(); break; + case 'getutxos': + return this._handleGetUTXOs(payload); + case 'utxos': + return this._handleUTXOs(payload); case 'havewitness': this.haveWitness = true; - this.fire(cmd, payload); - break; - case 'verack': - this.fire(cmd, payload); - break; - case 'notfound': - this.fire(cmd, payload); + this.fire(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': @@ -973,14 +978,6 @@ Peer.prototype._onPacket = function onPacket(packet) { return this._handleAuthReply(payload); case 'authpropose': return this._handleAuthPropose(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); default: this.logger.warning('Unknown packet: %s.', cmd); this.fire(cmd, payload); @@ -1018,13 +1015,13 @@ Peer.prototype.fire = function fire(cmd, payload) { * @param {Object} */ -Peer.prototype._handleFilterLoad = function _handleFilterLoad(payload) { - if (!payload.isWithinConstraints()) { +Peer.prototype._handleFilterLoad = function _handleFilterLoad(filter) { + if (!filter.isWithinConstraints()) { this.setMisbehavior(100); return; } - this.spvFilter = payload; + this.spvFilter = filter; this.relay = true; }; @@ -1034,14 +1031,14 @@ Peer.prototype._handleFilterLoad = function _handleFilterLoad(payload) { * @param {Object} */ -Peer.prototype._handleFilterAdd = function _handleFilterAdd(payload) { - if (payload.data.length > constants.script.MAX_PUSH) { +Peer.prototype._handleFilterAdd = function _handleFilterAdd(data) { + if (data.length > constants.script.MAX_PUSH) { this.setMisbehavior(100); return; } if (this.spvFilter) - this.spvFilter.add(payload.data); + this.spvFilter.add(data); this.relay = true; }; @@ -1052,109 +1049,40 @@ Peer.prototype._handleFilterAdd = function _handleFilterAdd(payload) { * @param {Object} */ -Peer.prototype._handleFilterClear = function _handleFilterClear(payload) { +Peer.prototype._handleFilterClear = function _handleFilterClear() { if (this.spvFilter) this.spvFilter.reset(); this.relay = true; }; -/** - * Handle `utxos` packet. - * @private - * @param {Object} - */ - -Peer.prototype._handleUTXOs = function _handleUTXOs(payload) { - this.logger.debug('Received %d utxos (%s).', - payload.coins.length, this.hostname); - this.fire('utxos', payload); -}; - /** * Handle `feefilter` packet. * @private * @param {Object} */ -Peer.prototype._handleFeeFilter = function _handleFeeFilter(payload) { - if (!(payload.rate >= 0 && payload.rate <= constants.MAX_MONEY)) { +Peer.prototype._handleFeeFilter = function _handleFeeFilter(rate) { + if (!(rate >= 0 && rate <= constants.MAX_MONEY)) { this.setMisbehavior(100); return; } - this.feeRate = payload.rate; + this.feeRate = rate; - this.fire('feefilter', payload); + this.fire('feefilter', rate); }; /** - * Request UTXOs from peer. - * @param {Outpoint[]} outpoints - * @param {Function} callback - Returns [Error, {@link Coin}[]]. - */ - -Peer.prototype.getUTXOs = function getUTXOs(outpoints, callback) { - var self = this; - var reqs = []; - var coins = []; - var i; - - for (i = 0; i < outpoints.length; i += 15) - reqs.push(outpoints.slice(i, i + 15)); - - utils.forEachSerial(reqs, function(outpoints, next) { - self._getUTXOs(outpoints, function(err, coin) { - if (err) - return next(err); - - coins = coins.concat(coin); - - next(); - }); - }, function(err) { - if (err) - return callback(err); - - callback(null, coins); - }); -}; - -/** - * Send non-chunked getuxos to peer. + * Handle `utxos` packet. * @private - * @param {Outpoint[]} outpoints - * @param {Function} callback + * @param {Object} */ -Peer.prototype._getUTXOs = function _getUTXOs(outpoints, callback) { - var index = 0; - var i, prevout, coin; - - this.request('utxos', function(err, payload) { - if (err) - return callback(err); - - for (i = 0; i < payload.hits.length; i++) { - if (payload.hits[i]) { - prevout = outpoints[i]; - coin = payload.coins[index++]; - - if (!prevout || !coin) - return callback(new Error('Malformed utxos message.')); - - coin.hash = prevout.hash; - coin.index = prevout.index; - } - } - - callback(null, payload.coins); - }); - - this.write(this.framer.getUTXOs({ - mempool: true, - prevout: outpoints - })); +Peer.prototype._handleUTXOs = function _handleUTXOs(utxos) { + this.logger.debug('Received %d utxos (%s).', + utxos.coins.length, this.hostname); + this.fire('utxos', utxos); }; /** @@ -1164,9 +1092,8 @@ Peer.prototype._getUTXOs = function _getUTXOs(outpoints, callback) { Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) { var self = this; - var coins = []; - var hits = []; var unlock = this._lock(_handleGetUTXOs, [payload, utils.nop]); + var utxos; if (!unlock) return; @@ -1191,6 +1118,8 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) { if (payload.prevout.length > 15) return done(); + utxos = new GetUTXOsPacket(); + utils.forEachSerial(payload.prevout, function(prevout, next) { var hash = prevout.hash; var index = prevout.index; @@ -1200,13 +1129,13 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) { coin = self.mempool.getCoin(hash, index); if (coin) { - hits.push(1); - coins.push(coin); + utxos.hits.push(1); + utxos.coins.push(coin); return next(); } if (self.mempool.isSpent(hash, index)) { - hits.push(0); + utxos.hits.push(0); return next(); } } @@ -1216,12 +1145,12 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) { return next(err); if (!coin) { - hits.push(0); + utxos.hits.push(0); return next(); } - hits.push(1); - coins.push(coin); + utxos.hits.push(1); + utxos.coins.push(coin); next(); }); @@ -1229,12 +1158,10 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) { if (err) return done(err); - self.write(self.framer.UTXOs({ - height: self.chain.height, - tip: self.chain.tip.hash, - hits: hits, - coins: coins - })); + utxos.height = self.chain.height; + utxos.tip = self.chain.tip.hash; + + self.write(self.framer.UTXOs(utxos)); done(); }); @@ -1726,8 +1653,8 @@ Peer.prototype._handleAddr = function _handleAddr(addrs) { * @param {Object} */ -Peer.prototype._handlePing = function _handlePing(data) { - this.write(this.framer.pong(data)); +Peer.prototype._handlePing = function _handlePing(nonce) { + this.write(this.framer.pong(nonce)); this.fire('ping', this.minPing); }; @@ -1737,7 +1664,7 @@ Peer.prototype._handlePing = function _handlePing(data) { * @param {Object} */ -Peer.prototype._handlePong = function _handlePong(data) { +Peer.prototype._handlePong = function _handlePong(nonce) { var now = utils.ms(); if (!this.challenge) { @@ -1745,8 +1672,8 @@ Peer.prototype._handlePong = function _handlePong(data) { return; } - if (data.nonce.cmp(this.challenge) !== 0) { - if (data.nonce.cmpn(0) === 0) { + if (nonce.cmp(this.challenge) !== 0) { + if (nonce.cmpn(0) === 0) { this.logger.debug('Peer sent a zero nonce (%s).', this.hostname); this.challenge = null; return; @@ -1879,15 +1806,15 @@ Peer.prototype._handleHeaders = function _handleHeaders(headers) { * @param {Object} */ -Peer.prototype._handleReject = function _handleReject(payload) { +Peer.prototype._handleReject = function _handleReject(details) { var hash, entry; - this.fire('reject', payload); + this.fire('reject', details); - if (!payload.data) + if (!details.data) return; - hash = payload.data; + hash = details.data; entry = this.pool.invMap[hash]; if (!entry) @@ -1913,12 +1840,12 @@ Peer.prototype._handleAlert = function _handleAlert(alert) { * @param {Object} */ -Peer.prototype._handleEncinit = function _handleEncinit(payload) { +Peer.prototype._handleEncinit = function _handleEncinit(data) { if (!this.bip151) return; try { - this.bip151.encinit(payload); + this.bip151.encinit(data); } catch (e) { this._error(e); return; @@ -1926,7 +1853,7 @@ Peer.prototype._handleEncinit = function _handleEncinit(payload) { this.write(this.framer.encack(this.bip151.toEncack())); - this.fire('encinit', payload); + this.fire('encinit', data); }; /** @@ -1935,18 +1862,18 @@ Peer.prototype._handleEncinit = function _handleEncinit(payload) { * @param {Object} */ -Peer.prototype._handleEncack = function _handleEncack(payload) { +Peer.prototype._handleEncack = function _handleEncack(data) { if (!this.bip151) return; try { - this.bip151.encack(payload); + this.bip151.encack(data); } catch (e) { this._error(e); return; } - this.fire('encack', payload); + this.fire('encack', data); }; /** @@ -1955,14 +1882,14 @@ Peer.prototype._handleEncack = function _handleEncack(payload) { * @param {Object} */ -Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(payload) { +Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(data) { var result; if (!this.bip150) return; try { - result = this.bip150.challenge(payload); + result = this.bip150.challenge(data); } catch (e) { this._error(e); return; @@ -1970,7 +1897,7 @@ Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(payload) { this.write(this.framer.authReply(result)); - this.fire('authchallenge', payload); + this.fire('authchallenge', data); }; /** @@ -1979,14 +1906,14 @@ Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(payload) { * @param {Object} */ -Peer.prototype._handleAuthReply = function _handleAuthReply(payload) { +Peer.prototype._handleAuthReply = function _handleAuthReply(data) { var result; if (!this.bip150) return; try { - result = this.bip150.reply(payload); + result = this.bip150.reply(data); } catch (e) { this._error(e); return; @@ -1995,7 +1922,7 @@ Peer.prototype._handleAuthReply = function _handleAuthReply(payload) { if (result) this.write(this.framer.authPropose(result)); - this.fire('authreply', payload); + this.fire('authreply', data); }; /** @@ -2004,14 +1931,14 @@ Peer.prototype._handleAuthReply = function _handleAuthReply(payload) { * @param {Object} */ -Peer.prototype._handleAuthPropose = function _handleAuthPropose(payload) { +Peer.prototype._handleAuthPropose = function _handleAuthPropose(data) { var result; if (!this.bip150) return; try { - result = this.bip150.propose(payload); + result = this.bip150.propose(data); } catch (e) { this._error(e); return; @@ -2019,7 +1946,7 @@ Peer.prototype._handleAuthPropose = function _handleAuthPropose(payload) { this.write(this.framer.authChallenge(result)); - this.fire('authpropose', payload); + this.fire('authpropose', data); }; /** @@ -2028,21 +1955,25 @@ Peer.prototype._handleAuthPropose = function _handleAuthPropose(payload) { * @param {Object} */ -Peer.prototype._handleSendCmpct = function _handleSendCmpct(payload) { - if (payload.version !== 1) { +Peer.prototype._handleSendCmpct = function _handleSendCmpct(cmpct) { + if (cmpct.version !== 1) { // Ignore + this.logger.info('Peer request compact blocks version %d (%s).', + cmpct.version, this.hostname); return; } - if (payload.mode !== 0) { + if (cmpct.mode !== 0) { // Ignore (we can't do mode 1 yet). + this.logger.info('Peer request compact blocks mode %d (%s).', + cmpct.mode, this.hostname); return; } this.logger.info('Peer initialized compact blocks (%s).', this.hostname); - this.compactMode = payload; - this.fire('sendcmpct', payload); + this.compactMode = cmpct; + this.fire('sendcmpct', cmpct); }; /** @@ -2210,15 +2141,22 @@ Peer.prototype.sendAlert = function sendAlert(alert) { Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) { var packet = new GetBlocksPacket(locator, stop); + var height = -1; + var hash = null; this.logger.debug( 'Requesting headers packet from peer with getheaders (%s).', this.hostname); - this.logger.debug('Height: %d, Hash: %s, Stop: %s', - locator && locator.length ? this.chain._getCachedHeight(locator[0]) : -1, - locator && locator.length ? utils.revHex(locator[0]) : 0, - stop ? utils.revHex(stop) : 0); + if (packet.locator.length > 0) { + height = this.chain._getCachedHeight(packet.locator[0]); + hash = utils.revHex(packet.locator[0]); + } + + if (stop) + stop = utils.revHex(stop); + + this.logger.debug('Height: %d, Hash: %s, Stop: %s', height, hash, stop); this.write(this.framer.getHeaders(packet)); }; @@ -2231,15 +2169,22 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) { Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) { var packet = new GetBlocksPacket(locator, stop); + var height = -1; + var hash = null; this.logger.debug( 'Requesting inv packet from peer with getblocks (%s).', this.hostname); - this.logger.debug('Height: %d, Hash: %s, Stop: %s', - locator && locator.length ? this.chain._getCachedHeight(locator[0]) : -1, - locator && locator.length ? utils.revHex(locator[0]) : 0, - stop ? utils.revHex(stop) : 0); + if (packet.locator.length > 0) { + height = this.chain._getCachedHeight(packet.locator[0]); + hash = utils.revHex(packet.locator[0]); + } + + if (stop) + stop = utils.revHex(stop); + + this.logger.debug('Height: %d, Hash: %s, Stop: %s', height, hash, stop); this.write(this.framer.getBlocks(packet)); }; diff --git a/lib/net/pool.js b/lib/net/pool.js index 04048877..3548f18d 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -1709,60 +1709,6 @@ Pool.prototype.setFeeRate = function setFeeRate(rate) { this.peers.regular[i].sendFeeRate(rate); }; -/** - * Request UTXOs from peer. - * @param {Outpoint[]} outpoints - * @param {Function} callback - Returns [Error, {@link Coin}[]]. - */ - -Pool.prototype.getUTXOs = function getUTXOs(outpoints, callback) { - var i, peer; - - for (i = 0; i < this.peers.all.length; i++) { - peer = this.peers.all[i]; - - if (!peer.version) - continue; - - if (peer.version.services & constants.services.GETUXO) - break; - } - - if (i === this.peers.regular.length) - return utils.asyncify(callback)(new Error('No peer available.')); - - peer.getUTXOs(outpoints, callback); -}; - -/** - * Attempt to fill transaction using getutxos (note: unreliable). - * @param {TX} tx - * @param {Function} callback - Returns [Error, {@link TX}]. - */ - -Pool.prototype.fillCoins = function fillCoins(tx, callback) { - var outpoints = []; - var i, input; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - if (!input.coin) - outpoints.push(input.prevout); - } - - if (outpoints.length === 0) - return utils.asyncify(callback)(null, tx); - - this.getUTXOs(outpoints, function(err, coins) { - if (err) - return callback(err); - - tx.fillCoins(coins); - - callback(null, tx); - }); -}; - /** * Allocate a new loader host. * @returns {NetworkAddress}