net: refactor inv type handling.

This commit is contained in:
Christopher Jeffrey 2016-12-18 01:44:04 -08:00
parent 2691f9fcb6
commit 82d1345311
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 80 additions and 63 deletions

View File

@ -27,6 +27,7 @@ var TX = require('../primitives/tx');
var errors = require('../btc/errors'); var errors = require('../btc/errors');
var List = require('../utils/list'); var List = require('../utils/list');
var IP = require('../utils/ip'); var IP = require('../utils/ip');
var invTypes = constants.inv;
var packetTypes = packets.types; var packetTypes = packets.types;
var VerifyResult = errors.VerifyResult; var VerifyResult = errors.VerifyResult;
@ -673,10 +674,10 @@ Peer.prototype.announceList = function announceList() {
for (item = this.pool.invItems.head; item; item = item.next) { for (item = this.pool.invItems.head; item; item = item.next) {
switch (item.type) { switch (item.type) {
case constants.inv.TX: case invTypes.TX:
txs.push(item.msg); txs.push(item.msg);
break; break;
case constants.inv.BLOCK: case invTypes.BLOCK:
blocks.push(item.msg); blocks.push(item.msg);
break; break;
default: default:
@ -1065,6 +1066,39 @@ Peer.prototype.response = function response(cmd, payload) {
return true; return true;
}; };
/**
* Calculate peer block inv type (filtered,
* compact, witness, or non-witness).
* @returns {Number}
*/
Peer.prototype.blockType = function blockType() {
if (this.options.spv)
return invTypes.FILTERED_BLOCK;
if (this.options.compact && this.compactMode) {
if (!this.options.witness || this.compactWitness)
return invTypes.CMPCT_BLOCK;
}
if (this.haveWitness)
return invTypes.WITNESS_BLOCK;
return invTypes.BLOCK;
};
/**
* Calculate peer tx inv type (witness or non-witness).
* @returns {Number}
*/
Peer.prototype.txType = function txType() {
if (this.haveWitness)
return invTypes.WITNESS_TX;
return invTypes.TX;
};
/** /**
* Send `getdata` to peer. * Send `getdata` to peer.
* @param {LoadRequest[]} items * @param {LoadRequest[]} items
@ -1076,16 +1110,7 @@ Peer.prototype.getData = function getData(items) {
for (i = 0; i < items.length; i++) { for (i = 0; i < items.length; i++) {
item = items[i]; item = items[i];
item = item.toInv(); inv.push(item.toInv());
if (this.options.compact
&& this.compactMode
&& item.isBlock()
&& !item.hasWitness()) {
item.type = constants.inv.CMPCT_BLOCK;
}
inv.push(item);
} }
this.send(new packets.GetDataPacket(inv)); this.send(new packets.GetDataPacket(inv));
@ -1503,7 +1528,7 @@ Peer.prototype.handleGetBlocks = co(function* handleGetBlocks(packet) {
hash = yield this.chain.db.getNextHash(hash); hash = yield this.chain.db.getNextHash(hash);
while (hash) { while (hash) {
blocks.push(new InvItem(constants.inv.BLOCK, hash)); blocks.push(new InvItem(invTypes.BLOCK, hash));
if (hash === packet.stop) if (hash === packet.stop)
break; break;
@ -1622,7 +1647,7 @@ Peer.prototype.handleMempool = co(function* handleMempool(packet) {
hashes = this.mempool.getSnapshot(); hashes = this.mempool.getSnapshot();
for (i = 0; i < hashes.length; i++) for (i = 0; i < hashes.length; i++)
items.push(new InvItem(constants.inv.TX, hashes[i])); items.push(new InvItem(invTypes.TX, hashes[i]));
this.logger.debug('Sending mempool snapshot (%s).', this.hostname); this.logger.debug('Sending mempool snapshot (%s).', this.hostname);
@ -1644,7 +1669,7 @@ Peer.prototype.getBroadcasted = function getBroadcasted(item) {
this.logger.debug( this.logger.debug(
'Peer requested %s %s as a %s packet (%s).', 'Peer requested %s %s as a %s packet (%s).',
entry.type === constants.inv.TX ? 'tx' : 'block', entry.type === invTypes.TX ? 'tx' : 'block',
util.revHex(entry.hash), util.revHex(entry.hash),
item.hasWitness() ? 'witness' : 'normal', item.hasWitness() ? 'witness' : 'normal',
this.hostname); this.hostname);
@ -1655,10 +1680,10 @@ Peer.prototype.getBroadcasted = function getBroadcasted(item) {
return; return;
if (item.isTX()) { if (item.isTX()) {
if (entry.type !== constants.inv.TX) if (entry.type !== invTypes.TX)
return; return;
} else { } else {
if (entry.type !== constants.inv.BLOCK) if (entry.type !== invTypes.BLOCK)
return; return;
} }
@ -1810,8 +1835,8 @@ Peer.prototype.handleGetData = co(function* handleGetData(packet) {
} }
switch (item.type) { switch (item.type) {
case constants.inv.BLOCK: case invTypes.BLOCK:
case constants.inv.WITNESS_BLOCK: case invTypes.WITNESS_BLOCK:
result = yield this.sendBlock(item, item.hasWitness()); result = yield this.sendBlock(item, item.hasWitness());
if (!result) { if (!result) {
notFound.push(item); notFound.push(item);
@ -1819,8 +1844,8 @@ Peer.prototype.handleGetData = co(function* handleGetData(packet) {
} }
blocks++; blocks++;
break; break;
case constants.inv.FILTERED_BLOCK: case invTypes.FILTERED_BLOCK:
case constants.inv.WITNESS_FILTERED_BLOCK: case invTypes.WITNESS_FILTERED_BLOCK:
if (!this.spvFilter) { if (!this.spvFilter) {
notFound.push(item); notFound.push(item);
continue; continue;
@ -1846,7 +1871,7 @@ Peer.prototype.handleGetData = co(function* handleGetData(packet) {
blocks++; blocks++;
break; break;
case constants.inv.CMPCT_BLOCK: case invTypes.CMPCT_BLOCK:
// Fallback to full block. // Fallback to full block.
height = yield this.chain.db.getHeight(item.hash); height = yield this.chain.db.getHeight(item.hash);
if (height < this.chain.tip.height - 10) { if (height < this.chain.tip.height - 10) {
@ -1878,7 +1903,7 @@ Peer.prototype.handleGetData = co(function* handleGetData(packet) {
} }
if (item.hash === this.hashContinue) { if (item.hash === this.hashContinue) {
this.sendInv([new InvItem(constants.inv.BLOCK, this.chain.tip.hash)]); this.sendInv([new InvItem(invTypes.BLOCK, this.chain.tip.hash)]);
this.hashContinue = null; this.hashContinue = null;
} }
} }
@ -2058,10 +2083,10 @@ Peer.prototype.handleInv = co(function* handleInv(packet) {
for (i = 0; i < items.length; i++) { for (i = 0; i < items.length; i++) {
item = items[i]; item = items[i];
switch (item.type) { switch (item.type) {
case constants.inv.TX: case invTypes.TX:
txs.push(item.hash); txs.push(item.hash);
break; break;
case constants.inv.BLOCK: case invTypes.BLOCK:
blocks.push(item.hash); blocks.push(item.hash);
break; break;
default: default:
@ -2408,7 +2433,7 @@ Peer.prototype.handleGetBlockTxn = co(function* handleGetBlockTxn(packet) {
if (this.options.selfish) if (this.options.selfish)
return; return;
item = new InvItem(constants.inv.BLOCK, req.hash); item = new InvItem(invTypes.BLOCK, req.hash);
block = yield this.getItem(item); block = yield this.getItem(item);
@ -2715,7 +2740,7 @@ Peer.prototype.sync = co(function* sync() {
if (!this.version.hasNetwork()) if (!this.version.hasNetwork())
return; return;
if (this.options.witness && !this.version.hasWitness()) if (this.options.witness && !this.haveWitness)
return; return;
if (!this.isLoader()) { if (!this.isLoader()) {

View File

@ -28,6 +28,7 @@ var request = require('../http/request');
var List = require('../utils/list'); var List = require('../utils/list');
var tcp = require('./tcp'); var tcp = require('./tcp');
var dns = require('./dns'); var dns = require('./dns');
var invTypes = constants.inv;
var VerifyError = errors.VerifyError; var VerifyError = errors.VerifyError;
var VerifyResult = errors.VerifyResult; var VerifyResult = errors.VerifyResult;
@ -128,9 +129,6 @@ function Pool(options) {
this.peers = new PeerList(this); this.peers = new PeerList(this);
this.hosts = new HostList(this); this.hosts = new HostList(this);
this.blockType = constants.inv.BLOCK;
this.txType = constants.inv.TX;
this.localNonce = util.nonce(); this.localNonce = util.nonce();
this.spvFilter = null; this.spvFilter = null;
@ -220,16 +218,7 @@ Pool.prototype._initOptions = function _initOptions() {
this.hosts.setSeeds(this.options.seeds); this.hosts.setSeeds(this.options.seeds);
if (this.options.preferredSeed) if (this.options.preferredSeed)
this.hosts.addSeed(this.options.preferredSeed); this.hosts.setSeeds([this.options.preferredSeed]);
if (this.options.witness) {
this.blockType |= constants.WITNESS_MASK;
this.txType |= constants.WITNESS_MASK;
}
// Note: No witness bit for merkleblocks.
if (this.options.spv)
this.blockType = constants.inv.FILTERED_BLOCK;
if (this.options.spv) if (this.options.spv)
this.spvFilter = Bloom.fromRate(10000, 0.001, constants.bloom.ALL); this.spvFilter = Bloom.fromRate(10000, 0.001, constants.bloom.ALL);
@ -1285,7 +1274,7 @@ Pool.prototype._handleBlockInv = co(function* handleBlockInv(hashes, peer) {
if (!this.chain.synced && !peer.isLoader()) if (!this.chain.synced && !peer.isLoader())
return; return;
if (this.options.witness && !peer.version.hasWitness()) if (this.options.witness && !peer.haveWitness)
return; return;
// Request headers instead. // Request headers instead.
@ -1668,13 +1657,16 @@ Pool.prototype.getBlock = function getBlock(peer, hash) {
if (!this.loaded) if (!this.loaded)
return; return;
if (!peer.ack)
throw new Error('Peer handshake not complete (getdata).');
if (peer.destroyed) if (peer.destroyed)
throw new Error('Peer is already destroyed (getdata).'); throw new Error('Peer is already destroyed (getdata).');
if (this.requestMap[hash]) if (this.requestMap[hash])
return; return;
item = new LoadRequest(this, peer, this.blockType, hash); item = new LoadRequest(this, peer, invTypes.BLOCK, hash);
peer.queueBlock.push(item); peer.queueBlock.push(item);
}; };
@ -1714,13 +1706,16 @@ Pool.prototype.getTX = function getTX(peer, hash) {
if (!this.loaded) if (!this.loaded)
return; return;
if (!peer.ack)
throw new Error('Peer handshake not complete (getdata).');
if (peer.destroyed) if (peer.destroyed)
throw new Error('Peer is already destroyed (getdata).'); throw new Error('Peer is already destroyed (getdata).');
if (this.hasTX(hash)) if (this.hasTX(hash))
return true; return true;
item = new LoadRequest(this, peer, this.txType, hash); item = new LoadRequest(this, peer, invTypes.TX, hash);
if (peer.queueTX.size === 0) { if (peer.queueTX.size === 0) {
util.nextTick(function() { util.nextTick(function() {
@ -2410,7 +2405,7 @@ LoadRequest.prototype.destroy = function destroy() {
*/ */
LoadRequest.prototype._onTimeout = function _onTimeout() { LoadRequest.prototype._onTimeout = function _onTimeout() {
if (this.type !== this.pool.txType && this.peer.isLoader()) { if (this.type === invTypes.BLOCK && this.peer.isLoader()) {
this.pool.logger.debug( this.pool.logger.debug(
'Loader took too long serving a block. Finding a new one.'); 'Loader took too long serving a block. Finding a new one.');
this.peer.destroy(); this.peer.destroy();
@ -2428,7 +2423,7 @@ LoadRequest.prototype.start = function start() {
this.active = true; this.active = true;
this.pool.activeRequest++; this.pool.activeRequest++;
if (this.type === this.pool.txType) if (this.type === invTypes.TX)
this.pool.activeTX++; this.pool.activeTX++;
else else
this.pool.activeBlocks++; this.pool.activeBlocks++;
@ -2442,19 +2437,22 @@ LoadRequest.prototype.start = function start() {
*/ */
LoadRequest.prototype.finish = function finish() { LoadRequest.prototype.finish = function finish() {
if (this.pool.requestMap[this.hash] === this) { var entry = this.pool.requestMap[this.hash];
if (entry) {
assert(entry === this);
delete this.pool.requestMap[this.hash]; delete this.pool.requestMap[this.hash];
if (this.active) { if (this.active) {
this.active = false; this.active = false;
this.pool.activeRequest--; this.pool.activeRequest--;
if (this.type === this.pool.txType) if (this.type === invTypes.TX)
this.pool.activeTX--; this.pool.activeTX--;
else else
this.pool.activeBlocks--; this.pool.activeBlocks--;
} }
} }
if (this.type === this.pool.txType) if (this.type === invTypes.TX)
this.peer.queueTX.remove(this); this.peer.queueTX.remove(this);
else else
this.peer.queueBlock.remove(this); this.peer.queueBlock.remove(this);
@ -2473,7 +2471,7 @@ LoadRequest.prototype.finish = function finish() {
LoadRequest.prototype.inspect = function inspect() { LoadRequest.prototype.inspect = function inspect() {
return '<LoadRequest:' return '<LoadRequest:'
+ ' id=' + this.id + ' id=' + this.id
+ ' type=' + (this.type === this.pool.txType ? 'tx' : 'block') + ' type=' + (this.type === invTypes.TX ? 'tx' : 'block')
+ ' active=' + this.active + ' active=' + this.active
+ ' hash=' + util.revHex(this.hash) + ' hash=' + util.revHex(this.hash)
+ '>'; + '>';
@ -2485,7 +2483,11 @@ LoadRequest.prototype.inspect = function inspect() {
*/ */
LoadRequest.prototype.toInv = function toInv() { LoadRequest.prototype.toInv = function toInv() {
return new InvItem(this.type, this.hash); var type = this.type === invTypes.BLOCK
? this.peer.blockType()
: this.peer.txType();
return new InvItem(type, this.hash);
}; };
/** /**
@ -2571,10 +2573,10 @@ BroadcastItem.prototype.refresh = function refresh() {
BroadcastItem.prototype.announce = function announce() { BroadcastItem.prototype.announce = function announce() {
switch (this.type) { switch (this.type) {
case constants.inv.TX: case invTypes.TX:
this.pool.announceTX(this.msg); this.pool.announceTX(this.msg);
break; break;
case constants.inv.BLOCK: case invTypes.BLOCK:
this.pool.announceBlock(this.msg); this.pool.announceBlock(this.msg);
break; break;
default: default:
@ -2649,7 +2651,7 @@ BroadcastItem.prototype.reject = function reject(peer) {
BroadcastItem.prototype.inspect = function inspect() { BroadcastItem.prototype.inspect = function inspect() {
return '<BroadcastItem:' return '<BroadcastItem:'
+ ' id=' + this.id + ' id=' + this.id
+ ' type=' + (this.type === constants.inv.TX ? 'tx' : 'block') + ' type=' + (this.type === invTypes.TX ? 'tx' : 'block')
+ ' hash=' + util.revHex(this.hash) + ' hash=' + util.revHex(this.hash)
+ '>'; + '>';
}; };

View File

@ -338,18 +338,8 @@ FullNode.prototype.sendTX = co(function* sendTX(tx) {
if (missing) { if (missing) {
this.logger.warning('TX was orphaned in mempool: %s.', tx.txid()); this.logger.warning('TX was orphaned in mempool: %s.', tx.txid());
// Avoid getting dos'd by bitcoind for now.
// See: https://github.com/bitcoin/bitcoin/issues/9182
// Once this fix is widely deployed, we can remove this.
if (tx.hasWitness()) {
this.logger.warning('Not broadcasting for now to avoid bitcoind DoS.');
return;
}
this.logger.warning('Attempting to broadcast anyway...'); this.logger.warning('Attempting to broadcast anyway...');
this.broadcast(tx); this.broadcast(tx);
return; return;
} }