498 lines
14 KiB
JavaScript
498 lines
14 KiB
JavaScript
/*!
|
|
* env.js - environment for bcoin
|
|
* Copyright (c) 2014-2015, Fedor Indutny (MIT License).
|
|
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
|
* https://github.com/bcoin-org/bcoin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var utils = require('./utils');
|
|
var global = utils.global;
|
|
var fs;
|
|
|
|
if (!utils.isBrowser)
|
|
fs = require('f' + 's');
|
|
|
|
/**
|
|
* A BCoin "environment" which is used for
|
|
* bootstrapping the initial `bcoin` module.
|
|
* It exposes all constructors for primitives,
|
|
* the blockchain, mempool, wallet, etc. It
|
|
* also sets the default network if there is
|
|
* one. It exposes a global {@link TimeData}
|
|
* object for adjusted time, as well as a
|
|
* global worker pool.
|
|
*
|
|
* @exports Environment
|
|
* @constructor
|
|
*
|
|
* @param {(Object|NetworkType)?} options - Options object or network type.
|
|
* @param {(Network|NetworkType)?} options.network
|
|
* @param {String} [options.prefix=~/.bcoin] - Prefix for filesystem.
|
|
* @param {String} [options.db=leveldb] - Database backend.
|
|
* @param {Boolean} [options.debug=false] - Whether to display debug output.
|
|
* @param {String|Boolean} [options.debugFile=~/.bcoin/debug.log] - A file to
|
|
* pipe debug output to.
|
|
* @param {Boolean} [options.profile=false] - Enable profiler.
|
|
* @param {Boolean} [options.useWorkers=false] - Enable workers.
|
|
* @param {Number} [options.maxWorkers=6] - Max size of
|
|
* the worker pool.
|
|
* @param {String} [options.workerUri=/bcoin-worker.js] Location of the bcoin
|
|
* worker.js file for web workers.
|
|
* @param {String} [options.proxyServer=localhost:8080] -
|
|
* Websocket->tcp proxy server for browser.
|
|
* @param {Object?} options.logger - Custom logger.
|
|
* @property {Boolean} isBrowser
|
|
* @property {NetworkType} networkType
|
|
*
|
|
* @property {Function} bn - Big number constructor
|
|
* (see {@link https://github.com/indutny/bn.js} for docs).
|
|
* @property {Object} utils - {@link module:utils}.
|
|
* @property {Function} locker - {@link Locker} constructor.
|
|
* @property {Function} reader - {@link BufferReader} constructor.
|
|
* @property {Function} writer - {@link BufferWriter} constructor.
|
|
* @property {Object} ec - {@link module:ec}.
|
|
* @property {Function} lru - {@link LRU} constructor.
|
|
* @property {Function} bloom - {@link Bloom} constructor.
|
|
* @property {Function} bst - {@link BST} constructor.
|
|
* @property {Function} lowlevelup - See {@link LowlevelUp}.
|
|
* @property {Function} uri - See {@link module:uri}.
|
|
*
|
|
* @property {Object} protocol
|
|
* @property {Function} protocol.constants - See {@link module:constants}.
|
|
* @property {Function} protocol.network - See {@link module:network}.
|
|
* @property {Function} protocol.framer - {@link Framer} constructor.
|
|
* @property {Function} protocol.parser - {@link Parser} constructor.
|
|
* @property {Object} errors
|
|
* @property {Function} errors.VerifyError - {@link VerifyError} constructor.
|
|
* @property {Function} errors.ScriptError - {@link ScriptError} constructor.
|
|
* @property {Function} profiler - {@link module:profiler}.
|
|
* @property {Function} ldb - See {@link module:ldb}.
|
|
* @property {Function} script - {@link Script} constructor.
|
|
* @property {Function} opcode - {@link Opcode} constructor.
|
|
* @property {Function} stack - {@link Stack} constructor.
|
|
* @property {Function} witness - {@link Witness} constructor.
|
|
* @property {Function} input - {@link Input} constructor.
|
|
* @property {Function} output - {@link Output} constructor.
|
|
* @property {Function} coin - {@link Coin} constructor.
|
|
* @property {Function} coins - {@link Coins} constructor.
|
|
* @property {Function} coinview - {@link CoinView} constructor.
|
|
* @property {Function} tx - {@link TX} constructor.
|
|
* @property {Function} mtx - {@link MTX} constructor.
|
|
* @property {Function} txdb - {@link TXDB} constructor.
|
|
* @property {Function} abstractblock - {@link AbstractBlock} constructor.
|
|
* @property {Function} memblock - {@link MemBlock} constructor.
|
|
* @property {Function} block - {@link Block} constructor.
|
|
* @property {Function} merkleblock - {@link MerkleBlock} constructor.
|
|
* @property {Function} headers - {@link Headers} constructor.
|
|
* @property {Function} node - {@link Node} constructor.
|
|
* @property {Function} spvnode - {@link SPVNode} constructor.
|
|
* @property {Function} fullnode - {@link Fullnode} constructor.
|
|
* @property {Function} chainentry - {@link ChainEntry} constructor.
|
|
* @property {Function} chaindb - {@link ChainDB} constructor.
|
|
* @property {Function} chain - {@link Chain} constructor.
|
|
* @property {Function} mempool - {@link Mempool} constructor.
|
|
* @property {Function} mempoolentry - {@link MempoolEntry} constructor.
|
|
* @property {Function} keypair - {@link KeyPair} constructor.
|
|
* @property {Function} hd - {@link HD} constructor.
|
|
* @property {Function} address - {@link Address} constructor.
|
|
* @property {Function} wallet - {@link Wallet} constructor.
|
|
* @property {Function} walletdb - {@link WalletDB} constructor.
|
|
* @property {Function} provider - {@link Provider} constructor.
|
|
* @property {Function} peer - {@link Peer} constructor.
|
|
* @property {Function} pool - {@link Pool} constructor.
|
|
* @property {Function} miner - {@link Miner} constructor.
|
|
* @property {Function} minerblock - {@link MinerBlock} constructor.
|
|
* @property {Object} http
|
|
* @property {Function} http.client - {@link HTTPClient} constructor.
|
|
* @property {Function} http.http - {@link HTTPBase} constructor.
|
|
* @property {Function} http.provider - {@link HTTPProvider} constructor.
|
|
* @property {Function} http.request - See {@link request}.
|
|
* @property {Function} http.server - {@link HTTPServer} constructor.
|
|
* @property {Object} workers - See {@link module:workers}.
|
|
* @property {TimeData} time - For adjusted time.
|
|
* @property {Workers?} workerPool - Default global worker pool.
|
|
*/
|
|
|
|
function Environment(options) {
|
|
if (!options)
|
|
options = {};
|
|
|
|
if (typeof options === 'string')
|
|
options = { network: options };
|
|
|
|
this.options = options;
|
|
|
|
this._debug = null;
|
|
|
|
this.isBrowser = utils.isBrowser;
|
|
|
|
this.env = Environment;
|
|
this.bn = require('bn.js');
|
|
this.utils = require('./utils');
|
|
this.locker = require('./locker');
|
|
this.reader = require('./reader');
|
|
this.writer = require('./writer');
|
|
this.ec = require('./ec');
|
|
this.lru = require('./lru');
|
|
this.bloom = require('./bloom');
|
|
this.bst = require('./bst');
|
|
this.lowlevelup = require('./lowlevelup');
|
|
this.uri = require('./uri');
|
|
|
|
this.protocol = require('./protocol');
|
|
this.network = require('./network');
|
|
this.errors = require('./errors');
|
|
this.ldb = require('./ldb');
|
|
this.profiler = require('./profiler');
|
|
this.timedata = require('./timedata');
|
|
this.script = require('./script');
|
|
this.opcode = this.script.Opcode;
|
|
this.stack = this.script.Stack;
|
|
this.witness = this.script.Witness;
|
|
this.address = require('./address');
|
|
this.input = require('./input');
|
|
this.output = require('./output');
|
|
this.coin = require('./coin');
|
|
this.coins = require('./coins');
|
|
this.coinview = require('./coinview');
|
|
this.tx = require('./tx');
|
|
this.mtx = require('./mtx');
|
|
this.txdb = require('./txdb');
|
|
this.abstractblock = require('./abstractblock');
|
|
this.memblock = require('./memblock');
|
|
this.block = require('./block');
|
|
this.merkleblock = require('./merkleblock');
|
|
this.headers = require('./headers');
|
|
this.node = require('./node');
|
|
this.spvnode = require('./spvnode');
|
|
this.fullnode = require('./fullnode');
|
|
this.chainentry = require('./chainentry');
|
|
this.chaindb = require('./chaindb');
|
|
this.chain = require('./chain');
|
|
this.mempool = require('./mempool');
|
|
this.mempoolentry = this.mempool.MempoolEntry;
|
|
this.keypair = require('./keypair');
|
|
this.hd = require('./hd');
|
|
this.keyring = require('./keyring');
|
|
this.wallet = require('./wallet');
|
|
this.account = this.wallet.Account;
|
|
this.walletdb = require('./walletdb');
|
|
this.provider = this.walletdb.Provider;
|
|
this.peer = require('./peer');
|
|
this.pool = require('./pool');
|
|
this.miner = require('./miner');
|
|
this.minerblock = this.miner.MinerBlock;
|
|
this.http = require('./http');
|
|
|
|
this.workers = null;
|
|
this.workerPool = null;
|
|
|
|
this.prefix = null;
|
|
this.networkType = null;
|
|
this.db = null;
|
|
this.debugLogs = null;
|
|
this.debugFile = null;
|
|
this.profile = null;
|
|
this.useWorkers = null;
|
|
this.maxWorkers = null;
|
|
this.workerTimeout = null;
|
|
this.workerUri = null;
|
|
this.proxyServer = null;
|
|
this.logger = null;
|
|
|
|
this.time = new this.timedata();
|
|
|
|
this.set(options);
|
|
}
|
|
|
|
Environment.prototype.set = function set(options) {
|
|
if (typeof options === 'string')
|
|
options = { network: options };
|
|
|
|
if (!options)
|
|
options = {};
|
|
|
|
options = utils.merge({}, options);
|
|
|
|
options.network = options.network
|
|
|| process.env.BCOIN_NETWORK
|
|
|| 'main';
|
|
|
|
options.prefix = options.prefix
|
|
|| process.env.BCOIN_PREFIX;
|
|
|
|
if (!options.prefix)
|
|
options.prefix = utils.HOME + '/.bcoin';
|
|
|
|
if (!options.db)
|
|
options.db = process.env.BCOIN_DB;
|
|
|
|
if (options.debug == null && process.env.BCOIN_DEBUG != null)
|
|
options.debug = +process.env.BCOIN_DEBUG === 1;
|
|
|
|
if (options.debugFile == null && process.env.BCOIN_DEBUGFILE != null) {
|
|
if (process.env.BCOIN_DEBUGFILE === '0'
|
|
|| process.env.BCOIN_DEBUGFILE === '1') {
|
|
options.debugFile = +process.env.BCOIN_DEBUGFILE !== 0;
|
|
} else {
|
|
options.debugFile = process.env.BCOIN_DEBUGFILE;
|
|
}
|
|
}
|
|
|
|
if (options.profile == null && process.env.BCOIN_PROFILE != null)
|
|
options.profile = +process.env.BCOIN_PROFILE === 1;
|
|
|
|
if (options.useWorkers == null && process.env.BCOIN_USE_WORKERS != null)
|
|
options.useWorkers = +process.env.BCOIN_USE_WORKERS === 1;
|
|
|
|
if (options.maxWorkers == null && process.env.BCOIN_MAX_WORKERS != null)
|
|
options.maxWorkers = +process.env.BCOIN_MAX_WORKERS;
|
|
|
|
if (options.workerTime == null && process.env.BCOIN_WORKER_TIMEOUT != null)
|
|
options.workerTimeout = +process.env.BCOIN_WORKER_TIMEOUT;
|
|
|
|
if (options.debugFile && typeof options.debugFile !== 'string') {
|
|
options.debugFile = options.prefix;
|
|
if (options.network !== 'main')
|
|
options.debugFile += '/' + options.network;
|
|
options.debugFile += '/debug.log';
|
|
}
|
|
|
|
this.prefix = normalize(options.prefix);
|
|
this.networkType = options.network;
|
|
this.db = options.db;
|
|
this.debugLogs = !!options.debug;
|
|
this.debugFile = options.debugFile
|
|
? normalize(options.debugFile)
|
|
: null;
|
|
this.profile = options.profile;
|
|
this.useWorkers = !!options.useWorkers;
|
|
this.maxWorkers = options.maxWorkers;
|
|
this.workerTimeout = options.workerTimeout;
|
|
this.workerUri = options.workerUri || '/bcoin-worker.js';
|
|
this.proxyServer = options.proxyServer;
|
|
this.logger = options.logger;
|
|
|
|
this.network.set(this.networkType);
|
|
|
|
if (this.isBrowser && this.useWorkers) {
|
|
this.useWorkers = typeof global.Worker === 'function'
|
|
|| typeof global.postMessage === 'function';
|
|
}
|
|
|
|
if (this.useWorkers) {
|
|
this.workers = require('./workers');
|
|
this.workerPool = new this.workers({
|
|
size: this.maxWorkers,
|
|
timeout: this.workerTimeout,
|
|
network: this.network.get(this.network.primary)
|
|
});
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Ensure a directory.
|
|
* @param {String} path
|
|
* @param {Boolean?} dirname
|
|
*/
|
|
|
|
Environment.prototype.mkdir = function mkdir(path, dirname) {
|
|
if (this.isBrowser)
|
|
return;
|
|
|
|
path = normalize(path, dirname);
|
|
|
|
if (!mkdir.paths)
|
|
mkdir.paths = {};
|
|
|
|
if (mkdir.paths[path])
|
|
return;
|
|
|
|
mkdir.paths[path] = true;
|
|
|
|
return mkdirp(path);
|
|
};
|
|
|
|
/**
|
|
* Output a debug message.
|
|
* @param {Object|String} obj
|
|
* @param {...String} args
|
|
* @example
|
|
* bcoin.debug('foo: %d', 10);
|
|
*/
|
|
|
|
Environment.prototype.debug = function debug() {
|
|
var args = new Array(arguments.length);
|
|
var i, msg;
|
|
|
|
for (i = 0; i < args.length; i++)
|
|
args[i] = arguments[i];
|
|
|
|
if (this.logger) {
|
|
if (this.debugLogs)
|
|
this.logger.debug(args);
|
|
return;
|
|
}
|
|
|
|
if (this.isBrowser) {
|
|
if (this.debugLogs) {
|
|
msg = typeof args[0] !== 'object'
|
|
? utils.format(args, false)
|
|
: args[0];
|
|
console.log(msg);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (this.debugLogs) {
|
|
msg = utils.format(args, true);
|
|
process.stderr.write(msg + '\n');
|
|
}
|
|
|
|
if (this.debugFile) {
|
|
msg = utils.format(args, false);
|
|
this.write(msg);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Output an error.
|
|
* @param {Error} err
|
|
*/
|
|
|
|
Environment.prototype.error = function error(err) {
|
|
var msg;
|
|
|
|
if (!err)
|
|
return;
|
|
|
|
if (typeof err === 'string')
|
|
err = new Error(err);
|
|
|
|
if (this.logger) {
|
|
if (this.debugLogs)
|
|
this.logger.error(err);
|
|
return;
|
|
}
|
|
|
|
if (this.isBrowser) {
|
|
if (this.debugLogs)
|
|
console.error(err);
|
|
return;
|
|
}
|
|
|
|
if (this.debugLogs) {
|
|
msg = (err.message + '').replace(/^ *Error: */, '');
|
|
|
|
if (process.stdout && process.stdout.isTTY)
|
|
msg = '\x1b[1;31m[Error]\x1b[m ' + msg;
|
|
else
|
|
msg = '[Error] ' + msg;
|
|
|
|
process.stderr.write(msg + '\n');
|
|
}
|
|
|
|
if (this.debugFile)
|
|
this.write(err.stack + '');
|
|
};
|
|
|
|
/**
|
|
* Write a message to the debug log.
|
|
* @param {String} msg
|
|
*/
|
|
|
|
Environment.prototype.write = function write(msg) {
|
|
if (this.isBrowser)
|
|
return;
|
|
|
|
if (!this._debug) {
|
|
this.mkdir(this.debugFile, true);
|
|
this._debug = fs.createWriteStream(this.debugFile, { flags: 'a' });
|
|
}
|
|
|
|
this._debug.write(process.pid + ' (' + utils.date() + '): ' + msg + '\n');
|
|
};
|
|
|
|
/**
|
|
* Get the adjusted time.
|
|
* @returns {Number} Adjusted time.
|
|
*/
|
|
|
|
Environment.prototype.now = function now() {
|
|
return this.time.now();
|
|
};
|
|
|
|
/**
|
|
* Normalize a path.
|
|
* @param {String} path
|
|
* @param {Boolean?} dirname
|
|
*/
|
|
|
|
function normalize(path, dirname) {
|
|
var parts;
|
|
|
|
path = path.replace(/\\/g, '/');
|
|
path = path.replace(/\/+$/, '');
|
|
parts = path.split(/\/+/);
|
|
|
|
if (dirname)
|
|
parts.pop();
|
|
|
|
return parts.join('/');
|
|
}
|
|
|
|
/**
|
|
* Create a full directory structure.
|
|
* @param {String} path
|
|
*/
|
|
|
|
function mkdirp(path) {
|
|
var i, parts;
|
|
|
|
if (!fs)
|
|
return;
|
|
|
|
path = path.replace(/\\/g, '/');
|
|
path = path.replace(/\/+$/, '');
|
|
parts = path.split(/\/+/);
|
|
path = '';
|
|
|
|
if (process.platform === 'win32') {
|
|
if (parts[0].indexOf(':') !== -1)
|
|
path = parts.shift() + '/';
|
|
}
|
|
|
|
if (parts[0].length === 0) {
|
|
parts.shift();
|
|
path = '/';
|
|
}
|
|
|
|
for (i = 0; i < parts.length; i++) {
|
|
path += parts[i];
|
|
|
|
try {
|
|
fs.statSync(path);
|
|
} catch (e) {
|
|
if (e.code === 'ENOENT')
|
|
fs.mkdirSync(path, 488 /* 0750 */);
|
|
else
|
|
throw e;
|
|
}
|
|
|
|
path += '/';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Expose by converting `exports` to an
|
|
* Environment.
|
|
*/
|
|
|
|
utils.merge(exports, Environment.prototype);
|
|
|
|
Environment.call(exports);
|