net: improvements. see comments.
- Improved local address handling. - Enabled upnp discovery. - Added retry for dns seeds. - Improved hostlist in general.
This commit is contained in:
parent
614a952015
commit
cdbad54a8c
@ -1,7 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var net = require('net');
|
var net = require('net');
|
||||||
var dns = require('dns');
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var IOServer = require('socket.io');
|
var IOServer = require('socket.io');
|
||||||
var util = require('../lib/utils/util');
|
var util = require('../lib/utils/util');
|
||||||
@ -65,47 +64,6 @@ WSProxy.prototype._handleSocket = function _handleSocket(ws) {
|
|||||||
ws.on('tcp connect', function(port, host, nonce) {
|
ws.on('tcp connect', function(port, host, nonce) {
|
||||||
self._handleConnect(ws, port, host, nonce);
|
self._handleConnect(ws, port, host, nonce);
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on('dns resolve', function(name, record, callback) {
|
|
||||||
self._handleResolve(ws, name, record, callback);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
WSProxy.prototype._handleResolve = function _handleResolve(ws, name, record, callback) {
|
|
||||||
if (typeof name !== 'string') {
|
|
||||||
ws.disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof record !== 'string') {
|
|
||||||
ws.disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof callback !== 'function') {
|
|
||||||
ws.disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (record !== 'A' && record !== 'AAAA') {
|
|
||||||
this.log('Client sent a bad record type: %s.', record);
|
|
||||||
ws.disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NAME_REGEX.test(name) || name.length > 200) {
|
|
||||||
this.log('Client sent a bad domain: %s.', name);
|
|
||||||
ws.disconnect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dns.resolve(name, record, function(err, result) {
|
|
||||||
if (err) {
|
|
||||||
callback({ message: err.message, code: err.code });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callback(null, result);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce) {
|
WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce) {
|
||||||
|
|||||||
@ -63,6 +63,7 @@ max-inbound: 30
|
|||||||
# Proxy Server (browser=websockets, node=socks)
|
# Proxy Server (browser=websockets, node=socks)
|
||||||
# proxy: foo:bar@127.0.0.1:9050
|
# proxy: foo:bar@127.0.0.1:9050
|
||||||
# onion: true
|
# onion: true
|
||||||
|
# upnp: true
|
||||||
|
|
||||||
# Custom list of DNS seeds
|
# Custom list of DNS seeds
|
||||||
# seeds: seed.bitcoin.sipa.be
|
# seeds: seed.bitcoin.sipa.be
|
||||||
|
|||||||
@ -386,7 +386,7 @@ RPC.prototype.getnetworkinfo = co(function* getnetworkinfo(args, help) {
|
|||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
subversion: this.pool.options.agent,
|
subversion: this.pool.options.agent,
|
||||||
protocolversion: this.pool.options.version,
|
protocolversion: this.pool.options.version,
|
||||||
localservices: this.pool.address.services,
|
localservices: this.pool.options.services,
|
||||||
timeoffset: this.network.time.offset,
|
timeoffset: this.network.time.offset,
|
||||||
connections: this.pool.peers.size(),
|
connections: this.pool.peers.size(),
|
||||||
networks: [],
|
networks: [],
|
||||||
@ -522,7 +522,9 @@ RPC.prototype.getpeerinfo = co(function* getpeerinfo(args, help) {
|
|||||||
peers.push({
|
peers.push({
|
||||||
id: id++,
|
id: id++,
|
||||||
addr: peer.hostname(),
|
addr: peer.hostname(),
|
||||||
addrlocal: peer.hostname(),
|
addrlocal: !peer.local.isNull()
|
||||||
|
? peer.local.hostname
|
||||||
|
: undefined,
|
||||||
relaytxes: !peer.noRelay,
|
relaytxes: !peer.noRelay,
|
||||||
lastsend: peer.lastSend / 1000 | 0,
|
lastsend: peer.lastSend / 1000 | 0,
|
||||||
lastrecv: peer.lastRecv / 1000 | 0,
|
lastrecv: peer.lastRecv / 1000 | 0,
|
||||||
|
|||||||
@ -623,6 +623,10 @@ HTTPServer.prototype._init = function _init() {
|
|||||||
this.get('/', co(function* (req, res) {
|
this.get('/', co(function* (req, res) {
|
||||||
var totalTX = this.mempool ? this.mempool.totalTX : 0;
|
var totalTX = this.mempool ? this.mempool.totalTX : 0;
|
||||||
var size = this.mempool ? this.mempool.getSize() : 0;
|
var size = this.mempool ? this.mempool.getSize() : 0;
|
||||||
|
var addr = this.pool.hosts.getLocal();
|
||||||
|
|
||||||
|
if (!addr)
|
||||||
|
addr = this.pool.hosts.address;
|
||||||
|
|
||||||
res.send(200, {
|
res.send(200, {
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
@ -633,10 +637,10 @@ HTTPServer.prototype._init = function _init() {
|
|||||||
progress: this.chain.getProgress()
|
progress: this.chain.getProgress()
|
||||||
},
|
},
|
||||||
pool: {
|
pool: {
|
||||||
host: this.pool.address.host,
|
host: addr.host,
|
||||||
port: this.pool.address.port,
|
port: addr.port,
|
||||||
agent: this.pool.options.agent,
|
agent: this.pool.options.agent,
|
||||||
services: this.pool.address.services.toString(2),
|
services: this.pool.options.services.toString(2),
|
||||||
outbound: this.pool.peers.outbound,
|
outbound: this.pool.peers.outbound,
|
||||||
inbound: this.pool.peers.inbound
|
inbound: this.pool.peers.inbound
|
||||||
},
|
},
|
||||||
|
|||||||
@ -467,7 +467,7 @@ function AuthDB(options) {
|
|||||||
return new AuthDB(options);
|
return new AuthDB(options);
|
||||||
|
|
||||||
this.logger = null;
|
this.logger = null;
|
||||||
this.resolve = dns.resolve;
|
this.resolve = dns.lookup;
|
||||||
this.dnsKnown = [];
|
this.dnsKnown = [];
|
||||||
|
|
||||||
this.known = {};
|
this.known = {};
|
||||||
@ -476,6 +476,26 @@ function AuthDB(options) {
|
|||||||
this._init(options);
|
this._init(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open auth database (lookup known peers).
|
||||||
|
* @method
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
AuthDB.prototype.open = co(function* open() {
|
||||||
|
yield this.lookup();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close auth database.
|
||||||
|
* @method
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
AuthDB.prototype.close = co(function* close() {
|
||||||
|
;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize authdb with options.
|
* Initialize authdb with options.
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
@ -601,7 +621,7 @@ AuthDB.prototype.getKnown = function getKnown(hostname) {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
AuthDB.prototype.discover = co(function* discover() {
|
AuthDB.prototype.lookup = co(function* lookup() {
|
||||||
var jobs = [];
|
var jobs = [];
|
||||||
var i, addr;
|
var i, addr;
|
||||||
|
|
||||||
@ -610,8 +630,6 @@ AuthDB.prototype.discover = co(function* discover() {
|
|||||||
jobs.push(this.populate(addr[0], addr[1]));
|
jobs.push(this.populate(addr[0], addr[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dnsKnown.length = 0;
|
|
||||||
|
|
||||||
yield Promise.all(jobs);
|
yield Promise.all(jobs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -6,36 +6,17 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var ProxySocket = require('./proxysocket');
|
|
||||||
var socket;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve host (no getaddrinfo).
|
* Resolve host (no getaddrinfo).
|
||||||
* @ignore
|
* @ignore
|
||||||
* @param {String} host
|
* @param {String} host
|
||||||
* @param {String?} proxy
|
* @param {String?} proxy - Tor socks proxy.
|
||||||
* @param {Boolean?} onion
|
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.resolve = function resolve(host, proxy, onion) {
|
exports.resolve = function resolve(host, proxy) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
if (!socket)
|
reject(new Error('DNS not supported.'));
|
||||||
socket = new ProxySocket(proxy);
|
|
||||||
|
|
||||||
socket.resolve(host, 'A', function(err, result) {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.length === 0) {
|
|
||||||
reject(new Error('No DNS results.'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(result);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,11 +24,12 @@ exports.resolve = function resolve(host, proxy, onion) {
|
|||||||
* Resolve host (getaddrinfo).
|
* Resolve host (getaddrinfo).
|
||||||
* @ignore
|
* @ignore
|
||||||
* @param {String} host
|
* @param {String} host
|
||||||
* @param {String?} proxy
|
* @param {String?} proxy - Tor socks proxy.
|
||||||
* @param {Boolean?} onion
|
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.lookup = function lookup(host, proxy, onion) {
|
exports.lookup = function lookup(host, proxy) {
|
||||||
return exports.resolve(host, proxy, onion);
|
return new Promise(function(resolve, reject) {
|
||||||
|
reject(new Error('DNS not supported.'));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -22,13 +22,12 @@ var options = {
|
|||||||
/**
|
/**
|
||||||
* Resolve host (no getaddrinfo).
|
* Resolve host (no getaddrinfo).
|
||||||
* @param {String} host
|
* @param {String} host
|
||||||
* @param {String?} proxy
|
* @param {String?} proxy - Tor socks proxy.
|
||||||
* @param {Boolean?} onion
|
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.resolve = function resolve(host, proxy, onion) {
|
exports.resolve = function resolve(host, proxy) {
|
||||||
if (proxy && onion)
|
if (proxy)
|
||||||
return socks.resolve(proxy, host);
|
return socks.resolve(proxy, host);
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
@ -51,13 +50,12 @@ exports.resolve = function resolve(host, proxy, onion) {
|
|||||||
/**
|
/**
|
||||||
* Resolve host (getaddrinfo).
|
* Resolve host (getaddrinfo).
|
||||||
* @param {String} host
|
* @param {String} host
|
||||||
* @param {String?} proxy
|
* @param {String?} proxy - Tor socks proxy.
|
||||||
* @param {Boolean?} onion
|
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.lookup = function lookup(host, proxy, onion) {
|
exports.lookup = function lookup(host, proxy) {
|
||||||
if (proxy && onion)
|
if (proxy)
|
||||||
return socks.resolve(proxy, host);
|
return socks.resolve(proxy, host);
|
||||||
|
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
var fs = require('fs');
|
||||||
var util = require('../utils/util');
|
var util = require('../utils/util');
|
||||||
var IP = require('../utils/ip');
|
var IP = require('../utils/ip');
|
||||||
var co = require('../utils/co');
|
var co = require('../utils/co');
|
||||||
@ -16,8 +17,9 @@ var List = require('../utils/list');
|
|||||||
var murmur3 = require('../utils/murmur3');
|
var murmur3 = require('../utils/murmur3');
|
||||||
var Map = require('../utils/map');
|
var Map = require('../utils/map');
|
||||||
var common = require('./common');
|
var common = require('./common');
|
||||||
|
var seeds = require('./seeds');
|
||||||
var dns = require('./dns');
|
var dns = require('./dns');
|
||||||
var fs = require('fs');
|
var Logger = require('../node/logger');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Host List
|
* Host List
|
||||||
@ -30,45 +32,76 @@ function HostList(options) {
|
|||||||
if (!(this instanceof HostList))
|
if (!(this instanceof HostList))
|
||||||
return new HostList(options);
|
return new HostList(options);
|
||||||
|
|
||||||
this.network = Network.primary;
|
this.options = new HostListOptions(options);
|
||||||
this.logger = null;
|
this.network = this.options.network;
|
||||||
this.address = new NetAddress();
|
this.logger = this.options.logger;
|
||||||
this.resolve = dns.resolve;
|
this.address = this.options.address;
|
||||||
this.banTime = common.BAN_TIME;
|
this.resolve = this.options.resolve;
|
||||||
|
|
||||||
this.rawSeeds = this.network.seeds;
|
|
||||||
this.dnsSeeds = [];
|
this.dnsSeeds = [];
|
||||||
this.rawNodes = [];
|
|
||||||
this.dnsNodes = [];
|
this.dnsNodes = [];
|
||||||
this.nodes = [];
|
|
||||||
|
|
||||||
this.banned = {};
|
|
||||||
|
|
||||||
this.map = {};
|
this.map = {};
|
||||||
this.fresh = [];
|
this.fresh = [];
|
||||||
this.used = [];
|
|
||||||
|
|
||||||
this.totalFresh = 0;
|
this.totalFresh = 0;
|
||||||
|
this.used = [];
|
||||||
this.totalUsed = 0;
|
this.totalUsed = 0;
|
||||||
|
this.nodes = [];
|
||||||
|
this.local = new Map();
|
||||||
|
this.banned = {};
|
||||||
|
|
||||||
this.maxBuckets = 20;
|
|
||||||
this.maxEntries = 50;
|
|
||||||
this.maxAddresses = this.maxBuckets * this.maxEntries;
|
|
||||||
|
|
||||||
this.horizonDays = 30;
|
|
||||||
this.retries = 3;
|
|
||||||
this.minFailDays = 7;
|
|
||||||
this.maxFailures = 10;
|
|
||||||
this.maxRefs = 8;
|
|
||||||
|
|
||||||
this.filename = null;
|
|
||||||
this.flushInterval = 120000;
|
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
|
|
||||||
this._initOptions(options);
|
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of days before considering
|
||||||
|
* an address stale.
|
||||||
|
* @const {Number}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.HORIZON_DAYS = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of retries (without success)
|
||||||
|
* before considering an address stale.
|
||||||
|
* @const {Number}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.RETRIES = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of days after reaching
|
||||||
|
* MAX_FAILURES to consider an
|
||||||
|
* address stale.
|
||||||
|
* @const {Number}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.MIN_FAIL_DAYS = 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of failures
|
||||||
|
* allowed before considering
|
||||||
|
* an address stale.
|
||||||
|
* @const {Number}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.MAX_FAILURES = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of references
|
||||||
|
* in fresh buckets.
|
||||||
|
* @const {Number}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.MAX_REFS = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization version.
|
* Serialization version.
|
||||||
* @const {Number}
|
* @const {Number}
|
||||||
@ -78,58 +111,19 @@ function HostList(options) {
|
|||||||
HostList.VERSION = 0;
|
HostList.VERSION = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize options.
|
* Local address scores.
|
||||||
* @private
|
* @enum {Number}
|
||||||
|
* @default
|
||||||
*/
|
*/
|
||||||
|
|
||||||
HostList.prototype._initOptions = function initOptions(options) {
|
HostList.scores = {
|
||||||
if (!options)
|
NONE: 0,
|
||||||
return;
|
IF: 1,
|
||||||
|
BIND: 2,
|
||||||
if (options.network != null) {
|
UPNP: 3,
|
||||||
this.network = Network.get(options.network);
|
HTTP: 3,
|
||||||
this.rawSeeds = this.network.seeds;
|
MANUAL: 4,
|
||||||
}
|
MAX: 5
|
||||||
|
|
||||||
if (options.logger != null) {
|
|
||||||
assert(typeof options.logger === 'object');
|
|
||||||
this.logger = options.logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.address != null) {
|
|
||||||
assert(options.address instanceof NetAddress);
|
|
||||||
this.address = options.address;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.resolve != null) {
|
|
||||||
assert(typeof options.resolve === 'function');
|
|
||||||
this.resolve = options.resolve;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.banTime != null) {
|
|
||||||
assert(options.banTime >= 0);
|
|
||||||
this.banTime = options.banTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.seeds) {
|
|
||||||
assert(Array.isArray(options.seeds));
|
|
||||||
this.rawSeeds = options.seeds;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.nodes) {
|
|
||||||
assert(Array.isArray(options.nodes));
|
|
||||||
this.rawNodes = options.nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.hostLocation != null) {
|
|
||||||
assert(typeof options.hostLocation === 'string');
|
|
||||||
this.filename = options.hostLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.flushInterval != null) {
|
|
||||||
assert(options.flushInterval >= 0);
|
|
||||||
this.flushInterval = options.flushInterval;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,16 +132,28 @@ HostList.prototype._initOptions = function initOptions(options) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
HostList.prototype._init = function init() {
|
HostList.prototype._init = function init() {
|
||||||
var i;
|
var options = this.options;
|
||||||
|
var scores = HostList.scores;
|
||||||
|
var hosts = IP.getPublic();
|
||||||
|
var port = this.address.port;
|
||||||
|
var i, host;
|
||||||
|
|
||||||
for (i = 0; i < this.maxBuckets; i++)
|
for (i = 0; i < this.options.maxBuckets; i++)
|
||||||
this.fresh.push(new Map());
|
this.fresh.push(new Map());
|
||||||
|
|
||||||
for (i = 0; i < this.maxBuckets; i++)
|
for (i = 0; i < this.options.maxBuckets; i++)
|
||||||
this.used.push(new List());
|
this.used.push(new List());
|
||||||
|
|
||||||
this.setSeeds(this.rawSeeds);
|
this.setSeeds(options.seeds);
|
||||||
this.setNodes(this.rawNodes);
|
this.setNodes(options.nodes);
|
||||||
|
|
||||||
|
this.pushLocal(this.address, scores.MANUAL);
|
||||||
|
this.addLocal(options.host, options.port, scores.BIND);
|
||||||
|
|
||||||
|
for (i = 0; i < hosts.length; i++) {
|
||||||
|
host = hosts[i];
|
||||||
|
this.addLocal(host, port, scores.IF);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,14 +164,17 @@ HostList.prototype._init = function init() {
|
|||||||
|
|
||||||
HostList.prototype.open = co(function* open() {
|
HostList.prototype.open = co(function* open() {
|
||||||
try {
|
try {
|
||||||
yield this.read();
|
yield this.loadFile();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (this.logger) {
|
this.logger.warning('Hosts deserialization failed.');
|
||||||
this.logger.warning('Hosts deserialization failed.');
|
this.logger.error(e);
|
||||||
this.logger.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.size() === 0)
|
||||||
|
this.injectSeeds();
|
||||||
|
|
||||||
|
yield this.discoverNodes();
|
||||||
|
|
||||||
this.start();
|
this.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -178,6 +187,7 @@ HostList.prototype.open = co(function* open() {
|
|||||||
HostList.prototype.close = co(function* close() {
|
HostList.prototype.close = co(function* close() {
|
||||||
this.stop();
|
this.stop();
|
||||||
yield this.flush();
|
yield this.flush();
|
||||||
|
this.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,11 +195,11 @@ HostList.prototype.close = co(function* close() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
HostList.prototype.start = function start() {
|
HostList.prototype.start = function start() {
|
||||||
if (!this.filename)
|
if (!this.options.hostLocation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(!this.timer);
|
assert(this.timer == null);
|
||||||
this.timer = setInterval(this.flush.bind(this), this.flushInterval);
|
this.timer = co.setInterval(this.flush, this.options.flushInterval, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,28 +207,56 @@ HostList.prototype.start = function start() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
HostList.prototype.stop = function stop() {
|
HostList.prototype.stop = function stop() {
|
||||||
if (!this.filename)
|
if (!this.options.hostLocation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(this.timer != null);
|
assert(this.timer != null);
|
||||||
clearInterval(this.timer);
|
co.clearInterval(this.timer);
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read and initialie from hosts file.
|
* Read and initialize from hosts file.
|
||||||
* @method
|
* @method
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
HostList.prototype.read = co(function* read() {
|
HostList.prototype.injectSeeds = function injectSeeds() {
|
||||||
|
var nodes = seeds.get(this.network.type);
|
||||||
|
var i, node, addr;
|
||||||
|
|
||||||
|
for (i = 0; i < nodes.length; i++) {
|
||||||
|
node = nodes[i];
|
||||||
|
addr = NetAddress.fromHostname(node, this.network);
|
||||||
|
|
||||||
|
if (!addr.isRoutable())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!this.options.onion && addr.isOnion())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (addr.port === 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this.add(addr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and initialize from hosts file.
|
||||||
|
* @method
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.prototype.loadFile = co(function* loadFile() {
|
||||||
|
var filename = this.options.hostLocation;
|
||||||
var data, json;
|
var data, json;
|
||||||
|
|
||||||
if (!this.filename)
|
if (!filename)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data = yield readFile(this.filename, 'utf8');
|
data = yield readFile(filename, 'utf8');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code === 'ENOENT')
|
if (e.code === 'ENOENT')
|
||||||
return;
|
return;
|
||||||
@ -237,24 +275,22 @@ HostList.prototype.read = co(function* read() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
HostList.prototype.flush = co(function* flush() {
|
HostList.prototype.flush = co(function* flush() {
|
||||||
|
var filename = this.options.hostLocation;
|
||||||
var json, data;
|
var json, data;
|
||||||
|
|
||||||
if (!this.filename)
|
if (!filename)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.logger)
|
this.logger.debug('Writing hosts to %s.', filename);
|
||||||
this.logger.debug('Writing hosts to %s.', this.filename);
|
|
||||||
|
|
||||||
json = this.toJSON();
|
json = this.toJSON();
|
||||||
data = JSON.stringify(json);
|
data = JSON.stringify(json);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
yield writeFile(this.filename, data, 'utf8');
|
yield writeFile(filename, data, 'utf8');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (this.logger) {
|
this.logger.warning('Writing hosts failed.');
|
||||||
this.logger.warning('Writing hosts failed.');
|
this.logger.error(e);
|
||||||
this.logger.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -273,7 +309,8 @@ HostList.prototype.size = function size() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
HostList.prototype.isFull = function isFull() {
|
HostList.prototype.isFull = function isFull() {
|
||||||
return this.size() >= this.maxAddresses;
|
var max = this.options.maxBuckets * this.options.maxEntries;
|
||||||
|
return this.size() >= max;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -297,6 +334,8 @@ HostList.prototype.reset = function reset() {
|
|||||||
|
|
||||||
this.totalFresh = 0;
|
this.totalFresh = 0;
|
||||||
this.totalUsed = 0;
|
this.totalUsed = 0;
|
||||||
|
|
||||||
|
this.nodes.length = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -337,7 +376,7 @@ HostList.prototype.isBanned = function isBanned(host) {
|
|||||||
if (time == null)
|
if (time == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (util.now() > time + this.banTime) {
|
if (util.now() > time + this.options.banTime) {
|
||||||
delete this.banned[host];
|
delete this.banned[host];
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -477,10 +516,10 @@ HostList.prototype.add = function add(addr, src) {
|
|||||||
|
|
||||||
// Do not update if the max
|
// Do not update if the max
|
||||||
// reference count is reached.
|
// reference count is reached.
|
||||||
if (entry.refCount === this.maxRefs)
|
if (entry.refCount === HostList.MAX_REFS)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
assert(entry.refCount < this.maxRefs);
|
assert(entry.refCount < HostList.MAX_REFS);
|
||||||
|
|
||||||
// Stochastic test: previous refCount
|
// Stochastic test: previous refCount
|
||||||
// N: 2^N times harder to increase it.
|
// N: 2^N times harder to increase it.
|
||||||
@ -506,7 +545,7 @@ HostList.prototype.add = function add(addr, src) {
|
|||||||
if (bucket.has(entry.key()))
|
if (bucket.has(entry.key()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bucket.size >= this.maxEntries)
|
if (bucket.size >= this.options.maxEntries)
|
||||||
this.evictFresh(bucket);
|
this.evictFresh(bucket);
|
||||||
|
|
||||||
bucket.set(entry.key(), entry);
|
bucket.set(entry.key(), entry);
|
||||||
@ -579,14 +618,14 @@ HostList.prototype.isStale = function isStale(entry) {
|
|||||||
if (entry.addr.ts === 0)
|
if (entry.addr.ts === 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (now - entry.addr.ts > this.horizonDays * 24 * 60 * 60)
|
if (now - entry.addr.ts > HostList.HORIZON_DAYS * 24 * 60 * 60)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (entry.lastSuccess === 0 && entry.attempts >= this.retries)
|
if (entry.lastSuccess === 0 && entry.attempts >= HostList.RETRIES)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (now - entry.lastSuccess > this.minFailDays * 24 * 60 * 60) {
|
if (now - entry.lastSuccess > HostList.MIN_FAIL_DAYS * 24 * 60 * 60) {
|
||||||
if (entry.attempts >= this.maxFailures)
|
if (entry.attempts >= HostList.MAX_FAILURES)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,7 +752,7 @@ HostList.prototype.markAck = function markAck(hostname, services) {
|
|||||||
// Find room in used bucket.
|
// Find room in used bucket.
|
||||||
bucket = this.usedBucket(entry);
|
bucket = this.usedBucket(entry);
|
||||||
|
|
||||||
if (bucket.size < this.maxEntries) {
|
if (bucket.size < this.options.maxEntries) {
|
||||||
entry.used = true;
|
entry.used = true;
|
||||||
bucket.push(entry);
|
bucket.push(entry);
|
||||||
this.totalUsed++;
|
this.totalUsed++;
|
||||||
@ -725,7 +764,7 @@ HostList.prototype.markAck = function markAck(hostname, services) {
|
|||||||
fresh = this.freshBucket(evicted);
|
fresh = this.freshBucket(evicted);
|
||||||
|
|
||||||
// Move to entry's old bucket if no room.
|
// Move to entry's old bucket if no room.
|
||||||
if (fresh.size >= this.maxEntries)
|
if (fresh.size >= this.options.maxEntries)
|
||||||
fresh = old;
|
fresh = old;
|
||||||
|
|
||||||
// Swap to evicted's used bucket.
|
// Swap to evicted's used bucket.
|
||||||
@ -855,27 +894,131 @@ HostList.prototype.setNodes = function setNodes(nodes) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discover hosts from seeds and nodes.
|
* Add a local address.
|
||||||
|
* @param {String} host
|
||||||
|
* @param {Number} port
|
||||||
|
* @param {Number} score
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.prototype.addLocal = function addLocal(host, port, score) {
|
||||||
|
var addr = NetAddress.fromHost(host, port, this.network);
|
||||||
|
addr.services = this.options.services;
|
||||||
|
return this.pushLocal(addr, score);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a local address.
|
||||||
|
* @param {NetAddress} addr
|
||||||
|
* @param {Number} score
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.prototype.pushLocal = function pushLocal(addr, score) {
|
||||||
|
var local;
|
||||||
|
|
||||||
|
if (!addr.isRoutable())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this.local.has(addr.hostname))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
local = new LocalAddress(addr, score);
|
||||||
|
|
||||||
|
this.local.set(addr.hostname, local);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get local address based on reachability.
|
||||||
|
* @param {NetAddress?} src
|
||||||
|
* @returns {NetAddress}
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.prototype.getLocal = function getLocal(src) {
|
||||||
|
var keys = this.local.keys();
|
||||||
|
var bestReach = -1;
|
||||||
|
var bestScore = -1;
|
||||||
|
var bestDest = null;
|
||||||
|
var i, key, dest, reach;
|
||||||
|
|
||||||
|
if (!src)
|
||||||
|
src = this.address;
|
||||||
|
|
||||||
|
if (keys.length === 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for (i = 0; i < keys.length; i++) {
|
||||||
|
key = keys[i];
|
||||||
|
dest = this.local.get(key);
|
||||||
|
reach = src.getReachability(dest.addr);
|
||||||
|
|
||||||
|
if (reach < bestReach)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (reach > bestReach || dest.score > bestScore) {
|
||||||
|
bestReach = reach;
|
||||||
|
bestScore = dest.score;
|
||||||
|
bestDest = dest.addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bestDest.ts = this.network.now();
|
||||||
|
|
||||||
|
return bestDest;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark local address as seen during a handshake.
|
||||||
|
* @param {NetAddress} addr
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.prototype.markLocal = function markLocal(addr) {
|
||||||
|
var local = this.local.get(addr.hostname);
|
||||||
|
|
||||||
|
if (!local)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
local.score++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover hosts from seeds.
|
||||||
* @method
|
* @method
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
HostList.prototype.discover = co(function* discover() {
|
HostList.prototype.discoverSeeds = co(function* discoverSeeds() {
|
||||||
var jobs = [];
|
var jobs = [];
|
||||||
var i, node, seed;
|
var i, seed;
|
||||||
|
|
||||||
for (i = 0; i < this.dnsSeeds.length; i++) {
|
for (i = 0; i < this.dnsSeeds.length; i++) {
|
||||||
seed = this.dnsSeeds[i];
|
seed = this.dnsSeeds[i];
|
||||||
jobs.push(this.populateSeed(seed));
|
jobs.push(this.populateSeed(seed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield Promise.all(jobs);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover hosts from nodes.
|
||||||
|
* @method
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostList.prototype.discoverNodes = co(function* discoverNodes() {
|
||||||
|
var jobs = [];
|
||||||
|
var i, node;
|
||||||
|
|
||||||
for (i = 0; i < this.dnsNodes.length; i++) {
|
for (i = 0; i < this.dnsNodes.length; i++) {
|
||||||
node = this.dnsNodes[i];
|
node = this.dnsNodes[i];
|
||||||
jobs.push(this.populateNode(node));
|
jobs.push(this.populateNode(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dnsNodes.length = 0;
|
|
||||||
|
|
||||||
yield Promise.all(jobs);
|
yield Promise.all(jobs);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -926,14 +1069,12 @@ HostList.prototype.populate = co(function* populate(target) {
|
|||||||
|
|
||||||
assert(target.type === IP.types.DNS, 'Resolved host passed.');
|
assert(target.type === IP.types.DNS, 'Resolved host passed.');
|
||||||
|
|
||||||
if (this.logger)
|
this.logger.info('Resolving host: %s.', target.host);
|
||||||
this.logger.info('Resolving host: %s.', target.host);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hosts = yield this.resolve(target.host);
|
hosts = yield this.resolve(target.host);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (this.logger)
|
this.logger.error(e);
|
||||||
this.logger.error(e);
|
|
||||||
return addrs;
|
return addrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1027,6 +1168,8 @@ HostList.prototype.fromJSON = function fromJSON(json) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert(Array.isArray(json.fresh));
|
assert(Array.isArray(json.fresh));
|
||||||
|
assert(json.fresh.length <= this.options.maxBuckets,
|
||||||
|
'Buckets mismatch.');
|
||||||
|
|
||||||
for (i = 0; i < json.fresh.length; i++) {
|
for (i = 0; i < json.fresh.length; i++) {
|
||||||
keys = json.fresh[i];
|
keys = json.fresh[i];
|
||||||
@ -1042,7 +1185,7 @@ HostList.prototype.fromJSON = function fromJSON(json) {
|
|||||||
bucket.set(key, entry);
|
bucket.set(key, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(bucket.size <= this.maxEntries,
|
assert(bucket.size <= this.options.maxEntries,
|
||||||
'Bucket size mismatch.');
|
'Bucket size mismatch.');
|
||||||
|
|
||||||
fresh.push(bucket);
|
fresh.push(bucket);
|
||||||
@ -1052,6 +1195,8 @@ HostList.prototype.fromJSON = function fromJSON(json) {
|
|||||||
'Buckets mismatch.');
|
'Buckets mismatch.');
|
||||||
|
|
||||||
assert(Array.isArray(json.used));
|
assert(Array.isArray(json.used));
|
||||||
|
assert(json.used.length <= this.options.maxBuckets,
|
||||||
|
'Buckets mismatch.');
|
||||||
|
|
||||||
for (i = 0; i < json.used.length; i++) {
|
for (i = 0; i < json.used.length; i++) {
|
||||||
keys = json.used[i];
|
keys = json.used[i];
|
||||||
@ -1068,7 +1213,7 @@ HostList.prototype.fromJSON = function fromJSON(json) {
|
|||||||
bucket.push(entry);
|
bucket.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(bucket.size <= this.maxEntries,
|
assert(bucket.size <= this.options.maxEntries,
|
||||||
'Bucket size mismatch.');
|
'Bucket size mismatch.');
|
||||||
|
|
||||||
used.push(bucket);
|
used.push(bucket);
|
||||||
@ -1285,6 +1430,161 @@ HostEntry.fromJSON = function fromJSON(json, network) {
|
|||||||
return new HostEntry().fromJSON(json, network);
|
return new HostEntry().fromJSON(json, network);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LocalAddress
|
||||||
|
* @alias module:net.LocalAddress
|
||||||
|
* @constructor
|
||||||
|
* @param {NetAddress} addr
|
||||||
|
* @param {Number?} score
|
||||||
|
*/
|
||||||
|
|
||||||
|
function LocalAddress(addr, score) {
|
||||||
|
this.addr = addr;
|
||||||
|
this.score = score || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Host List Options
|
||||||
|
* @alias module:net.HostListOptions
|
||||||
|
* @constructor
|
||||||
|
* @param {Object?} options
|
||||||
|
*/
|
||||||
|
|
||||||
|
function HostListOptions(options) {
|
||||||
|
if (!(this instanceof HostListOptions))
|
||||||
|
return new HostListOptions(options);
|
||||||
|
|
||||||
|
this.network = Network.primary;
|
||||||
|
this.logger = Logger.global;
|
||||||
|
this.resolve = dns.lookup;
|
||||||
|
this.host = '0.0.0.0';
|
||||||
|
this.port = this.network.port;
|
||||||
|
this.services = common.LOCAL_SERVICES;
|
||||||
|
this.onion = false;
|
||||||
|
this.banTime = common.BAN_TIME;
|
||||||
|
|
||||||
|
this.address = new NetAddress();
|
||||||
|
this.address.services = this.services;
|
||||||
|
this.address.ts = this.network.now();
|
||||||
|
|
||||||
|
this.seeds = this.network.seeds;
|
||||||
|
this.nodes = [];
|
||||||
|
|
||||||
|
this.maxBuckets = 20;
|
||||||
|
this.maxEntries = 50;
|
||||||
|
|
||||||
|
this.hostLocation = null;
|
||||||
|
this.flushInterval = 120000;
|
||||||
|
|
||||||
|
if (options)
|
||||||
|
this.fromOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject properties from options.
|
||||||
|
* @private
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
|
||||||
|
HostListOptions.prototype.fromOptions = function fromOptions(options) {
|
||||||
|
var raw;
|
||||||
|
|
||||||
|
assert(options, 'Options are required.');
|
||||||
|
|
||||||
|
if (options.network != null) {
|
||||||
|
this.network = Network.get(options.network);
|
||||||
|
this.seeds = this.network.seeds;
|
||||||
|
this.address.port = this.network.port;
|
||||||
|
this.port = this.network.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.logger != null) {
|
||||||
|
assert(typeof options.logger === 'object');
|
||||||
|
this.logger = options.logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.resolve != null) {
|
||||||
|
assert(typeof options.resolve === 'function');
|
||||||
|
this.resolve = options.resolve;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.banTime != null) {
|
||||||
|
assert(options.banTime >= 0);
|
||||||
|
this.banTime = options.banTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.seeds) {
|
||||||
|
assert(Array.isArray(options.seeds));
|
||||||
|
this.seeds = options.seeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.nodes) {
|
||||||
|
assert(Array.isArray(options.nodes));
|
||||||
|
this.nodes = options.nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.host != null) {
|
||||||
|
assert(typeof options.host === 'string');
|
||||||
|
raw = IP.toBuffer(options.host);
|
||||||
|
this.host = IP.toString(raw);
|
||||||
|
if (IP.isRoutable(raw))
|
||||||
|
this.address.setHost(this.host);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.port != null) {
|
||||||
|
assert(typeof options.port === 'number');
|
||||||
|
assert(options.port > 0 && options.port <= 0xffff);
|
||||||
|
this.port = options.port;
|
||||||
|
this.address.setPort(this.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.publicHost != null) {
|
||||||
|
assert(typeof options.publicHost === 'string');
|
||||||
|
this.address.setHost(options.publicHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.publicPort != null) {
|
||||||
|
assert(typeof options.publicPort === 'number');
|
||||||
|
assert(options.publicPort > 0 && options.publicPort <= 0xffff);
|
||||||
|
this.address.setPort(options.publicPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.services != null) {
|
||||||
|
assert(typeof options.services === 'number');
|
||||||
|
this.services = options.services;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.onion != null) {
|
||||||
|
assert(typeof options.onion === 'boolean');
|
||||||
|
this.onion = options.onion;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.maxBuckets != null) {
|
||||||
|
assert(typeof options.maxBuckets === 'number');
|
||||||
|
this.maxBuckets = options.maxBuckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.maxEntries != null) {
|
||||||
|
assert(typeof options.maxEntries === 'number');
|
||||||
|
this.maxEntries = options.maxEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.hostLocation != null) {
|
||||||
|
assert(typeof options.hostLocation === 'string');
|
||||||
|
this.hostLocation = options.hostLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.flushInterval != null) {
|
||||||
|
assert(options.flushInterval >= 0);
|
||||||
|
this.flushInterval = options.flushInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.address.ts = this.network.now();
|
||||||
|
this.address.services = this.services;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helpers
|
* Helpers
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -169,8 +169,8 @@ function VersionPacket(options) {
|
|||||||
this.version = common.PROTOCOL_VERSION;
|
this.version = common.PROTOCOL_VERSION;
|
||||||
this.services = common.LOCAL_SERVICES;
|
this.services = common.LOCAL_SERVICES;
|
||||||
this.ts = util.now();
|
this.ts = util.now();
|
||||||
this.recv = new NetAddress();
|
this.remote = new NetAddress();
|
||||||
this.from = new NetAddress();
|
this.local = new NetAddress();
|
||||||
this.nonce = encoding.ZERO_U64;
|
this.nonce = encoding.ZERO_U64;
|
||||||
this.agent = common.USER_AGENT;
|
this.agent = common.USER_AGENT;
|
||||||
this.height = 0;
|
this.height = 0;
|
||||||
@ -201,11 +201,11 @@ VersionPacket.prototype.fromOptions = function fromOptions(options) {
|
|||||||
if (options.ts != null)
|
if (options.ts != null)
|
||||||
this.ts = options.ts;
|
this.ts = options.ts;
|
||||||
|
|
||||||
if (options.recv)
|
if (options.remote)
|
||||||
this.recv.fromOptions(options.recv);
|
this.remote.fromOptions(options.remote);
|
||||||
|
|
||||||
if (options.from)
|
if (options.local)
|
||||||
this.from.fromOptions(options.from);
|
this.local.fromOptions(options.local);
|
||||||
|
|
||||||
if (options.nonce)
|
if (options.nonce)
|
||||||
this.nonce = options.nonce;
|
this.nonce = options.nonce;
|
||||||
@ -240,8 +240,8 @@ VersionPacket.fromOptions = function fromOptions(options) {
|
|||||||
VersionPacket.prototype.getSize = function getSize() {
|
VersionPacket.prototype.getSize = function getSize() {
|
||||||
var size = 0;
|
var size = 0;
|
||||||
size += 20;
|
size += 20;
|
||||||
size += this.recv.getSize(false);
|
size += this.remote.getSize(false);
|
||||||
size += this.from.getSize(false);
|
size += this.local.getSize(false);
|
||||||
size += 8;
|
size += 8;
|
||||||
size += encoding.sizeVarString(this.agent, 'ascii');
|
size += encoding.sizeVarString(this.agent, 'ascii');
|
||||||
size += 5;
|
size += 5;
|
||||||
@ -258,8 +258,8 @@ VersionPacket.prototype.toWriter = function toWriter(bw) {
|
|||||||
bw.writeU32(this.services);
|
bw.writeU32(this.services);
|
||||||
bw.writeU32(0);
|
bw.writeU32(0);
|
||||||
bw.write64(this.ts);
|
bw.write64(this.ts);
|
||||||
this.recv.toWriter(bw, false);
|
this.remote.toWriter(bw, false);
|
||||||
this.from.toWriter(bw, false);
|
this.local.toWriter(bw, false);
|
||||||
bw.writeBytes(this.nonce);
|
bw.writeBytes(this.nonce);
|
||||||
bw.writeVarString(this.agent, 'ascii');
|
bw.writeVarString(this.agent, 'ascii');
|
||||||
bw.write32(this.height);
|
bw.write32(this.height);
|
||||||
@ -292,10 +292,10 @@ VersionPacket.prototype.fromReader = function fromReader(br) {
|
|||||||
br.readU32();
|
br.readU32();
|
||||||
|
|
||||||
this.ts = br.read53();
|
this.ts = br.read53();
|
||||||
this.recv.fromReader(br, false);
|
this.remote.fromReader(br, false);
|
||||||
|
|
||||||
if (br.left() > 0) {
|
if (br.left() > 0) {
|
||||||
this.from.fromReader(br, false);
|
this.local.fromReader(br, false);
|
||||||
this.nonce = br.readBytes(8);
|
this.nonce = br.readBytes(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -84,6 +84,7 @@ function Peer(options) {
|
|||||||
this.outbound = false;
|
this.outbound = false;
|
||||||
this.loader = false;
|
this.loader = false;
|
||||||
this.address = new NetAddress();
|
this.address = new NetAddress();
|
||||||
|
this.local = new NetAddress();
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.destroyed = false;
|
this.destroyed = false;
|
||||||
this.ack = false;
|
this.ack = false;
|
||||||
@ -979,8 +980,9 @@ Peer.prototype.sendVersion = function sendVersion() {
|
|||||||
packet.version = this.options.version;
|
packet.version = this.options.version;
|
||||||
packet.services = this.options.services;
|
packet.services = this.options.services;
|
||||||
packet.ts = this.network.now();
|
packet.ts = this.network.now();
|
||||||
packet.recv = this.address;
|
packet.remote = this.address;
|
||||||
packet.from = this.options.address;
|
packet.local.setNull();
|
||||||
|
packet.local.services = this.options.services;
|
||||||
packet.nonce = this.options.createNonce(this.hostname());
|
packet.nonce = this.options.createNonce(this.hostname());
|
||||||
packet.agent = this.options.agent;
|
packet.agent = this.options.agent;
|
||||||
packet.height = this.options.getHeight();
|
packet.height = this.options.getHeight();
|
||||||
@ -1703,6 +1705,7 @@ Peer.prototype.handleVersion = co(function* handleVersion(packet) {
|
|||||||
this.height = packet.height;
|
this.height = packet.height;
|
||||||
this.agent = packet.agent;
|
this.agent = packet.agent;
|
||||||
this.noRelay = packet.noRelay;
|
this.noRelay = packet.noRelay;
|
||||||
|
this.local = packet.remote;
|
||||||
|
|
||||||
if (!this.network.selfConnect) {
|
if (!this.network.selfConnect) {
|
||||||
if (this.options.hasNonce(packet.nonce))
|
if (this.options.hasNonce(packet.nonce))
|
||||||
@ -2275,7 +2278,6 @@ function PeerOptions(options) {
|
|||||||
this.createSocket = tcp.createSocket;
|
this.createSocket = tcp.createSocket;
|
||||||
this.version = common.PROTOCOL_VERSION;
|
this.version = common.PROTOCOL_VERSION;
|
||||||
this.services = common.LOCAL_SERVICES;
|
this.services = common.LOCAL_SERVICES;
|
||||||
this.address = new NetAddress();
|
|
||||||
this.agent = common.USER_AGENT;
|
this.agent = common.USER_AGENT;
|
||||||
this.noRelay = false;
|
this.noRelay = false;
|
||||||
this.spv = false;
|
this.spv = false;
|
||||||
@ -2327,11 +2329,6 @@ PeerOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
this.services = options.services;
|
this.services = options.services;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.address != null) {
|
|
||||||
assert(typeof options.address === 'object');
|
|
||||||
this.address = options.address;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.agent != null) {
|
if (options.agent != null) {
|
||||||
assert(typeof options.agent === 'string');
|
assert(typeof options.agent === 'string');
|
||||||
this.agent = options.agent;
|
this.agent = options.agent;
|
||||||
|
|||||||
260
lib/net/pool.js
260
lib/net/pool.js
@ -15,7 +15,6 @@ var IP = require('../utils/ip');
|
|||||||
var co = require('../utils/co');
|
var co = require('../utils/co');
|
||||||
var common = require('./common');
|
var common = require('./common');
|
||||||
var chainCommon = require('../blockchain/common');
|
var chainCommon = require('../blockchain/common');
|
||||||
var NetAddress = require('../primitives/netaddress');
|
|
||||||
var Address = require('../primitives/address');
|
var Address = require('../primitives/address');
|
||||||
var BIP150 = require('./bip150');
|
var BIP150 = require('./bip150');
|
||||||
var BIP151 = require('./bip151');
|
var BIP151 = require('./bip151');
|
||||||
@ -30,12 +29,14 @@ var List = require('../utils/list');
|
|||||||
var tcp = require('./tcp');
|
var tcp = require('./tcp');
|
||||||
var dns = require('./dns');
|
var dns = require('./dns');
|
||||||
var HostList = require('./hostlist');
|
var HostList = require('./hostlist');
|
||||||
|
var UPNP = require('./upnp');
|
||||||
var InvItem = require('../primitives/invitem');
|
var InvItem = require('../primitives/invitem');
|
||||||
var Map = require('../utils/map');
|
var Map = require('../utils/map');
|
||||||
var packets = require('./packets');
|
var packets = require('./packets');
|
||||||
var services = common.services;
|
var services = common.services;
|
||||||
var invTypes = InvItem.types;
|
var invTypes = InvItem.types;
|
||||||
var packetTypes = packets.types;
|
var packetTypes = packets.types;
|
||||||
|
var scores = HostList.scores;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pool of peers for handling all network activity.
|
* A pool of peers for handling all network activity.
|
||||||
@ -85,7 +86,6 @@ function Pool(options) {
|
|||||||
this.mempool = this.options.mempool;
|
this.mempool = this.options.mempool;
|
||||||
this.server = this.options.createServer();
|
this.server = this.options.createServer();
|
||||||
this.nonces = this.options.nonces;
|
this.nonces = this.options.nonces;
|
||||||
this.address = this.options.address;
|
|
||||||
|
|
||||||
this.locker = new Lock(true);
|
this.locker = new Lock(true);
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
@ -127,7 +127,15 @@ util.inherits(Pool, AsyncObject);
|
|||||||
* @default
|
* @default
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.MAX_HEADER_FAILS = 500;
|
Pool.MAX_HEADER_FAILS = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovery interval for UPNP and DNS seeds.
|
||||||
|
* @const {Number}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.DISCOVERY_INTERVAL = 120000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the pool.
|
* Initialize the pool.
|
||||||
@ -286,38 +294,24 @@ Pool.prototype.connect = co(function* connect() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype._connect = co(function* connect() {
|
Pool.prototype._connect = co(function* connect() {
|
||||||
var ip;
|
|
||||||
|
|
||||||
assert(this.loaded, 'Pool is not loaded.');
|
assert(this.loaded, 'Pool is not loaded.');
|
||||||
|
|
||||||
if (this.connected)
|
if (this.connected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this.address.isNull() && !this.options.proxy) {
|
yield this.hosts.open();
|
||||||
try {
|
yield this.authdb.open();
|
||||||
ip = yield this.getIP();
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.error(e);
|
|
||||||
}
|
|
||||||
if (ip) {
|
|
||||||
this.address.setHost(ip);
|
|
||||||
this.logger.info('External IP found: %s.', ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yield this.authdb.discover();
|
yield this.discoverGateway();
|
||||||
|
yield this.discoverExternal();
|
||||||
yield this.hosts.discover();
|
yield this.discoverSeeds();
|
||||||
|
|
||||||
if (this.hosts.size() === 0)
|
|
||||||
throw new Error('No hosts available. Do you have an internet connection?');
|
|
||||||
|
|
||||||
this.logger.info('Resolved %d hosts from DNS seeds.', this.hosts.size());
|
|
||||||
|
|
||||||
this.fillOutbound();
|
this.fillOutbound();
|
||||||
|
|
||||||
yield this.listen();
|
yield this.listen();
|
||||||
|
|
||||||
|
this.startTimer();
|
||||||
|
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -380,6 +374,11 @@ Pool.prototype._disconnect = co(function* disconnect() {
|
|||||||
this.headerChain.reset();
|
this.headerChain.reset();
|
||||||
this.headerNext = null;
|
this.headerNext = null;
|
||||||
|
|
||||||
|
this.stopTimer();
|
||||||
|
|
||||||
|
yield this.authdb.close();
|
||||||
|
yield this.hosts.close();
|
||||||
|
|
||||||
yield this.unlisten();
|
yield this.unlisten();
|
||||||
|
|
||||||
this.disconnecting = false;
|
this.disconnecting = false;
|
||||||
@ -423,6 +422,160 @@ Pool.prototype.unlisten = co(function* unlisten() {
|
|||||||
yield this.server.close();
|
yield this.server.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start discovery timer.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype.startTimer = function startTimer() {
|
||||||
|
assert(this.timer == null, 'Timer already started.');
|
||||||
|
this.timer = co.setInterval(this.discover, Pool.DISCOVERY_INTERVAL, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop discovery timer.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype.stopTimer = function stopTimer() {
|
||||||
|
assert(this.timer != null, 'Timer already stopped.');
|
||||||
|
co.clearInterval(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rediscover seeds and internet gateway.
|
||||||
|
* Attempt to add port mapping once again.
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype.discover = co(function* discover() {
|
||||||
|
yield this.discoverGateway();
|
||||||
|
yield this.discoverSeeds(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to add port mapping (i.e.
|
||||||
|
* remote:8333->local:8333) via UPNP.
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype.discoverGateway = co(function* discoverGateway() {
|
||||||
|
var src = this.options.publicPort;
|
||||||
|
var dest = this.options.port;
|
||||||
|
var wan, host;
|
||||||
|
|
||||||
|
// Pointless if we're not listening.
|
||||||
|
if (!this.options.listen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// UPNP is always optional, since
|
||||||
|
// it's likely to not work anyway.
|
||||||
|
if (!this.options.upnp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.logger.debug('Discovering internet gateway (upnp).');
|
||||||
|
wan = yield UPNP.discover();
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.debug('UPNP error:');
|
||||||
|
this.logger.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
host = yield wan.getExternalIP();
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.debug('Could not find external IP.');
|
||||||
|
this.logger.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hosts.addLocal(host, src, scores.UPNP))
|
||||||
|
this.logger.info('External IP found (upnp): %s.', host);
|
||||||
|
|
||||||
|
this.logger.debug(
|
||||||
|
'Adding port mapping %d->%d.',
|
||||||
|
src, dest);
|
||||||
|
|
||||||
|
try {
|
||||||
|
yield wan.addPortMapping(host, src, dest);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.debug('Could not add port mapping.');
|
||||||
|
this.logger.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to resolve DNS seeds if necessary.
|
||||||
|
* @param {Boolean} checkPeers
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype.discoverSeeds = co(function* discoverSeeds(checkPeers) {
|
||||||
|
var total = 0;
|
||||||
|
var max = Math.min(2, this.options.maxOutbound);
|
||||||
|
var size = this.hosts.size();
|
||||||
|
var peer;
|
||||||
|
|
||||||
|
for (peer = this.peers.head(); peer; peer = peer.next) {
|
||||||
|
if (peer.handshake) {
|
||||||
|
if (++total > max)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size === 0 || (checkPeers && total < max)) {
|
||||||
|
this.logger.warning('Could not find enough peers.');
|
||||||
|
this.logger.warning('Hitting DNS seeds...');
|
||||||
|
|
||||||
|
yield this.hosts.discoverSeeds();
|
||||||
|
|
||||||
|
this.logger.info(
|
||||||
|
'Resolved %d hosts from DNS seeds.',
|
||||||
|
this.hosts.size() - size);
|
||||||
|
|
||||||
|
this.refill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to discover external IP via HTTP.
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Pool.prototype.discoverExternal = co(function* discoverExternal() {
|
||||||
|
var port = this.options.publicPort;
|
||||||
|
var host;
|
||||||
|
|
||||||
|
// Pointless if we're not listening.
|
||||||
|
if (!this.options.listen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Never hit an HTTP server if
|
||||||
|
// we're using an outbound proxy.
|
||||||
|
if (this.options.proxy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Try not to hit this if we can avoid it.
|
||||||
|
if (this.hosts.local.size > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
host = yield this.getIP();
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.debug('Could not find external IP.');
|
||||||
|
this.logger.error(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hosts.addLocal(host, port, scores.HTTP))
|
||||||
|
this.logger.info('External IP found (http): %s.', host);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle incoming connection.
|
* Handle incoming connection.
|
||||||
* @private
|
* @private
|
||||||
@ -1118,11 +1271,13 @@ Pool.prototype.handleConnect = co(function* handleConnect(peer) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Pool.prototype.handleOpen = co(function* handleOpen(peer) {
|
Pool.prototype.handleOpen = co(function* handleOpen(peer) {
|
||||||
|
var addr;
|
||||||
|
|
||||||
// Advertise our address.
|
// Advertise our address.
|
||||||
if (!this.address.isNull()
|
if (!this.options.selfish && this.options.listen) {
|
||||||
&& !this.options.selfish
|
addr = this.hosts.getLocal(peer.address);
|
||||||
&& this.options.listen) {
|
if (addr)
|
||||||
peer.send(new packets.AddrPacket([this.address]));
|
peer.send(new packets.AddrPacket([addr]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want compact blocks!
|
// We want compact blocks!
|
||||||
@ -1239,6 +1394,9 @@ Pool.prototype.handleVersion = co(function* handleVersion(peer, packet) {
|
|||||||
|
|
||||||
this.network.time.add(peer.hostname(), packet.ts);
|
this.network.time.add(peer.hostname(), packet.ts);
|
||||||
this.nonces.remove(peer.hostname());
|
this.nonces.remove(peer.hostname());
|
||||||
|
|
||||||
|
if (!peer.outbound && packet.remote.isRoutable())
|
||||||
|
this.hosts.markLocal(packet.remote);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2492,7 +2650,7 @@ Pool.prototype.handleSendCmpct = co(function* handleSendCmpct(peer, packet) {
|
|||||||
Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
|
Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
|
||||||
var block = packet.block;
|
var block = packet.block;
|
||||||
var hash = block.hash('hex');
|
var hash = block.hash('hex');
|
||||||
var witness = peer.hasWitness();
|
var witness = peer.compactWitness;
|
||||||
var flags = chainCommon.flags.VERIFY_BODY;
|
var flags = chainCommon.flags.VERIFY_BODY;
|
||||||
var result;
|
var result;
|
||||||
|
|
||||||
@ -2507,7 +2665,7 @@ Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!peer.hasCompactSupport()) {
|
if (!peer.hasCompactSupport() && !peer.hasCompact()) {
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
'Peer sent unsolicited cmpctblock (%s).',
|
'Peer sent unsolicited cmpctblock (%s).',
|
||||||
peer.hostname());
|
peer.hostname());
|
||||||
@ -3394,8 +3552,6 @@ function PoolOptions(options) {
|
|||||||
this.mempool = null;
|
this.mempool = null;
|
||||||
|
|
||||||
this.nonces = new NonceList();
|
this.nonces = new NonceList();
|
||||||
this.address = new NetAddress();
|
|
||||||
this.address.port = this.network.port;
|
|
||||||
|
|
||||||
this.checkpoints = true;
|
this.checkpoints = true;
|
||||||
this.spv = false;
|
this.spv = false;
|
||||||
@ -3405,6 +3561,8 @@ function PoolOptions(options) {
|
|||||||
this.noRelay = false;
|
this.noRelay = false;
|
||||||
this.host = '0.0.0.0';
|
this.host = '0.0.0.0';
|
||||||
this.port = this.network.port;
|
this.port = this.network.port;
|
||||||
|
this.publicHost = '0.0.0.0';
|
||||||
|
this.publicPort = this.network.port;
|
||||||
this.maxOutbound = 8;
|
this.maxOutbound = 8;
|
||||||
this.maxInbound = 8;
|
this.maxInbound = 8;
|
||||||
this.createSocket = this._createSocket.bind(this);
|
this.createSocket = this._createSocket.bind(this);
|
||||||
@ -3412,6 +3570,7 @@ function PoolOptions(options) {
|
|||||||
this.resolve = this._resolve.bind(this);
|
this.resolve = this._resolve.bind(this);
|
||||||
this.proxy = null;
|
this.proxy = null;
|
||||||
this.onion = false;
|
this.onion = false;
|
||||||
|
this.upnp = false;
|
||||||
this.selfish = false;
|
this.selfish = false;
|
||||||
this.version = common.PROTOCOL_VERSION;
|
this.version = common.PROTOCOL_VERSION;
|
||||||
this.agent = common.USER_AGENT;
|
this.agent = common.USER_AGENT;
|
||||||
@ -3441,6 +3600,8 @@ function PoolOptions(options) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
||||||
|
var raw;
|
||||||
|
|
||||||
assert(options, 'Pool requires options.');
|
assert(options, 'Pool requires options.');
|
||||||
assert(options.chain && typeof options.chain === 'object',
|
assert(options.chain && typeof options.chain === 'object',
|
||||||
'Pool options require a blockchain.');
|
'Pool options require a blockchain.');
|
||||||
@ -3451,7 +3612,8 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
|
|
||||||
this.port = this.network.port;
|
this.port = this.network.port;
|
||||||
this.seeds = this.network.seeds;
|
this.seeds = this.network.seeds;
|
||||||
this.address.port = this.network.port;
|
this.port = this.network.port;
|
||||||
|
this.publicPort = this.network.port;
|
||||||
|
|
||||||
if (options.logger != null) {
|
if (options.logger != null) {
|
||||||
assert(typeof options.logger === 'object');
|
assert(typeof options.logger === 'object');
|
||||||
@ -3501,30 +3663,33 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
|
|
||||||
if (options.host != null) {
|
if (options.host != null) {
|
||||||
assert(typeof options.host === 'string');
|
assert(typeof options.host === 'string');
|
||||||
this.host = options.host;
|
raw = IP.toBuffer(options.host);
|
||||||
this.address.setHost(this.host);
|
this.host = IP.toString(raw);
|
||||||
if (!this.address.isRoutable())
|
if (IP.isRoutable(raw))
|
||||||
this.address.setNull();
|
this.publicHost = this.host;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.port != null) {
|
if (options.port != null) {
|
||||||
assert(typeof options.port === 'number');
|
assert(typeof options.port === 'number');
|
||||||
|
assert(options.port > 0 && options.port <= 0xffff);
|
||||||
this.port = options.port;
|
this.port = options.port;
|
||||||
this.address.setPort(this.port);
|
this.publicPort = options.port;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.publicHost != null) {
|
if (options.publicHost != null) {
|
||||||
assert(typeof options.publicHost === 'string');
|
assert(typeof options.publicHost === 'string');
|
||||||
this.address.setHost(options.publicHost);
|
this.publicHost = IP.normalize(options.publicHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.publicPort != null) {
|
if (options.publicPort != null) {
|
||||||
assert(typeof options.port === 'number');
|
assert(typeof options.publicPort === 'number');
|
||||||
this.address.setPort(options.publicPort);
|
assert(options.publicPort > 0 && options.publicPort <= 0xffff);
|
||||||
|
this.publicPort = options.publicPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.maxOutbound != null) {
|
if (options.maxOutbound != null) {
|
||||||
assert(typeof options.maxOutbound === 'number');
|
assert(typeof options.maxOutbound === 'number');
|
||||||
|
assert(options.maxOutbound > 0);
|
||||||
this.maxOutbound = options.maxOutbound;
|
this.maxOutbound = options.maxOutbound;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3558,6 +3723,11 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
this.onion = options.onion;
|
this.onion = options.onion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.upnp != null) {
|
||||||
|
assert(typeof options.upnp === 'boolean');
|
||||||
|
this.upnp = options.upnp;
|
||||||
|
}
|
||||||
|
|
||||||
if (options.selfish) {
|
if (options.selfish) {
|
||||||
assert(typeof options.selfish === 'boolean');
|
assert(typeof options.selfish === 'boolean');
|
||||||
this.selfish = options.selfish;
|
this.selfish = options.selfish;
|
||||||
@ -3669,9 +3839,6 @@ PoolOptions.prototype.fromOptions = function fromOptions(options) {
|
|||||||
this.requiredServices = options.requiredServices;
|
this.requiredServices = options.requiredServices;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.address.services = this.services;
|
|
||||||
this.address.ts = this.network.now();
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3791,7 +3958,10 @@ PoolOptions.prototype._createSocket = function createSocket(port, host) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
PoolOptions.prototype._resolve = function resolve(name) {
|
PoolOptions.prototype._resolve = function resolve(name) {
|
||||||
return dns.resolve(name, this.proxy, this.onion);
|
if (this.onion)
|
||||||
|
return dns.lookup(name, this.proxy);
|
||||||
|
|
||||||
|
return dns.lookup(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -96,19 +96,6 @@ ProxySocket.prototype._init = function _init() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
ProxySocket.prototype.resolve = function resolve(name, record, callback) {
|
|
||||||
var e;
|
|
||||||
this.socket.emit('dns resolve', name, record, function(err, results) {
|
|
||||||
if (err) {
|
|
||||||
e = new Error(err.message);
|
|
||||||
e.code = err.code || null;
|
|
||||||
callback(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callback(null, results);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
ProxySocket.prototype.connect = function connect(port, host) {
|
ProxySocket.prototype.connect = function connect(port, host) {
|
||||||
var nonce = 0;
|
var nonce = 0;
|
||||||
var i, pow;
|
var i, pow;
|
||||||
|
|||||||
41
lib/net/upnp-browser.js
Normal file
41
lib/net/upnp-browser.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*!
|
||||||
|
* upnp-browser.js - upnp for bcoin
|
||||||
|
* Copyright (c) 2017, Christopher Jeffrey (MIT License).
|
||||||
|
* https://github.com/bcoin-org/bcoin
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UPNP
|
||||||
|
* @constructor
|
||||||
|
* @ignore
|
||||||
|
* @param {String?} host - Multicast IP.
|
||||||
|
* @param {Number?} port - Multicast port.
|
||||||
|
* @param {String?} gateway - Gateway name.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function UPNP(host, port, gateway) {
|
||||||
|
throw new Error('UPNP not supported.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover gateway and resolve service.
|
||||||
|
* @param {String?} host - Multicast IP.
|
||||||
|
* @param {Number?} port - Multicast port.
|
||||||
|
* @param {String?} gateway - Gateway type.
|
||||||
|
* @param {String[]?} targets - Target service types.
|
||||||
|
* @returns {Promise} Service.
|
||||||
|
*/
|
||||||
|
|
||||||
|
UPNP.discover = function discover(host, port, gateway, targets) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
reject(new Error('UPNP not supported.'));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Expose
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = UPNP;
|
||||||
@ -60,7 +60,7 @@ UPNP.WAN_SERVICES = [
|
|||||||
* @default
|
* @default
|
||||||
*/
|
*/
|
||||||
|
|
||||||
UPNP.RESPONSE_TIMEOUT = 3000;
|
UPNP.RESPONSE_TIMEOUT = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up current job.
|
* Clean up current job.
|
||||||
@ -430,14 +430,14 @@ UPNPService.prototype.addPortMapping = co(function* addPortMapping(remote, src,
|
|||||||
var local = IP.getPrivate();
|
var local = IP.getPrivate();
|
||||||
var xml, child;
|
var xml, child;
|
||||||
|
|
||||||
if (!local)
|
if (local.length === 0)
|
||||||
throw new Error('Cannot determine local IP.');
|
throw new Error('Cannot determine local IP.');
|
||||||
|
|
||||||
xml = yield this.soapRequest(action, [
|
xml = yield this.soapRequest(action, [
|
||||||
['NewRemoteHost', remote],
|
['NewRemoteHost', remote],
|
||||||
['NewExternalPort', src],
|
['NewExternalPort', src],
|
||||||
['NewProtocol', 'TCP'],
|
['NewProtocol', 'TCP'],
|
||||||
['NewInternalClient', local],
|
['NewInternalClient', local[0]],
|
||||||
['NewInternalPort', dest],
|
['NewInternalPort', dest],
|
||||||
['NewEnabled', 'True'],
|
['NewEnabled', 'True'],
|
||||||
['NewPortMappingDescription', 'upnp:bcoin'],
|
['NewPortMappingDescription', 'upnp:bcoin'],
|
||||||
|
|||||||
@ -267,6 +267,7 @@ config.toOptions = function toOptions(data) {
|
|||||||
options.identityKey = key(data.identitykey);
|
options.identityKey = key(data.identitykey);
|
||||||
options.proxy = str(data.proxy);
|
options.proxy = str(data.proxy);
|
||||||
options.onion = bool(data.onion);
|
options.onion = bool(data.onion);
|
||||||
|
options.upnp = bool(data.upnp);
|
||||||
options.seeds = list(data.seeds);
|
options.seeds = list(data.seeds);
|
||||||
options.nodes = list(data.nodes);
|
options.nodes = list(data.nodes);
|
||||||
options.maxOutbound = num(data.maxoutbound);
|
options.maxOutbound = num(data.maxoutbound);
|
||||||
|
|||||||
@ -102,6 +102,7 @@ function FullNode(options) {
|
|||||||
maxInbound: this.options.maxInbound,
|
maxInbound: this.options.maxInbound,
|
||||||
proxy: this.options.proxy,
|
proxy: this.options.proxy,
|
||||||
onion: this.options.onion,
|
onion: this.options.onion,
|
||||||
|
upnp: this.options.upnp,
|
||||||
seeds: this.options.seeds,
|
seeds: this.options.seeds,
|
||||||
nodes: this.options.nodes,
|
nodes: this.options.nodes,
|
||||||
publicHost: this.options.publicHost,
|
publicHost: this.options.publicHost,
|
||||||
|
|||||||
@ -62,6 +62,7 @@ function SPVNode(options) {
|
|||||||
chain: this.chain,
|
chain: this.chain,
|
||||||
proxy: this.options.proxy,
|
proxy: this.options.proxy,
|
||||||
onion: this.options.onion,
|
onion: this.options.onion,
|
||||||
|
upnp: this.options.upnp,
|
||||||
seeds: this.options.seeds,
|
seeds: this.options.seeds,
|
||||||
nodes: this.options.nodes,
|
nodes: this.options.nodes,
|
||||||
bip151: this.options.bip151,
|
bip151: this.options.bip151,
|
||||||
|
|||||||
@ -168,6 +168,29 @@ NetAddress.prototype.isOnion = function isOnion() {
|
|||||||
return IP.isOnion(this.raw);
|
return IP.isOnion(this.raw);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare against another network address.
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
NetAddress.prototype.equal = function equal(addr) {
|
||||||
|
return this.compare(addr) === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare against another network address.
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
|
||||||
|
NetAddress.prototype.compare = function compare(addr) {
|
||||||
|
var cmp = util.cmp(this.raw, addr.raw);
|
||||||
|
|
||||||
|
if (cmp !== 0)
|
||||||
|
return cmp;
|
||||||
|
|
||||||
|
return this.port - addr.port;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get reachable score to destination.
|
* Get reachable score to destination.
|
||||||
* @param {NetAddress} dest
|
* @param {NetAddress} dest
|
||||||
@ -205,6 +228,7 @@ NetAddress.prototype.setHost = function setHost(host) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
NetAddress.prototype.setPort = function setPort(port) {
|
NetAddress.prototype.setPort = function setPort(port) {
|
||||||
|
assert(port >= 0 && port <= 0xffff);
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.hostname = IP.toHostname(this.host, port);
|
this.hostname = IP.toHostname(this.host, port);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -43,17 +43,18 @@ main = network.main = {};
|
|||||||
main.type = 'main';
|
main.type = 'main';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default seeds.
|
* Default DNS seeds.
|
||||||
* @const {String[]}
|
* @const {String[]}
|
||||||
* @default
|
* @default
|
||||||
*/
|
*/
|
||||||
|
|
||||||
main.seeds = [
|
main.seeds = [
|
||||||
|
'seed.bitcoin.sipa.be', // Pieter Wuille
|
||||||
'dnsseed.bluematt.me', // Matt Corallo
|
'dnsseed.bluematt.me', // Matt Corallo
|
||||||
'dnsseed.bitcoin.dashjr.org', // Luke Dashjr
|
'dnsseed.bitcoin.dashjr.org', // Luke Dashjr
|
||||||
'seed.bitcoinstats.com', // Christian Decker
|
'seed.bitcoinstats.com', // Christian Decker
|
||||||
'bitseed.xf2.org', // Jeff Garzik
|
'seed.bitcoin.jonasschnelli.ch', // Jonas Schnelli
|
||||||
'seed.bitcoin.jonasschnelli.ch' // Jonas Schnelli
|
'seed.btc.petertodd.org' // Peter Todd
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -469,9 +470,10 @@ testnet = network.testnet = {};
|
|||||||
testnet.type = 'testnet';
|
testnet.type = 'testnet';
|
||||||
|
|
||||||
testnet.seeds = [
|
testnet.seeds = [
|
||||||
'testnet-seed.bitcoin.petertodd.org',
|
'testnet-seed.bitcoin.jonasschnelli.ch', // Jonas Schnelli
|
||||||
'testnet-seed.bluematt.me',
|
'seed.tbtc.petertodd.org', // Peter Todd
|
||||||
'testnet-seed.bitcoin.schildbach.de'
|
'testnet-seed.bluematt.me', // Matt Corallo
|
||||||
|
'testnet-seed.bitcoin.schildbach.de' // Andreas Schildbach
|
||||||
];
|
];
|
||||||
|
|
||||||
testnet.magic = 0x0709110b;
|
testnet.magic = 0x0709110b;
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
* @module utils/co
|
* @module utils/co
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
var nextTick = require('./nexttick');
|
var nextTick = require('./nexttick');
|
||||||
var every;
|
var every;
|
||||||
|
|
||||||
@ -230,6 +231,83 @@ every = co(function* every(jobs) {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start an interval. Wait for promise
|
||||||
|
* to resolve on each iteration.
|
||||||
|
* @param {Function} func
|
||||||
|
* @param {Number?} time
|
||||||
|
* @param {Object?} self
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function startInterval(func, time, self) {
|
||||||
|
var cb, ctx;
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
timer: null,
|
||||||
|
stopped: false
|
||||||
|
};
|
||||||
|
|
||||||
|
cb = co(function* () {
|
||||||
|
assert(ctx.timer != null);
|
||||||
|
ctx.timer = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
yield func.call(self);
|
||||||
|
} finally {
|
||||||
|
if (!ctx.stopped)
|
||||||
|
ctx.timer = setTimeout(cb, time);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.timer = setTimeout(cb, time);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear an interval.
|
||||||
|
* @param {Object} ctx
|
||||||
|
*/
|
||||||
|
|
||||||
|
function stopInterval(ctx) {
|
||||||
|
assert(ctx);
|
||||||
|
if (ctx.timer != null) {
|
||||||
|
clearTimeout(ctx.timer);
|
||||||
|
ctx.timer = null;
|
||||||
|
}
|
||||||
|
ctx.stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a timeout.
|
||||||
|
* @param {Function} func
|
||||||
|
* @param {Number?} time
|
||||||
|
* @param {Object?} self
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function startTimeout(func, time, self) {
|
||||||
|
return {
|
||||||
|
timer: setTimeout(func.bind(self), time),
|
||||||
|
stopped: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a timeout.
|
||||||
|
* @param {Object} ctx
|
||||||
|
*/
|
||||||
|
|
||||||
|
function stopTimeout(ctx) {
|
||||||
|
assert(ctx);
|
||||||
|
if (ctx.timer != null) {
|
||||||
|
clearTimeout(ctx.timer);
|
||||||
|
ctx.timer = null;
|
||||||
|
}
|
||||||
|
ctx.stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a job object.
|
* Create a job object.
|
||||||
* @returns {Job}
|
* @returns {Job}
|
||||||
@ -282,6 +360,10 @@ exports.timeout = timeout;
|
|||||||
exports.wrap = wrap;
|
exports.wrap = wrap;
|
||||||
exports.promisify = promisify;
|
exports.promisify = promisify;
|
||||||
exports.every = every;
|
exports.every = every;
|
||||||
|
exports.setInterval = startInterval;
|
||||||
|
exports.clearInterval = stopInterval;
|
||||||
|
exports.setTimeout = startTimeout;
|
||||||
|
exports.clearTimeout = stopTimeout;
|
||||||
exports.job = job;
|
exports.job = job;
|
||||||
|
|
||||||
module.exports = exports;
|
module.exports = exports;
|
||||||
|
|||||||
@ -984,15 +984,16 @@ IP.isEqual = function isEqual(a, b) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get IP address from network interfaces.
|
* Get IP address from network interfaces.
|
||||||
* @param {String} name - `public` or `private`.
|
* @param {String?} name - `public` or `private`.
|
||||||
* @param {String} family - IP family name.
|
* @param {String?} family - IP family name.
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
IP.getInterface = function getInterface(name, family) {
|
IP.getInterfaces = function _getInterfaces(name, family) {
|
||||||
var interfaces = getInterfaces();
|
var interfaces = getInterfaces();
|
||||||
var keys = Object.keys(interfaces);
|
var keys = Object.keys(interfaces);
|
||||||
var i, j, key, items, details, type, ip;
|
var result = [];
|
||||||
|
var i, j, key, items, details, type, raw;
|
||||||
|
|
||||||
for (i = 0; i < keys.length; i++) {
|
for (i = 0; i < keys.length; i++) {
|
||||||
key = keys[i];
|
key = keys[i];
|
||||||
@ -1003,57 +1004,57 @@ IP.getInterface = function getInterface(name, family) {
|
|||||||
|
|
||||||
type = details.family.toLowerCase();
|
type = details.family.toLowerCase();
|
||||||
|
|
||||||
if (type !== family)
|
if (family && type !== family)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (details.internal)
|
if (details.internal)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ip = IP.toBuffer(details.address);
|
raw = IP.toBuffer(details.address);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IP.isLocal(ip))
|
if (IP.isNull(raw))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IP.isLocal(raw))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (name === 'public') {
|
if (name === 'public') {
|
||||||
if (!IP.isRoutable(ip))
|
if (!IP.isRoutable(raw))
|
||||||
|
continue;
|
||||||
|
} else if (name === 'private') {
|
||||||
|
if (IP.isRoutable(raw))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return IP.toString(ip);
|
result.push(IP.toString(raw));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get private IP from network interfaces.
|
* Get private IP from network interfaces.
|
||||||
|
* @param {String?} family - IP family name.
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
IP.getPrivate = function getPrivate() {
|
IP.getPrivate = function getPrivate(family) {
|
||||||
var ip = IP.getInterface('private', 'ipv4');
|
return IP.getInterfaces('private', family);
|
||||||
|
|
||||||
if (ip)
|
|
||||||
return ip;
|
|
||||||
|
|
||||||
return IP.getInterface('private', 'ipv6');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get public IP from network interfaces.
|
* Get public IP from network interfaces.
|
||||||
|
* @param {String?} family - IP family name.
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
IP.getPublic = function getPublic() {
|
IP.getPublic = function getPublic(family) {
|
||||||
var ip = IP.getInterface('public', 'ipv4');
|
return IP.getInterfaces('public', family);
|
||||||
|
|
||||||
if (ip)
|
|
||||||
return ip;
|
|
||||||
|
|
||||||
return IP.getInterface('public', 'ipv6');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
"./lib/mempool/layout": "./lib/mempool/layout-browser.js",
|
"./lib/mempool/layout": "./lib/mempool/layout-browser.js",
|
||||||
"./lib/net/dns": "./lib/net/dns-browser.js",
|
"./lib/net/dns": "./lib/net/dns-browser.js",
|
||||||
"./lib/net/tcp": "./lib/net/tcp-browser.js",
|
"./lib/net/tcp": "./lib/net/tcp-browser.js",
|
||||||
|
"./lib/net/upnp": "./lib/net/upnp-browser.js",
|
||||||
"./lib/utils/native": "./browser/empty.js",
|
"./lib/utils/native": "./browser/empty.js",
|
||||||
"./lib/utils/nfkd": "./lib/utils/nfkd-browser.js",
|
"./lib/utils/nfkd": "./lib/utils/nfkd-browser.js",
|
||||||
"./lib/utils/nexttick": "./lib/utils/nexttick-browser.js",
|
"./lib/utils/nexttick": "./lib/utils/nexttick-browser.js",
|
||||||
@ -87,6 +88,7 @@
|
|||||||
"bcoin-native": "./browser/empty.js",
|
"bcoin-native": "./browser/empty.js",
|
||||||
"child_process": "./browser/empty.js",
|
"child_process": "./browser/empty.js",
|
||||||
"crypto": "./browser/empty.js",
|
"crypto": "./browser/empty.js",
|
||||||
|
"dgram": "./browser/empty.js",
|
||||||
"fs": "./browser/empty.js",
|
"fs": "./browser/empty.js",
|
||||||
"net": "./browser/empty.js",
|
"net": "./browser/empty.js",
|
||||||
"os": "./browser/empty.js",
|
"os": "./browser/empty.js",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user