fcoin/lib/net/packets.js
2016-08-25 01:35:20 -07:00

1245 lines
26 KiB
JavaScript

/*!
* 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 bn = require('bn.js');
var IP = require('../utils/ip');
var assert = utils.assert;
/**
* Version Packet
* @constructor
* @exports VersionPacket
* @param {Object?} options
* @param {Number} options.version - Protocol version.
* @param {Number} options.services - Service bits.
* @param {Number} options.ts - Timestamp of discovery.
* @param {NetworkAddress} options.local - Our address.
* @param {NetworkAddress} options.remote - Their address.
* @param {BN} options.nonce
* @param {String} options.agent - User agent string.
* @param {Number} options.height - Chain height.
* @param {Boolean} options.relay - Whether transactions
* should be relayed immediately.
* @property {Number} version - Protocol version.
* @property {Number} services - Service bits.
* @property {Number} ts - Timestamp of discovery.
* @property {NetworkAddress} local - Our address.
* @property {NetworkAddress} remote - Their address.
* @property {BN} nonce
* @property {String} agent - User agent string.
* @property {Number} height - Chain height.
* @property {Boolean} relay - Whether transactions
* should be relayed immediately.
*/
function VersionPacket(options) {
if (!(this instanceof VersionPacket))
return new VersionPacket(options);
this.version = constants.VERSION;
this.services = constants.LOCAL_SERVICES;
this.ts = bcoin.now();
this.recv = new NetworkAddress();
this.from = new NetworkAddress();
this.nonce = new bn(0);
this.agent = constants.USER_AGENT;
this.height = 0;
this.relay = true;
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options.
* @private
* @param {Object} options
*/
VersionPacket.prototype.fromOptions = function fromOptions(options) {
if (options.version != null)
this.version = options.version;
if (options.services != null)
this.services = options.services;
if (options.ts != null)
this.ts = options.ts;
if (options.recv)
this.recv.fromOptions(options.recv);
if (options.from)
this.from.fromOptions(options.from);
if (options.nonce)
this.nonce = options.nonce;
if (options.agent)
this.agent = options.agent;
if (options.height != null)
this.height = options.height;
if (options.relay != null)
this.relay = options.relay;
return this;
};
/**
* Instantiate version packet from options.
* @param {Object} options
* @returns {VersionPacket}
*/
VersionPacket.fromOptions = function fromOptions(options) {
return new VersionPacket().fromOptions(options);
};
/**
* Serialize version packet.
* @returns {Buffer}
*/
VersionPacket.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
p.write32(this.version);
p.writeU64(this.services);
p.write64(this.ts);
this.recv.toRaw(false, p);
this.from.toRaw(false, p);
p.writeU64(this.nonce);
p.writeVarString(this.agent, 'ascii');
p.write32(this.height);
p.writeU8(this.relay ? 1 : 0);
if (!writer)
p = p.render();
return p;
};
/**
* Test whether the NETWORK service bit is set.
* @returns {Boolean}
*/
VersionPacket.prototype.hasNetwork = function hasNetwork() {
return (this.services & constants.services.NETWORK) !== 0;
};
/**
* Test whether the BLOOM service bit is set.
* @returns {Boolean}
*/
VersionPacket.prototype.hasBloom = function hasBloom() {
return this.version >= 70011
&& (this.services & constants.services.BLOOM) !== 0;
};
/**
* Test whether the GETUTXO service bit is set.
* @returns {Boolean}
*/
VersionPacket.prototype.hasUTXO = function hasUTXO() {
return (this.services & constants.services.GETUTXO) !== 0;
};
/**
* Test whether the WITNESS service bit is set.
* @returns {Boolean}
*/
VersionPacket.prototype.hasWitness = function hasWitness() {
return (this.services & constants.services.WITNESS) !== 0;
};
/**
* Test whether the protocol version supports getheaders.
* @returns {Boolean}
*/
VersionPacket.prototype.hasHeaders = function hasHeaders() {
return this.version >= 31800;
};
/**
* Test whether the protocol version supports bip152.
* @returns {Boolean}
*/
VersionPacket.prototype.hasCompact = function hasCompact() {
return this.version >= 70014;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
VersionPacket.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
this.version = p.read32();
this.services = p.readU53();
this.ts = p.read53();
this.recv.fromRaw(p, false);
if (p.left() > 0) {
this.from.fromRaw(p, false);
this.nonce = p.readU64();
}
if (p.left() > 0)
this.agent = p.readVarString('ascii', 256);
if (p.left() > 0)
this.height = p.read32();
if (p.left() > 0)
this.relay = p.readU8() === 1;
if (this.version === 10300)
this.version = 300;
assert(this.version >= 0, 'Version is negative.');
assert(this.ts >= 0, 'Timestamp is negative.');
// assert(this.height >= 0, 'Height is negative.');
return this;
};
/**
* Instantiate version packet from serialized data.
* @param {Buffer} data
* @param {String?} enc
* @returns {VersionPacket}
*/
VersionPacket.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new VersionPacket().fromRaw(data, enc);
};
/**
* 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);
this.version = constants.VERSION;
this.locator = locator || [];
this.stop = stop || null;
}
/**
* 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);
};
/**
* Alert Packet
* @exports AlertPacket
* @constructor
* @param {Object} options
* @property {Number} version
* @property {Number} relayUntil
* @property {Number} expiration
* @property {Number} id
* @property {Number} cancel
* @property {Number[]} cancels
* @property {Number} minVer
* @property {Number} maxVer
* @property {String[]} subVers
* @property {Number} priority
* @property {String} comment
* @property {String} statusBar
* @property {String?} reserved
* @property {Buffer?} signature - Payload signature.
*/
function AlertPacket(options) {
var time;
if (!(this instanceof AlertPacket))
return new AlertPacket(options);
time = bcoin.now() + 7 * 86400;
this.version = 1;
this.relayUntil = time;
this.expiration = time;
this.id = 1;
this.cancel = 0;
this.cancels = [];
this.minVer = 10000;
this.maxVer = constants.VERSION;
this.subVers = [];
this.priority = 100;
this.comment = '';
this.statusBar = '';
this.reserved = '';
this.signature = null;
this._payload = null;
this._hash = null;
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
AlertPacket.prototype.fromOptions = function fromOptions(options) {
if (options.version != null)
this.version = options.version;
if (options.relayUntil != null)
this.relayUntil = options.relayUntil;
if (options.expiration != null)
this.expiration = options.expiration;
if (options.id != null)
this.id = options.id;
if (options.cancel != null)
this.cancel = options.cancel;
if (options.cancels)
this.cancels = options.cancels;
if (options.minVer != null)
this.minVer = options.minVer;
if (options.maxVer != null)
this.maxVer = options.maxVer;
if (options.subVers)
this.subVers = options.subVers;
if (options.priority != null)
this.priority = options.priority;
if (options.comment != null)
this.comment = options.comment;
if (options.statusBar != null)
this.statusBar = options.statusBar;
if (options.reserved != null)
this.reserved = options.reserved;
this.signature = options.signature;
return this;
};
/**
* Instantiate alert packet from options.
* @param {Object} options
* @returns {AlertPacket}
*/
AlertPacket.fromOptions = function fromOptions(options) {
return new AlertPacket().fromOptions(options);
};
/**
* Get the hash256 of the alert payload.
* @param {String?} enc
* @returns {Hash}
*/
AlertPacket.prototype.hash = function hash(enc) {
if (!this._hash)
this._hash = utils.hash256(this.toPayload());
return enc === 'hex' ? this._hash.toString('hex') : this._hash;
};
/**
* Serialize the packet to its payload.
* @returns {Buffer}
*/
AlertPacket.prototype.toPayload = function toPayload() {
if (!this._payload)
this._payload = this.framePayload();
return this._payload;
};
/**
* Sign the alert packet payload.
* @param {Buffer} key - Private key.
*/
AlertPacket.prototype.sign = function sign(key) {
this.signature = bcoin.ec.sign(this.hash(), key);
};
/**
* Verify the alert packet.
* @param {Buffer} key - Public key.
* @returns {Boolean}
*/
AlertPacket.prototype.verify = function verify(key) {
return bcoin.ec.verify(this.hash(), this.signature, key);
};
/**
* Serialize the alert packet (includes payload _and_ signature).
* @returns {Buffer}
*/
AlertPacket.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
p.writeVarBytes(this.toPayload());
p.writeVarBytes(this.signature);
if (!writer)
p = p.render();
return p;
};
/**
* Serialize the alert packet payload.
* @private
* @returns {Buffer}
*/
AlertPacket.prototype.framePayload = function framePayload(writer) {
var p = bcoin.writer(writer);
var i;
p.write32(this.version);
p.write64(this.relayUntil);
p.write64(this.expiration);
p.write32(this.id);
p.write32(this.cancel);
p.writeVarint(this.cancels.length);
for (i = 0; i < this.cancels.length; i++)
p.write32(this.cancels[i]);
p.write32(this.minVer);
p.write32(this.maxVer);
p.writeVarint(this.subVers.length);
for (i = 0; i < this.subVers.length; i++)
p.writeVarString(this.subVers[i], 'ascii');
p.write32(this.priority);
p.writeVarString(this.comment, 'ascii');
p.writeVarString(this.statusBar, 'ascii');
p.writeVarString(this.reserved, 'ascii');
if (!writer)
p = p.render();
return p;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
AlertPacket.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
var i, count;
this._payload = p.readVarBytes();
this.signature = p.readVarBytes();
p = bcoin.reader(this._payload);
this.version = p.read32();
this.relayUntil = p.read53();
this.expiration = p.read53();
this.id = p.read32();
this.cancel = p.read32();
count = p.readVarint();
for (i = 0; i < count; i++)
this.cancels.push(p.read32());
this.minVer = p.read32();
this.maxVer = p.read32();
count = p.readVarint();
for (i = 0; i < count; i++)
this.subVers.push(p.readVarString('ascii'));
this.priority = p.read32();
this.comment = p.readVarString('ascii');
this.statusBar = p.readVarString('ascii');
this.reserved = p.readVarString('ascii');
return this;
};
/**
* Instantiate alert packet from serialized data.
* @param {Buffer} data
* @param {String?} enc
* @returns {AlertPacket}
*/
AlertPacket.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new AlertPacket().fromRaw(data, enc);
};
/**
* Reject Packet
* @exports RejectPacket
* @constructor
* @property {(Number|String)?} code - Code
* (see {@link constants.reject}).
* @property {String?} msg - Message.
* @property {String?} reason - Reason.
* @property {(Hash|Buffer)?} data - Transaction or block hash.
*/
function RejectPacket(options) {
if (!(this instanceof RejectPacket))
return new RejectPacket(options);
this.message = '';
this.code = constants.reject.INVALID;
this.reason = '';
this.data = null;
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
RejectPacket.prototype.fromOptions = function fromOptions(options) {
var code = options.code;
if (options.message)
this.message = options.message;
if (code != null) {
if (typeof code === 'string')
code = constants.reject[code.toUpperCase()];
if (code >= constants.reject.INTERNAL)
code = constants.reject.INVALID;
this.code = code;
}
if (options.reason)
this.reason = options.reason;
if (options.data)
this.data = options.data;
return this;
};
/**
* Instantiate reject packet from options.
* @param {Object} options
* @returns {RejectPacket}
*/
RejectPacket.fromOptions = function fromOptions(options) {
return new RejectPacket().fromOptions(options);
};
/**
* Serialize reject packet.
* @returns {Buffer}
*/
RejectPacket.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
assert(this.message.length <= 12);
assert(this.reason.length <= 111);
p.writeVarString(this.message, 'ascii');
p.writeU8(this.code);
p.writeVarString(this.reason, 'ascii');
if (this.data)
p.writeHash(this.data);
if (!writer)
p = p.render();
return p;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
RejectPacket.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
this.message = p.readVarString('ascii', 12);
this.code = p.readU8();
this.reason = p.readVarString('ascii', 111);
if (this.message === 'block' || this.message === 'tx')
this.data = p.readHash('hex');
else
this.data = null;
return this;
};
/**
* Instantiate reject packet from serialized data.
* @param {Buffer} data
* @param {String?} enc
* @returns {RejectPacket}
*/
RejectPacket.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new RejectPacket().fromRaw(data, enc);
};
/**
* Inject properties from reason message and object.
* @private
* @param {Number} code
* @param {String} reason
* @param {(TX|Block)?} obj
*/
RejectPacket.prototype.fromReason = function fromReason(code, reason, obj) {
if (typeof code === 'string')
code = constants.reject[code.toUpperCase()];
if (!code)
code = constants.reject.INVALID;
if (code >= constants.reject.INTERNAL)
code = constants.reject.INVALID;
this.message = '';
this.code = code;
this.reason = reason;
if (obj) {
this.message = (obj instanceof bcoin.tx) ? 'tx' : 'block';
this.data = obj.hash('hex');
}
return this;
};
/**
* Instantiate reject packet from reason message.
* @param {Number} code
* @param {String} reason
* @param {(TX|Block)?} obj
* @returns {RejectPacket}
*/
RejectPacket.fromReason = function fromReason(code, reason, obj) {
return new RejectPacket().fromReason(code, reason, obj);
};
/**
* Instantiate reject packet from verify error.
* @param {VerifyError} err
* @param {(TX|Block)?} obj
* @returns {RejectPacket}
*/
RejectPacket.fromError = function fromError(err, obj) {
return RejectPacket.fromReason(err.code, err.reason, obj);
};
/**
* Inspect reject packet.
* @returns {String}
*/
RejectPacket.prototype.inspect = function inspect() {
return '<Reject:'
+ ' msg=' + this.message
+ ' code=' + (constants.rejectByVal[this.code] || this.code)
+ ' reason=' + this.reason
+ ' data=' + this.data
+ '>';
};
/**
* 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;
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;
}
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;
};
/**
* Inspect the network address.
* @returns {Object}
*/
NetworkAddress.prototype.inspect = function inspect() {
return '<NetworkAddress:'
+ ' id=' + this.id
+ ' hostname=' + IP.hostname(this.host, this.port)
+ ' services=' + this.services.toString(2)
+ ' date=' + utils.date(this.ts)
+ '>';
};
/**
* 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();
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 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;
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;
};
/**
* 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
*/
exports.VersionPacket = VersionPacket;
exports.GetBlocksPacket = GetBlocksPacket;
exports.AlertPacket = AlertPacket;
exports.RejectPacket = RejectPacket;
exports.GetUTXOsPacket = GetUTXOsPacket;
exports.UTXOsPacket = UTXOsPacket;
exports.NetworkAddress = NetworkAddress;