/*!
 * packets.js - packets for bcoin
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

/**
 * @module net/packets
 */

var common = require('./common');
var util = require('../utils/util');
var assert = require('assert');
var Bloom = require('../utils/bloom');
var bip152 = require('./bip152');
var NetAddress = require('../primitives/netaddress');
var Headers = require('../primitives/headers');
var InvItem = require('../primitives/invitem');
var MemBlock = require('../primitives/memblock');
var MerkleBlock = require('../primitives/merkleblock');
var TX = require('../primitives/tx');
var BufferReader = require('../utils/reader');
var StaticWriter = require('../utils/staticwriter');
var encoding = require('../utils/encoding');
var DUMMY = new Buffer(0);

/**
 * Packet types.
 * @enum {Number}
 * @default
 */

exports.types = {
  VERSION: 0,
  VERACK: 1,
  PING: 2,
  PONG: 3,
  GETADDR: 4,
  ADDR: 5,
  INV: 6,
  GETDATA: 7,
  NOTFOUND: 8,
  GETBLOCKS: 9,
  GETHEADERS: 10,
  HEADERS: 11,
  SENDHEADERS: 12,
  BLOCK: 13,
  TX: 14,
  REJECT: 15,
  MEMPOOL: 16,
  FILTERLOAD: 17,
  FILTERADD: 18,
  FILTERCLEAR: 19,
  MERKLEBLOCK: 20,
  FEEFILTER: 21,
  SENDCMPCT: 22,
  CMPCTBLOCK: 23,
  GETBLOCKTXN: 24,
  BLOCKTXN: 25,
  ENCINIT: 26,
  ENCACK: 27,
  AUTHCHALLENGE: 28,
  AUTHREPLY: 29,
  AUTHPROPOSE: 30,
  UNKNOWN: 31,
  // Internal
  INTERNAL: 100,
  DATA: 101
};

/**
 * Packet types by value.
 * @const {Object}
 * @default
 */

exports.typesByVal = util.revMap(exports.types);

/**
 * Base Packet
 * @constructor
 */

function Packet() {}

Packet.prototype.type = -1;
Packet.prototype.cmd = '';

/**
 * Get serialization size.
 * @returns {Number}
 */

Packet.prototype.getSize = function getSize() {
  return 0;
};

/**
 * Serialize packet to writer.
 * @param {BufferWriter} bw
 */

Packet.prototype.toWriter = function toWriter(bw) {
  return bw;
};

/**
 * Serialize packet.
 * @returns {Buffer}
 */

Packet.prototype.toRaw = function toRaw() {
  return DUMMY;
};

/**
 * Inject properties from buffer reader.
 * @param {BufferReader} br
 */

Packet.prototype.fromReader = function fromReader(br) {
  return this;
};

/**
 * Inject properties from serialized data.
 * @param {Buffer} data
 */

Packet.prototype.fromRaw = function fromRaw(data) {
  return this;
};

/**
 * Version Packet
 * @constructor
 * @param {Object?} options
 * @param {Number} options.version - Protocol version.
 * @param {Number} options.services - Service bits.
 * @param {Number} options.ts - Timestamp of discovery.
 * @param {NetAddress} options.local - Our address.
 * @param {NetAddress} options.remote - Their address.
 * @param {Buffer} options.nonce
 * @param {String} options.agent - User agent string.
 * @param {Number} options.height - Chain height.
 * @param {Boolean} options.noRelay - Whether transactions
 * should be relayed immediately.
 * @property {Number} version - Protocol version.
 * @property {Number} services - Service bits.
 * @property {Number} ts - Timestamp of discovery.
 * @property {NetAddress} local - Our address.
 * @property {NetAddress} remote - Their address.
 * @property {Buffer} nonce
 * @property {String} agent - User agent string.
 * @property {Number} height - Chain height.
 * @property {Boolean} noRelay - Whether transactions
 * should be relayed immediately.
 */

function VersionPacket(options) {
  if (!(this instanceof VersionPacket))
    return new VersionPacket(options);

  Packet.call(this);

  this.version = common.PROTOCOL_VERSION;
  this.services = common.LOCAL_SERVICES;
  this.ts = util.now();
  this.recv = new NetAddress();
  this.from = new NetAddress();
  this.nonce = encoding.ZERO_U64;
  this.agent = common.USER_AGENT;
  this.height = 0;
  this.noRelay = false;

  if (options)
    this.fromOptions(options);
}

util.inherits(VersionPacket, Packet);

VersionPacket.prototype.cmd = 'version';
VersionPacket.prototype.type = exports.types.VERSION;

/**
 * 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.noRelay != null)
    this.noRelay = options.noRelay;

  return this;
};

/**
 * Instantiate version packet from options.
 * @param {Object} options
 * @returns {VersionPacket}
 */

VersionPacket.fromOptions = function fromOptions(options) {
  return new VersionPacket().fromOptions(options);
};

/**
 * Get serialization size.
 * @returns {Number}
 */

VersionPacket.prototype.getSize = function getSize() {
  var size = 0;
  size += 20;
  size += this.recv.getSize(false);
  size += this.from.getSize(false);
  size += 8;
  size += encoding.sizeVarString(this.agent, 'ascii');
  size += 5;
  return size;
};

/**
 * Write version packet to buffer writer.
 * @param {BufferWriter} bw
 */

VersionPacket.prototype.toWriter = function toWriter(bw) {
  bw.write32(this.version);
  bw.writeU32(this.services);
  bw.writeU32(0);
  bw.write64(this.ts);
  this.recv.toWriter(bw, false);
  this.from.toWriter(bw, false);
  bw.writeBytes(this.nonce);
  bw.writeVarString(this.agent, 'ascii');
  bw.write32(this.height);
  bw.writeU8(this.noRelay ? 0 : 1);
  return bw;
};

/**
 * Serialize version packet.
 * @returns {Buffer}
 */

VersionPacket.prototype.toRaw = function toRaw() {
  var size = this.getSize();
  return this.toWriter(new StaticWriter(size)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

VersionPacket.prototype.fromReader = function fromReader(br) {
  this.version = br.read32();
  this.services = br.readU32();

  // Note: hi service bits
  // are currently unused.
  br.readU32();

  this.ts = br.read53();
  this.recv.fromReader(br, false);

  if (br.left() > 0) {
    this.from.fromReader(br, false);
    this.nonce = br.readBytes(8);
  }

  if (br.left() > 0)
    this.agent = br.readVarString('ascii', 256);

  if (br.left() > 0)
    this.height = br.read32();

  if (br.left() > 0)
    this.noRelay = br.readU8() === 0;

  if (this.version === 10300)
    this.version = 300;

  assert(this.version >= 0, 'Version is negative.');
  assert(this.ts >= 0, 'Timestamp is negative.');

  // No idea why so many peers do this.
  if (this.height < 0)
    this.height = 0;

  return this;
};

/**
 * Instantiate version packet from buffer reader.
 * @param {BufferReader} br
 * @returns {VersionPacket}
 */

VersionPacket.fromReader = function fromReader(br) {
  return new VersionPacket().fromReader(br);
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

VersionPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * 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 `verack` packet.
 * @constructor
 */

function VerackPacket() {
  if (!(this instanceof VerackPacket))
    return new VerackPacket();

  Packet.call(this);
}

util.inherits(VerackPacket, Packet);

VerackPacket.prototype.cmd = 'verack';
VerackPacket.prototype.type = exports.types.VERACK;

/**
 * Instantiate verack packet from serialized data.
 * @param {BufferReader} br
 * @returns {VerackPacket}
 */

VerackPacket.fromReader = function fromReader(br) {
  return new VerackPacket().fromReader(br);
};

/**
 * Instantiate verack packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {VerackPacket}
 */

VerackPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new VerackPacket().fromRaw(data);
};

/**
 * Represents a `ping` packet.
 * @constructor
 * @param {BN?} nonce
 * @property {BN|null} nonce
 */

function PingPacket(nonce) {
  if (!(this instanceof PingPacket))
    return new PingPacket(nonce);

  Packet.call(this);

  this.nonce = nonce || null;
}

util.inherits(PingPacket, Packet);

PingPacket.prototype.cmd = 'ping';
PingPacket.prototype.type = exports.types.PING;

/**
 * Get serialization size.
 * @returns {Number}
 */

PingPacket.prototype.getSize = function getSize() {
  return this.nonce ? 8 : 0;
};

/**
 * Serialize ping packet.
 * @returns {Buffer}
 */

PingPacket.prototype.toRaw = function toRaw() {
  var size = this.getSize();
  return this.toWriter(new StaticWriter(size)).render();
};

/**
 * Serialize ping packet to writer.
 * @param {BufferWriter} bw
 */

PingPacket.prototype.toWriter = function toWriter(bw) {
  if (this.nonce)
    bw.writeBytes(this.nonce);
  return bw;
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

PingPacket.prototype.fromReader = function fromReader(br) {
  if (br.left() >= 8)
    this.nonce = br.readBytes(8);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

PingPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate ping packet from serialized data.
 * @param {BufferReader} br
 * @returns {PingPacket}
 */

PingPacket.fromReader = function fromReader(br) {
  return new PingPacket().fromRaw(br);
};

/**
 * Instantiate ping packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {PingPacket}
 */

PingPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new PingPacket().fromRaw(data);
};

/**
 * Represents a `pong` packet.
 * @constructor
 * @param {BN?} nonce
 * @property {BN} nonce
 */

function PongPacket(nonce) {
  if (!(this instanceof PongPacket))
    return new PongPacket(nonce);

  Packet.call(this);

  this.nonce = nonce || encoding.ZERO_U64;
}

util.inherits(PongPacket, Packet);

PongPacket.prototype.cmd = 'pong';
PongPacket.prototype.type = exports.types.PONG;

/**
 * Get serialization size.
 * @returns {Number}
 */

PongPacket.prototype.getSize = function getSize() {
  return 8;
};

/**
 * Serialize pong packet to writer.
 * @param {BufferWriter} bw
 */

PongPacket.prototype.toWriter = function toWriter(bw) {
  bw.writeBytes(this.nonce);
  return bw;
};

/**
 * Serialize pong packet.
 * @returns {Buffer}
 */

PongPacket.prototype.toRaw = function toRaw() {
  return this.toWriter(new StaticWriter(8)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

PongPacket.prototype.fromReader = function fromReader(br) {
  this.nonce = br.readBytes(8);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

PongPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate pong packet from buffer reader.
 * @param {BufferReader} br
 * @returns {VerackPacket}
 */

PongPacket.fromReader = function fromReader(br) {
  return new PongPacket().fromReader(br);
};

/**
 * Instantiate pong packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {VerackPacket}
 */

PongPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new PongPacket().fromRaw(data);
};

/**
 * Represents a `getaddr` packet.
 * @constructor
 */

function GetAddrPacket() {
  if (!(this instanceof GetAddrPacket))
    return new GetAddrPacket();

  Packet.call(this);
}

util.inherits(GetAddrPacket, Packet);

GetAddrPacket.prototype.cmd = 'getaddr';
GetAddrPacket.prototype.type = exports.types.GETADDR;

/**
 * Instantiate getaddr packet from buffer reader.
 * @param {BufferReader} br
 * @returns {GetAddrPacket}
 */

GetAddrPacket.fromReader = function fromReader(br) {
  return new GetAddrPacket().fromReader(br);
};

/**
 * Instantiate getaddr packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {GetAddrPacket}
 */

GetAddrPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new GetAddrPacket().fromRaw(data);
};

/**
 * Represents a `addr` packet.
 * @constructor
 * @param {(NetAddress[])?} items
 * @property {NetAddress[]} items
 */

function AddrPacket(items) {
  if (!(this instanceof AddrPacket))
    return new AddrPacket(items);

  Packet.call(this);

  this.items = items || [];
}

util.inherits(AddrPacket, Packet);

AddrPacket.prototype.cmd = 'addr';
AddrPacket.prototype.type = exports.types.ADDR;

/**
 * Get serialization size.
 * @returns {Number}
 */

AddrPacket.prototype.getSize = function getSize() {
  var size = 0;
  size += encoding.sizeVarint(this.items.length);
  size += 30 * this.items.length;
  return size;
};

/**
 * Serialize addr packet to writer.
 * @param {BufferWriter} bw
 */

AddrPacket.prototype.toWriter = function toWriter(bw) {
  var i, item;

  bw.writeVarint(this.items.length);

  for (i = 0; i < this.items.length; i++) {
    item = this.items[i];
    item.toWriter(bw, true);
  }

  return bw;
};

/**
 * Serialize addr packet.
 * @returns {Buffer}
 */

AddrPacket.prototype.toRaw = function toRaw() {
  var size = this.getSize();
  return this.toWriter(new StaticWriter(size)).render();
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

AddrPacket.prototype.fromRaw = function fromRaw(data) {
  var br = new BufferReader(data);
  var i, count;

  count = br.readVarint();

  for (i = 0; i < count; i++)
    this.items.push(NetAddress.fromReader(br, true));

  return this;
};

/**
 * Instantiate addr packet from Buffer reader.
 * @param {BufferReader} br
 * @returns {AddrPacket}
 */

AddrPacket.fromReader = function fromReader(br) {
  return new AddrPacket().fromReader(br);
};

/**
 * Instantiate addr packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {AddrPacket}
 */

AddrPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new AddrPacket().fromRaw(data);
};

/**
 * Represents a `inv` packet.
 * @constructor
 * @param {(InvItem[])?} items
 * @property {InvItem[]} items
 */

function InvPacket(items) {
  if (!(this instanceof InvPacket))
    return new InvPacket(items);

  Packet.call(this);

  this.items = items || [];
}

util.inherits(InvPacket, Packet);

InvPacket.prototype.cmd = 'inv';
InvPacket.prototype.type = exports.types.INV;

/**
 * Get serialization size.
 * @returns {Number}
 */

InvPacket.prototype.getSize = function getSize() {
  var size = 0;
  size += encoding.sizeVarint(this.items.length);
  size += 36 * this.items.length;
  return size;
};

/**
 * Serialize inv packet to writer.
 * @param {Buffer} bw
 */

InvPacket.prototype.toWriter = function toWriter(bw) {
  var i, item;

  assert(this.items.length <= 50000);

  bw.writeVarint(this.items.length);

  for (i = 0; i < this.items.length; i++) {
    item = this.items[i];
    item.toWriter(bw);
  }

  return bw;
};

/**
 * Serialize inv packet.
 * @returns {Buffer}
 */

InvPacket.prototype.toRaw = function toRaw() {
  var size = this.getSize();
  return this.toWriter(new StaticWriter(size)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

InvPacket.prototype.fromReader = function fromReader(br) {
  var i, count;

  count = br.readVarint();

  assert(count <= 50000, 'Inv item count too high.');

  for (i = 0; i < count; i++)
    this.items.push(InvItem.fromReader(br));

  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

InvPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate inv packet from buffer reader.
 * @param {BufferReader} br
 * @returns {InvPacket}
 */

InvPacket.fromReader = function fromReader(br) {
  return new InvPacket().fromRaw(br);
};

/**
 * Instantiate inv packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {InvPacket}
 */

InvPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new InvPacket().fromRaw(data);
};

/**
 * Represents a `getdata` packet.
 * @extends InvPacket
 * @constructor
 * @param {(InvItem[])?} items
 */

function GetDataPacket(items) {
  if (!(this instanceof GetDataPacket))
    return new GetDataPacket(items);

  InvPacket.call(this, items);
}

util.inherits(GetDataPacket, InvPacket);

GetDataPacket.prototype.cmd = 'getdata';
GetDataPacket.prototype.type = exports.types.GETDATA;

/**
 * Instantiate getdata packet from buffer reader.
 * @param {BufferReader} br
 * @returns {GetDataPacket}
 */

GetDataPacket.fromReader = function fromReader(br) {
  return new GetDataPacket().fromReader(br);
};

/**
 * Instantiate getdata packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {GetDataPacket}
 */

GetDataPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new GetDataPacket().fromRaw(data);
};

/**
 * Represents a `notfound` packet.
 * @extends InvPacket
 * @constructor
 * @param {(InvItem[])?} items
 */

function NotFoundPacket(items) {
  if (!(this instanceof NotFoundPacket))
    return new NotFoundPacket(items);

  InvPacket.call(this, items);
}

util.inherits(NotFoundPacket, InvPacket);

NotFoundPacket.prototype.cmd = 'notfound';
NotFoundPacket.prototype.type = exports.types.NOTFOUND;

/**
 * Instantiate notfound packet from buffer reader.
 * @param {BufferReader} br
 * @returns {NotFoundPacket}
 */

NotFoundPacket.fromReader = function fromReader(br) {
  return new NotFoundPacket().fromReader(br);
};

/**
 * Instantiate notfound packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {NotFoundPacket}
 */

NotFoundPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new NotFoundPacket().fromRaw(data);
};

/**
 * Represents a `getblocks` packet.
 * @constructor
 * @param {Hash[]} locator
 * @param {Hash?} stop
 * @property {Hash[]} locator
 * @property {Hash|null} stop
 */

function GetBlocksPacket(locator, stop) {
  if (!(this instanceof GetBlocksPacket))
    return new GetBlocksPacket(locator, stop);

  Packet.call(this);

  this.version = common.PROTOCOL_VERSION;
  this.locator = locator || [];
  this.stop = stop || null;
}

util.inherits(GetBlocksPacket, Packet);

GetBlocksPacket.prototype.cmd = 'getblocks';
GetBlocksPacket.prototype.type = exports.types.GETBLOCKS;

/**
 * Get serialization size.
 * @returns {Number}
 */

GetBlocksPacket.prototype.getSize = function getSize() {
  var size = 0;
  size += 4;
  size += encoding.sizeVarint(this.locator.length);
  size += 32 * this.locator.length;
  size += 32;
  return size;
};

/**
 * Serialize getblocks packet to writer.
 * @param {BufferWriter} bw
 */

GetBlocksPacket.prototype.toWriter = function toWriter(bw) {
  var i;

  assert(this.locator.length <= 50000, 'Too many block hashes.');

  bw.writeU32(this.version);
  bw.writeVarint(this.locator.length);

  for (i = 0; i < this.locator.length; i++)
    bw.writeHash(this.locator[i]);

  bw.writeHash(this.stop || encoding.ZERO_HASH);

  return bw;
};

/**
 * Serialize getblocks packet.
 * @returns {Buffer}
 */

GetBlocksPacket.prototype.toRaw = function toRaw() {
  var size = this.getSize();
  return this.toWriter(new StaticWriter(size)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

GetBlocksPacket.prototype.fromReader = function fromReader(br) {
  var i, count;

  this.version = br.readU32();

  count = br.readVarint();

  assert(count <= 50000, 'Too many block hashes.');

  for (i = 0; i < count; i++)
    this.locator.push(br.readHash('hex'));

  this.stop = br.readHash('hex');

  if (this.stop === encoding.NULL_HASH)
    this.stop = null;

  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

GetBlocksPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate getblocks packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {GetBlocksPacket}
 */

GetBlocksPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new GetBlocksPacket().fromRaw(data);
};

/**
 * Represents a `getheaders` packet.
 * @extends GetBlocksPacket
 * @constructor
 * @param {Hash[]} locator
 * @param {Hash?} stop
 */

function GetHeadersPacket(locator, stop) {
  if (!(this instanceof GetHeadersPacket))
    return new GetHeadersPacket(locator, stop);

  GetBlocksPacket.call(this, locator, stop);
}

util.inherits(GetHeadersPacket, GetBlocksPacket);

GetHeadersPacket.prototype.cmd = 'getheaders';
GetHeadersPacket.prototype.type = exports.types.GETHEADERS;

/**
 * Instantiate getheaders packet from buffer reader.
 * @param {BufferReader} br
 * @returns {GetHeadersPacket}
 */

GetHeadersPacket.fromReader = function fromReader(br) {
  return new GetHeadersPacket().fromReader(br);
};

/**
 * Instantiate getheaders packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {GetHeadersPacket}
 */

GetHeadersPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new GetHeadersPacket().fromRaw(data);
};

/**
 * Represents a `headers` packet.
 * @constructor
 * @param {(Headers[])?} items
 * @property {Headers[]} items
 */

function HeadersPacket(items) {
  if (!(this instanceof HeadersPacket))
    return new HeadersPacket(items);

  Packet.call(this);

  this.items = items || [];
}

util.inherits(HeadersPacket, Packet);

HeadersPacket.prototype.cmd = 'headers';
HeadersPacket.prototype.type = exports.types.HEADERS;

/**
 * Get serialization size.
 * @returns {Number}
 */

HeadersPacket.prototype.getSize = function getSize() {
  var size = 0;
  var i, item;

  size += encoding.sizeVarint(this.items.length);

  for (i = 0; i < this.items.length; i++) {
    item = this.items[i];
    size += item.getSize();
  }

  return size;
};

/**
 * Serialize headers packet to writer.
 * @param {BufferWriter} bw
 */

HeadersPacket.prototype.toWriter = function toWriter(bw) {
  var i, item;

  assert(this.items.length <= 2000, 'Too many headers.');

  bw.writeVarint(this.items.length);

  for (i = 0; i < this.items.length; i++) {
    item = this.items[i];
    item.toWriter(bw);
  }

  return bw;
};

/**
 * Serialize headers packet.
 * @returns {Buffer}
 */

HeadersPacket.prototype.toRaw = function toRaw() {
  var size = this.getSize();
  return this.toWriter(new StaticWriter(size)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

HeadersPacket.prototype.fromReader = function fromReader(br) {
  var count = br.readVarint();
  var i;

  assert(count <= 2000, 'Too many headers.');

  for (i = 0; i < count; i++)
    this.items.push(Headers.fromReader(br));

  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

HeadersPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate headers packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {VerackPacket}
 */

HeadersPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new HeadersPacket().fromRaw(data);
};

/**
 * Represents a `sendheaders` packet.
 * @constructor
 */

function SendHeadersPacket() {
  if (!(this instanceof SendHeadersPacket))
    return new SendHeadersPacket();

  Packet.call(this);
}

util.inherits(SendHeadersPacket, Packet);

SendHeadersPacket.prototype.cmd = 'sendheaders';
SendHeadersPacket.prototype.type = exports.types.SENDHEADERS;

/**
 * Instantiate sendheaders packet from buffer reader.
 * @param {BufferReader} br
 * @returns {SendHeadersPacket}
 */

SendHeadersPacket.fromReader = function fromReader(br) {
  return new SendHeadersPacket().fromReader(br);
};

/**
 * Instantiate sendheaders packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {SendHeadersPacket}
 */

SendHeadersPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new SendHeadersPacket().fromRaw(data);
};

/**
 * Represents a `block` packet.
 * @constructor
 * @param {Block|null} block
 * @param {Boolean?} witness
 * @property {Block} block
 * @property {Boolean} witness
 */

function BlockPacket(block, witness) {
  if (!(this instanceof BlockPacket))
    return new BlockPacket(block, witness);

  Packet.call(this);

  this.block = block || new MemBlock();
  this.witness = witness || false;
}

util.inherits(BlockPacket, Packet);

BlockPacket.prototype.cmd = 'block';
BlockPacket.prototype.type = exports.types.BLOCK;

/**
 * Get serialization size.
 * @returns {Number}
 */

BlockPacket.prototype.getSize = function getSize() {
  if (this.witness)
    return this.block.getSize();
  return this.block.getBaseSize();
};

/**
 * Serialize block packet to writer.
 * @param {BufferWriter} bw
 */

BlockPacket.prototype.toWriter = function toWriter(bw) {
  if (this.witness)
    return this.block.toWriter(bw);
  return this.block.toNormalWriter(bw);
};

/**
 * Serialize block packet.
 * @returns {Buffer}
 */

BlockPacket.prototype.toRaw = function toRaw() {
  if (this.witness)
    return this.block.toRaw();
  return this.block.toNormal();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

BlockPacket.prototype.fromReader = function fromReader(br) {
  this.block.fromReader(br);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

BlockPacket.prototype.fromRaw = function fromRaw(data) {
  this.block.fromRaw(data);
  return this;
};

/**
 * Instantiate block packet from buffer reader.
 * @param {BufferReader} br
 * @returns {BlockPacket}
 */

BlockPacket.fromReader = function fromReader(br) {
  return new BlockPacket().fromReader(br);
};

/**
 * Instantiate block packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {BlockPacket}
 */

BlockPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new BlockPacket().fromRaw(data);
};

/**
 * Represents a `tx` packet.
 * @constructor
 * @param {TX|null} tx
 * @param {Boolean?} witness
 * @property {TX} block
 * @property {Boolean} witness
 */

function TXPacket(tx, witness) {
  if (!(this instanceof TXPacket))
    return new TXPacket(tx, witness);

  Packet.call(this);

  this.tx = tx || new TX();
  this.witness = witness || false;
}

util.inherits(TXPacket, Packet);

TXPacket.prototype.cmd = 'tx';
TXPacket.prototype.type = exports.types.TX;

/**
 * Get serialization size.
 * @returns {Number}
 */

TXPacket.prototype.getSize = function getSize() {
  if (this.witness)
    return this.tx.getSize();
  return this.tx.getBaseSize();
};

/**
 * Serialize tx packet to writer.
 * @param {BufferWriter} bw
 */

TXPacket.prototype.toWriter = function toWriter(bw) {
  if (this.witness)
    return this.tx.toWriter(bw);
  return this.tx.toNormalWriter(bw);
};

/**
 * Serialize tx packet.
 * @returns {Buffer}
 */

TXPacket.prototype.toRaw = function toRaw() {
  if (this.witness)
    return this.tx.toRaw();
  return this.tx.toNormal();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

TXPacket.prototype.fromReader = function fromReader(br) {
  this.tx.fromRaw(br);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

TXPacket.prototype.fromRaw = function fromRaw(data) {
  this.tx.fromRaw(data);
  return this;
};

/**
 * Instantiate tx packet from buffer reader.
 * @param {BufferReader} br
 * @returns {TXPacket}
 */

TXPacket.fromReader = function fromReader(br) {
  return new TXPacket().fromReader(br);
};

/**
 * Instantiate tx packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {TXPacket}
 */

TXPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new TXPacket().fromRaw(data);
};

/**
 * Reject Packet
 * @constructor
 * @property {(Number|String)?} code - Code
 * (see {@link RejectPacket.codes}).
 * @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);

  Packet.call(this);

  this.message = '';
  this.code = RejectPacket.codes.INVALID;
  this.reason = '';
  this.hash = null;

  if (options)
    this.fromOptions(options);
}

util.inherits(RejectPacket, Packet);

/**
 * Reject codes. Note that `internal` and higher
 * are not meant for use on the p2p network.
 * @enum {Number}
 * @default
 */

RejectPacket.codes = {
  MALFORMED: 0x01,
  INVALID: 0x10,
  OBSOLETE: 0x11,
  DUPLICATE: 0x12,
  NONSTANDARD: 0x40,
  DUST: 0x41,
  INSUFFICIENTFEE: 0x42,
  CHECKPOINT: 0x43,
  // Internal codes (NOT FOR USE ON NETWORK)
  INTERNAL: 0x100,
  HIGHFEE: 0x100,
  ALREADYKNOWN: 0x101,
  CONFLICT: 0x102
};

/**
 * Reject codes by value.
 * @const {RevMap}
 */

RejectPacket.codesByVal = util.revMap(RejectPacket.codes);

RejectPacket.prototype.cmd = 'reject';
RejectPacket.prototype.type = exports.types.REJECT;

/**
 * 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 = RejectPacket.codes[code.toUpperCase()];

    if (code >= RejectPacket.codes.INTERNAL)
      code = RejectPacket.codes.INVALID;

    this.code = code;
  }

  if (options.reason)
    this.reason = options.reason;

  if (options.hash)
    this.hash = options.hash;

  return this;
};

/**
 * Instantiate reject packet from options.
 * @param {Object} options
 * @returns {RejectPacket}
 */

RejectPacket.fromOptions = function fromOptions(options) {
  return new RejectPacket().fromOptions(options);
};

/**
 * Get uint256le hash if present.
 * @returns {Hash}
 */

RejectPacket.prototype.rhash = function rhash() {
  return this.hash ? util.revHex(this.hash) : null;
};

/**
 * Get symbolic code.
 * @returns {String}
 */

RejectPacket.prototype.getCode = function getCode() {
  var code = RejectPacket.codesByVal[this.code];

  if (!code)
    return this.code + '';

  return code.toLowerCase();
};

/**
 * Get serialization size.
 * @returns {Number}
 */

RejectPacket.prototype.getSize = function getSize() {
  var size = 0;

  size += encoding.sizeVarString(this.message, 'ascii');
  size += 1;
  size += encoding.sizeVarString(this.reason, 'ascii');

  if (this.hash)
    size += 32;

  return size;
};

/**
 * Serialize reject packet to writer.
 * @param {BufferWriter} bw
 */

RejectPacket.prototype.toWriter = function toWriter(bw) {
  assert(this.message.length <= 12);
  assert(this.reason.length <= 111);

  bw.writeVarString(this.message, 'ascii');
  bw.writeU8(this.code);
  bw.writeVarString(this.reason, 'ascii');

  if (this.hash)
    bw.writeHash(this.hash);

  return bw;
};

/**
 * Serialize reject packet.
 * @returns {Buffer}
 */

RejectPacket.prototype.toRaw = function toRaw() {
  var size = this.getSize();
  return this.toWriter(new StaticWriter(size)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

RejectPacket.prototype.fromReader = function fromReader(br) {
  this.message = br.readVarString('ascii', 12);
  this.code = br.readU8();
  this.reason = br.readVarString('ascii', 111);

  switch (this.message) {
    case 'block':
    case 'tx':
      this.hash = br.readHash('hex');
      break;
    default:
      this.hash = null;
      break;
  }

  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

RejectPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate reject packet from buffer reader.
 * @param {BufferReader} br
 * @returns {RejectPacket}
 */

RejectPacket.fromReader = function fromReader(br) {
  return new RejectPacket().fromReader(br);
};

/**
 * 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)?} msg
 */

RejectPacket.prototype.fromReason = function fromReason(code, reason, msg) {
  if (typeof code === 'string')
    code = RejectPacket.codes[code.toUpperCase()];

  if (!code)
    code = RejectPacket.codes.INVALID;

  if (code >= RejectPacket.codes.INTERNAL)
    code = RejectPacket.codes.INVALID;

  this.message = '';
  this.code = code;
  this.reason = reason;

  if (msg) {
    this.message = (msg instanceof TX) ? 'tx' : 'block';
    this.hash = msg.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=' + (RejectPacket.codesByVal[this.code] || this.code)
    + ' reason=' + this.reason
    + ' hash=' + (this.hash ? util.revHex(this.hash) : null)
    + '>';
};

/**
 * Represents a `mempool` packet.
 * @constructor
 */

function MempoolPacket() {
  if (!(this instanceof MempoolPacket))
    return new MempoolPacket();

  Packet.call(this);
}

util.inherits(MempoolPacket, Packet);

MempoolPacket.prototype.cmd = 'mempool';
MempoolPacket.prototype.type = exports.types.MEMPOOL;

/**
 * Instantiate mempool packet from buffer reader.
 * @param {BufferReader} br
 * @returns {VerackPacket}
 */

MempoolPacket.fromReader = function fromReader(br) {
  return new MempoolPacket().fromReader(br);
};

/**
 * Instantiate mempool packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {VerackPacket}
 */

MempoolPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new MempoolPacket().fromRaw(data);
};

/**
 * Represents a `filterload` packet.
 * @constructor
 * @param {Bloom|null} filter
 */

function FilterLoadPacket(filter) {
  if (!(this instanceof FilterLoadPacket))
    return new FilterLoadPacket(filter);

  Packet.call(this);

  this.filter = filter || new Bloom();
}

util.inherits(FilterLoadPacket, Packet);

FilterLoadPacket.prototype.cmd = 'filterload';
FilterLoadPacket.prototype.type = exports.types.FILTERLOAD;

/**
 * Get serialization size.
 * @returns {Number}
 */

FilterLoadPacket.prototype.getSize = function getSize() {
  return this.filter.getSize();
};

/**
 * Serialize filterload packet to writer.
 * @param {BufferWriter} bw
 */

FilterLoadPacket.prototype.toWriter = function toWriter(bw) {
  return this.filter.toWriter(bw);
};

/**
 * Serialize filterload packet.
 * @returns {Buffer}
 */

FilterLoadPacket.prototype.toRaw = function toRaw() {
  return this.filter.toRaw();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

FilterLoadPacket.prototype.fromReader = function fromReader(br) {
  this.filter.fromReader(br);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

FilterLoadPacket.prototype.fromRaw = function fromRaw(data) {
  this.filter.fromRaw(data);
  return this;
};

/**
 * Instantiate filterload packet from buffer reader.
 * @param {BufferReader} br
 * @returns {FilterLoadPacket}
 */

FilterLoadPacket.fromReader = function fromReader(br) {
  return new FilterLoadPacket().fromReader(br);
};

/**
 * Instantiate filterload packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {FilterLoadPacket}
 */

FilterLoadPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new FilterLoadPacket().fromRaw(data);
};

/**
 * Ensure the filter is within the size limits.
 * @returns {Boolean}
 */

FilterLoadPacket.prototype.isWithinConstraints = function isWithinConstraints() {
  return this.filter.isWithinConstraints();
};

/**
 * Represents a `filteradd` packet.
 * @constructor
 * @param {Buffer?} data
 * @property {Buffer} data
 */

function FilterAddPacket(data) {
  if (!(this instanceof FilterAddPacket))
    return new FilterAddPacket(data);

  Packet.call(this);

  this.data = data || DUMMY;
}

util.inherits(FilterAddPacket, Packet);

FilterAddPacket.prototype.cmd = 'filteradd';
FilterAddPacket.prototype.type = exports.types.FILTERADD;

/**
 * Get serialization size.
 * @returns {Number}
 */

FilterAddPacket.prototype.getSize = function getSize() {
  return encoding.sizeVarBytes(this.data);
};

/**
 * Serialize filteradd packet to writer.
 * @returns {BufferWriter} bw
 */

FilterAddPacket.prototype.toWriter = function toWriter(bw) {
  bw.writeVarBytes(this.data);
  return bw;
};

/**
 * Serialize filteradd packet.
 * @returns {Buffer}
 */

FilterAddPacket.prototype.toRaw = function toRaw() {
  var size = this.getSize();
  return this.toWriter(new StaticWriter(size)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

FilterAddPacket.prototype.fromReader = function fromReader(br) {
  this.data = br.readVarBytes();
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

FilterAddPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate filteradd packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {FilterAddPacket}
 */

FilterAddPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new FilterAddPacket().fromRaw(data);
};

/**
 * Represents a `filterclear` packet.
 * @constructor
 */

function FilterClearPacket() {
  if (!(this instanceof FilterClearPacket))
    return new FilterClearPacket();

  Packet.call(this);
}

util.inherits(FilterClearPacket, Packet);

FilterClearPacket.prototype.cmd = 'filterclear';
FilterClearPacket.prototype.type = exports.types.FILTERCLEAR;

/**
 * Instantiate filterclear packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {FilterClearPacket}
 */

FilterClearPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new FilterClearPacket().fromRaw(data);
};

/**
 * Represents a `merkleblock` packet.
 * @constructor
 * @param {MerkleBlock?} block
 * @property {MerkleBlock} block
 */

function MerkleBlockPacket(block) {
  if (!(this instanceof MerkleBlockPacket))
    return new MerkleBlockPacket(block);

  Packet.call(this);

  this.block = block || new MerkleBlock();
}

util.inherits(MerkleBlockPacket, Packet);

MerkleBlockPacket.prototype.cmd = 'merkleblock';
MerkleBlockPacket.prototype.type = exports.types.MERKLEBLOCK;

/**
 * Get serialization size.
 * @returns {Number}
 */

MerkleBlockPacket.prototype.getSize = function getSize() {
  return this.block.getSize();
};

/**
 * Serialize merkleblock packet to writer.
 * @param {BufferWriter} bw
 */

MerkleBlockPacket.prototype.toWriter = function toWriter(bw) {
  return this.block.toWriter(bw);
};

/**
 * Serialize merkleblock packet.
 * @returns {Buffer}
 */

MerkleBlockPacket.prototype.toRaw = function toRaw() {
  return this.block.toRaw();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

MerkleBlockPacket.prototype.fromReader = function fromReader(br) {
  this.block.fromReader(br);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

MerkleBlockPacket.prototype.fromRaw = function fromRaw(data) {
  this.block.fromRaw(data);
  return this;
};

/**
 * Instantiate merkleblock packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {MerkleBlockPacket}
 */

MerkleBlockPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new MerkleBlockPacket().fromRaw(data);
};

/**
 * Represents a `feefilter` packet.
 * @constructor
 * @param {Rate?} rate
 * @property {Rate} rate
 */

function FeeFilterPacket(rate) {
  if (!(this instanceof FeeFilterPacket))
    return new FeeFilterPacket(rate);

  Packet.call(this);

  this.rate = rate || 0;
}

util.inherits(FeeFilterPacket, Packet);

FeeFilterPacket.prototype.cmd = 'feefilter';
FeeFilterPacket.prototype.type = exports.types.FEEFILTER;

/**
 * Get serialization size.
 * @returns {Number}
 */

FeeFilterPacket.prototype.getSize = function getSize() {
  return 8;
};

/**
 * Serialize feefilter packet to writer.
 * @param {BufferWriter} bw
 */

FeeFilterPacket.prototype.toWriter = function toWriter(bw) {
  bw.write64(this.rate);
  return bw;
};

/**
 * Serialize feefilter packet.
 * @returns {Buffer}
 */

FeeFilterPacket.prototype.toRaw = function toRaw() {
  return this.toWriter(new StaticWriter(8)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

FeeFilterPacket.prototype.fromReader = function fromReader(br) {
  this.rate = br.read64();
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

FeeFilterPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate feefilter packet from buffer reader.
 * @param {BufferReader} br
 * @returns {FeeFilterPacket}
 */

FeeFilterPacket.fromReader = function fromReader(br) {
  return new FeeFilterPacket().fromReader(br);
};

/**
 * Instantiate feefilter packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {FeeFilterPacket}
 */

FeeFilterPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new FeeFilterPacket().fromRaw(data);
};

/**
 * Represents a `sendcmpct` packet.
 * @constructor
 * @param {Number|null} mode
 * @param {Number|null} version
 * @property {Number} mode
 * @property {Number} version
 */

function SendCmpctPacket(mode, version) {
  if (!(this instanceof SendCmpctPacket))
    return new SendCmpctPacket(mode, version);

  Packet.call(this);

  this.mode = mode || 0;
  this.version = version || 1;
}

util.inherits(SendCmpctPacket, Packet);

SendCmpctPacket.prototype.cmd = 'sendcmpct';
SendCmpctPacket.prototype.type = exports.types.SENDCMPCT;

/**
 * Get serialization size.
 * @returns {Number}
 */

SendCmpctPacket.prototype.getSize = function getSize() {
  return 9;
};

/**
 * Serialize sendcmpct packet to writer.
 * @param {BufferWriter} bw
 */

SendCmpctPacket.prototype.toWriter = function toWriter(bw) {
  bw.writeU8(this.mode);
  bw.writeU64(this.version);
  return bw;
};

/**
 * Serialize sendcmpct packet.
 * @returns {Buffer}
 */

SendCmpctPacket.prototype.toRaw = function toRaw() {
  return this.toWriter(new StaticWriter(9)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

SendCmpctPacket.prototype.fromReader = function fromReader(br) {
  this.mode = br.readU8();
  this.version = br.readU53();
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

SendCmpctPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate sendcmpct packet from buffer reader.
 * @param {BufferReader} br
 * @returns {SendCmpctPacket}
 */

SendCmpctPacket.fromReader = function fromReader(br) {
  return new SendCmpctPacket().fromReader(br);
};

/**
 * Instantiate sendcmpct packet from buffer reader.
 * @param {BufferReader} br
 * @returns {SendCmpctPacket}
 */

SendCmpctPacket.fromReader = function fromReader(br) {
  return new SendCmpctPacket().fromReader(br);
};

/**
 * Instantiate sendcmpct packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {SendCmpctPacket}
 */

SendCmpctPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new SendCmpctPacket().fromRaw(data);
};

/**
 * Represents a `cmpctblock` packet.
 * @constructor
 * @param {Block|null} block
 * @param {Boolean|null} witness
 * @property {Block} block
 * @property {Boolean} witness
 */

function CmpctBlockPacket(block, witness) {
  if (!(this instanceof CmpctBlockPacket))
    return new CmpctBlockPacket(block, witness);

  Packet.call(this);

  this.block = block || new bip152.CompactBlock();
  this.witness = witness || false;
}

util.inherits(CmpctBlockPacket, Packet);

CmpctBlockPacket.prototype.cmd = 'cmpctblock';
CmpctBlockPacket.prototype.type = exports.types.CMPCTBLOCK;

/**
 * Serialize cmpctblock packet.
 * @returns {Buffer}
 */

CmpctBlockPacket.prototype.getSize = function getSize() {
  if (this.witness)
    return this.block.getSize(true);
  return this.block.getSize(false);
};

/**
 * Serialize cmpctblock packet to writer.
 * @param {BufferWriter} bw
 */

CmpctBlockPacket.prototype.toWriter = function toWriter(bw) {
  if (this.witness)
    return this.block.toWriter(bw);
  return this.block.toNormalWriter(bw);
};

/**
 * Serialize cmpctblock packet.
 * @returns {Buffer}
 */

CmpctBlockPacket.prototype.toRaw = function toRaw() {
  if (this.witness)
    return this.block.toRaw();
  return this.block.toNormal();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

CmpctBlockPacket.prototype.fromReader = function fromReader(br) {
  this.block.fromReader(br);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

CmpctBlockPacket.prototype.fromRaw = function fromRaw(data) {
  this.block.fromRaw(data);
  return this;
};

/**
 * Instantiate cmpctblock packet from buffer reader.
 * @param {BufferReader} br
 * @returns {CmpctBlockPacket}
 */

CmpctBlockPacket.fromReader = function fromReader(br) {
  return new CmpctBlockPacket().fromRaw(br);
};

/**
 * Instantiate cmpctblock packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {CmpctBlockPacket}
 */

CmpctBlockPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new CmpctBlockPacket().fromRaw(data);
};

/**
 * Represents a `getblocktxn` packet.
 * @constructor
 * @param {TXRequest?} request
 * @property {TXRequest} request
 */

function GetBlockTxnPacket(request) {
  if (!(this instanceof GetBlockTxnPacket))
    return new GetBlockTxnPacket(request);

  Packet.call(this);

  this.request = request || new bip152.TXRequest();
}

util.inherits(GetBlockTxnPacket, Packet);

GetBlockTxnPacket.prototype.cmd = 'getblocktxn';
GetBlockTxnPacket.prototype.type = exports.types.GETBLOCKTXN;

/**
 * Get serialization size.
 * @returns {Number}
 */

GetBlockTxnPacket.prototype.getSize = function getSize() {
  return this.request.getSize();
};

/**
 * Serialize getblocktxn packet to writer.
 * @param {BufferWriter} bw
 */

GetBlockTxnPacket.prototype.toWriter = function toWriter(bw) {
  return this.request.toWriter(bw);
};

/**
 * Serialize getblocktxn packet.
 * @returns {Buffer}
 */

GetBlockTxnPacket.prototype.toRaw = function toRaw() {
  return this.request.toRaw();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

GetBlockTxnPacket.prototype.fromReader = function fromReader(br) {
  this.request.fromReader(br);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

GetBlockTxnPacket.prototype.fromRaw = function fromRaw(data) {
  this.request.fromRaw(data);
  return this;
};

/**
 * Instantiate getblocktxn packet from buffer reader.
 * @param {BufferReader} br
 * @returns {GetBlockTxnPacket}
 */

GetBlockTxnPacket.fromReader = function fromReader(br) {
  return new GetBlockTxnPacket().fromReader(br);
};

/**
 * Instantiate getblocktxn packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {GetBlockTxnPacket}
 */

GetBlockTxnPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new GetBlockTxnPacket().fromRaw(data);
};

/**
 * Represents a `blocktxn` packet.
 * @constructor
 * @param {TXResponse?} response
 * @param {Boolean?} witness
 * @property {TXResponse} response
 * @property {Boolean} witness
 */

function BlockTxnPacket(response, witness) {
  if (!(this instanceof BlockTxnPacket))
    return new BlockTxnPacket(response, witness);

  Packet.call(this);

  this.response = response || new bip152.TXResponse();
  this.witness = witness || false;
}

util.inherits(BlockTxnPacket, Packet);

BlockTxnPacket.prototype.cmd = 'blocktxn';
BlockTxnPacket.prototype.type = exports.types.BLOCKTXN;

/**
 * Get serialization size.
 * @returns {Number}
 */

BlockTxnPacket.prototype.getSize = function getSize() {
  if (this.witness)
    return this.response.getSize(true);
  return this.response.getSize(false);
};

/**
 * Serialize blocktxn packet to writer.
 * @param {BufferWriter} bw
 */

BlockTxnPacket.prototype.toWriter = function toWriter(bw) {
  if (this.witness)
    return this.response.toWriter(bw);
  return this.response.toNormalWriter(bw);
};

/**
 * Serialize blocktxn packet.
 * @returns {Buffer}
 */

BlockTxnPacket.prototype.toRaw = function toRaw() {
  if (this.witness)
    return this.response.toRaw();
  return this.response.toNormal();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

BlockTxnPacket.prototype.fromReader = function fromReader(br) {
  this.response.fromReader(br);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

BlockTxnPacket.prototype.fromRaw = function fromRaw(data) {
  this.response.fromRaw(data);
  return this;
};

/**
 * Instantiate blocktxn packet from buffer reader.
 * @param {BufferReader} br
 * @returns {BlockTxnPacket}
 */

BlockTxnPacket.fromReader = function fromReader(br) {
  return new BlockTxnPacket().fromReader(br);
};

/**
 * Instantiate blocktxn packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {BlockTxnPacket}
 */

BlockTxnPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new BlockTxnPacket().fromRaw(data);
};

/**
 * Represents a `encinit` packet.
 * @constructor
 * @param {Buffer|null} publicKey
 * @param {Number|null} cipher
 * @property {Buffer} publicKey
 * @property {Number} cipher
 */

function EncinitPacket(publicKey, cipher) {
  if (!(this instanceof EncinitPacket))
    return new EncinitPacket(publicKey, cipher);

  Packet.call(this);

  this.publicKey = publicKey || encoding.ZERO_KEY;
  this.cipher = cipher || 0;
}

util.inherits(EncinitPacket, Packet);

EncinitPacket.prototype.cmd = 'encinit';
EncinitPacket.prototype.type = exports.types.ENCINIT;

/**
 * Get serialization size.
 * @returns {Number}
 */

EncinitPacket.prototype.getSize = function getSize() {
  return 34;
};

/**
 * Serialize encinit packet to writer.
 * @param {BufferWriter} bw
 */

EncinitPacket.prototype.toWriter = function toWriter(bw) {
  bw.writeBytes(this.publicKey);
  bw.writeU8(this.cipher);
  return bw;
};

/**
 * Serialize encinit packet.
 * @returns {Buffer}
 */

EncinitPacket.prototype.toRaw = function toRaw() {
  return this.toWriter(new StaticWriter(34)).render();
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

EncinitPacket.prototype.fromReader = function fromReader(br) {
  this.publicKey = br.readBytes(33);
  this.cipher = br.readU8();
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

EncinitPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate getblocks packet from buffer reader.
 * @param {BufferReader} br
 * @returns {EncinitPacket}
 */

EncinitPacket.fromReader = function fromReader(br) {
  return new EncinitPacket().fromReader(br);
};

/**
 * Instantiate getblocks packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {EncinitPacket}
 */

EncinitPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new EncinitPacket().fromRaw(data);
};

/**
 * Represents a `encack` packet.
 * @constructor
 * @param {Buffer?} publicKey
 * @property {Buffer} publicKey
 */

function EncackPacket(publicKey) {
  if (!(this instanceof EncackPacket))
    return new EncackPacket(publicKey);

  Packet.call(this);

  this.publicKey = publicKey || encoding.ZERO_KEY;
}

util.inherits(EncackPacket, Packet);

EncackPacket.prototype.cmd = 'encack';
EncackPacket.prototype.type = exports.types.ENCACK;

/**
 * Get serialization size.
 * @returns {Number}
 */

EncackPacket.prototype.getSize = function getSize() {
  return 33;
};

/**
 * Serialize encack packet to writer.
 * @param {BufferWriter} bw
 */

EncackPacket.prototype.toWriter = function toWriter(bw) {
  bw.writeBytes(this.publicKey);
  return bw;
};

/**
 * Serialize encack packet.
 * @returns {Buffer}
 */

EncackPacket.prototype.toRaw = function toRaw() {
  return this.publicKey;
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

EncackPacket.prototype.fromReader = function fromReader(br) {
  this.publicKey = br.readBytes(33);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

EncackPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate encack packet from buffer reader.
 * @param {BufferReader} br
 * @returns {EncackPacket}
 */

EncackPacket.fromReader = function fromReader(br) {
  return new EncackPacket().fromReader(br);
};

/**
 * Instantiate encack packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {EncackPacket}
 */

EncackPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new EncackPacket().fromRaw(data);
};

/**
 * Represents a `authchallenge` packet.
 * @constructor
 * @param {Buffer?} hash
 * @property {Buffer} hash
 */

function AuthChallengePacket(hash) {
  if (!(this instanceof AuthChallengePacket))
    return new AuthChallengePacket(hash);

  Packet.call(this);

  this.hash = hash || encoding.ZERO_HASH;
}

util.inherits(AuthChallengePacket, Packet);

AuthChallengePacket.prototype.cmd = 'authchallenge';
AuthChallengePacket.prototype.type = exports.types.AUTHCHALLENGE;

/**
 * Get serialization size.
 * @returns {Number}
 */

EncackPacket.prototype.getSize = function getSize() {
  return 32;
};

/**
 * Serialize authchallenge packet to writer.
 * @param {BufferWriter} bw
 */

AuthChallengePacket.prototype.toWriter = function toWriter(bw) {
  bw.writeBytes(this.hash);
  return bw;
};

/**
 * Serialize authchallenge packet.
 * @returns {Buffer}
 */

AuthChallengePacket.prototype.toRaw = function toRaw() {
  return this.hash;
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

AuthChallengePacket.prototype.fromReader = function fromReader(br) {
  this.hash = br.readHash();
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

AuthChallengePacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate authchallenge packet from buffer reader.
 * @param {BufferReader} br
 * @returns {AuthChallengePacket}
 */

AuthChallengePacket.fromReader = function fromReader(br) {
  return new AuthChallengePacket().fromReader(br);
};

/**
 * Instantiate authchallenge packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {AuthChallengePacket}
 */

AuthChallengePacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new AuthChallengePacket().fromRaw(data);
};

/**
 * Represents a `authreply` packet.
 * @constructor
 * @param {Buffer?} signature
 * @property {Buffer} signature
 */

function AuthReplyPacket(signature) {
  if (!(this instanceof AuthReplyPacket))
    return new AuthReplyPacket(signature);

  Packet.call(this);

  this.signature = signature || encoding.ZERO_SIG64;
}

util.inherits(AuthReplyPacket, Packet);

AuthReplyPacket.prototype.cmd = 'authreply';
AuthReplyPacket.prototype.type = exports.types.AUTHREPLY;

/**
 * Get serialization size.
 * @returns {Number}
 */

AuthReplyPacket.prototype.getSize = function getSize() {
  return 64;
};

/**
 * Serialize authreply packet to writer.
 * @param {BufferWriter} bw
 */

AuthReplyPacket.prototype.toWriter = function toWriter(bw) {
  bw.writeBytes(this.signature);
  return bw;
};

/**
 * Serialize authreply packet.
 * @returns {Buffer}
 */

AuthReplyPacket.prototype.toRaw = function toRaw() {
  return this.signature;
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

AuthReplyPacket.prototype.fromReader = function fromReader(br) {
  this.signature = br.readBytes(64);
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

AuthReplyPacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate authreply packet from buffer reader.
 * @param {BufferReader} br
 * @returns {AuthReplyPacket}
 */

AuthReplyPacket.fromReader = function fromReader(br) {
  return new AuthReplyPacket().fromReader(br);
};

/**
 * Instantiate authreply packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {AuthReplyPacket}
 */

AuthReplyPacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new AuthReplyPacket().fromRaw(data);
};

/**
 * Represents a `authpropose` packet.
 * @constructor
 * @param {Hash?} hash
 * @property {Hash} hash
 */

function AuthProposePacket(hash) {
  if (!(this instanceof AuthProposePacket))
    return new AuthProposePacket(hash);

  Packet.call(this);

  this.hash = hash || encoding.ZERO_HASH;
}

util.inherits(AuthProposePacket, Packet);

AuthProposePacket.prototype.cmd = 'authpropose';
AuthProposePacket.prototype.type = exports.types.AUTHPROPOSE;

/**
 * Get serialization size.
 * @returns {Number}
 */

AuthProposePacket.prototype.getSize = function getSize() {
  return 32;
};

/**
 * Serialize authpropose packet to writer.
 * @param {BufferWriter} bw
 */

AuthProposePacket.prototype.toWriter = function toWriter(bw) {
  bw.writeBytes(this.hash);
  return bw;
};

/**
 * Serialize authpropose packet.
 * @returns {Buffer}
 */

AuthProposePacket.prototype.toRaw = function toRaw() {
  return this.hash;
};

/**
 * Inject properties from buffer reader.
 * @private
 * @param {BufferReader} br
 */

AuthProposePacket.prototype.fromReader = function fromReader(br) {
  this.hash = br.readHash();
  return this;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

AuthProposePacket.prototype.fromRaw = function fromRaw(data) {
  return this.fromReader(new BufferReader(data));
};

/**
 * Instantiate authpropose packet from buffer reader.
 * @param {BufferReader} br
 * @returns {AuthProposePacket}
 */

AuthProposePacket.fromReader = function fromReader(br) {
  return new AuthProposePacket().fromReader(br);
};

/**
 * Instantiate authpropose packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {AuthProposePacket}
 */

AuthProposePacket.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new AuthProposePacket().fromRaw(data);
};

/**
 * Represents an unknown packet.
 * @constructor
 * @param {String|null} cmd
 * @param {Buffer|null} data
 * @property {String} cmd
 * @property {Buffer} data
 */

function UnknownPacket(cmd, data) {
  if (!(this instanceof UnknownPacket))
    return new UnknownPacket(cmd, data);

  Packet.call(this);

  this.cmd = cmd;
  this.data = data;
}

util.inherits(UnknownPacket, Packet);

UnknownPacket.prototype.type = exports.types.UNKNOWN;

/**
 * Get serialization size.
 * @returns {Number}
 */

UnknownPacket.prototype.getSize = function getSize() {
  return this.data.length;
};

/**
 * Serialize unknown packet to writer.
 * @param {BufferWriter} bw
 */

UnknownPacket.prototype.toWriter = function toWriter(bw) {
  bw.writeBytes(this.data);
  return bw;
};

/**
 * Serialize unknown packet.
 * @returns {Buffer}
 */

UnknownPacket.prototype.toRaw = function toRaw() {
  return this.data;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

UnknownPacket.prototype.fromRaw = function fromRaw(cmd, data) {
  assert(Buffer.isBuffer(data));
  this.cmd = cmd;
  this.data = data;
  return this;
};

/**
 * Instantiate unknown packet from serialized data.
 * @param {Buffer} data
 * @param {String?} enc
 * @returns {UnknownPacket}
 */

UnknownPacket.fromRaw = function fromRaw(cmd, data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new UnknownPacket().fromRaw(cmd, data);
};

/**
 * Parse a payload.
 * @param {String} cmd
 * @param {Buffer} data
 * @returns {Packet}
 */

exports.fromRaw = function fromRaw(cmd, data) {
  switch (cmd) {
    case 'version':
      return VersionPacket.fromRaw(data);
    case 'verack':
      return VerackPacket.fromRaw(data);
    case 'ping':
      return PingPacket.fromRaw(data);
    case 'pong':
      return PongPacket.fromRaw(data);
    case 'getaddr':
      return GetAddrPacket.fromRaw(data);
    case 'addr':
      return AddrPacket.fromRaw(data);
    case 'inv':
      return InvPacket.fromRaw(data);
    case 'getdata':
      return GetDataPacket.fromRaw(data);
    case 'notfound':
      return NotFoundPacket.fromRaw(data);
    case 'getblocks':
      return GetBlocksPacket.fromRaw(data);
    case 'getheaders':
      return GetHeadersPacket.fromRaw(data);
    case 'headers':
      return HeadersPacket.fromRaw(data);
    case 'sendheaders':
      return SendHeadersPacket.fromRaw(data);
    case 'block':
      return BlockPacket.fromRaw(data);
    case 'tx':
      return TXPacket.fromRaw(data);
    case 'reject':
      return RejectPacket.fromRaw(data);
    case 'mempool':
      return MempoolPacket.fromRaw(data);
    case 'filterload':
      return FilterLoadPacket.fromRaw(data);
    case 'filteradd':
      return FilterAddPacket.fromRaw(data);
    case 'filterclear':
      return FilterClearPacket.fromRaw(data);
    case 'merkleblock':
      return MerkleBlockPacket.fromRaw(data);
    case 'feefilter':
      return FeeFilterPacket.fromRaw(data);
    case 'sendcmpct':
      return SendCmpctPacket.fromRaw(data);
    case 'cmpctblock':
      return CmpctBlockPacket.fromRaw(data);
    case 'getblocktxn':
      return GetBlockTxnPacket.fromRaw(data);
    case 'blocktxn':
      return BlockTxnPacket.fromRaw(data);
    case 'encinit':
      return EncinitPacket.fromRaw(data);
    case 'encack':
      return EncackPacket.fromRaw(data);
    case 'authchallenge':
      return AuthChallengePacket.fromRaw(data);
    case 'authreply':
      return AuthReplyPacket.fromRaw(data);
    case 'authpropose':
      return AuthProposePacket.fromRaw(data);
    default:
      return UnknownPacket.fromRaw(cmd, data);
  }
};

/*
 * Expose
 */

exports.Packet = Packet;
exports.VersionPacket = VersionPacket;
exports.VerackPacket = VerackPacket;
exports.PingPacket = PingPacket;
exports.PongPacket = PongPacket;
exports.GetAddrPacket = GetAddrPacket;
exports.AddrPacket = AddrPacket;
exports.InvPacket = InvPacket;
exports.GetDataPacket = GetDataPacket;
exports.NotFoundPacket = NotFoundPacket;
exports.GetBlocksPacket = GetBlocksPacket;
exports.GetHeadersPacket = GetHeadersPacket;
exports.HeadersPacket = HeadersPacket;
exports.SendHeadersPacket = SendHeadersPacket;
exports.BlockPacket = BlockPacket;
exports.TXPacket = TXPacket;
exports.RejectPacket = RejectPacket;
exports.MempoolPacket = MempoolPacket;
exports.FilterLoadPacket = FilterLoadPacket;
exports.FilterAddPacket = FilterAddPacket;
exports.FilterClearPacket = FilterClearPacket;
exports.MerkleBlockPacket = MerkleBlockPacket;
exports.FeeFilterPacket = FeeFilterPacket;
exports.SendCmpctPacket = SendCmpctPacket;
exports.CmpctBlockPacket = CmpctBlockPacket;
exports.GetBlockTxnPacket = GetBlockTxnPacket;
exports.BlockTxnPacket = BlockTxnPacket;
exports.EncinitPacket = EncinitPacket;
exports.EncackPacket = EncackPacket;
exports.AuthChallengePacket = AuthChallengePacket;
exports.AuthReplyPacket = AuthReplyPacket;
exports.AuthProposePacket = AuthProposePacket;
exports.UnknownPacket = UnknownPacket;
