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

View File

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

View File

@ -338,18 +338,8 @@ FullNode.prototype.sendTX = co(function* sendTX(tx) {
if (missing) {
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.broadcast(tx);
return;
}