diff --git a/lib/bcoin/fullnode.js b/lib/bcoin/fullnode.js index 24cfe658..cfbd9d60 100644 --- a/lib/bcoin/fullnode.js +++ b/lib/bcoin/fullnode.js @@ -178,6 +178,18 @@ Fullnode.prototype._init = function _init() { this.http.open(load); }; +Fullnode.prototype.broadcast = function broadcast(item, callback) { + return this.pool.broadcast(item, callback); +}; + +Fullnode.prototype.sendTX = function sendTX(item, callback) { + return this.pool.sendTX(item, callback); +}; + +Fullnode.prototype.sendBlock = function sendBlock(item, callback) { + return this.pool.sendBlock(item, callback); +}; + Fullnode.prototype.startSync = function startSync() { return this.pool.startSync(); }; diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 7bb041cf..f291f6f5 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -50,6 +50,7 @@ function Mempool(node, options) { this.relayPriority = this.options.relayPriority !== false; this.requireStandard = this.options.requireStandard !== false; this.rejectInsaneFees = this.options.rejectInsaneFees !== false; + this.relay = this.options.relay || false; Mempool.global = this; @@ -400,6 +401,9 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, peer, callback) { utils.debug('Added tx %s to the mempool.', tx.rhash); + if (self.options.relay) + self.node.broadcast(tx); + self.resolveOrphans(tx, function(err, resolved) { if (err) return callback(err); diff --git a/lib/bcoin/merkleblock.js b/lib/bcoin/merkleblock.js index 40d9c852..d5ee5dd2 100644 --- a/lib/bcoin/merkleblock.js +++ b/lib/bcoin/merkleblock.js @@ -39,6 +39,14 @@ MerkleBlock.prototype.render = function render() { return this.getRaw(); }; +MerkleBlock.prototype.renderNormal = function renderNormal() { + return this.getRaw(); +}; + +MerkleBlock.prototype.renderWitness = function renderWitness() { + return this.getRaw(); +}; + MerkleBlock.prototype.getSize = function getSize() { if (this._size == null) this.getRaw(); diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index 7b094bec..1bade9ee 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -212,7 +212,8 @@ Peer.prototype.createSocket = function createSocket(port, host) { Peer.prototype.broadcast = function broadcast(items) { var self = this; - var result; + var result = []; + var payload = []; if (this.destroyed) return; @@ -220,21 +221,35 @@ Peer.prototype.broadcast = function broadcast(items) { if (!Array.isArray(items)) items = [items]; - result = items.map(function(item) { + items.forEach(function(item) { var key = item.hash('hex'); var old = this._broadcast.map[key]; + var type = item.type; + var entry, packetType; + if (old) { clearTimeout(old.timer); clearInterval(old.interval); } - var inv = this.framer.inv([{ - type: item.type, - hash: item.hash() - }]); + if (typeof type === 'string') + type = constants.inv[type]; + + // INV does not set the witness + // mask (only GETDATA does this). + type &= ~constants.invWitnessMask; + + if (type === constants.inv.block) + packetType = 'block'; + else if (type === constants.inv.tx) + packetType = 'tx'; + else if (type === constants.inv.filtered) + packetType = 'merkleblock'; + else + assert(false, 'Bad type.'); // Auto-cleanup broadcast map after timeout - var entry = { + entry = { e: new EventEmitter(), timeout: setTimeout(function() { entry.e.emit('timeout'); @@ -244,24 +259,31 @@ Peer.prototype.broadcast = function broadcast(items) { // Retransmit interval: setInterval(function() { - self._write(inv); + self._write(entry.inv); }, this._broadcast.interval), - type: item.type, - value: item.render() + inv: this.framer.inv([{ + type: type, + hash: item.hash() + }]), + + packetType: packetType, + type: type, + hash: item.hash(), + msg: item }; this._broadcast.map[key] = entry; - return entry.e; + result.push(entry.e); + + payload.push({ + type: entry.type, + hash: entry.hash + }); }, this); - this._write(this.framer.inv(items.map(function(item) { - return { - type: item.type, - hash: item.hash() - }; - }))); + this._write(this.framer.inv(payload)); return result; }; @@ -493,13 +515,41 @@ Peer.prototype._handleGetData = function handleGetData(items) { items.forEach(function(item) { // Filter out not broadcasted things var hash = utils.toHex(item.hash); + var entry = this._broadcast.map[hash]; + var isWitness = item.type & constants.invWitnessMask; + var value; - if (!this._broadcast.map[hash]) + if (!entry) return; - var entry = this._broadcast.map[hash]; + if ((item.type & ~constants.invWitnessMask) !== entry.type) { + utils.debug( + 'Peer %s requested an existing item with the wrong type.', + this.host); + return; + } - this._write(this.framer.packet(entry.type, entry.value)); + if (isWitness) { + if (!entry.witnessValue) + entry.witnessValue = entry.msg.renderWitness(); + value = entry.witnessValue; + } else { + if (!entry.value) + entry.value = entry.msg.renderNormal(); + value = entry.value; + } + + utils.debug( + 'Peer %s requested %s:%s as a %s packet.', + this.host, + entry.packetType, + utils.revHex(utils.toHex(entry.hash)), + isWitness ? 'witness' : 'normal'); + + if (entry.value && entry.witnessValue) + delete entry.msg; + + this._write(this.framer.packet(entry.packetType, value)); entry.e.emit('request'); }, this); diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index d0b6e880..eadf26b7 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -231,7 +231,6 @@ Parser.parseInvList = function parseInvList(p) { for (i = 0; i < count; i++) { items.push({ - // type: constants.invByVal[p.readU32()], type: p.readU32(), hash: p.readHash() }); @@ -250,8 +249,8 @@ Parser.parseMerkleBlock = function parseMerkleBlock(p) { p.start(); version = p.read32(); - prevBlock = p.readHash('hex'); - merkleRoot = p.readHash('hex'); + prevBlock = p.readHash(); + merkleRoot = p.readHash(); ts = p.readU32(); bits = p.readU32(); nonce = p.readU32(); @@ -262,7 +261,7 @@ Parser.parseMerkleBlock = function parseMerkleBlock(p) { hashes = new Array(hashCount); for (i = 0; i < hashCount; i++) - hashes[i] = p.readHash('hex'); + hashes[i] = p.readHash(); flags = p.readVarBytes(); @@ -292,8 +291,8 @@ Parser.parseHeaders = function parseHeaders(p) { for (i = 0; i < count; i++) { headers.push({ version: p.read32(), - prevBlock: p.readHash('hex'), - merkleRoot: p.readHash('hex'), + prevBlock: p.readHash(), + merkleRoot: p.readHash(), ts: p.readU32(), bits: p.readU32(), nonce: p.readU32(), @@ -316,8 +315,8 @@ Parser.parseBlock = function parseBlock(p) { p.start(); version = p.read32(); - prevBlock = p.readHash('hex'); - merkleRoot = p.readHash('hex'); + prevBlock = p.readHash(); + merkleRoot = p.readHash(); ts = p.readU32(); bits = p.readU32(); nonce = p.readU32(); @@ -351,8 +350,8 @@ Parser.parseBlockCompact = function parseBlockCompact(p) { p.start(); version = p.read32(); - prevBlock = p.readHash('hex'); - merkleRoot = p.readHash('hex'); + prevBlock = p.readHash(); + merkleRoot = p.readHash(); ts = p.readU32(); bits = p.readU32(); nonce = p.readU32(); @@ -403,7 +402,7 @@ Parser.parseInput = function parseInput(p) { p = new BufferReader(p); p.start(); - hash = p.readHash('hex'); + hash = p.readHash(); index = p.readU32(); script = Parser.parseScript(p); sequence = p.readU32(); @@ -472,7 +471,7 @@ Parser.parseCoin = function parseCoin(p, extended) { coinbase = p.readU8() === 1; if (extended) { - hash = p.readHash('hex'); + hash = p.readHash(); index = p.readU32(); } @@ -655,7 +654,7 @@ Parser.parseReject = function parseReject(p) { reason = p.readVarString('ascii'); try { - data = p.readHash('hex'); + data = p.readHash(); } catch (e) { data = null; }