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.spv = false;
this.witness = false;
this.witness = this.network.witness;
this.prune = false;
this.indexTX = false;
this.indexAddress = false;
@ -2307,6 +2307,8 @@ ChainOptions.prototype.fromOptions = function fromOptions(options) {
if (options.network != null)
this.network = Network.get(options.network);
this.witness = this.network.witness;
if (options.logger != null) {
assert(typeof options.logger === 'object');
this.logger = options.logger;

View File

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

View File

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

View File

@ -227,6 +227,14 @@ config.parseData = function parseData(data, prefix, dirname) {
if (options.authPeers != null)
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;
};

View File

@ -175,7 +175,7 @@ util.inherits(FullNode, Node);
FullNode.prototype._init = function _init() {
var self = this;
var onError = this._error.bind(this);
var onError = this.error.bind(this);
// Bind to errors
this.chain.on('error', onError);
@ -201,7 +201,7 @@ FullNode.prototype._init = function _init() {
try {
yield self.mempool.addBlock(entry, block.txs);
} catch (e) {
self._error(e);
self.error(e);
}
}
self.emit('block', block);
@ -213,7 +213,7 @@ FullNode.prototype._init = function _init() {
try {
yield self.mempool.removeBlock(entry, block.txs);
} catch (e) {
self._error(e);
self.error(e);
}
}
self.emit('disconnect', entry, block);
@ -223,7 +223,7 @@ FullNode.prototype._init = function _init() {
try {
yield self.mempool.reset();
} catch (e) {
self._error(e);
self.error(e);
}
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.
* Safer than {@link FullNode#broadcast}.
* @example
* node.sendTX(tx, callback);
* node.sendTX(tx, true, callback);
* Add transaction to mempool, broadcast.
* @param {TX} tx
*/
@ -320,7 +316,7 @@ FullNode.prototype.sendTX = co(function* sendTX(tx) {
missing = yield this.mempool.addTX(tx);
} catch (err) {
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('Attempting to broadcast anyway...');
this.broadcast(tx);
@ -338,10 +334,24 @@ FullNode.prototype.sendTX = co(function* sendTX(tx) {
// We need to announce by hand if
// we're running in selfish mode.
if (this.options.selfish)
if (this.pool.options.selfish)
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.
* @returns {Promise}

View File

@ -6,9 +6,9 @@
'use strict';
var util = require('../utils/util');
var assert = require('assert');
var fs = require('fs');
var util = require('../utils/util');
/**
* Basic stdout and file logger.
@ -23,14 +23,15 @@ function Logger(options) {
if (!(this instanceof Logger))
return new Logger(options);
this.level = Logger.levels.WARNING;
this.level = Logger.levels.NONE;
this.colors = Logger.HAS_TTY;
this.console = true;
this.file = null;
this.filename = null;
this.stream = null;
this.closed = false;
this.lastFail = 0;
this._init(options);
this.init(options);
}
/**
@ -56,7 +57,8 @@ Logger.levels = {
/**
* Available log levels.
* @enum {Number}
* @const {String[]}
* @default
*/
Logger.levelsByVal = [
@ -70,7 +72,8 @@ Logger.levelsByVal = [
/**
* Default CSI colors.
* @enum {String}
* @const {String[]}
* @default
*/
Logger.colors = [
@ -88,7 +91,7 @@ Logger.colors = [
* @param {Object} options
*/
Logger.prototype._init = function _init(options) {
Logger.prototype.init = function init(options) {
if (!options)
return;
@ -112,9 +115,9 @@ Logger.prototype._init = function _init(options) {
this.console = options.console;
}
if (options.file != null) {
assert(typeof options.file === 'string', 'Bad file.');
this.file = options.file;
if (options.filename != null) {
assert(typeof options.filename === 'string', 'Bad file.');
this.filename = options.filename;
}
if (options.stream != null) {
@ -151,6 +154,17 @@ Logger.prototype.close = function close() {
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.
* @param {String} level
@ -324,6 +338,49 @@ Logger.prototype.writeConsole = function writeConsole(level, args) {
: 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).
* @param {String} level
@ -332,26 +389,14 @@ Logger.prototype.writeConsole = function writeConsole(level, args) {
Logger.prototype.writeStream = function writeStream(level, args) {
var name = Logger.levelsByVal[level];
var stream = this.getStream();
var prefix, msg;
assert(name, 'Invalid log level.');
if (this.closed)
if (!stream)
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 + '] ';
msg = prefix + util.format(args, false);
msg = '(' + util.date() + '): ' + msg + '\n';
@ -359,7 +404,7 @@ Logger.prototype.writeStream = function writeStream(level, args) {
if (!util.isBrowser)
msg = process.pid + ' ' + msg;
this.stream.write(msg);
stream.write(msg);
};
/**
@ -411,7 +456,7 @@ Logger.prototype.memory = function memory() {
* Default
*/
Logger.global = new Logger('none');
Logger.global = new Logger();
/*
* Expose

View File

@ -7,10 +7,10 @@
'use strict';
var assert = require('assert');
var AsyncObject = require('../utils/async');
var util = require('../utils/util');
var co = require('../utils/co');
var assert = require('assert');
var Network = require('../protocol/network');
var Logger = require('./logger');
var NodeClient = require('./nodeclient');
@ -31,16 +31,13 @@ function Node(options) {
AsyncObject.call(this);
if (!options)
options = {};
this.options = {};
this.network = Network.primary;
this.prefix = util.HOME + '/.bcoin';
this.startTime = -1;
this.bound = [];
this.parseOptions(options);
this.options = options;
this.network = Network.get(options.network);
this.prefix = options.prefix;
this.logger = options.logger;
this.logger = new Logger();
this.chain = null;
this.fees = null;
this.mempool = null;
@ -49,37 +46,77 @@ function Node(options) {
this.walletdb = null;
this.wallet = null;
this.http = null;
this.client = null;
// Local client for walletdb
this.client = new NodeClient(this);
this.startTime = -1;
this._bound = [];
this.__init();
this.init(options);
}
util.inherits(Node, AsyncObject);
/**
* Initialize node.
* Initialize options.
* @private
* @param {Object} options
*/
Node.prototype.__init = function __init() {
var self = this;
Node.prototype.initOptions = function initOptions(options) {
if (!options)
return;
if (!this.logger) {
this.logger = new Logger({
level: this.options.logLevel || 'none',
console: this.options.logConsole,
file: this.options.logFile
});
assert(typeof options === 'object');
this.options = options;
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() {
self._onOpen();
self.handleOpen();
});
this.on('open', function() {
@ -88,7 +125,7 @@ Node.prototype.__init = function __init() {
this.on('close', function() {
self.startTime = -1;
self._onClose();
self.handleClose();
});
};
@ -97,33 +134,35 @@ Node.prototype.__init = function __init() {
* @private
*/
Node.prototype._onOpen = function _onOpen() {
Node.prototype.handleOpen = function handleOpen() {
var self = this;
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);
});
this._bind(this.network.time, 'sample', function(sample, total) {
self.logger.debug('Added time data: samples=%d, offset=%d (%d minutes).',
this.bind(this.network.time, 'sample', function(sample, total) {
self.logger.debug(
'Added time data: samples=%d, offset=%d (%d minutes).',
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!');
});
this._bind(workerPool, 'spawn', function(child) {
this.bind(workerPool, 'spawn', function(child) {
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);
});
this._bind(workerPool, 'error', function(err, child) {
this.bind(workerPool, 'error', function(err, child) {
if (child) {
self.logger.error('Worker %d error: %s', child.id, err.message);
return;
@ -137,17 +176,17 @@ Node.prototype._onOpen = function _onOpen() {
* @private
*/
Node.prototype._onClose = function _onClose() {
Node.prototype.handleClose = function handleClose() {
var i, bound;
this.logger.close();
for (i = 0; i < this._bound.length; i++) {
bound = this._bound[i];
for (i = 0; i < this.bound.length; i++) {
bound = this.bound[i];
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
*/
Node.prototype._bind = function _bind(obj, event, listener) {
this._bound.push([obj, event, listener]);
Node.prototype.bind = function bind(obj, event, listener) {
this.bound.push([obj, event, listener]);
obj.on(event, listener);
};
@ -169,7 +208,7 @@ Node.prototype._bind = function _bind(obj, event, listener) {
* @param {Error} err
*/
Node.prototype._error = function _error(err) {
Node.prototype.error = function error(err) {
if (!err)
return;
@ -192,48 +231,6 @@ Node.prototype._error = function _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
* as well as the node's prefix.
@ -243,7 +240,7 @@ Node.prototype.parseOptions = function parseOptions(options) {
Node.prototype.location = function location(name) {
var path = this.prefix;
if (this.network.type !== 'main')
if (this.network !== Network.main)
path += '/' + this.network.type;
path += '/' + name;
return path;

View File

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

View File

@ -305,6 +305,16 @@ SPVNode.prototype.sendTX = function sendTX(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.
* @returns {Promise}

View File

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