pool: refactor host and peer list.

This commit is contained in:
Christopher Jeffrey 2016-08-25 19:30:56 -07:00
parent 159679e5eb
commit 0ff93dc705
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
6 changed files with 132 additions and 130 deletions

View File

@ -35,7 +35,7 @@ index-address: false
# Pool # Pool
# selfish: false # selfish: false
headers: false headers: false
compact: true compact: false
bip151: true bip151: true
# proxy-server: localhost # proxy-server: localhost
# preferred-seed: seed.bitcoin.sipa.be # preferred-seed: seed.bitcoin.sipa.be

View File

@ -11,6 +11,7 @@ var utils = require('../utils/utils');
var IP = require('../utils/ip'); var IP = require('../utils/ip');
var assert = utils.assert; var assert = utils.assert;
var constants = bcoin.constants; var constants = bcoin.constants;
var NetworkAddress = bcoin.packets.NetworkAddress;
var fs; var fs;
try { try {
@ -383,7 +384,7 @@ RPC.prototype.addnode = function addnode(args, callback) {
node = toString(args[0]); node = toString(args[0]);
cmd = toString(args[1]); cmd = toString(args[1]);
host = bcoin.packets.NetworkAddress.fromHostname(node, this.network); host = NetworkAddress.fromHostname(node, this.network);
switch (cmd) { switch (cmd) {
case 'add': case 'add':
@ -392,7 +393,7 @@ RPC.prototype.addnode = function addnode(args, callback) {
case 'remove': case 'remove':
for (i = 0; i < this.pool.seeds.length; i++) { for (i = 0; i < this.pool.seeds.length; i++) {
seed = this.pool.seeds[i]; seed = this.pool.seeds[i];
if (seed.host === host.host) { if (seed.hostname === host.hostname) {
this.pool.seeds.splice(i, 1); this.pool.seeds.splice(i, 1);
break; break;
} }
@ -412,7 +413,7 @@ RPC.prototype.disconnectnode = function disconnectnode(args, callback) {
return callback(new RPCError('disconnectnode "node"')); return callback(new RPCError('disconnectnode "node"'));
node = toString(args[0]); node = toString(args[0]);
node = IP.normalize(node); node = NetworkAddress.fromHostname(node, this.network);
peer = this.pool.peers.get(node); peer = this.pool.peers.get(node);
if (peer) if (peer)
@ -543,7 +544,7 @@ RPC.prototype.setban = function setban(args, callback) {
} }
host = toString(args[0]); host = toString(args[0]);
ip = IP.normalize(host); ip = NetworkAddress.fromHostname(host, this.network);
switch (args[1]) { switch (args[1]) {
case 'add': case 'add':

View File

@ -896,6 +896,26 @@ NetworkAddress.prototype.hasWitness = function hasWitness() {
return (this.services & constants.services.WITNESS) !== 0; return (this.services & constants.services.WITNESS) !== 0;
}; };
/**
* Set host.
* @param {String} host
*/
NetworkAddress.prototype.setHost = function setHost(host) {
this.host = host;
this.hostname = IP.hostname(host, this.port);
};
/**
* Set port.
* @param {Number} port
*/
NetworkAddress.prototype.setPort = function setPort(port) {
this.host = port;
this.hostname = IP.hostname(this.host, port);
};
/** /**
* Inspect the network address. * Inspect the network address.
* @returns {Object} * @returns {Object}

View File

@ -2259,7 +2259,7 @@ Peer.prototype.sendCompact = function sendCompact() {
*/ */
Peer.prototype.isMisbehaving = function isMisbehaving() { Peer.prototype.isMisbehaving = function isMisbehaving() {
return this.pool.hosts.isMisbehaving(this.host); return this.pool.hosts.isMisbehaving(this);
}; };
/** /**
@ -2268,7 +2268,7 @@ Peer.prototype.isMisbehaving = function isMisbehaving() {
*/ */
Peer.prototype.isIgnored = function isIgnored() { Peer.prototype.isIgnored = function isIgnored() {
return this.pool.hosts.isIgnored(this.host); return this.pool.hosts.isIgnored(this);
}; };
/** /**

View File

@ -170,8 +170,7 @@ Pool.prototype._initOptions = function _initOptions() {
this.address.ts = utils.now(); this.address.ts = utils.now();
this.address.services = this.services; this.address.services = this.services;
this.address.port = this.port; this.address.setPort(this.port);
this.address.hostname = IP.hostname(this.address.host, this.port);
if (this.options.maxPeers != null) if (this.options.maxPeers != null)
this.maxPeers = this.options.maxPeers; this.maxPeers = this.options.maxPeers;
@ -320,8 +319,7 @@ Pool.prototype._open = function _open(callback) {
self.logger.error(err); self.logger.error(err);
if (ip) { if (ip) {
self.address.host = ip; self.address.setHost(ip);
self.address.hostname = IP.hostname(ip, self.address.port);
self.logger.info('External IP found: %s.', ip); self.logger.info('External IP found: %s.', ip);
} }
@ -483,7 +481,7 @@ Pool.prototype.unlisten = function unlisten(callback) {
*/ */
Pool.prototype._handleLeech = function _handleLeech(socket) { Pool.prototype._handleLeech = function _handleLeech(socket) {
var hostname, host; var hostname, addr;
if (!socket.remoteAddress) { if (!socket.remoteAddress) {
this.logger.debug('Ignoring disconnected leech.'); this.logger.debug('Ignoring disconnected leech.');
@ -491,24 +489,22 @@ Pool.prototype._handleLeech = function _handleLeech(socket) {
return; return;
} }
host = IP.normalize(socket.remoteAddress); hostname = IP.hostname(socket.remoteAddress, socket.remotePort);
addr = NetworkAddress.fromHostname(hostname, this.network);
if (this.peers.leeches.length >= this.maxLeeches) { if (this.peers.leeches.length >= this.maxLeeches) {
hostname = IP.hostname(host, socket.remotePort);
this.logger.debug('Ignoring leech: too many leeches (%s).', hostname); this.logger.debug('Ignoring leech: too many leeches (%s).', hostname);
socket.destroy(); socket.destroy();
return; return;
} }
if (this.hosts.isMisbehaving(host)) { if (this.hosts.isMisbehaving(addr)) {
hostname = IP.hostname(host, socket.remotePort);
this.logger.debug('Ignoring misbehaving leech (%s).', hostname); this.logger.debug('Ignoring misbehaving leech (%s).', hostname);
socket.destroy(); socket.destroy();
return; return;
} }
if (this.hosts.isIgnored(host)) { if (this.hosts.isIgnored(addr)) {
hostname = IP.hostname(host, socket.remotePort);
this.logger.debug('Ignoring leech (%s).', hostname); this.logger.debug('Ignoring leech (%s).', hostname);
socket.destroy(); socket.destroy();
return; return;
@ -604,7 +600,7 @@ Pool.prototype.stopInterval = function stopInterval() {
Pool.prototype.addLoader = function addLoader() { Pool.prototype.addLoader = function addLoader() {
var self = this; var self = this;
var peer, host; var peer, addr;
if (!this.loaded) if (!this.loaded)
return; return;
@ -614,8 +610,8 @@ Pool.prototype.addLoader = function addLoader() {
return; return;
} }
host = this.getLoaderHost(); addr = this.getLoaderHost();
peer = this.peers.get(host); peer = this.peers.get(addr);
if (peer) { if (peer) {
this.peers.repurpose(peer); this.peers.repurpose(peer);
@ -627,7 +623,7 @@ Pool.prototype.addLoader = function addLoader() {
} }
peer = this.createPeer({ peer = this.createPeer({
host: host, host: addr,
type: bcoin.peer.types.LOADER type: bcoin.peer.types.LOADER
}); });
@ -1186,7 +1182,7 @@ Pool.prototype.createPeer = function createPeer(options) {
version.services.toString(2), version.services.toString(2),
version.agent); version.agent);
bcoin.time.add(peer.host, version.ts); bcoin.time.add(peer.hostname, version.ts);
self.emit('version', version, peer); self.emit('version', version, peer);
}); });
@ -1365,7 +1361,7 @@ Pool.prototype.addLeech = function addLeech(socket) {
Pool.prototype.addPeer = function addPeer() { Pool.prototype.addPeer = function addPeer() {
var self = this; var self = this;
var peer, host; var peer, addr;
if (!this.loaded) if (!this.loaded)
return; return;
@ -1377,13 +1373,13 @@ Pool.prototype.addPeer = function addPeer() {
if (!this.peers.load) if (!this.peers.load)
return; return;
host = this.hosts.getHost(); addr = this.hosts.getHost();
if (!host) if (!addr)
return; return;
peer = this.createPeer({ peer = this.createPeer({
host: host, host: addr,
type: bcoin.peer.types.REGULAR type: bcoin.peer.types.REGULAR
}); });
@ -1771,9 +1767,8 @@ Pool.prototype.setMisbehavior = function setMisbehavior(peer, score) {
peer.banScore += score; peer.banScore += score;
if (peer.banScore >= constants.BAN_SCORE) { if (peer.banScore >= constants.BAN_SCORE) {
this.hosts.ban(peer.host); this.ban(peer);
this.logger.debug('Ban threshold exceeded (%s).', peer.host); this.logger.debug('Ban threshold exceeded (%s).', peer.host);
peer.destroy();
return true; return true;
} }
@ -1782,14 +1777,14 @@ Pool.prototype.setMisbehavior = function setMisbehavior(peer, score) {
/** /**
* Ban a peer. * Ban a peer.
* @param {String|NetworkAddress} host * @param {NetworkAddress} host
*/ */
Pool.prototype.ban = function ban(host) { Pool.prototype.ban = function ban(addr) {
var peer = this.peers.get(host); var peer = this.peers.get(addr);
this.logger.debug('Banning peer (%s).', host); this.logger.debug('Banning peer (%s).', addr.hostname);
this.hosts.ban(host); this.hosts.ban(addr);
if (peer) if (peer)
peer.destroy(); peer.destroy();
@ -1797,21 +1792,21 @@ Pool.prototype.ban = function ban(host) {
/** /**
* Unban a peer. * Unban a peer.
* @param {String|NetworkAddress} host * @param {String|NetworkAddress} addr
*/ */
Pool.prototype.unban = function unban(host) { Pool.prototype.unban = function unban(addr) {
this.hosts.unban(host); this.hosts.unban(addr);
}; };
/** /**
* Test whether the host/peer is banned. * Test whether the host/peer is banned.
* @param {String} host * @param {NetworkAddress} addr
* @returns {Boolean} * @returns {Boolean}
*/ */
Pool.prototype.isMisbehaving = function isMisbehaving(host) { Pool.prototype.isMisbehaving = function isMisbehaving(addr) {
return this.hosts.isMisbehaving(host); return this.hosts.isMisbehaving(addr);
}; };
/** /**
@ -1819,20 +1814,24 @@ Pool.prototype.isMisbehaving = function isMisbehaving(host) {
* @param {Peer} peer * @param {Peer} peer
*/ */
Pool.prototype.ignore = function ignore(peer) { Pool.prototype.ignore = function ignore(addr) {
this.logger.debug('Ignoring peer (%s).', peer.hostname); var peer = this.peers.get(addr);
this.hosts.ignore(peer.host);
peer.destroy(); this.logger.debug('Ignoring peer (%s).', addr.hostname);
this.hosts.ignore(addr);
if (peer)
peer.destroy();
}; };
/** /**
* Test whether the host/peer is ignored. * Test whether the host/peer is ignored.
* @param {String} host * @param {NetworkAddress} addr
* @returns {Boolean} * @returns {Boolean}
*/ */
Pool.prototype.isIgnored = function isIgnored(host) { Pool.prototype.isIgnored = function isIgnored(addr) {
return this.hosts.isIgnored(host); return this.hosts.isIgnored(addr);
}; };
/** /**
@ -1915,14 +1914,15 @@ function PeerList(pool) {
this.load = null; this.load = null;
// All peers // All peers
this.all = []; this.all = [];
// Map of hosts // Map of hostnames
this.map = {}; this.map = {};
} }
PeerList.prototype.addPending = function addPending(peer) { PeerList.prototype.addPending = function addPending(peer) {
this.pending.push(peer); this.pending.push(peer);
this.all.push(peer); this.all.push(peer);
this.map[peer.host] = peer; assert(!this.map[peer.hostname]);
this.map[peer.hostname] = peer;
}; };
PeerList.prototype.promote = function promote(peer) { PeerList.prototype.promote = function promote(peer) {
@ -1936,7 +1936,8 @@ PeerList.prototype.remove = function remove(peer) {
utils.binaryRemove(this.leeches, peer, compare); utils.binaryRemove(this.leeches, peer, compare);
utils.binaryRemove(this.all, peer, compare); utils.binaryRemove(this.all, peer, compare);
delete this.map[peer.host]; assert(this.map[peer.hostname]);
delete this.map[peer.hostname];
if (this.load === peer) { if (this.load === peer) {
this.pool.logger.info('Removed loader peer (%s).', peer.hostname); this.pool.logger.info('Removed loader peer (%s).', peer.hostname);
@ -1946,11 +1947,11 @@ PeerList.prototype.remove = function remove(peer) {
PeerList.prototype.repurpose = function repurpose(peer) { PeerList.prototype.repurpose = function repurpose(peer) {
assert(peer.type === bcoin.peer.types.REGULAR); assert(peer.type === bcoin.peer.types.REGULAR);
peer.type = bcoin.peer.types.LOADER;
utils.binaryRemove(this.pending, peer, compare); utils.binaryRemove(this.pending, peer, compare);
utils.binaryRemove(this.regular, peer, compare); utils.binaryRemove(this.regular, peer, compare);
assert(!this.peer.load); peer.type = bcoin.peer.types.LOADER;
this.peers.load = peer; assert(!this.load);
this.load = peer;
}; };
PeerList.prototype.isFull = function isFull() { PeerList.prototype.isFull = function isFull() {
@ -1958,19 +1959,19 @@ PeerList.prototype.isFull = function isFull() {
}; };
PeerList.prototype.addLeech = function addLeech(peer) { PeerList.prototype.addLeech = function addLeech(peer) {
this.peers.leeches.push(peer); this.leeches.push(peer);
this.peers.all.push(peer); this.all.push(peer);
this.peers.map[peer.host] = peer; this.map[peer.hostname] = peer;
}; };
PeerList.prototype.addLoader = function addLoader(peer) { PeerList.prototype.addLoader = function addLoader(peer) {
this.load = peer; this.load = peer;
this.all.push(peer); this.all.push(peer);
this.map[peer.host] = peer; this.map[peer.hostname] = peer;
}; };
PeerList.prototype.get = function get(host) { PeerList.prototype.get = function get(addr) {
return this.map[host]; return this.map[addr.hostname];
}; };
PeerList.prototype.destroy = function destroy() { PeerList.prototype.destroy = function destroy() {
@ -2030,15 +2031,15 @@ HostList.prototype.clear = function clear() {
*/ */
HostList.prototype.getLoaderHost = function getLoaderHost() { HostList.prototype.getLoaderHost = function getLoaderHost() {
var host = this.getRandom(this.seeds); var addr = this.getRandom(this.seeds);
if (host) if (addr)
return host; return addr;
host = this.getRandom(this.items); addr = this.getRandom(this.items);
if (host) if (addr)
return host; return addr;
this.pool.logger.warning('All seeds banned or ignored. Clearing...'); this.pool.logger.warning('All seeds banned or ignored. Clearing...');
this.clear(); this.clear();
@ -2052,10 +2053,10 @@ HostList.prototype.getLoaderHost = function getLoaderHost() {
*/ */
HostList.prototype.getHost = function getHost() { HostList.prototype.getHost = function getHost() {
var host = this.getRandom(this.seeds, true); var addr = this.getRandom(this.seeds, true);
if (host) if (addr)
return host; return addr;
return this.getRandom(this.items, true); return this.getRandom(this.items, true);
}; };
@ -2070,22 +2071,22 @@ HostList.prototype.getHost = function getHost() {
HostList.prototype.getRandom = function getRandom(hosts, unique) { HostList.prototype.getRandom = function getRandom(hosts, unique) {
var index = Math.random() * hosts.length | 0; var index = Math.random() * hosts.length | 0;
var last = -1; var last = -1;
var i, host; var i, addr;
for (i = 0; i < hosts.length; i++) { for (i = 0; i < hosts.length; i++) {
host = hosts[i]; addr = hosts[i];
if (this.isMisbehaving(host.host)) if (this.isMisbehaving(addr))
continue; continue;
if (this.isIgnored(host.host)) if (this.isIgnored(addr))
continue; continue;
if (unique && this.pool.peers.get(host.host)) if (unique && this.pool.peers.get(addr))
continue; continue;
if (i >= index) if (i >= index)
return host; return addr;
last = i; last = i;
} }
@ -2098,92 +2099,75 @@ HostList.prototype.getRandom = function getRandom(hosts, unique) {
/** /**
* Add host to host list. * Add host to host list.
* @param {String|NetworkAddress} host * @param {NetworkAddress} addr
* @returns {Boolean} * @returns {Boolean}
*/ */
HostList.prototype.add = function add(host) { HostList.prototype.add = function add(addr) {
if (typeof host === 'string')
host = NetworkAddress.fromHostname(host, this.network);
if (this.items.length > 500) if (this.items.length > 500)
return; return;
if (this.map[host.host]) if (this.map[addr.hostname])
return; return;
utils.binaryInsert(this.items, host, compare); utils.binaryInsert(this.items, addr, compare);
this.map[host.host] = host; this.map[addr.hostname] = addr;
return host; return addr;
}; };
/** /**
* Remove host from host list. * Remove host from host list.
* @param {String|NetworkAddress} host * @param {NetworkAddress} addr
* @returns {Boolean} * @returns {Boolean}
*/ */
HostList.prototype.remove = function remove(host) { HostList.prototype.remove = function remove(addr) {
if (host.host) addr = this.map[addr.hostname];
host = host.host;
host = this.map[host]; if (!addr)
if (!host)
return; return;
utils.binaryRemove(this.items, host, compare); utils.binaryRemove(this.items, addr, compare);
delete this.map[host]; delete this.map[addr.hostname];
return host; return addr;
}; };
/** /**
* Increase peer's ban score. * Increase peer's ban score.
* @param {String} host * @param {NetworkAddress} addr
*/ */
HostList.prototype.ban = function ban(host) { HostList.prototype.ban = function ban(addr) {
if (host.host) this.misbehaving[addr.host] = utils.now();
host = host.host; this.remove(addr);
this.misbehaving[host] = utils.now();
this.remove(host);
}; };
/** /**
* Unban host. * Unban host.
* @param {String} host * @param {NetworkAddress} addr
*/ */
HostList.prototype.unban = function unban(host) { HostList.prototype.unban = function unban(addr) {
if (host.host) delete this.misbehaving[addr.host];
host = host.host; delete this.ignored[addr.host];
delete this.misbehaving[host];
delete this.ignored[host];
}; };
/** /**
* Test whether the host/peer is banned. * Test whether the host/peer is banned.
* @param {String} host * @param {NetworkAddress} addr
* @returns {Boolean} * @returns {Boolean}
*/ */
HostList.prototype.isMisbehaving = function isMisbehaving(host) { HostList.prototype.isMisbehaving = function isMisbehaving(addr) {
var time; var time = this.misbehaving[addr.host];
if (host.host)
host = host.host;
time = this.misbehaving[host];
if (time != null) { if (time != null) {
if (utils.now() > time + constants.BAN_TIME) { if (utils.now() > time + constants.BAN_TIME) {
delete this.misbehaving[host]; delete this.misbehaving[addr.host];
return false; return false;
} }
return true; return true;
@ -2194,28 +2178,22 @@ HostList.prototype.isMisbehaving = function isMisbehaving(host) {
/** /**
* Ignore peer. * Ignore peer.
* @param {Peer} peer * @param {NetworkAddress} addr
*/ */
HostList.prototype.ignore = function ignore(host) { HostList.prototype.ignore = function ignore(addr) {
if (host.host) if (!this.remove(addr))
host = host.host; this.ignored[addr.host] = true;
if (!this.remove(host))
this.ignored[host] = true;
}; };
/** /**
* Test whether the host/peer is ignored. * Test whether the host/peer is ignored.
* @param {String} host * @param {NetworkAddress} addr
* @returns {Boolean} * @returns {Boolean}
*/ */
HostList.prototype.isIgnored = function isIgnored(host) { HostList.prototype.isIgnored = function isIgnored(addr) {
if (host.host) return this.ignored[addr.host] === true;
host = host.host;
return this.ignored[host] === true;
}; };
/** /**

View File

@ -54,7 +54,10 @@ exports.parseHost = function parseHost(addr) {
*/ */
exports.hostname = function hostname(host, port) { exports.hostname = function hostname(host, port) {
if (exports.version(host) === 6) var version = exports.version(host);
if (version !== -1)
host = exports.normalize(host);
if (version === 6)
host = '[' + host + ']'; host = '[' + host + ']';
return host + ':' + port; return host + ':' + port;
}; };