|
|
@ -1,10 +1,8 @@ |
|
|
|
|
|
|
|
|
|
|
|
var imports = require('soop').imports(); |
|
|
|
var extend = imports.extend || require('extend'); |
|
|
|
var log = imports.log || require('../util/log'); |
|
|
|
var bitcoreDefaults = imports.config || require('../config'); |
|
|
|
var Connection = imports.Connection || require ('./Connection'); |
|
|
|
|
|
|
|
var Peer = imports.Peer || require('./Peer'); |
|
|
|
|
|
|
|
GetAdjustedTime = imports.GetAdjustedTime || function () { |
|
|
@ -13,11 +11,13 @@ GetAdjustedTime = imports.GetAdjustedTime || function () { |
|
|
|
}; |
|
|
|
|
|
|
|
function PeerManager(config) { |
|
|
|
this.config = config || bitcoreDefaults; |
|
|
|
// extend defaults with config
|
|
|
|
this.config = extend(true, config || {}, bitcoreDefaults); |
|
|
|
this.active = false; |
|
|
|
this.timer = null; |
|
|
|
|
|
|
|
this.peers = []; |
|
|
|
this.pool = []; |
|
|
|
this.connections = []; |
|
|
|
this.isConnected = false; |
|
|
|
this.peerDiscovery = false; |
|
|
@ -26,6 +26,12 @@ function PeerManager(config) { |
|
|
|
this.interval = 5000; |
|
|
|
this.minConnections = 8; |
|
|
|
this.minKnownPeers = 10; |
|
|
|
|
|
|
|
// keep track of tried seeds and results
|
|
|
|
this.seeds = { |
|
|
|
resolved: [], |
|
|
|
failed: [] |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
PeerManager.parent = imports.parent || require('events').EventEmitter; |
|
|
@ -61,6 +67,13 @@ PeerManager.prototype.addPeer = function(peer, port) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
PeerManager.prototype.removePeer = function(peer) { |
|
|
|
var index = this.peers.indexOf(peer); |
|
|
|
var exists = !!~index; |
|
|
|
if (exists) this.peers.splice(index, 1); |
|
|
|
return exists; |
|
|
|
}; |
|
|
|
|
|
|
|
PeerManager.prototype.checkStatus = function checkStatus() { |
|
|
|
// Make sure we are connected to all forcePeers
|
|
|
|
if(this.peers.length) { |
|
|
@ -77,6 +90,13 @@ PeerManager.prototype.checkStatus = function checkStatus() { |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// for debug purposes, print how many of our peers are actually connected
|
|
|
|
var connected = 0 |
|
|
|
this.peers.forEach(function(p) { |
|
|
|
if (p.connection && !p.connection._connecting) connected++ |
|
|
|
}); |
|
|
|
log.info(connected + ' of ' + this.peers.length + ' peers connected'); |
|
|
|
|
|
|
|
Object.keys(peerIndex).forEach(function(i) { |
|
|
|
this.connectTo(peerIndex[i]); |
|
|
|
}.bind(this)); |
|
|
@ -84,7 +104,7 @@ PeerManager.prototype.checkStatus = function checkStatus() { |
|
|
|
}; |
|
|
|
|
|
|
|
PeerManager.prototype.connectTo = function(peer) { |
|
|
|
log.info('connecting to '+peer); |
|
|
|
log.info('connecting to ' + peer); |
|
|
|
try { |
|
|
|
return this.addConnection(peer.createConnection(), peer); |
|
|
|
} catch (e) { |
|
|
@ -174,10 +194,16 @@ PeerManager.prototype.handleError = function(e) { |
|
|
|
}; |
|
|
|
|
|
|
|
PeerManager.prototype.handleDisconnect = function(e) { |
|
|
|
log.info('disconnected from peer '+e.peer); |
|
|
|
log.info('disconnected from peer ' + e.peer); |
|
|
|
var i = this.connections.indexOf(e.conn); |
|
|
|
if(i != -1) this.connections.splice(i, 1); |
|
|
|
|
|
|
|
this.removePeer(e.peer); |
|
|
|
if (this.pool.length) { |
|
|
|
log.info('replacing peer using the pool of ' + this.pool.length + ' seeds'); |
|
|
|
this.addPeer(this.pool.pop()); |
|
|
|
} |
|
|
|
|
|
|
|
if(!this.connections.length) { |
|
|
|
this.emit('netDisconnected'); |
|
|
|
this.isConnected = false; |
|
|
@ -212,4 +238,72 @@ PeerManager.prototype.getActiveConnections = function () { |
|
|
|
return this.connections.slice(0); |
|
|
|
}; |
|
|
|
|
|
|
|
PeerManager.prototype.discover = function(options, callback) { |
|
|
|
var self = this; |
|
|
|
var async = imports.async || require('async'); |
|
|
|
var dns = imports.dns || require('dns'); |
|
|
|
var networks = imports.networks || require('../networks'); |
|
|
|
var seeds = networks[self.config.network].dnsSeeds; |
|
|
|
|
|
|
|
self.limit = options.limit || 12; |
|
|
|
|
|
|
|
var dnsExecutor = seeds.map(function(seed) { |
|
|
|
return function(done) { |
|
|
|
// have we already resolved this seed?
|
|
|
|
if (~self.seeds.resolved.indexOf(seed)) { |
|
|
|
// if so, just pass back cached peer list
|
|
|
|
return done(null, self.seeds.results[seed]); |
|
|
|
} |
|
|
|
|
|
|
|
// has this seed failed to resolve?
|
|
|
|
if (~self.seeds.failed.indexOf(seed)) { |
|
|
|
// if so, pass back empty results
|
|
|
|
return done(null, []); |
|
|
|
} |
|
|
|
|
|
|
|
log.info('resolving dns seed '+ seed); |
|
|
|
|
|
|
|
dns.resolve(seed, function(err, peers) { |
|
|
|
if (err) { |
|
|
|
log.err('failed to resolve dns seed '+ seed, err); |
|
|
|
self.seeds.failed.push(seed); |
|
|
|
return done(null, []); |
|
|
|
} |
|
|
|
|
|
|
|
log.info('found '+ peers.length + ' peers from ' + seed); |
|
|
|
self.seeds.resolved.push(seed); |
|
|
|
|
|
|
|
// transform that list into a list of Peer instances
|
|
|
|
peers = peers.map(function(ip) { |
|
|
|
return new Peer(ip, networks.defaultClientPort); |
|
|
|
}); |
|
|
|
|
|
|
|
peers.forEach(function(p) { |
|
|
|
if (self.peers.length < self.limit) self.addPeer(p); |
|
|
|
else self.pool.push(p); |
|
|
|
}); |
|
|
|
|
|
|
|
self.emit('peers', peers); |
|
|
|
|
|
|
|
return done(null, peers); |
|
|
|
}); |
|
|
|
|
|
|
|
}; |
|
|
|
}); |
|
|
|
|
|
|
|
// try resolving all seeds
|
|
|
|
async.parallel(dnsExecutor, function(err, results) { |
|
|
|
var peers = []; |
|
|
|
|
|
|
|
// consolidate all resolved peers into one list
|
|
|
|
results.forEach(function(peerlist) { |
|
|
|
peers = peers.concat(peerlist); |
|
|
|
}); |
|
|
|
|
|
|
|
if (typeof callback === 'function') callback(null, peers); |
|
|
|
}); |
|
|
|
|
|
|
|
return self; |
|
|
|
}; |
|
|
|
|
|
|
|
module.exports = require('soop')(PeerManager); |
|
|
|