node: better option handling.

This commit is contained in:
Christopher Jeffrey 2017-01-14 19:21:46 -08:00
parent a486bd3a18
commit 199699d73a
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
10 changed files with 209 additions and 131 deletions

View File

@ -2281,7 +2281,7 @@ function ChainOptions(options) {
this.bufferKeys = !util.isBrowser; this.bufferKeys = !util.isBrowser;
this.spv = false; this.spv = false;
this.witness = false; this.witness = this.network.witness;
this.prune = false; this.prune = false;
this.indexTX = false; this.indexTX = false;
this.indexAddress = false; this.indexAddress = false;
@ -2307,6 +2307,8 @@ ChainOptions.prototype.fromOptions = function fromOptions(options) {
if (options.network != null) if (options.network != null)
this.network = Network.get(options.network); this.network = Network.get(options.network);
this.witness = this.network.witness;
if (options.logger != null) { if (options.logger != null) {
assert(typeof options.logger === 'object'); assert(typeof options.logger === 'object');
this.logger = options.logger; this.logger = options.logger;

View File

@ -2190,7 +2190,7 @@ RPC.prototype.sendrawtransaction = co(function* sendrawtransaction(args) {
tx = TX.fromRaw(args[0], 'hex'); tx = TX.fromRaw(args[0], 'hex');
this.node.sendTX(tx).catch(util.nop); this.node.relay(tx);
return tx.txid(); return tx.txid();
}); });

View File

@ -1927,7 +1927,7 @@ function PoolOptions(options) {
this.chain = null; this.chain = null;
this.mempool = null; this.mempool = null;
this.witness = false; this.witness = this.network.witness;
this.spv = false; this.spv = false;
this.listen = false; this.listen = false;
this.headers = false; this.headers = false;

View File

@ -227,6 +227,14 @@ config.parseData = function parseData(data, prefix, dirname) {
if (options.authPeers != null) if (options.authPeers != null)
options.authPeers = config.parseAuth(options.authPeers); options.authPeers = config.parseAuth(options.authPeers);
// Alias
if (options.fast) {
options.headers = true;
options.useCheckpoints = true;
options.cacheSize = 300 << 20;
options.coinCache = 100 << 20;
}
return options; return options;
}; };

View File

@ -175,7 +175,7 @@ util.inherits(FullNode, Node);
FullNode.prototype._init = function _init() { FullNode.prototype._init = function _init() {
var self = this; var self = this;
var onError = this._error.bind(this); var onError = this.error.bind(this);
// Bind to errors // Bind to errors
this.chain.on('error', onError); this.chain.on('error', onError);
@ -201,7 +201,7 @@ FullNode.prototype._init = function _init() {
try { try {
yield self.mempool.addBlock(entry, block.txs); yield self.mempool.addBlock(entry, block.txs);
} catch (e) { } catch (e) {
self._error(e); self.error(e);
} }
} }
self.emit('block', block); self.emit('block', block);
@ -213,7 +213,7 @@ FullNode.prototype._init = function _init() {
try { try {
yield self.mempool.removeBlock(entry, block.txs); yield self.mempool.removeBlock(entry, block.txs);
} catch (e) { } catch (e) {
self._error(e); self.error(e);
} }
} }
self.emit('disconnect', entry, block); self.emit('disconnect', entry, block);
@ -223,7 +223,7 @@ FullNode.prototype._init = function _init() {
try { try {
yield self.mempool.reset(); yield self.mempool.reset();
} catch (e) { } catch (e) {
self._error(e); self.error(e);
} }
self.emit('reset', tip); self.emit('reset', tip);
})); }));
@ -305,11 +305,7 @@ FullNode.prototype.broadcast = co(function* broadcast(item) {
}); });
/** /**
* Verify a transaction, add it to the mempool, and broadcast. * Add transaction to mempool, broadcast.
* Safer than {@link FullNode#broadcast}.
* @example
* node.sendTX(tx, callback);
* node.sendTX(tx, true, callback);
* @param {TX} tx * @param {TX} tx
*/ */
@ -320,7 +316,7 @@ FullNode.prototype.sendTX = co(function* sendTX(tx) {
missing = yield this.mempool.addTX(tx); missing = yield this.mempool.addTX(tx);
} catch (err) { } catch (err) {
if (err.type === 'VerifyError' && err.score === 0) { if (err.type === 'VerifyError' && err.score === 0) {
this._error(err); this.error(err);
this.logger.warning('Verification failed for tx: %s.', tx.txid()); this.logger.warning('Verification failed for tx: %s.', tx.txid());
this.logger.warning('Attempting to broadcast anyway...'); this.logger.warning('Attempting to broadcast anyway...');
this.broadcast(tx); this.broadcast(tx);
@ -338,10 +334,24 @@ FullNode.prototype.sendTX = co(function* sendTX(tx) {
// We need to announce by hand if // We need to announce by hand if
// we're running in selfish mode. // we're running in selfish mode.
if (this.options.selfish) if (this.pool.options.selfish)
this.pool.announceTX(tx); this.pool.announceTX(tx);
}); });
/**
* Add transaction to mempool, broadcast. Silence errors.
* @param {TX} tx
* @returns {Promise}
*/
FullNode.prototype.relay = co(function* relay(tx) {
try {
yield this.sendTX(tx);
} catch (e) {
this.error(e);
}
});
/** /**
* Connect to the network. * Connect to the network.
* @returns {Promise} * @returns {Promise}

View File

@ -6,9 +6,9 @@
'use strict'; 'use strict';
var util = require('../utils/util');
var assert = require('assert'); var assert = require('assert');
var fs = require('fs'); var fs = require('fs');
var util = require('../utils/util');
/** /**
* Basic stdout and file logger. * Basic stdout and file logger.
@ -23,14 +23,15 @@ function Logger(options) {
if (!(this instanceof Logger)) if (!(this instanceof Logger))
return new Logger(options); return new Logger(options);
this.level = Logger.levels.WARNING; this.level = Logger.levels.NONE;
this.colors = Logger.HAS_TTY; this.colors = Logger.HAS_TTY;
this.console = true; this.console = true;
this.file = null; this.filename = null;
this.stream = null; this.stream = null;
this.closed = false; this.closed = false;
this.lastFail = 0;
this._init(options); this.init(options);
} }
/** /**
@ -56,7 +57,8 @@ Logger.levels = {
/** /**
* Available log levels. * Available log levels.
* @enum {Number} * @const {String[]}
* @default
*/ */
Logger.levelsByVal = [ Logger.levelsByVal = [
@ -70,7 +72,8 @@ Logger.levelsByVal = [
/** /**
* Default CSI colors. * Default CSI colors.
* @enum {String} * @const {String[]}
* @default
*/ */
Logger.colors = [ Logger.colors = [
@ -88,7 +91,7 @@ Logger.colors = [
* @param {Object} options * @param {Object} options
*/ */
Logger.prototype._init = function _init(options) { Logger.prototype.init = function init(options) {
if (!options) if (!options)
return; return;
@ -112,9 +115,9 @@ Logger.prototype._init = function _init(options) {
this.console = options.console; this.console = options.console;
} }
if (options.file != null) { if (options.filename != null) {
assert(typeof options.file === 'string', 'Bad file.'); assert(typeof options.filename === 'string', 'Bad file.');
this.file = options.file; this.filename = options.filename;
} }
if (options.stream != null) { if (options.stream != null) {
@ -151,6 +154,17 @@ Logger.prototype.close = function close() {
this.closed = true; this.closed = true;
}; };
/**
* Set the log file location.
* @param {String} filename
*/
Logger.prototype.setFile = function setFile(filename) {
assert(typeof filename === 'string');
assert(!this.stream, 'Log stream has already been created.');
this.filename = filename;
};
/** /**
* Set or reset the log level. * Set or reset the log level.
* @param {String} level * @param {String} level
@ -324,6 +338,49 @@ Logger.prototype.writeConsole = function writeConsole(level, args) {
: process.stdout.write(msg + '\n'); : process.stdout.write(msg + '\n');
}; };
/**
* Create or get the current file stream.
* @returns {Object}
*/
Logger.prototype.getStream = function getStream() {
if (this.closed)
return;
if (!this.filename)
return;
if (fs.unsupported)
return;
if (this.stream)
return this.stream;
if (this.lastFail > util.now() - 10)
return;
this.lastFail = 0;
util.mkdir(this.filename, true);
this.stream = fs.createWriteStream(this.filename, { flags: 'a' });
this.stream.on('error', function(err) {
self.writeConsole(Logger.levels.WARNING, 'Log file stream died!');
self.writeConsole(Logger.levels.ERROR, err.message);
try {
self.stream.close();
} catch (e) {
;
}
// Retry in ten seconds.
self.stream = null;
self.lastFail = util.now();
});
};
/** /**
* Write a string to the output stream (usually a file). * Write a string to the output stream (usually a file).
* @param {String} level * @param {String} level
@ -332,26 +389,14 @@ Logger.prototype.writeConsole = function writeConsole(level, args) {
Logger.prototype.writeStream = function writeStream(level, args) { Logger.prototype.writeStream = function writeStream(level, args) {
var name = Logger.levelsByVal[level]; var name = Logger.levelsByVal[level];
var stream = this.getStream();
var prefix, msg; var prefix, msg;
assert(name, 'Invalid log level.'); assert(name, 'Invalid log level.');
if (this.closed) if (!stream)
return; return;
if (!this.stream) {
if (!this.file)
return;
if (fs.unsupported)
return;
util.mkdir(this.file, true);
this.stream = fs.createWriteStream(this.file, { flags: 'a' });
this.stream.on('error', function() {});
}
prefix = '[' + name + '] '; prefix = '[' + name + '] ';
msg = prefix + util.format(args, false); msg = prefix + util.format(args, false);
msg = '(' + util.date() + '): ' + msg + '\n'; msg = '(' + util.date() + '): ' + msg + '\n';
@ -359,7 +404,7 @@ Logger.prototype.writeStream = function writeStream(level, args) {
if (!util.isBrowser) if (!util.isBrowser)
msg = process.pid + ' ' + msg; msg = process.pid + ' ' + msg;
this.stream.write(msg); stream.write(msg);
}; };
/** /**
@ -411,7 +456,7 @@ Logger.prototype.memory = function memory() {
* Default * Default
*/ */
Logger.global = new Logger('none'); Logger.global = new Logger();
/* /*
* Expose * Expose

View File

@ -7,10 +7,10 @@
'use strict'; 'use strict';
var assert = require('assert');
var AsyncObject = require('../utils/async'); var AsyncObject = require('../utils/async');
var util = require('../utils/util'); var util = require('../utils/util');
var co = require('../utils/co'); var co = require('../utils/co');
var assert = require('assert');
var Network = require('../protocol/network'); var Network = require('../protocol/network');
var Logger = require('./logger'); var Logger = require('./logger');
var NodeClient = require('./nodeclient'); var NodeClient = require('./nodeclient');
@ -31,16 +31,13 @@ function Node(options) {
AsyncObject.call(this); AsyncObject.call(this);
if (!options) this.options = {};
options = {}; this.network = Network.primary;
this.prefix = util.HOME + '/.bcoin';
this.startTime = -1;
this.bound = [];
this.parseOptions(options); this.logger = new Logger();
this.options = options;
this.network = Network.get(options.network);
this.prefix = options.prefix;
this.logger = options.logger;
this.chain = null; this.chain = null;
this.fees = null; this.fees = null;
this.mempool = null; this.mempool = null;
@ -49,37 +46,77 @@ function Node(options) {
this.walletdb = null; this.walletdb = null;
this.wallet = null; this.wallet = null;
this.http = null; this.http = null;
this.client = null;
// Local client for walletdb this.init(options);
this.client = new NodeClient(this);
this.startTime = -1;
this._bound = [];
this.__init();
} }
util.inherits(Node, AsyncObject); util.inherits(Node, AsyncObject);
/** /**
* Initialize node. * Initialize options.
* @private * @private
* @param {Object} options
*/ */
Node.prototype.__init = function __init() { Node.prototype.initOptions = function initOptions(options) {
var self = this; if (!options)
return;
if (!this.logger) { assert(typeof options === 'object');
this.logger = new Logger({
level: this.options.logLevel || 'none', this.options = options;
console: this.options.logConsole,
file: this.options.logFile if (options.network != null)
}); this.network = Network.get(options.network);
if (options.prefix != null) {
assert(typeof options.prefix === 'string');
this.prefix = util.normalize(options.prefix);
} }
if (options.logger != null) {
assert(typeof options.logger === 'object');
this.logger = options.logger;
}
if (options.logFile != null) {
if (typeof options.logFile === 'string') {
this.logger.setFile(options.logFile);
} else {
assert(typeof options.logFile === 'boolean');
if (options.logFile)
this.logger.setFile(this.location('debug.log'));
}
}
if (options.logLevel != null) {
assert(typeof options.logLevel === 'string');
this.logger.setLevel(options.logLevel);
}
if (options.logConsole != null) {
assert(typeof options.logConsole === 'boolean');
this.logger.console = options.logConsole;
}
};
/**
* Initialize node.
* @private
* @param {Object} options
*/
Node.prototype.init = function init(options) {
var self = this;
this.initOptions(options);
// Local client for walletdb
this.client = new NodeClient(this);
this.on('preopen', function() { this.on('preopen', function() {
self._onOpen(); self.handleOpen();
}); });
this.on('open', function() { this.on('open', function() {
@ -88,7 +125,7 @@ Node.prototype.__init = function __init() {
this.on('close', function() { this.on('close', function() {
self.startTime = -1; self.startTime = -1;
self._onClose(); self.handleClose();
}); });
}; };
@ -97,33 +134,35 @@ Node.prototype.__init = function __init() {
* @private * @private
*/ */
Node.prototype._onOpen = function _onOpen() { Node.prototype.handleOpen = function handleOpen() {
var self = this; var self = this;
this.logger.open(); this.logger.open();
this._bind(this.network.time, 'offset', function(offset) { this.bind(this.network.time, 'offset', function(offset) {
self.logger.info('Time offset: %d (%d minutes).', offset, offset / 60 | 0); self.logger.info('Time offset: %d (%d minutes).', offset, offset / 60 | 0);
}); });
this._bind(this.network.time, 'sample', function(sample, total) { this.bind(this.network.time, 'sample', function(sample, total) {
self.logger.debug('Added time data: samples=%d, offset=%d (%d minutes).', self.logger.debug(
'Added time data: samples=%d, offset=%d (%d minutes).',
total, sample, sample / 60 | 0); total, sample, sample / 60 | 0);
}); });
this._bind(this.network.time, 'mismatch', function() { this.bind(this.network.time, 'mismatch', function() {
self.logger.warning('Adjusted time mismatch!');
self.logger.warning('Please make sure your system clock is correct!'); self.logger.warning('Please make sure your system clock is correct!');
}); });
this._bind(workerPool, 'spawn', function(child) { this.bind(workerPool, 'spawn', function(child) {
self.logger.info('Spawning worker process: %d.', child.id); self.logger.info('Spawning worker process: %d.', child.id);
}); });
this._bind(workerPool, 'exit', function(code, child) { this.bind(workerPool, 'exit', function(code, child) {
self.logger.warning('Worker %d exited: %s.', child.id, code); self.logger.warning('Worker %d exited: %s.', child.id, code);
}); });
this._bind(workerPool, 'error', function(err, child) { this.bind(workerPool, 'error', function(err, child) {
if (child) { if (child) {
self.logger.error('Worker %d error: %s', child.id, err.message); self.logger.error('Worker %d error: %s', child.id, err.message);
return; return;
@ -137,17 +176,17 @@ Node.prototype._onOpen = function _onOpen() {
* @private * @private
*/ */
Node.prototype._onClose = function _onClose() { Node.prototype.handleClose = function handleClose() {
var i, bound; var i, bound;
this.logger.close(); this.logger.close();
for (i = 0; i < this._bound.length; i++) { for (i = 0; i < this.bound.length; i++) {
bound = this._bound[i]; bound = this.bound[i];
bound[0].removeListener(bound[1], bound[2]); bound[0].removeListener(bound[1], bound[2]);
} }
this._bound.length = 0; this.bound.length = 0;
}; };
/** /**
@ -158,8 +197,8 @@ Node.prototype._onClose = function _onClose() {
* @param {Function} listener * @param {Function} listener
*/ */
Node.prototype._bind = function _bind(obj, event, listener) { Node.prototype.bind = function bind(obj, event, listener) {
this._bound.push([obj, event, listener]); this.bound.push([obj, event, listener]);
obj.on(event, listener); obj.on(event, listener);
}; };
@ -169,7 +208,7 @@ Node.prototype._bind = function _bind(obj, event, listener) {
* @param {Error} err * @param {Error} err
*/ */
Node.prototype._error = function _error(err) { Node.prototype.error = function error(err) {
if (!err) if (!err)
return; return;
@ -192,48 +231,6 @@ Node.prototype._error = function _error(err) {
this.emit('error', err); this.emit('error', err);
}; };
/**
* Parse options object.
* @private
* @param {Object} options
* @returns {Object}
*/
Node.prototype.parseOptions = function parseOptions(options) {
options.network = Network.get(options.network);
if (!options.prefix)
options.prefix = util.HOME + '/.bcoin';
if (!options.db)
options.db = 'memory';
options.prefix = util.normalize(options.prefix);
if (options.logFile && typeof options.logFile !== 'string') {
options.logFile = options.prefix;
if (options.network.type !== 'main')
options.logFile += '/' + options.network.type;
options.logFile += '/debug.log';
}
options.logFile = options.logFile
? util.normalize(options.logFile)
: null;
if (options.fast) {
options.headers = true;
options.useCheckpoints = true;
options.cacheSize = 300 << 20;
options.coinCache = 100 << 20;
}
if (options.witness == null)
options.witness = options.network.witness;
return options;
};
/** /**
* Create a file path from a name * Create a file path from a name
* as well as the node's prefix. * as well as the node's prefix.
@ -243,7 +240,7 @@ Node.prototype.parseOptions = function parseOptions(options) {
Node.prototype.location = function location(name) { Node.prototype.location = function location(name) {
var path = this.prefix; var path = this.prefix;
if (this.network.type !== 'main') if (this.network !== Network.main)
path += '/' + this.network.type; path += '/' + this.network.type;
path += '/' + name; path += '/' + name;
return path; return path;

View File

@ -123,7 +123,7 @@ NodeClient.prototype.getEntry = co(function* getEntry(hash) {
*/ */
NodeClient.prototype.send = function send(tx) { NodeClient.prototype.send = function send(tx) {
this.node.sendTX(tx).catch(util.nop); this.node.relay(tx);
return Promise.resolve(); return Promise.resolve();
}; };

View File

@ -305,6 +305,16 @@ SPVNode.prototype.sendTX = function sendTX(tx) {
return this.broadcast(tx); return this.broadcast(tx);
}; };
/**
* Broadcast a transaction. Silence errors.
* @param {TX} tx
* @returns {Promise}
*/
SPVNode.prototype.relay = function relay(tx) {
return this.broadcast(tx);
};
/** /**
* Connect to the network. * Connect to the network.
* @returns {Promise} * @returns {Promise}

View File

@ -46,6 +46,8 @@ function WorkerPool(options) {
this.enabled = true; this.enabled = true;
this.set(options); this.set(options);
this.on('error', util.nop);
} }
util.inherits(WorkerPool, EventEmitter); util.inherits(WorkerPool, EventEmitter);
@ -871,13 +873,17 @@ exports.pool = new WorkerPool();
exports.pool.enabled = false; exports.pool.enabled = false;
exports.set = function set(options) { exports.set = function set(options) {
this.pool.set(options); this.pool.set({
enabled: options.useWorkers,
size: options.maxWorkers || null,
timeout: options.workerTimeout || null
});
}; };
exports.set({ exports.set({
useWorkers: +process.env.BCOIN_USE_WORKERS === 1, useWorkers: +process.env.BCOIN_USE_WORKERS === 1,
maxWorkers: +process.env.BCOIN_MAX_WORKERS || null, maxWorkers: +process.env.BCOIN_MAX_WORKERS,
workerTimeout: +process.env.BCOIN_WORKER_TIMEOUT || null workerTimeout: +process.env.BCOIN_WORKER_TIMEOUT
}); });
/* /*