compact block relay support.

This commit is contained in:
Christopher Jeffrey 2016-07-20 19:30:52 -07:00
parent a85f3edde5
commit 95067ba938
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
11 changed files with 601 additions and 195 deletions

View File

@ -17,6 +17,7 @@ var node = new bcoin.fullnode({
logFile: true,
db: 'leveldb',
prune: process.argv.indexOf('--prune') !== -1,
compact: process.argv.indexOf('--compact') !== -1,
useCheckpoints: process.argv.indexOf('--checkpoints') !== -1,
selfish: process.argv.indexOf('--selfish') !== -1,
headers: process.argv.indexOf('--headers') !== -1,

View File

@ -39,6 +39,7 @@ function CompactBlock(options) {
this.count = 0;
this.k0 = null;
this.k1 = null;
this.timeout = null;
if (options)
this.fromOptions(options);
@ -126,7 +127,7 @@ CompactBlock.fromRaw = function fromRaw(data, enc) {
return new CompactBlock().fromRaw(data);
};
CompactBlock.prototype.toRaw = function toRaw(writer) {
CompactBlock.prototype.toRaw = function toRaw(witness, writer) {
var p = bcoin.writer(writer);
var i, id, lo, hi, ptx;
@ -155,7 +156,10 @@ CompactBlock.prototype.toRaw = function toRaw(writer) {
for (i = 0; i < this.ptx.length; i++) {
ptx = this.ptx[i];
p.writeVarint2(ptx[0]);
ptx[1].toRaw(p);
if (witness)
ptx[1].toRaw(p);
else
ptx[1].toNormal(p);
}
if (!writer)
@ -353,6 +357,18 @@ CompactBlock.fromBlock = function fromBlock(block, nonce) {
return new CompactBlock().fromBlock(block, nonce);
};
CompactBlock.prototype.startTimeout = function startTimeout(time, callback) {
assert(this.timeout == null);
this.timeout = setTimeout(callback, time);
};
CompactBlock.prototype.stopTimeout = function stopTimeout() {
if (this.timeout != null) {
clearTimeout(this.timeout);
this.timeout = null;
}
};
/**
* Represents a BlockTransactionsRequest (bip152): `getblocktxn` packet.
* @see https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki
@ -527,10 +543,10 @@ BlockTX.prototype.toRaw = function toRaw(witness, writer) {
for (i = 0; i < this.txs.length; i++) {
tx = this.txs[i];
if (!witness)
tx.toNormal(p);
else
if (witness)
tx.toRaw(p);
else
tx.toNormal(p);
}
if (!writer)

View File

@ -148,6 +148,8 @@ function Environment() {
this.mtx = require('./mtx');
this.txdb = require('./txdb');
this.abstractblock = require('./abstractblock');
this.bip151 = require('./bip151');
this.bip152 = require('./bip152');
this.memblock = require('./memblock');
this.block = require('./block');
this.merkleblock = require('./merkleblock');

View File

@ -95,6 +95,7 @@ function Fullnode(options) {
witness: this.network.witness,
selfish: this.options.selfish,
headers: this.options.headers,
compact: this.options.compact,
proxyServer: this.options.proxyServer,
preferredSeed: this.options.preferredSeed,
spv: false

View File

@ -1148,7 +1148,7 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx) {
var prevout = {};
var i, hash, input, prev;
if (tx.getSize() > 5000) {
if (tx.getSize() > 99999) {
this.logger.debug('Ignoring large orphan: %s', tx.rhash);
this.emit('bad orphan', tx);
return;

View File

@ -106,6 +106,9 @@ function Peer(pool, options) {
this.syncSent = false;
this.loader = options.loader || false;
this._connectTimeout = null;
this.compactMode = null;
this.compactBlocks = {};
this.sentAddr = false;
this.challenge = null;
this.lastPong = -1;
@ -266,6 +269,10 @@ Peer.prototype._onConnect = function _onConnect() {
self.write(self.framer.haveWitness());
}
// We want compact blocks!
if (self.options.compact && self.version.hasCompact())
self.sendCompact();
// Find some more peers.
self.write(self.framer.getAddr());
@ -697,8 +704,17 @@ Peer.prototype.getData = function getData(items) {
for (i = 0; i < items.length; i++) {
item = items[i];
if (item.toInv)
item = item.toInv();
if (this.options.compact
&& this.compactMode
&& item.isBlock()
&& !item.hasWitness()) {
item.type = constants.inv.CMPCT_BLOCK;
}
data[i] = item;
}
@ -792,6 +808,18 @@ Peer.prototype._onPacket = function onPacket(packet) {
case 'notfound':
this.fire(cmd, payload);
break;
case 'encinit':
return this._handleEncinit(payload);
case 'encack':
return this._handleEncack(payload);
case 'sendcmpct':
return this._handleSendCmpct(payload);
case 'cmpctblock':
return this._handleCmpctBlock(payload);
case 'getblocktxn':
return this._handleGetBlockTxn(payload);
case 'blocktxn':
return this._handleBlockTxn(payload);
default:
this.logger.warning('Unknown packet: %s.', cmd);
this.fire(cmd, payload);
@ -1341,6 +1369,48 @@ Peer.prototype._handleMempool = function _handleMempool() {
});
};
/**
* Get a block/tx either from the broadcast map, mempool, or blockchain.
* @param {InvItem} item
* @param {Function} callback - Returns
* [Error, {@link Block}|{@link MempoolEntry}].
*/
Peer.prototype._getItem = function _getItem(item, callback) {
var entry = this.pool.inv.map[item.hash];
if (entry) {
this.logger.debug(
'Peer requested %s %s as a %s packet (%s).',
entry.type === constants.inv.TX ? 'tx' : 'block',
utils.revHex(entry.hash),
item.hasWitness() ? 'witness' : 'normal',
this.hostname);
entry.ack(peer);
if (entry.msg)
return callback(null, entry.msg);
}
if (this.options.selfish)
return callback();
if (item.isTX()) {
if (!this.mempool)
return callback();
return this.mempool.getEntry(item.hash, callback);
}
if (this.chain.db.options.spv)
return callback();
if (this.chain.db.options.prune)
return callback();
return this.chain.db.getBlock(item.hash, callback);
};
/**
* Handle `getdata` packet.
* @private
@ -1349,9 +1419,7 @@ Peer.prototype._handleMempool = function _handleMempool() {
Peer.prototype._handleGetData = function _handleGetData(items) {
var self = this;
var check = [];
var notfound = [];
var i, item, entry, witness;
var notFound = [];
var unlock = this.locker.lock(_handleGetData, [items]);
if (!unlock)
@ -1370,68 +1438,31 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
return done();
}
// Hit the broadcast queue first.
for (i = 0; i < items.length; i++) {
item = items[i];
entry = this.pool.inv.map[item.hash];
witness = (item.type & constants.WITNESS_MASK) !== 0;
utils.forEachSerial(items, function(item, next) {
var i, tx, block;
// If the item isn't present, queue it up
// to be checked in the mempool or chain.
if (!entry) {
check.push(item);
continue;
}
self._getItem(item, function(err, entry) {
if (err)
return next(err);
if ((item.type & ~constants.WITNESS_MASK) !== entry.type) {
self.logger.debug(
'Peer requested an existing item with the wrong type (%s).',
this.hostname);
continue;
}
this.logger.debug(
'Peer requested %s %s as a %s packet (%s).',
entry.type === constants.inv.TX ? 'tx' : 'block',
utils.revHex(entry.hash),
witness ? 'witness' : 'normal',
this.hostname);
// Try to send the data.
// If the item was announce-only (i.e. the data
// isn't actually contained in the broadcast
// item), check this on the mempool/chain.
if (!entry.send(this, witness))
check.push(item);
}
if (this.pool.options.selfish)
return done();
// Item wasn't found in broadcast queue.
// Check the mempool and chain.
utils.forEachSerial(check, function(item, next) {
var type = item.type & ~constants.WITNESS_MASK;
var witness = (item.type & constants.WITNESS_MASK) !== 0;
var hash = item.hash;
var i, tx, data;
if (type === constants.inv.TX) {
if (!self.mempool) {
notfound.push(new InvItem(constants.inv.TX, hash));
if (!entry) {
notFound.push(item);
return next();
}
return self.mempool.getEntry(hash, function(err, entry) {
if (err)
return next(err);
if (!entry) {
notfound.push(new InvItem(constants.inv.TX, hash));
if (item.isTX()) {
tx = entry.tx;
// Coinbases are an insta-ban from any node.
// This should technically never happen, but
// it's worth keeping here just in case. A
// 24-hour ban from any node is rough.
if (tx.isCoinbase()) {
notFound.push(item);
self.logger.warning('Failsafe: tried to relay a coinbase.');
return next();
}
tx = entry.tx;
// We should technically calculate this in
// the `mempool` handler, but it would be
// too slow.
@ -1440,105 +1471,93 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
return next();
}
data = witness
? self.framer.witnessTX(tx)
: self.framer.tx(tx);
if (item.hasWitness())
self.write(self.framer.witnessTX(tx));
else
self.write(self.framer.tx(tx));
self.write(data);
next();
});
}
if (type === constants.inv.BLOCK) {
if (self.chain.db.options.spv) {
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
if (self.chain.db.options.prune) {
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
return self.chain.db.getBlock(hash, function(err, block) {
if (err)
return next(err);
if (!block) {
notfound.push(new InvItem(constants.inv.BLOCK, hash));
block = entry;
switch (item.type) {
case constants.inv.BLOCK:
case constants.inv.WITNESS_BLOCK:
if (item.hasWitness())
self.write(self.framer.witnessBlock(block));
else
self.write(self.framer.block(block));
break;
case constants.inv.FILTERED_BLOCK:
case constants.inv.WITNESS_FILTERED_BLOCK:
if (!self.spvFilter) {
notFound.push(item);
return next();
}
block = block.toMerkle(self.spvFilter);
self.write(self.framer.merkleBlock(block));
for (i = 0; i < block.txs.length; i++) {
tx = block.txs[i];
if (item.hasWitness())
self.write(self.framer.witnessTX(tx));
else
self.write(self.framer.tx(tx));
}
break;
case constants.inv.CMPCT_BLOCK:
// Fallback to full block.
if (block.height < self.chain.tip.height - 10) {
self.write(self.framer.block(block));
break;
}
// Try again with a new nonce
// if we get a siphash collision.
for (;;) {
try {
block = bcoin.bip152.CompactBlock.fromBlock(block);
} catch (e) {
continue;
}
break;
}
self.write(self.framer.cmpctBlock(block));
break;
default:
self.logger.warning(
'Peer sent an unknown getdata type: %s (%s).',
item.type,
self.hostname);
notFound.push(item);
return next();
}
data = witness
? self.framer.witnessBlock(block)
: self.framer.block(block);
self.write(data);
if (hash === self.hashContinue) {
self.sendInv(new InvItem(constants.inv.BLOCK, self.chain.tip.hash));
self.hashContinue = null;
}
next();
});
}
if (type === constants.inv.FILTERED_BLOCK) {
if (self.chain.db.options.spv) {
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
if (self.chain.db.options.prune) {
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
if (item.hash === self.hashContinue) {
self.sendInv(new InvItem(constants.inv.BLOCK, self.chain.tip.hash));
self.hashContinue = null;
}
return self.chain.db.getBlock(hash, function(err, block) {
if (err)
return next(err);
if (!block) {
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
block = block.toMerkle(self.spvFilter);
self.write(self.framer.merkleBlock(block));
for (i = 0; i < block.txs.length; i++) {
tx = block.txs[i];
tx = witness
? self.framer.witnessTX(tx)
: self.framer.tx(tx);
self.write(tx);
}
if (hash === self.hashContinue) {
self.sendInv(new InvItem(constants.inv.BLOCK, self.chain.tip.hash));
self.hashContinue = null;
}
next();
});
}
notfound.push(new InvItem(type, hash));
return next();
next();
});
}, function(err) {
if (err)
self.emit('error', err);
return done(err);
self.logger.debug(
'Served %d items with getdata (notfound=%d) (%s).',
items.length - notfound.length,
notfound.length,
items.length - notFound.length,
notFound.length,
self.hostname);
if (notfound.length > 0)
self.write(self.framer.notFound(notfound));
if (notFound.length > 0)
self.write(self.framer.notFound(notFound));
done();
});
@ -1628,6 +1647,13 @@ Peer.prototype._handleGetAddr = function _handleGetAddr() {
if (this.pool.options.selfish)
return;
if (this.sentAddr) {
this.logger.debug('Ignoring repeated getaddr (%s).', this.hostname);
return;
}
this.sentAddr = true;
for (i = 0; i < this.pool.hosts.length; i++) {
host = this.pool.hosts[i];
@ -1746,6 +1772,188 @@ Peer.prototype._handleAlert = function _handleAlert(alert) {
this.fire('alert', alert);
};
/**
* Handle `encinit` packet.
* @private
* @param {Object}
*/
Peer.prototype._handleEncinit = function _handleEncinit(payload) {
this.fire('encinit', payload);
};
/**
* Handle `encack` packet.
* @private
* @param {Object}
*/
Peer.prototype._handleEncack = function _handleEncack(payload) {
this.fire('encack', payload);
};
/**
* Handle `sendcmpct` packet.
* @private
* @param {Object}
*/
Peer.prototype._handleSendCmpct = function _handleSendCmpct(payload) {
if (payload.version !== 1) {
// Ignore
return;
}
if (payload.mode !== 0) {
// Ignore (we can't do mode 1 yet).
return;
}
this.compactMode = payload;
this.fire('sendcmpct', payload);
};
/**
* Handle `cmpctblock` packet.
* @private
* @param {Object}
*/
Peer.prototype._handleCmpctBlock = function _handleCmpctBlock(block) {
var self = this;
var hash = block.hash('hex');
function done(err) {
if (err) {
self.emit('error', err);
return;
}
self.fire('cmpctblock', block);
}
if (!this.options.compact) {
this.logger.info('Peer sent unsolicited cmpctblock (%s).', this.hostname);
return;
}
if (!this.mempool) {
this.logger.warning('Requesting compact blocks without a mempool!');
return done();
}
if (this.compactBlocks[hash]) {
this.logger.debug(
'Peer sent us a duplicate compact block (%s).',
this.hostname);
return done();
}
// Sort of a lock too.
this.compactBlocks[hash] = block;
block.fillMempool(this.mempool, function(err, result) {
if (err)
return done(err);
if (result) {
delete self.compactBlocks[hash];
self.emit('block', block.toBlock());
return;
}
self.write(self.framer.getBlockTxn(block.toRequest()));
block.startTimeout(10000, function() {
self.logger.debug(
'Compact block timed out: %s (%s).',
block.rhash, self.hostname);
delete self.compactBlocks[hash];
});
});
};
/**
* Handle `getblocktxn` packet.
* @private
* @param {Object}
*/
Peer.prototype._handleGetBlockTxn = function _handleGetBlockTxn(payload) {
var self = this;
var blockTX;
function done(err) {
if (err) {
self.emit('error', err);
return;
}
self.fire('blocktxn', payload);
}
if (this.chain.db.options.spv)
return done();
if (this.chain.db.options.prune)
return done();
if (this.options.selfish)
return done();
this.chain.db.getBlock(payload.hash, function(err, block) {
if (err)
return done(err);
if (!block) {
self.logger.info(
'Peer sent getblocktxn for non-existent block (%s).',
self.hostname);
self.setMisbehavior(100);
return done();
}
if (block.height < self.chain.tip.height - 15) {
self.logger.info(
'Peer sent a getblocktxn for a block > 15 deep (%s)',
self.hostname);
return done();
}
blockTX = bcoin.bip152.BlockTX.fromBlock(block, payload);
self.write(self.framer.blockTxn(blockTX));
done();
});
};
/**
* Handle `blocktxn` packet.
* @private
* @param {Object}
*/
Peer.prototype._handleBlockTxn = function _handleBlockTxn(payload) {
var block = this.compactBlocks[payload.hash];
if (!block) {
this.logger.info('Peer sent unsolicited blocktxn (%s).', this.hostname);
return;
}
this.fire('getblocktxn', payload);
block.stopTimeout();
delete this.compactBlocks[payload.hash];
if (!block.fillMissing(payload.txs)) {
this.setMisbehavior(100);
this.logger.info('Peer sent non-full blocktxn (%s).', this.hostname);
return;
}
this.emit('block', block.toBlock());
};
/**
* Send an `alert` to peer.
* @param {AlertPacket} alert
@ -1836,6 +2044,15 @@ Peer.prototype.sendReject = function sendReject(code, reason, obj) {
this.write(this.framer.reject(reject));
};
/**
* Send a `sendcmpct` packet.
*/
Peer.prototype.sendCompact = function sendCompact() {
var cmpct = new bcoin.bip152.SendCompact(0, 1);
this.write(this.framer.sendCmpct(cmpct));
};
/**
* Check whether the peer is misbehaving (banScore >= 100).
* @returns {Boolean}

View File

@ -572,7 +572,8 @@ Pool.prototype._addLoader = function _addLoader() {
loader: true,
network: true,
spv: this.options.spv,
witness: this.options.witness
witness: this.options.witness,
compact: this.options.compact
});
this.logger.info('Added loader peer (%s).', peer.hostname);
@ -973,7 +974,8 @@ Pool.prototype._createPeer = function _createPeer(options) {
network: options.network,
spv: options.spv,
witness: options.witness,
headers: this.options.headers
headers: this.options.headers,
compact: this.options.compact
});
peer.once('close', function() {
@ -1275,7 +1277,8 @@ Pool.prototype._addLeech = function _addLeech(socket) {
socket: socket,
network: false,
spv: false,
witness: false
witness: false,
compact: false
});
this.logger.info('Added leech peer (%s).', peer.hostname);
@ -1316,7 +1319,8 @@ Pool.prototype._addPeer = function _addPeer() {
host: host,
network: true,
spv: this.options.spv,
witness: this.options.witness
witness: this.options.witness,
compact: this.options.compact
});
this.peers.pending.push(peer);
@ -2046,7 +2050,7 @@ LoadRequest.prototype.destroy = function destroy() {
*/
LoadRequest.prototype._onTimeout = function _onTimeout() {
if (this.type === this.pool.block.type
if (this.type !== this.pool.tx.type
&& this.peer === this.pool.peers.load) {
this.pool.logger.debug(
'Loader took too long serving a block. Finding a new one.');
@ -2267,47 +2271,6 @@ BroadcastItem.prototype.finish = function finish(err) {
this.callback.length = 0;
};
/**
* Send the item to a peer.
* @param {Peer} peer
* @param {Boolean} witness - Whether to use the witness serialization.
* @returns {Boolean} Whether the data is available to be sent.
*/
BroadcastItem.prototype.send = function send(peer, witness) {
var data;
if (!this.msg) {
this.ack(peer);
return false;
}
if (this.type === constants.inv.TX) {
// Failsafe - we never want to relay coinbases.
// They are an insta-ban from any bitcoind node.
if (this.msg.isCoinbase()) {
peer.write(peer.framer.notFound([this.toInv()]));
this.pool.logger.warning('Failsafe: tried to relay a coinbase.');
this.finish(new Error('Coinbase.'));
return true;
}
data = witness
? peer.framer.witnessTX(this.msg)
: peer.framer.tx(this.msg);
} else {
data = witness
? peer.framer.witnessBlock(this.msg)
: peer.framer.block(this.msg);
}
peer.write(data);
this.ack(peer);
return true;
};
/**
* Handle an ack from a peer.
* @param {Peer} peer

View File

@ -27,7 +27,7 @@ exports.MIN_VERSION = 70001;
* @default
*/
exports.VERSION = 70012;
exports.VERSION = 70014;
/**
* Max message size (~4mb with segwit, formerly 2mb)

View File

@ -404,6 +404,66 @@ Framer.prototype.feeFilter = function feeFilter(options) {
return this.packet('feefilter', Framer.feeFilter(options));
};
/**
* Create an encinit packet with a header.
* @param {Object} options - See {@link Framer.encinit}.
* @returns {Buffer} encinit packet.
*/
Framer.prototype.encinit = function encinit(options) {
return this.packet('encinit', Framer.encinit(options));
};
/**
* Create an encack packet with a header.
* @param {Object} options - See {@link Framer.encack}.
* @returns {Buffer} encack packet.
*/
Framer.prototype.encack = function encack(options) {
return this.packet('encack', Framer.encack(options));
};
/**
* Create a sendcmpct packet with a header.
* @param {Object} options - See {@link Framer.sendCmpct}.
* @returns {Buffer} sendCmpct packet.
*/
Framer.prototype.sendCmpct = function sendCmpct(options) {
return this.packet('sendcmpct', Framer.sendCmpct(options));
};
/**
* Create a cmpctblock packet with a header.
* @param {Object} options - See {@link Framer.cmpctBlock}.
* @returns {Buffer} cmpctBlock packet.
*/
Framer.prototype.cmpctBlock = function cmpctBlock(options) {
return this.packet('cmpctblock', Framer.cmpctBlock(options));
};
/**
* Create a getblocktxn packet with a header.
* @param {Object} options - See {@link Framer.getBlockTxn}.
* @returns {Buffer} getBlockTxn packet.
*/
Framer.prototype.getBlockTxn = function getBlockTxn(options) {
return this.packet('getblocktxn', Framer.getBlockTxn(options));
};
/**
* Create a blocktxn packet with a header.
* @param {Object} options - See {@link Framer.blockTxn}.
* @returns {Buffer} blockTxn packet.
*/
Framer.prototype.blockTxn = function blockTxn(options) {
return this.packet('blocktxn', Framer.blockTxn(options));
};
/**
* Create a version packet (without a header).
* @param {VersionPacket} options
@ -868,6 +928,72 @@ Framer.feeFilter = function feeFilter(data, writer) {
return p;
};
/**
* Create an encinit packet (without a header).
* @param {BIP151} data
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.encinit = function encinit(data, writer) {
return data.toEncinit(writer);
};
/**
* Create an encinit packet (without a header).
* @param {BIP151} data
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.encack = function encack(data, writer) {
return data.toEncack(writer);
};
/**
* Create a sendcmpct packet (without a header).
* @param {SendCompact} data
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.sendCmpct = function sendCmpct(data, writer) {
return data.toRaw(writer);
};
/**
* Create a cmpctblock packet (without a header).
* @param {CompactBlock} data
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.cmpctBlock = function cmpctBlock(data, writer) {
return data.toRaw(false, writer);
};
/**
* Create a getblocktxn packet (without a header).
* @param {BlockTXRequest} data
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.getBlockTxn = function getBlockTxn(data, writer) {
return data.toRaw(writer);
};
/**
* Create a blocktxn packet (without a header).
* @param {BlockTX} data
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.blockTxn = function blockTxn(data, writer) {
return data.toRaw(false, writer);
};
/*
* Expose
*/

View File

@ -176,6 +176,15 @@ 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
@ -285,12 +294,45 @@ InvItem.fromRaw = function fromRaw(data, enc) {
return new InvItem().fromRaw(data);
};
/**
* Test whether the inv item is a block.
* @returns {Boolean}
*/
InvItem.prototype.isBlock = function isBlock() {
switch (this.type) {
case constants.inv.BLOCK:
case constants.inv.WITNESS_BLOCK:
case constants.inv.FILTERED_BLOCK:
case constants.inv.WITNESS_FILTERED_BLOCK:
case constants.inv.CMPCT_BLOCK:
return true;
default:
return false;
}
};
/**
* Test whether the inv item is a tx.
* @returns {Boolean}
*/
InvItem.prototype.isTX = function isTX() {
switch (this.type) {
case constants.inv.TX:
case constants.inv.WITNESS_TX:
return true;
default:
return false;
}
};
/**
* Test whether the inv item has the witness bit set.
* @returns {Boolean}
*/
InvItem.prototype.isWitness = function isWitness() {
InvItem.prototype.hasWitness = function hasWitness() {
return (this.type & constants.WITNESS_MASK) !== 0;
};

View File

@ -343,6 +343,18 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) {
return Parser.parseUTXOs(p);
case 'feefilter':
return Parser.parseFeeFilter(p);
case 'encinit':
return Parser.parseEncinit(p);
case 'encack':
return Parser.parseEncack(p);
case 'sendcmpct':
return Parser.parseSendCmpct(p);
case 'cmpctblock':
return Parser.parseCmpctBlock(p);
case 'getblocktxn':
return Parser.parseGetBlockTxn(p);
case 'blocktxn':
return Parser.parseBlockTxn(p);
default:
return p;
}
@ -654,6 +666,32 @@ Parser.parseFeeFilter = function parseFeeFilter(p) {
};
};
Parser.parseEncinit = function parseEncinit(p) {
// Handled elsewhere.
return p;
};
Parser.parseEncack = function parseEncack(p) {
// Handled elsewhere.
return p;
};
Parser.parseSendCmpct = function parseSendCmpct(p) {
return bcoin.bip152.SendCompact.fromRaw(p);
};
Parser.parseCmpctBlock = function parseCmpctBlock(p) {
return bcoin.bip152.CompactBlock.fromRaw(p);
};
Parser.parseGetBlockTxn = function parseGetBlockTxn(p) {
return bcoin.bip152.BlockTXRequest.fromRaw(p);
};
Parser.parseBlockTxn = function parseBlockTxn(p) {
return bcoin.bip152.BlockTX.fromRaw(p);
};
/**
* Packet
* @constructor