From 82d1345311ca1c8afe24bdf15247db54bd680831 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 18 Dec 2016 01:44:04 -0800 Subject: [PATCH] net: refactor inv type handling. --- lib/net/peer.js | 79 +++++++++++++++++++++++++++++--------------- lib/net/pool.js | 54 +++++++++++++++--------------- lib/node/fullnode.js | 10 ------ 3 files changed, 80 insertions(+), 63 deletions(-) diff --git a/lib/net/peer.js b/lib/net/peer.js index e8c305bf..2e12a402 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -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()) { diff --git a/lib/net/pool.js b/lib/net/pool.js index 364d800f..015c41d8 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -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 ''; @@ -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 ''; }; diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 0f4811b8..19a9ff83 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -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; }