/*! * framer.js - packet framer 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 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 * @exports Framer * @constructor * @param {Object} options */ function Framer(options) { if (!(this instanceof Framer)) return new Framer(options); if (!options) options = {}; this.options = options; this.network = bcoin.network.get(options.network); this.bip151 = options.bip151; } /** * Frame a payload with a header. * @param {String} cmd - Packet type. * @param {Buffer} payload * @returns {Buffer} Payload with header prepended. */ Framer.prototype.packet = function packet(cmd, payload, checksum) { var i, packet; assert(payload, 'No payload.'); if (this.bip151 && this.bip151.handshake) return this.bip151.packet(cmd, payload); assert(cmd.length < 12); assert(payload.length <= 0xffffffff); packet = new Buffer(24 + payload.length); // Magic value packet.writeUInt32LE(this.network.magic, 0, true); // Command packet.write(cmd, 4, 'ascii'); for (i = 4 + cmd.length; i < 16; i++) packet[i] = 0; // Payload length packet.writeUInt32LE(payload.length, 16, true); if (!checksum) checksum = crypto.hash256(payload); // Checksum checksum.copy(packet, 20, 0, 4); payload.copy(packet, 24); 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 */ module.exports = Framer;