pool: refactor and add compact block mode 1 support.

This commit is contained in:
Christopher Jeffrey 2017-01-20 09:03:24 -08:00
parent 7543049180
commit 18c4a83ed3
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 180 additions and 163 deletions

View File

@ -1678,10 +1678,10 @@ RejectPacket.fromRaw = function fromRaw(data, enc) {
* @private * @private
* @param {Number} code * @param {Number} code
* @param {String} reason * @param {String} reason
* @param {(TX|Block)?} obj * @param {(TX|Block)?} msg
*/ */
RejectPacket.prototype.fromReason = function fromReason(code, reason, obj) { RejectPacket.prototype.fromReason = function fromReason(code, reason, msg) {
if (typeof code === 'string') if (typeof code === 'string')
code = RejectPacket.codes[code.toUpperCase()]; code = RejectPacket.codes[code.toUpperCase()];
@ -1695,9 +1695,9 @@ RejectPacket.prototype.fromReason = function fromReason(code, reason, obj) {
this.code = code; this.code = code;
this.reason = reason; this.reason = reason;
if (obj) { if (msg) {
this.message = (obj instanceof TX) ? 'tx' : 'block'; this.message = (msg instanceof TX) ? 'tx' : 'block';
this.hash = obj.hash('hex'); this.hash = msg.hash('hex');
} }
return this; return this;

View File

@ -26,7 +26,6 @@ var BIP152 = require('./bip152');
var Block = require('../primitives/block'); var Block = require('../primitives/block');
var TX = require('../primitives/tx'); var TX = require('../primitives/tx');
var encoding = require('../utils/encoding'); var encoding = require('../utils/encoding');
var errors = require('../protocol/errors');
var NetAddress = require('../primitives/netaddress'); var NetAddress = require('../primitives/netaddress');
var services = common.services; var services = common.services;
var invTypes = InvItem.types; var invTypes = InvItem.types;
@ -115,8 +114,9 @@ function Peer(pool) {
this.bip150 = null; this.bip150 = null;
this.compactMode = -1; this.compactMode = -1;
this.compactWitness = false; this.compactWitness = false;
this.lastMerkle = null; this.merkleBlock = null;
this.waitingTX = 0; this.merkleTime = -1;
this.merkleMatches = 0;
this.syncSent = false; this.syncSent = false;
this.sentAddr = false; this.sentAddr = false;
this.sentGetAddr = false; this.sentGetAddr = false;
@ -270,13 +270,14 @@ Peer.prototype._init = function init() {
this.parser.on('error', function(err) { this.parser.on('error', function(err) {
self.error(err); self.error(err);
self.reject(null, 'malformed', 'error parsing message', 10); self.sendReject('malformed', 'error parsing message');
self.increaseBan(10);
}); });
if (this.bip151) { if (this.bip151) {
this.bip151.on('error', function(err) { this.bip151.on('error', function(err) {
self.error(err); self.error(err);
self.reject(null, 'malformed', 'error parsing message', 10); self.destroy();
}); });
this.bip151.on('rekey', function() { this.bip151.on('rekey', function() {
self.logger.debug('Rekeying with peer (%s).', self.hostname); self.logger.debug('Rekeying with peer (%s).', self.hostname);
@ -649,7 +650,7 @@ Peer.prototype.announceBlock = function announceBlock(blocks) {
// they're using compact block mode 1. // they're using compact block mode 1.
if (this.compactMode === 1) { if (this.compactMode === 1) {
this.invFilter.add(block.hash()); this.invFilter.add(block.hash());
this.sendCompactBlock(block, this.compactWitness); this.sendCompactBlock(block);
continue; continue;
} }
@ -824,11 +825,12 @@ Peer.prototype.sendHeaders = function sendHeaders(items) {
* Send a compact block. * Send a compact block.
* @private * @private
* @param {Block} block * @param {Block} block
* @param {Boolean} witness
* @returns {Boolean} * @returns {Boolean}
*/ */
Peer.prototype.sendCompactBlock = function sendCompactBlock(block, witness) { Peer.prototype.sendCompactBlock = function sendCompactBlock(block) {
var witness = this.compactWitness;
// Try again with a new nonce // Try again with a new nonce
// if we get a siphash collision. // if we get a siphash collision.
for (;;) { for (;;) {
@ -1122,6 +1124,15 @@ Peer.prototype.maybeTimeout = function maybeTimeout() {
} }
} }
if (this.merkleBlock) {
assert(this.merkleTime !== -1);
if (now > this.merkleTime + 60000) {
this.error('Peer is stalling (merkleblock).');
this.destroy();
return;
}
}
if (!this.pool.syncing || this.chain.synced) if (!this.pool.syncing || this.chain.synced)
return; return;
@ -1509,26 +1520,10 @@ Peer.prototype.handlePacket = co(function* handlePacket(packet) {
this.emit(packet.cmd, packet); this.emit(packet.cmd, packet);
}); });
/**
* Flush merkle block once all matched
* txs have been received.
* @private
* @returns {Promise}
*/
Peer.prototype.flushMerkle = co(function* flushMerkle() {
assert(this.lastMerkle);
yield this.pool.addBlock(this, this.lastMerkle);
this.lastMerkle = null;
this.waitingTX = 0;
});
/** /**
* Handle `filterload` packet. * Handle `filterload` packet.
* @private * @private
* @param {FilterLoadPacket} * @param {FilterLoadPacket} packet
*/ */
Peer.prototype.handleFilterLoad = co(function* handleFilterLoad(packet) { Peer.prototype.handleFilterLoad = co(function* handleFilterLoad(packet) {
@ -1546,7 +1541,7 @@ Peer.prototype.handleFilterLoad = co(function* handleFilterLoad(packet) {
/** /**
* Handle `filteradd` packet. * Handle `filteradd` packet.
* @private * @private
* @param {FilterAddPacket} * @param {FilterAddPacket} packet
*/ */
Peer.prototype.handleFilterAdd = co(function* handleFilterAdd(packet) { Peer.prototype.handleFilterAdd = co(function* handleFilterAdd(packet) {
@ -2025,7 +2020,7 @@ Peer.prototype.handleGetBlockTxn = co(function* handleGetBlockTxn(packet) {
*/ */
Peer.prototype.handleBlockTxn = co(function* handleBlockTxn(packet) { Peer.prototype.handleBlockTxn = co(function* handleBlockTxn(packet) {
yield this.pool.handleGetBlockTxn(this, packet); yield this.pool.handleBlockTxn(this, packet);
}); });
/** /**
@ -2037,24 +2032,22 @@ Peer.prototype.handleBlockTxn = co(function* handleBlockTxn(packet) {
Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) { Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
var packet = new packets.GetHeadersPacket(locator, stop); var packet = new packets.GetHeadersPacket(locator, stop);
var height = -1;
var hash = null; var hash = null;
var end = null;
if (packet.locator.length > 0)
hash = util.revHex(packet.locator[0]);
if (stop)
end = util.revHex(stop);
this.logger.debug( this.logger.debug(
'Requesting headers packet from peer with getheaders (%s).', 'Requesting headers packet from peer with getheaders (%s).',
this.hostname); this.hostname);
if (packet.locator.length > 0) {
height = this.chain.checkHeight(packet.locator[0]);
hash = util.revHex(packet.locator[0]);
}
if (stop)
stop = util.revHex(stop);
this.logger.debug( this.logger.debug(
'Height: %d, Hash: %s, Stop: %s', 'Sending getheaders (hash=%s, stop=%s).',
height, hash, stop || null); hash, end);
this.send(packet); this.send(packet);
}; };
@ -2069,10 +2062,7 @@ Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
var packet = new packets.GetBlocksPacket(locator, stop); var packet = new packets.GetBlocksPacket(locator, stop);
var height = -1; var height = -1;
var hash = null; var hash = null;
var end = null;
this.logger.debug(
'Requesting inv packet from peer with getblocks (%s).',
this.hostname);
if (packet.locator.length > 0) { if (packet.locator.length > 0) {
height = this.chain.checkHeight(packet.locator[0]); height = this.chain.checkHeight(packet.locator[0]);
@ -2080,11 +2070,15 @@ Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
} }
if (stop) if (stop)
stop = util.revHex(stop); end = util.revHex(stop);
this.logger.debug( this.logger.debug(
'Height: %d, Hash: %s, Stop: %s', 'Requesting inv packet from peer with getblocks (%s).',
height, hash, stop || null); this.hostname);
this.logger.debug(
'Sending getblocks (height=%d, hash=%s, stop=%s).',
height, hash, end);
this.send(packet); this.send(packet);
}; };
@ -2115,15 +2109,15 @@ Peer.prototype.sendMempool = function sendMempool() {
* Send `reject` to peer. * Send `reject` to peer.
* @param {Number} code * @param {Number} code
* @param {String} reason * @param {String} reason
* @param {TX|Block} obj * @param {TX|Block} msg
*/ */
Peer.prototype.sendReject = function sendReject(code, reason, obj) { Peer.prototype.sendReject = function sendReject(code, reason, msg) {
var reject = packets.RejectPacket.fromReason(code, reason, obj); var reject = packets.RejectPacket.fromReason(code, reason, msg);
if (obj) { if (msg) {
this.logger.debug('Rejecting %s %s (%s): ccode=%s reason=%s.', this.logger.debug('Rejecting %s %s (%s): ccode=%s reason=%s.',
reject.message, obj.rhash(), this.hostname, code, reason); reject.message, msg.rhash(), this.hostname, code, reason);
} else { } else {
this.logger.debug('Rejecting packet from %s: ccode=%s reason=%s.', this.logger.debug('Rejecting packet from %s: ccode=%s reason=%s.',
this.hostname, code, reason); this.hostname, code, reason);
@ -2138,12 +2132,13 @@ Peer.prototype.sendReject = function sendReject(code, reason, obj) {
/** /**
* Send a `sendcmpct` packet. * Send a `sendcmpct` packet.
* @param {Number} mode
*/ */
Peer.prototype.sendCompact = function sendCompact() { Peer.prototype.sendCompact = function sendCompact(mode) {
var version = this.options.witness ? 2 : 1; var version = this.options.witness ? 2 : 1;
this.logger.info('Initializing compact blocks (%s).', this.hostname); this.logger.info('Initializing compact blocks (%s).', this.hostname);
this.send(new packets.SendCmpctPacket(0, version)); this.send(new packets.SendCmpctPacket(mode, version));
}; };
/** /**
@ -2174,14 +2169,14 @@ Peer.prototype.ban = function ban() {
/** /**
* Send a `reject` packet to peer. * Send a `reject` packet to peer.
* @see Framer.reject * @see Framer.reject
* @param {(TX|Block)?} obj * @param {(TX|Block)?} msg
* @param {String} code - cccode. * @param {String} code
* @param {String} reason * @param {String} reason
* @param {Number} score * @param {Number} score
*/ */
Peer.prototype.reject = function reject(obj, code, reason, score) { Peer.prototype.reject = function reject(msg, code, reason, score) {
this.sendReject(code, reason, obj); this.sendReject(code, reason, msg);
this.increaseBan(score); this.increaseBan(score);
}; };

View File

@ -34,7 +34,6 @@ var Map = require('../utils/map');
var packets = require('./packets'); var packets = require('./packets');
var invTypes = InvItem.types; var invTypes = InvItem.types;
var VerifyError = errors.VerifyError; var VerifyError = errors.VerifyError;
var VerifyResult = errors.VerifyResult;
/** /**
* A pool of peers for handling all network activity. * A pool of peers for handling all network activity.
@ -104,7 +103,6 @@ function Pool(options) {
this.connected = false; this.connected = false;
this.syncing = false; this.syncing = false;
this.feeRate = this.options.feeRate;
this.nonce = util.nonce(); this.nonce = util.nonce();
this.spvFilter = null; this.spvFilter = null;
this.txFilter = null; this.txFilter = null;
@ -180,26 +178,6 @@ Pool.prototype._init = function _init() {
self.emit('block', block, entry); self.emit('block', block, entry);
}); });
this.chain.on('competitor', function(block, entry) {
self.emit('competitor', block, entry);
});
this.chain.on('fork', function(block, height, expected) {
self.emit('fork', block, height, expected);
});
this.chain.on('invalid', function(block, height) {
self.emit('invalid', block, height);
});
this.chain.on('exists', function(block, height) {
self.emit('exists', block, height);
});
this.chain.on('orphan', function(block, height) {
self.emit('orphan', block, height);
});
this.chain.on('reset', function() { this.chain.on('reset', function() {
self.resetChain(); self.resetChain();
self.forceSync(); self.forceSync();
@ -896,7 +874,7 @@ Pool.prototype.handleOpen = function handleOpen(peer) {
// We want compact blocks! // We want compact blocks!
if (this.options.compact) { if (this.options.compact) {
if (peer.version >= common.COMPACT_VERSION) if (peer.version >= common.COMPACT_VERSION)
peer.sendCompact(); peer.sendCompact(this.options.blockMode);
} }
// Find some more peers. // Find some more peers.
@ -911,8 +889,8 @@ Pool.prototype.handleOpen = function handleOpen(peer) {
this.announceList(peer); this.announceList(peer);
// Set a fee rate filter. // Set a fee rate filter.
if (this.feeRate !== -1) if (this.options.feeRate !== -1)
peer.sendFeeRate(this.feeRate); peer.sendFeeRate(this.options.feeRate);
// Start syncing the chain. // Start syncing the chain.
peer.sync(); peer.sync();
@ -982,7 +960,7 @@ Pool.prototype.handleVersion = co(function* handleVersion(peer, packet) {
*/ */
Pool.prototype.handleVerack = co(function* handleVerack(peer, packet) { Pool.prototype.handleVerack = co(function* handleVerack(peer, packet) {
; this.emit('verack', packet, peer);
}); });
/** /**
@ -993,7 +971,7 @@ Pool.prototype.handleVerack = co(function* handleVerack(peer, packet) {
*/ */
Pool.prototype.handlePing = co(function* handlePing(peer, packet) { Pool.prototype.handlePing = co(function* handlePing(peer, packet) {
; this.emit('ping', packet, peer);
}); });
/** /**
@ -1004,7 +982,7 @@ Pool.prototype.handlePing = co(function* handlePing(peer, packet) {
*/ */
Pool.prototype.handlePong = co(function* handlePong(peer, packet) { Pool.prototype.handlePong = co(function* handlePong(peer, packet) {
; this.emit('pong', packet, peer);
}); });
/** /**
@ -1051,6 +1029,8 @@ Pool.prototype.handleGetAddr = co(function* handleGetAddr(peer, packet) {
peer.hostname); peer.hostname);
peer.send(new packets.AddrPacket(items)); peer.send(new packets.AddrPacket(items));
this.emit('getaddr', packet, peer);
}); });
/** /**
@ -1066,8 +1046,6 @@ Pool.prototype.handleAddr = co(function* handleAddr(peer, packet) {
var services = this.options.requiredServices; var services = this.options.requiredServices;
var i, addr; var i, addr;
this.emit('addr', addrs, peer);
for (i = 0; i < addrs.length; i++) { for (i = 0; i < addrs.length; i++) {
addr = addrs[i]; addr = addrs[i];
@ -1096,6 +1074,8 @@ Pool.prototype.handleAddr = co(function* handleAddr(peer, packet) {
peer.hostname); peer.hostname);
this.fillOutbound(); this.fillOutbound();
this.emit('addr', packet, peer);
}); });
/** /**
@ -1159,13 +1139,13 @@ Pool.prototype._handleInv = co(function* handleInv(peer, packet) {
unknown, peer.hostname); unknown, peer.hostname);
} }
this.emit('inv', items, peer);
if (blocks.length > 0) if (blocks.length > 0)
yield this.handleBlockInv(peer, blocks); yield this.handleBlockInv(peer, blocks);
if (txs.length > 0) if (txs.length > 0)
yield this.handleTXInv(peer, txs); yield this.handleTXInv(peer, txs);
this.emit('inv', packet, peer);
}); });
/** /**
@ -1361,7 +1341,7 @@ Pool.prototype.handleGetData = co(function* handleGetData(peer, packet) {
continue; continue;
} }
peer.sendCompactBlock(block, peer.compactWitness); peer.sendCompactBlock(block);
blocks++; blocks++;
@ -1399,7 +1379,7 @@ Pool.prototype.handleGetData = co(function* handleGetData(peer, packet) {
unknown, peer.hostname); unknown, peer.hostname);
} }
this.emit('getdata', items, peer); this.emit('getdata', packet, peer);
}); });
/** /**
@ -1417,6 +1397,8 @@ Pool.prototype.handleNotFound = co(function* handleNotFound(peer, packet) {
item = items[i]; item = items[i];
this.fulfill(peer, item.hash); this.fulfill(peer, item.hash);
} }
this.emit('notfound', packet, peer);
}); });
/** /**
@ -1462,6 +1444,8 @@ Pool.prototype.handleGetBlocks = co(function* handleGetBlocks(peer, packet) {
} }
peer.sendInv(blocks); peer.sendInv(blocks);
this.emit('getblocks', packet, peer);
}); });
/** /**
@ -1511,6 +1495,8 @@ Pool.prototype.handleGetHeaders = co(function* handleGetHeaders(peer, packet) {
} }
peer.sendHeaders(headers); peer.sendHeaders(headers);
this.emit('getheaders', packet, peer);
}); });
/** /**
@ -1595,13 +1581,13 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
this.headerChain.push(node); this.headerChain.push(node);
} }
this.emit('headers', headers, peer);
this.logger.debug( this.logger.debug(
'Received %s headers from peer (%s).', 'Received %s headers from peer (%s).',
headers.length, headers.length,
peer.hostname); peer.hostname);
this.emit('headers', packet, peer);
// Request the hashes we just added. // Request the hashes we just added.
if (isCheckpoint) { if (isCheckpoint) {
this.headerChain.shift(); this.headerChain.shift();
@ -1622,7 +1608,7 @@ Pool.prototype._handleHeaders = co(function* handleHeaders(peer, packet) {
*/ */
Pool.prototype.handleSendHeaders = co(function* handleSendHeaders(peer, packet) { Pool.prototype.handleSendHeaders = co(function* handleSendHeaders(peer, packet) {
this.emit('sendheader', packet, peer); this.emit('sendheaders', packet, peer);
}); });
/** /**
@ -1645,7 +1631,7 @@ Pool.prototype.handleBlock = co(function* handleBlock(peer, packet) {
}); });
/** /**
* Handle `block` packet. Attempt to add to chain. * Attempt to add block to chain.
* @private * @private
* @param {Peer} peer * @param {Peer} peer
* @param {Block} block * @param {Block} block
@ -1663,7 +1649,7 @@ Pool.prototype.addBlock = co(function* addBlock(peer, block) {
}); });
/** /**
* Handle `block` packet. Attempt to add to chain (without a lock). * Attempt to add block to chain (without a lock).
* @private * @private
* @param {Peer} peer * @param {Peer} peer
* @param {Block} block * @param {Block} block
@ -1673,16 +1659,12 @@ Pool.prototype.addBlock = co(function* addBlock(peer, block) {
Pool.prototype._addBlock = co(function* addBlock(peer, block) { Pool.prototype._addBlock = co(function* addBlock(peer, block) {
var hash = block.hash('hex'); var hash = block.hash('hex');
var isCheckpoint = false; var isCheckpoint = false;
var requested, node, checkpoint; var node, checkpoint;
if (!this.syncing) if (!this.syncing)
return; return;
requested = this.fulfill(peer, hash); if (!this.fulfill(peer, hash)) {
// Someone is sending us blocks without
// us requesting them.
if (!requested) {
peer.invFilter.add(block.hash()); peer.invFilter.add(block.hash());
this.logger.warning( this.logger.warning(
'Received unrequested block: %s (%s).', 'Received unrequested block: %s (%s).',
@ -1817,23 +1799,28 @@ Pool.prototype.handleTX = co(function* handleTX(peer, packet) {
Pool.prototype._handleTX = co(function* handleTX(peer, packet) { Pool.prototype._handleTX = co(function* handleTX(peer, packet) {
var tx = packet.tx; var tx = packet.tx;
var hash = tx.hash('hex');
var requested, missing;
if (peer.lastMerkle) { if (peer.merkleBlock) {
assert(peer.waitingTX > 0); assert(peer.merkleMatches > 0);
if (peer.lastMerkle.hasTX(tx)) { if (peer.merkleBlock.hasTX(tx)) {
peer.lastMerkle.addTX(tx); peer.merkleBlock.addTX(tx);
if (--peer.waitingTX === 0) if (--peer.merkleMatches === 0) {
yield peer.flushMerkle(); yield this.addBlock(peer, peer.merkleBlock);
peer.merkleTime = -1;
peer.merkleBlock = null;
peer.merkleMatches = 0;
}
return; return;
} }
} }
var hash = tx.hash('hex'); requested = this.fulfill(peer, hash);
var requested = this.fulfill(peer, hash);
var missing;
if (!requested) { if (!requested) {
this.logger.warning('Peer sent unrequested tx: %s (%s).', this.logger.warning(
'Peer sent unrequested tx: %s (%s).',
tx.txid(), peer.hostname); tx.txid(), peer.hostname);
peer.invFilter.add(tx.hash()); peer.invFilter.add(tx.hash());
@ -1917,7 +1904,7 @@ Pool.prototype.handleReject = co(function* handleReject(peer, packet) {
Pool.prototype.handleMempool = co(function* handleMempool(peer, packet) { Pool.prototype.handleMempool = co(function* handleMempool(peer, packet) {
var items = []; var items = [];
var i, hashes; var i, hash, hashes;
if (!this.mempool) if (!this.mempool)
return; return;
@ -1930,14 +1917,16 @@ Pool.prototype.handleMempool = co(function* handleMempool(peer, 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(invTypes.TX, hashes[i])); hash = hashes[i];
items.push(new InvItem(invTypes.TX, hash));
}
this.logger.debug('Sending mempool snapshot (%s).', peer.hostname); this.logger.debug('Sending mempool snapshot (%s).', peer.hostname);
this.emit('mempool', peer);
peer.sendInv(items); peer.sendInv(items);
this.emit('mempool', packet, peer);
}); });
/** /**
@ -1970,7 +1959,7 @@ Pool.prototype.handleFilterAdd = co(function* handleFilterAdd(peer, packet) {
*/ */
Pool.prototype.handleFilterClear = co(function* handleFilterClear(peer, packet) { Pool.prototype.handleFilterClear = co(function* handleFilterClear(peer, packet) {
this.emit('filterclear', peer, packet); this.emit('filterclear', packet, peer);
}); });
/** /**
@ -1982,7 +1971,10 @@ Pool.prototype.handleFilterClear = co(function* handleFilterClear(peer, packet)
Pool.prototype.handleMerkleBlock = co(function* handleMerkleBlock(peer, packet) { Pool.prototype.handleMerkleBlock = co(function* handleMerkleBlock(peer, packet) {
var block = packet.block; var block = packet.block;
var ret = new VerifyResult(); var hash = block.hash('hex');
if (!this.syncing)
return;
// Potential DoS. // Potential DoS.
if (!this.options.spv) { if (!this.options.spv) {
@ -1993,7 +1985,15 @@ Pool.prototype.handleMerkleBlock = co(function* handleMerkleBlock(peer, packet)
return; return;
} }
if (peer.lastMerkle) { if (!peer.requestMap.has(hash)) {
this.logger.warning(
'Peer sent an unrequested merkleblock (%s).',
peer.hostname);
peer.destroy();
return;
}
if (peer.merkleBlock) {
this.logger.warning( this.logger.warning(
'Peer sent a merkleblock prematurely (%s).', 'Peer sent a merkleblock prematurely (%s).',
peer.hostname); peer.hostname);
@ -2001,19 +2001,24 @@ Pool.prototype.handleMerkleBlock = co(function* handleMerkleBlock(peer, packet)
return; return;
} }
if (!block.verify(ret)) { if (!block.verify()) {
this.logger.warning( this.logger.warning(
'Peer sent an invalid merkleblock (%s).', 'Peer sent an invalid merkleblock (%s).',
peer.hostname); peer.hostname);
peer.reject(block, 'invalid', ret.reason, ret.score); peer.increaseBan(100);
return; return;
} }
peer.lastMerkle = block; if (block.matches.length === 0) {
peer.waitingTX = block.matches.length; yield this.addBlock(peer, block);
return;
}
if (peer.waitingTX === 0) peer.merkleTime = util.ms();
yield peer.flushMerkle(); peer.merkleBlock = block;
peer.merkleMatches = block.matches.length;
this.emit('merkleblock', packet, peer);
}); });
/** /**
@ -2051,6 +2056,9 @@ Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
var witness = this.options.witness; var witness = this.options.witness;
var result; var result;
if (!this.syncing)
return;
if (!this.options.compact) { if (!this.options.compact) {
this.logger.info('Peer sent unsolicited cmpctblock (%s).', peer.hostname); this.logger.info('Peer sent unsolicited cmpctblock (%s).', peer.hostname);
return; return;
@ -2061,13 +2069,33 @@ Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
return; return;
} }
if (this.compactBlocks.has(hash)) { if (peer.compactBlocks.has(hash)) {
this.logger.debug( this.logger.debug(
'Peer sent us a duplicate compact block (%s).', 'Peer sent us a duplicate compact block (%s).',
peer.hostname); peer.hostname);
return; return;
} }
if (this.compactBlocks.has(hash)) {
this.logger.debug(
'Already waiting for compact block %s (%s).',
hash, peer.hostname);
return;
}
if (!peer.requestMap.has(hash)) {
if (this.options.blockMode !== 1) {
this.logger.warning(
'Peer sent us an unrequested compact block (%s).',
peer.hostname);
peer.destroy();
return;
}
peer.requestMap.insert(hash);
assert(!this.requestMap.has(hash));
this.requestMap.insert(hash);
}
if (!block.verify()) { if (!block.verify()) {
this.logger.debug( this.logger.debug(
'Peer sent an invalid compact block (%s).', 'Peer sent an invalid compact block (%s).',
@ -2086,10 +2114,12 @@ Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
return; return;
} }
if (peer.compactBlocks.size >= 10) { if (this.options.blockMode === 1) {
this.logger.warning('Compact block DoS attempt (%s).', peer.hostname); if (peer.compactBlocks.size >= 10) {
peer.destroy(); this.logger.warning('Compact block DoS attempt (%s).', peer.hostname);
return; peer.destroy();
return;
}
} }
assert(!peer.compactBlocks.has(hash)); assert(!peer.compactBlocks.has(hash));
@ -2101,9 +2131,9 @@ Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
'Received semi-full compact block %s (%s).', 'Received semi-full compact block %s (%s).',
block.rhash(), peer.hostname); block.rhash(), peer.hostname);
this.emit('cmpctblock', block, peer);
peer.send(new packets.GetBlockTxnPacket(block.toRequest())); peer.send(new packets.GetBlockTxnPacket(block.toRequest()));
this.emit('cmpctblock', packet, peer);
}); });
/** /**
@ -2147,11 +2177,11 @@ Pool.prototype.handleGetBlockTxn = co(function* handleGetBlockTxn(peer, packet)
return; return;
} }
this.emit('getblocktxn', req, peer);
res = BIP152.TXResponse.fromBlock(block, req); res = BIP152.TXResponse.fromBlock(block, req);
peer.send(new packets.BlockTxnPacket(res, peer.compactWitness)); peer.send(new packets.BlockTxnPacket(res, peer.compactWitness));
this.emit('getblocktxn', packet, peer);
}); });
/** /**
@ -2161,7 +2191,7 @@ Pool.prototype.handleGetBlockTxn = co(function* handleGetBlockTxn(peer, packet)
* @param {BlockTxnPacket} packet * @param {BlockTxnPacket} packet
*/ */
Peer.prototype.handleBlockTxn = co(function* handleBlockTxn(peer, packet) { Pool.prototype.handleBlockTxn = co(function* handleBlockTxn(peer, packet) {
var res = packet.response; var res = packet.response;
var block = peer.compactBlocks.get(res.hash); var block = peer.compactBlocks.get(res.hash);
@ -2185,9 +2215,9 @@ Peer.prototype.handleBlockTxn = co(function* handleBlockTxn(peer, packet) {
'Filled compact block %s (%s).', 'Filled compact block %s (%s).',
block.rhash(), peer.hostname); block.rhash(), peer.hostname);
this.emit('blocktxn', res, peer);
yield this.addBlock(peer, block.toBlock()); yield this.addBlock(peer, block.toBlock());
this.emit('blocktxn', packet, peer);
}); });
/** /**
@ -2197,7 +2227,7 @@ Peer.prototype.handleBlockTxn = co(function* handleBlockTxn(peer, packet) {
* @param {EncinitPacket} packet * @param {EncinitPacket} packet
*/ */
Peer.prototype.handleEncinit = co(function* handleEncinit(peer, packet) { Pool.prototype.handleEncinit = co(function* handleEncinit(peer, packet) {
this.emit('encinit', packet, peer); this.emit('encinit', packet, peer);
}); });
@ -2208,7 +2238,7 @@ Peer.prototype.handleEncinit = co(function* handleEncinit(peer, packet) {
* @param {EncackPacket} packet * @param {EncackPacket} packet
*/ */
Peer.prototype.handleEncack = co(function* handleEncack(peer, packet) { Pool.prototype.handleEncack = co(function* handleEncack(peer, packet) {
this.emit('encack', packet, peer); this.emit('encack', packet, peer);
}); });
@ -2219,7 +2249,7 @@ Peer.prototype.handleEncack = co(function* handleEncack(peer, packet) {
* @param {AuthChallengePacket} packet * @param {AuthChallengePacket} packet
*/ */
Peer.prototype.handleAuthChallenge = co(function* handleAuthChallenge(peer, packet) { Pool.prototype.handleAuthChallenge = co(function* handleAuthChallenge(peer, packet) {
this.emit('authchallenge', packet, peer); this.emit('authchallenge', packet, peer);
}); });
@ -2230,7 +2260,7 @@ Peer.prototype.handleAuthChallenge = co(function* handleAuthChallenge(peer, pack
* @param {AuthReplyPacket} packet * @param {AuthReplyPacket} packet
*/ */
Peer.prototype.handleAuthReply = co(function* handleAuthReply(peer, packet) { Pool.prototype.handleAuthReply = co(function* handleAuthReply(peer, packet) {
this.emit('authreply', packet, peer); this.emit('authreply', packet, peer);
}); });
@ -2241,7 +2271,7 @@ Peer.prototype.handleAuthReply = co(function* handleAuthReply(peer, packet) {
* @param {AuthProposePacket} packet * @param {AuthProposePacket} packet
*/ */
Peer.prototype.handleAuthPropose = co(function* handleAuthPropose(peer, packet) { Pool.prototype.handleAuthPropose = co(function* handleAuthPropose(peer, packet) {
this.emit('authpropose', packet, peer); this.emit('authpropose', packet, peer);
}); });
@ -2252,8 +2282,8 @@ Peer.prototype.handleAuthPropose = co(function* handleAuthPropose(peer, packet)
* @param {UnknownPacket} packet * @param {UnknownPacket} packet
*/ */
Peer.prototype.handleUnknown = co(function* handleUnknown(peer, packet) { Pool.prototype.handleUnknown = co(function* handleUnknown(peer, packet) {
this.logger.warning('Unknown packet: %s.', packet.cmd); this.logger.warning('Unknown packet: %s (%s).', packet.cmd, peer.hostname);
this.emit('unknown', packet, peer); this.emit('unknown', packet, peer);
}); });
@ -2780,20 +2810,6 @@ Pool.prototype.announceTX = function announceTX(msg) {
peer.announceTX(msg); peer.announceTX(msg);
}; };
/**
* Set a fee rate filter for all peers.
* @param {Rate} rate
*/
Pool.prototype.setFeeRate = function setFeeRate(rate) {
var peer;
this.feeRate = rate;
for (peer = this.peers.head(); peer; peer = peer.next)
peer.sendFeeRate(rate);
};
/** /**
* Attempt to retrieve external IP from icanhazip.com. * Attempt to retrieve external IP from icanhazip.com.
* @returns {Promise} * @returns {Promise}
@ -2898,6 +2914,7 @@ function PoolOptions(options) {
this.seeds = this.network.seeds; this.seeds = this.network.seeds;
this.nodes = []; this.nodes = [];
this.invTimeout = 60000; this.invTimeout = 60000;
this.blockMode = 0;
this.services = common.LOCAL_SERVICES; this.services = common.LOCAL_SERVICES;
this.requiredServices = common.REQUIRED_SERVICES; this.requiredServices = common.REQUIRED_SERVICES;
@ -3091,6 +3108,11 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
this.invTimeout = options.invTimeout; this.invTimeout = options.invTimeout;
} }
if (options.blockMode != null) {
assert(typeof options.blockMode === 'number');
this.blockMode = options.blockMode;
}
if (!this.witness) { if (!this.witness) {
this.services &= ~common.services.WITNESS; this.services &= ~common.services.WITNESS;
this.requiredServices &= ~common.services.WITNESS; this.requiredServices &= ~common.services.WITNESS;