node: better option handling.
This commit is contained in:
parent
a486bd3a18
commit
199699d73a
@ -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;
|
||||
|
||||
@ -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();
|
||||
});
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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
|
||||
|
||||
171
lib/node/node.js
171
lib/node/node.js
@ -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;
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Loading…
Reference in New Issue
Block a user