fixes. peer improvements.

This commit is contained in:
Christopher Jeffrey 2016-01-05 01:03:11 -08:00
parent 019ecf4f2a
commit 2f618c961b
4 changed files with 166 additions and 63 deletions

View File

@ -20,9 +20,7 @@ bcoin.output = require('./bcoin/output');
bcoin.tx = require('./bcoin/tx'); bcoin.tx = require('./bcoin/tx');
bcoin.txPool = require('./bcoin/tx-pool'); bcoin.txPool = require('./bcoin/tx-pool');
bcoin.block = require('./bcoin/block'); bcoin.block = require('./bcoin/block');
bcoin.chain = require('./bcoin/chain'); bcoin.chain = require('./bcoin/fullchain');
bcoin.spvChain = require('./bcoin/chain');
bcoin.fullChain = require('./bcoin/fullchain');
bcoin.wallet = require('./bcoin/wallet'); bcoin.wallet = require('./bcoin/wallet');
bcoin.peer = require('./bcoin/peer'); bcoin.peer = require('./bcoin/peer');
bcoin.pool = require('./bcoin/pool'); bcoin.pool = require('./bcoin/pool');

View File

@ -186,7 +186,7 @@ Chain.prototype.resetHeight = function resetHeight(height) {
var self = this; var self = this;
var ahead = this.index.entries.slice(height + 1); var ahead = this.index.entries.slice(height + 1);
assert(height <= this.index.entries.length - 1); assert(height < this.index.entries.length);
if (height === this.index.entries.length - 1) if (height === this.index.entries.length - 1)
return; return;

View File

@ -42,16 +42,21 @@ function Peer(pool, createSocket, options) {
this.destroyed = false; this.destroyed = false;
this.ack = false; this.ack = false;
this.ts = this.options.ts || 0; this.ts = this.options.ts || 0;
this.address = '0.0.0.0';
this.port = network.port; this.host = null;
this.port = 0;
if (this.options.backoff) { if (this.options.backoff) {
setTimeout(function() { setTimeout(function() {
self.socket = createSocket(self, pool); self.socket = createSocket(self, pool);
if (!self.socket)
throw new Error('No socket');
self.emit('socket'); self.emit('socket');
}, this.options.backoff); }, this.options.backoff);
} else { } else {
this.socket = createSocket(this, pool); this.socket = createSocket(this, pool);
if (!this.socket)
throw new Error('No socket');
} }
this._broadcast = { this._broadcast = {
@ -72,7 +77,7 @@ function Peer(pool, createSocket, options) {
interval: this.options.pingInterval || 30000 interval: this.options.pingInterval || 30000
}; };
this.setMaxListeners(4000); this.setMaxListeners(10000);
if (this.socket) if (this.socket)
this._init(); this._init();
@ -85,10 +90,19 @@ inherits(Peer, EventEmitter);
Peer.prototype._init = function init() { Peer.prototype._init = function init() {
var self = this; var self = this;
if (!this.host)
this.host = this.socket.remoteAddress || this.socket._host || null;
if (!this.port)
this.port = this.socket.remotePort || 0;
this.socket.once('connect', function() { this.socket.once('connect', function() {
self.ts = utils.now(); self.ts = utils.now();
self.address = self.socket.remoteAddress; self.connected = true;
self.port = self.socket.remotePort; if (!self.host)
self.host = self.socket.remoteAddress;
if (!self.port)
self.port = self.socket.remotePort;
}); });
this.socket.once('error', function(err) { this.socket.once('error', function(err) {
@ -97,6 +111,7 @@ Peer.prototype._init = function init() {
this.socket.once('close', function() { this.socket.once('close', function() {
self._error('socket hangup'); self._error('socket hangup');
self.connected = false;
}); });
this.socket.on('data', function(chunk) { this.socket.on('data', function(chunk) {
@ -115,7 +130,7 @@ Peer.prototype._init = function init() {
this.once('version', function() { this.once('version', function() {
self.pool.emit('debug', self.pool.emit('debug',
'Sent version (%s): height=%s', 'Sent version (%s): height=%s',
self.address, this.pool.chain.getStartHeight()); self.host, this.pool.chain.getStartHeight());
}); });
} }
@ -133,8 +148,10 @@ Peer.prototype._init = function init() {
})); }));
this._req('verack', function(err, payload) { this._req('verack', function(err, payload) {
if (err) if (err) {
self.destroy();
return self._error(err); return self._error(err);
}
self.ack = true; self.ack = true;
self.emit('ack'); self.emit('ack');
self.ts = utils.now(); self.ts = utils.now();
@ -358,12 +375,12 @@ Peer.prototype._onPacket = function onPacket(packet) {
if (cmd === 'merkleblock' || cmd === 'block') { if (cmd === 'merkleblock' || cmd === 'block') {
payload.network = true; payload.network = true;
payload.relayedBy = this.address; payload.relayedBy = this.host || '0.0.0.0';
payload = bcoin.block(payload, cmd); payload = bcoin.block(payload, cmd);
this.lastBlock = payload; this.lastBlock = payload;
} else if (cmd === 'tx') { } else if (cmd === 'tx') {
payload.network = true; payload.network = true;
payload.relayedBy = this.address; payload.relayedBy = this.host || '0.0.0.0';
payload = bcoin.tx(payload, this.lastBlock); payload = bcoin.tx(payload, this.lastBlock);
} }
@ -411,11 +428,14 @@ Peer.prototype._handleAddr = function handleAddr(addrs) {
service: addr.service, service: addr.service,
ipv4: addr.ipv4, ipv4: addr.ipv4,
ipv6: addr.ipv6, ipv6: addr.ipv6,
address: addr.ipv4, host: addr.ipv4,
address6: addr.ipv6, host6: addr.ipv6,
port: addr.port, port: addr.port,
host: addr.ipv4 + ':' + addr.port, addr: addr.ipv4 + ':' + addr.port,
host6: '[' + addr.ipv6 + ']:' + addr.port addr6: '[' + addr.ipv6 + ']:' + addr.port,
// Deprecated:
address: addr.ipv4,
address6: addr.ipv6
}); });
}, this); }, this);
}; };
@ -432,21 +452,15 @@ Peer.prototype._handleGetAddr = function handleGetAddr() {
var used = []; var used = [];
var peers, addrs; var peers, addrs;
peers = [].concat(
this.pool.peers.pending,
this.pool.peers.block,
this.pool.peers.load
).filter(Boolean);
// NOTE: For IPv6 BTC uses: // NOTE: For IPv6 BTC uses:
// '0000:0000:0000:0000:0000:xxxx:xxxx:ffff' // '0000:0000:0000:0000:0000:xxxx:xxxx:ffff'
peers = peers.map(function(peer) { peers = this.pool.peers.all.map(function(peer) {
if (!peer.socket || !peer.socket.remoteAddress) if (!peer.socket || !peer.socket.remoteAddress)
return; return;
return { return {
host: peer.socket.remoteAddress, host: peer.socket.remoteAddress,
port: peer.socket.remotePort || 8333, port: peer.socket.remotePort || network.port,
ts: peer.ts ts: peer.ts
}; };
}).filter(function(peer) { }).filter(function(peer) {
@ -532,14 +546,14 @@ Peer.prototype._handleHeaders = function handleHeaders(headers) {
Peer.prototype.loadHeaders = function loadHeaders(hashes, stop) { Peer.prototype.loadHeaders = function loadHeaders(hashes, stop) {
this.emit('debug', this.emit('debug',
'Requesting headers packet from %s with getheaders', 'Requesting headers packet from %s with getheaders',
this.address); this.host);
this._write(this.framer.getHeaders(hashes, stop)); this._write(this.framer.getHeaders(hashes, stop));
}; };
Peer.prototype.loadBlocks = function loadBlocks(hashes, stop) { Peer.prototype.loadBlocks = function loadBlocks(hashes, stop) {
this.emit('debug', this.emit('debug',
'Requesting inv packet from %s with getblocks', 'Requesting inv packet from %s with getblocks',
this.address); this.host);
this._write(this.framer.getBlocks(hashes, stop)); this._write(this.framer.getBlocks(hashes, stop));
}; };

View File

@ -43,6 +43,10 @@ function Pool(options) {
this.size = options.size || 32; this.size = options.size || 32;
this.parallel = options.parallel || 2000; this.parallel = options.parallel || 2000;
this.redundancy = options.redundancy || 2; this.redundancy = options.redundancy || 2;
this.seeds = network.seeds.slice();
this._createConnection = options.createConnection;
this._createSocket = options.createSocket;
if (!this.options.fullNode) { if (!this.options.fullNode) {
if (this.options.headers == null) if (this.options.headers == null)
@ -79,9 +83,7 @@ function Pool(options) {
this.maxRetries = options.maxRetries || 42; this.maxRetries = options.maxRetries || 42;
this.requestTimeout = options.requestTimeout || 10000; this.requestTimeout = options.requestTimeout || 10000;
Chain = bcoin.fullChain; this.chain = new bcoin.chain({
this.chain = new Chain({
storage: this.storage, storage: this.storage,
fullNode: this.options.fullNode fullNode: this.options.fullNode
}); });
@ -100,7 +102,9 @@ function Pool(options) {
// Peers that are still connecting // Peers that are still connecting
pending: [], pending: [],
// Peers that are loading block ids // Peers that are loading block ids
load: null load: null,
// All peers
all: []
}; };
this.block = { this.block = {
@ -127,7 +131,7 @@ function Pool(options) {
}; };
// Currently broadcasted TXs // Currently broadcasted TXs
this.tx = { this.inv = {
list: [], list: [],
timeout: options.txTimeout || 60000 timeout: options.txTimeout || 60000
}; };
@ -136,9 +140,6 @@ function Pool(options) {
this.options.wallets = this.options.wallets || []; this.options.wallets = this.options.wallets || [];
this.wallets = []; this.wallets = [];
this.createSocket = options.createConnection || options.createSocket;
assert(this.createSocket);
this.chain.on('debug', function() { this.chain.on('debug', function() {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
self.emit.apply(self, ['debug'].concat(args)); self.emit.apply(self, ['debug'].concat(args));
@ -240,6 +241,34 @@ Pool.prototype._stopInterval = function _stopInterval() {
delete this._interval; delete this._interval;
}; };
Pool.prototype.createConnection = function createConnection(peer, pool) {
var addr, parts, host, net, socket, port;
if (pool._createConnection)
return pool._createConnection(peer, pool);
addr = pool.usableSeed(true);
parts = addr.split(':');
host = parts[0];
port = +parts[1] || network.port;
peer.host = host;
peer.port = port;
if (this._createSocket) {
socket = this._createSocket(port, host);
} else {
net = require('net');
socket = net.connect(port, host);
}
socket.on('connect', function() {
pool.emit('debug', 'Connected to %s:%d', host, port);
});
return socket;
};
Pool.prototype._addLoader = function _addLoader() { Pool.prototype._addLoader = function _addLoader() {
var self = this; var self = this;
@ -251,13 +280,14 @@ Pool.prototype._addLoader = function _addLoader() {
if (this.peers.load != null) if (this.peers.load != null)
return; return;
peer = new bcoin.peer(this, this.createSocket, { peer = new bcoin.peer(this, this.createConnection, {
backoff: 750 * Math.random(), backoff: 750 * Math.random(),
startHeight: this.options.startHeight, startHeight: this.options.startHeight,
relay: this.options.relay relay: this.options.relay
}); });
this.peers.load = peer; this.peers.load = peer;
this.peers.all.push(peer);
peer.on('error', function(err) { peer.on('error', function(err) {
self.emit('error', err, peer); self.emit('error', err, peer);
@ -278,10 +308,6 @@ Pool.prototype._addLoader = function _addLoader() {
}); });
peer.once('ack', function() { peer.once('ack', function() {
if (!peer.socket) {
peer.destroy();
return;
}
peer.updateWatch(); peer.updateWatch();
if (!self._load()) if (!self._load())
self._stopTimer(); self._stopTimer();
@ -330,7 +356,7 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer) {
this.emit('debug', this.emit('debug',
'Recieved %s headers from %s', 'Recieved %s headers from %s',
headers.length, headers.length,
peer.address); peer.host);
if (headers.length > 2000) { if (headers.length > 2000) {
peer.destroy(); peer.destroy();
@ -376,7 +402,7 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer) {
this.emit('debug', this.emit('debug',
'Requesting %s block packets from %s with getdata', 'Requesting %s block packets from %s with getdata',
this.request.queue.length, this.request.queue.length,
peer.address peer.host
); );
}; };
@ -391,7 +417,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
this.emit('debug', this.emit('debug',
'Recieved %s block hashes from %s', 'Recieved %s block hashes from %s',
hashes.length, hashes.length,
peer.address); peer.host);
if (hashes.length > 500) { if (hashes.length > 500) {
peer.destroy(); peer.destroy();
@ -431,7 +457,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
this.emit('debug', this.emit('debug',
'Requesting %s block packets from %s with getdata', 'Requesting %s block packets from %s with getdata',
this.request.queue.length, this.request.queue.length,
peer.address peer.host
); );
}; };
@ -584,13 +610,19 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
if (this.peers.block.length + this.peers.pending.length >= this.size) if (this.peers.block.length + this.peers.pending.length >= this.size)
return; return;
peer = new bcoin.peer(this, this.createSocket, { if (!this._createConnection && !this.usableSeed()) {
setTimeout(this._addPeer.bind(this, 0), this.backoff.max);
return;
}
peer = new bcoin.peer(this, this.createConnection, {
backoff: backoff, backoff: backoff,
startHeight: this.options.startHeight, startHeight: this.options.startHeight,
relay: this.options.relay relay: this.options.relay
}); });
this.peers.pending.push(peer); this.peers.pending.push(peer);
this.peers.all.push(peer);
peer._retry = 0; peer._retry = 0;
@ -608,17 +640,12 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
self._removePeer(peer); self._removePeer(peer);
if (self.destroyed) if (self.destroyed)
return; return;
self._addPeer(Math.max(backoff + self.backoff.delta, self.backoff.max)); self._addPeer(Math.min(backoff + self.backoff.delta, self.backoff.max));
}); });
peer.once('ack', function() { peer.once('ack', function() {
var i; var i;
if (!peer.socket) {
peer.destroy();
return;
}
if (self.destroyed) if (self.destroyed)
return; return;
@ -630,8 +657,8 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
peer.updateWatch(); peer.updateWatch();
self.tx.list.forEach(function(entry) { self.inv.list.forEach(function(entry) {
var result = peer.broadcast(entry.tx); var result = peer.broadcast(entry.msg);
if (!result) if (!result)
return; return;
@ -677,6 +704,16 @@ Pool.prototype._addPeer = function _addPeer(backoff) {
}); });
peer.on('addr', function(addr) { peer.on('addr', function(addr) {
var host = addr.ipv4 + ':' + addr.port;
if (self.seeds.length > self.size * 2)
self.seeds = network.seeds.slice();
if (!~self.seeds.indexOf(host)) {
self.emit('debug', 'Found new peer: %s', host);
self.seeds.push(host);
}
self.emit('addr', addr, peer); self.emit('addr', addr, peer);
}); });
@ -720,6 +757,10 @@ Pool.prototype._removePeer = function _removePeer(peer) {
if (i !== -1) if (i !== -1)
this.peers.block.splice(i, 1); this.peers.block.splice(i, 1);
i = this.peers.all.indexOf(peer);
if (i !== -1)
this.peers.all.splice(i, 1);
if (this.peers.load === peer) if (this.peers.load === peer)
this.peers.load = null; this.peers.load = null;
}; };
@ -1178,7 +1219,7 @@ Pool.prototype.getBlock = function getBlock(hash, cb) {
}; };
Pool.prototype.sendBlock = function sendBlock(block) { Pool.prototype.sendBlock = function sendBlock(block) {
return this.sendTX(block); return this.broadcast(block);
}; };
Pool.prototype.getTX = function getTX(hash, range, cb) { Pool.prototype.getTX = function getTX(hash, range, cb) {
@ -1248,23 +1289,27 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
}; };
Pool.prototype.sendTX = function sendTX(tx) { Pool.prototype.sendTX = function sendTX(tx) {
return this.broadcast(tx);
};
Pool.prototype.broadcast = function broadcast(msg) {
var self = this; var self = this;
var e = new EventEmitter(); var e = new EventEmitter();
var entry = { var entry = {
tx: tx, msg: msg,
e: e, e: e,
timer: setTimeout(function() { timer: setTimeout(function() {
var i = self.tx.list.indexOf(entry); var i = self.inv.list.indexOf(entry);
if (i !== -1) if (i !== -1)
self.tx.list.splice(i, 1); self.inv.list.splice(i, 1);
}, this.tx.timeout) }, this.inv.timeout)
}; };
this.tx.list.push(entry); this.inv.list.push(entry);
this.peers.block.forEach(function(peer) { this.peers.block.forEach(function(peer) {
var result = peer.broadcast(tx); var result = peer.broadcast(msg);
if (!result) return; if (!result) return;
result[0].once('request', function() { result[0].once('request', function() {
e.emit('ack', peer); e.emit('ack', peer);
@ -1287,9 +1332,9 @@ Pool.prototype.destroy = function destroy() {
item.finish(null); item.finish(null);
}); });
this.tx.list.forEach(function(tx) { this.inv.list.forEach(function(entry) {
clearTimeout(tx.timer); clearTimeout(entry.timer);
tx.timer = null; entry.timer = null;
}); });
this.peers.pending.slice().forEach(function(peer) { this.peers.pending.slice().forEach(function(peer) {
@ -1306,6 +1351,52 @@ Pool.prototype.destroy = function destroy() {
this.load.timer = null; this.load.timer = null;
}; };
Pool.prototype.getPeer = function getPeer(addr) {
var parts, host, port, i, peer;
if (!addr)
return;
parts = addr.split(':');
host = parts[0];
port = +parts[1] || network.port;
for (i = 0; i < this.peers.all.length; i++) {
peer = this.peers.all[i];
if (peer.host === host)
return peer;
}
};
Pool.prototype.usableSeed = function usableSeed(addrs, force) {
var i, addr;
if (typeof addrs === 'boolean') {
force = addrs;
addrs = null;
}
if (!addrs)
addrs = this.seeds;
for (i = 0; i < addrs.length; i++) {
if (!this.getPeer(addrs[i]))
return addrs[i];
}
if (!force)
return;
if (addrs.length === 1)
return addrs[0];
do {
addr = addrs[Math.random() * (addrs.length - 1) | 0];
} while (this.peers.load && this.getPeer(addr) === this.peers.load);
return addr;
};
Pool.prototype.toJSON = function toJSON() { Pool.prototype.toJSON = function toJSON() {
return { return {
v: 1, v: 1,