From 401829db5a340765f51f1ef975b0f16f455e69e3 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 30 Oct 2017 21:56:51 -0700 Subject: [PATCH] node: start using blgr and bcfg. --- bin/cli | 2 +- lib/blockchain/chain.js | 2 +- lib/mempool/fees.js | 2 +- lib/net/bip150.js | 2 +- lib/net/hostlist.js | 2 +- lib/net/peer.js | 2 +- lib/node/config.js | 1127 ------------------------------------- lib/node/index.js | 2 - lib/node/logger.js | 995 -------------------------------- lib/node/node.js | 4 +- lib/wallet/walletdb.js | 2 +- test/util/node-context.js | 2 +- 12 files changed, 10 insertions(+), 2134 deletions(-) delete mode 100644 lib/node/config.js delete mode 100644 lib/node/logger.js diff --git a/bin/cli b/bin/cli index 9fead6e1..3ea18d41 100755 --- a/bin/cli +++ b/bin/cli @@ -2,7 +2,7 @@ 'use strict'; -const Config = require('../lib/node/config'); +const Config = require('bcfg'); const {NodeClient, WalletClient} = require('bclient'); const nports = { diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 8b05b122..76829369 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -11,7 +11,7 @@ const assert = require('assert'); const path = require('path'); const AsyncObject = require('../utils/asyncobject'); const Network = require('../protocol/network'); -const Logger = require('../node/logger'); +const Logger = require('blgr'); const ChainDB = require('./chaindb'); const common = require('./common'); const consensus = require('../protocol/consensus'); diff --git a/lib/mempool/fees.js b/lib/mempool/fees.js index a85e981f..4adab84c 100644 --- a/lib/mempool/fees.js +++ b/lib/mempool/fees.js @@ -15,7 +15,7 @@ const policy = require('../protocol/policy'); const BufferReader = require('bbuf/lib/reader'); const StaticWriter = require('bbuf/lib/staticwriter'); const encoding = require('bbuf/lib/encoding'); -const Logger = require('../node/logger'); +const Logger = require('blgr'); /* * Constants diff --git a/lib/net/bip150.js b/lib/net/bip150.js index 389dc661..0a1ee204 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -23,7 +23,7 @@ const base58 = require('bstr/lib/base58'); const encoding = require('bbuf/lib/encoding'); const IP = require('binet'); const dns = require('./dns'); -const Logger = require('../node/logger'); +const Logger = require('blgr'); /** * Represents a BIP150 input/output stream. diff --git a/lib/net/hostlist.js b/lib/net/hostlist.js index 243ca814..918e84f9 100644 --- a/lib/net/hostlist.js +++ b/lib/net/hostlist.js @@ -19,7 +19,7 @@ const List = require('../utils/list'); const common = require('./common'); const seeds = require('./seeds'); const dns = require('./dns'); -const Logger = require('../node/logger'); +const Logger = require('blgr'); const POOL32 = Buffer.allocUnsafe(32); /** diff --git a/lib/net/peer.js b/lib/net/peer.js index 292e8f56..3be13f32 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -27,7 +27,7 @@ const TX = require('../primitives/tx'); const encoding = require('bbuf/lib/encoding'); const NetAddress = require('../primitives/netaddress'); const Network = require('../protocol/network'); -const Logger = require('../node/logger'); +const Logger = require('blgr'); const tcp = require('./tcp'); const services = common.services; const invTypes = InvItem.types; diff --git a/lib/node/config.js b/lib/node/config.js deleted file mode 100644 index 23fc6844..00000000 --- a/lib/node/config.js +++ /dev/null @@ -1,1127 +0,0 @@ -/*! - * config.js - configuration parsing for bcoin - * Copyright (c) 2016-2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - */ - -'use strict'; - -const assert = require('assert'); -const Path = require('path'); -const os = require('os'); -const fs = require('bfile'); -const {fromFloat} = require('../utils/fixed'); -const HOME = os.homedir ? os.homedir() : '/'; - -/** - * Config Parser - * @alias module:node.Config - * @constructor - * @param {String} module - Module name (e.g. `bcoin`). - * @param {Object?} options - */ - -function Config(module, options) { - if (!(this instanceof Config)) - return new Config(module, options); - - assert(typeof module === 'string'); - assert(module.length > 0); - - this.module = module; - this.prefix = Path.join(HOME, `.${module}`); - this.suffix = null; - this.fallback = null; - this.alias = Object.create(null); - - this.options = Object.create(null); - this.data = Object.create(null); - this.env = Object.create(null); - this.args = Object.create(null); - this.argv = []; - this.pass = []; - this.query = Object.create(null); - this.hash = Object.create(null); - - if (options) - this.init(options); -} - -/** - * Initialize options. - * @private - * @param {Object} options - */ - -Config.prototype.init = function init(options) { - assert(options && typeof options === 'object'); - - if (options.suffix != null) { - assert(typeof options.suffix === 'string'); - this.suffix = options.suffix; - } - - if (options.fallback != null) { - assert(typeof options.fallback === 'string'); - this.fallback = options.fallback; - } - - if (options.alias) { - assert(typeof options.alias === 'object'); - for (const key of Object.keys(options.alias)) { - const value = options.alias[key]; - assert(typeof value === 'string'); - this.alias[key] = value; - } - } -}; - -/** - * Inject options. - * @param {Object} options - */ - -Config.prototype.inject = function inject(options) { - for (const key of Object.keys(options)) { - const value = options[key]; - - switch (key) { - case 'hash': - case 'query': - case 'env': - case 'argv': - case 'config': - continue; - } - - this.set(key, value); - } -}; - -/** - * Load options from hash, query, env, or args. - * @param {Object} options - */ - -Config.prototype.load = function load(options) { - if (options.hash) - this.parseHash(options.hash); - - if (options.query) - this.parseQuery(options.query); - - if (options.env) - this.parseEnv(options.env); - - if (options.argv) - this.parseArg(options.argv); - - this.prefix = this.getPrefix(); -}; - -/** - * Open a config file. - * @param {String} file - e.g. `bcoin.conf`. - * @throws on IO error - */ - -Config.prototype.open = function open(file) { - if (fs.unsupported) - return; - - const path = this.getFile(file); - - let text; - try { - text = fs.readFileSync(path, 'utf8'); - } catch (e) { - if (e.code === 'ENOENT') - return; - throw e; - } - - this.parseConfig(text); - - this.prefix = this.getPrefix(); -}; - -/** - * Set default option. - * @param {String} key - * @param {Object} value - */ - -Config.prototype.set = function set(key, value) { - assert(typeof key === 'string', 'Key must be a string.'); - - if (value == null) - return; - - key = key.replace(/-/g, ''); - key = key.toLowerCase(); - - this.options[key] = value; -}; - -/** - * Test whether a config option is present. - * @param {String} key - * @returns {Boolean} - */ - -Config.prototype.has = function has(key) { - if (typeof key === 'number') { - assert(key >= 0, 'Index must be positive.'); - if (key >= this.argv.length) - return false; - return true; - } - - assert(typeof key === 'string', 'Key must be a string.'); - - key = key.replace(/-/g, ''); - key = key.toLowerCase(); - - if (this.hash[key] != null) - return true; - - if (this.query[key] != null) - return true; - - if (this.args[key] != null) - return true; - - if (this.env[key] != null) - return true; - - if (this.data[key] != null) - return true; - - if (this.options[key] != null) - return true; - - return false; -}; - -/** - * Get a config option. - * @param {String} key - * @param {Object?} fallback - * @returns {Object|null} - */ - -Config.prototype.get = function get(key, fallback) { - if (fallback === undefined) - fallback = null; - - if (Array.isArray(key)) { - const keys = key; - for (const key of keys) { - const value = this.get(key); - if (value !== null) - return value; - } - return fallback; - } - - if (typeof key === 'number') { - assert(key >= 0, 'Index must be positive.'); - - if (key >= this.argv.length) - return fallback; - - if (this.argv[key] != null) - return this.argv[key]; - - return fallback; - } - - assert(typeof key === 'string', 'Key must be a string.'); - - key = key.replace(/-/g, ''); - key = key.toLowerCase(); - - if (this.hash[key] != null) - return this.hash[key]; - - if (this.query[key] != null) - return this.query[key]; - - if (this.args[key] != null) - return this.args[key]; - - if (this.env[key] != null) - return this.env[key]; - - if (this.data[key] != null) - return this.data[key]; - - if (this.options[key] != null) - return this.options[key]; - - return fallback; -}; - -/** - * Get a value's type. - * @param {String} key - * @returns {String} - */ - -Config.prototype.typeOf = function typeOf(key) { - const value = this.get(key); - - if (value === null) - return 'null'; - - return typeof value; -}; - -/** - * Get a config option (as a string). - * @param {String} key - * @param {Object?} fallback - * @returns {String|null} - */ - -Config.prototype.str = function str(key, fallback) { - const value = this.get(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (typeof value !== 'string') - throw new Error(`${fmt(key)} must be a string.`); - - return value; -}; - -/** - * Get a config option (as an integer). - * @param {String} key - * @param {Object?} fallback - * @returns {Number|null} - */ - -Config.prototype.int = function int(key, fallback) { - const value = this.get(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (typeof value !== 'string') { - if (typeof value !== 'number') - throw new Error(`${fmt(key)} must be an int.`); - - if (!Number.isSafeInteger(value)) - throw new Error(`${fmt(key)} must be an int.`); - - return value; - } - - if (!/^\-?\d+$/.test(value)) - throw new Error(`${fmt(key)} must be an int.`); - - const num = parseInt(value, 10); - - if (!Number.isSafeInteger(num)) - throw new Error(`${fmt(key)} must be an int.`); - - return num; -}; - -/** - * Get a config option (as a unsigned integer). - * @param {String} key - * @param {Object?} fallback - * @returns {Number|null} - */ - -Config.prototype.uint = function uint(key, fallback) { - const value = this.int(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (value < 0) - throw new Error(`${fmt(key)} must be a uint.`); - - return value; -}; - -/** - * Get a config option (as a float). - * @param {String} key - * @param {Object?} fallback - * @returns {Number|null} - */ - -Config.prototype.float = function float(key, fallback) { - const value = this.get(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (typeof value !== 'string') { - if (typeof value !== 'number') - throw new Error(`${fmt(key)} must be a float.`); - - if (!isFinite(value)) - throw new Error(`${fmt(key)} must be a float.`); - - return value; - } - - if (!/^\-?\d*(?:\.\d*)?$/.test(value)) - throw new Error(`${fmt(key)} must be a float.`); - - if (!/\d/.test(value)) - throw new Error(`${fmt(key)} must be a float.`); - - const num = parseFloat(value); - - if (!isFinite(num)) - throw new Error(`${fmt(key)} must be a float.`); - - return num; -}; - -/** - * Get a config option (as a positive float). - * @param {String} key - * @param {Object?} fallback - * @returns {Number|null} - */ - -Config.prototype.ufloat = function ufloat(key, fallback) { - const value = this.float(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (value < 0) - throw new Error(`${fmt(key)} must be a positive float.`); - - return value; -}; - -/** - * Get a value (as a fixed number). - * @param {String} key - * @param {Number?} exp - * @param {Object?} fallback - * @returns {Number|null} - */ - -Config.prototype.fixed = function fixed(key, exp, fallback) { - const value = this.float(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - try { - return fromFloat(value, exp || 0); - } catch (e) { - throw new Error(`${fmt(key)} must be a fixed number.`); - } -}; - -/** - * Get a value (as a positive fixed number). - * @param {String} key - * @param {Number?} exp - * @param {Object?} fallback - * @returns {Number|null} - */ - -Config.prototype.ufixed = function ufixed(key, exp, fallback) { - const value = this.fixed(key, exp); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (value < 0) - throw new Error(`${fmt(key)} must be a positive fixed number.`); - - return value; -}; - -/** - * Get a config option (as a boolean). - * @param {String} key - * @param {Object?} fallback - * @returns {Boolean|null} - */ - -Config.prototype.bool = function bool(key, fallback) { - const value = this.get(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - // Bitcoin Core compat. - if (typeof value === 'number') { - if (value === 1) - return true; - - if (value === 0) - return false; - } - - if (typeof value !== 'string') { - if (typeof value !== 'boolean') - throw new Error(`${fmt(key)} must be a boolean.`); - return value; - } - - if (value === 'true' || value === '1') - return true; - - if (value === 'false' || value === '0') - return false; - - throw new Error(`${fmt(key)} must be a boolean.`); -}; - -/** - * Get a config option (as a buffer). - * @param {String} key - * @param {Object?} fallback - * @returns {Buffer|null} - */ - -Config.prototype.buf = function buf(key, fallback, enc) { - const value = this.get(key); - - if (!enc) - enc = 'hex'; - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (typeof value !== 'string') { - if (!Buffer.isBuffer(value)) - throw new Error(`${fmt(key)} must be a buffer.`); - return value; - } - - const data = Buffer.from(value, enc); - - if (data.length !== Buffer.byteLength(value, enc)) - throw new Error(`${fmt(key)} must be a ${enc} string.`); - - return data; -}; - -/** - * Get a config option (as an array of strings). - * @param {String} key - * @param {Object?} fallback - * @returns {String[]|null} - */ - -Config.prototype.array = function array(key, fallback) { - const value = this.get(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (typeof value !== 'string') { - if (!Array.isArray(value)) - throw new Error(`${fmt(key)} must be an array.`); - return value; - } - - const parts = value.trim().split(/\s*,\s*/); - const result = []; - - for (const part of parts) { - if (part.length === 0) - continue; - - result.push(part); - } - - return result; -}; - -/** - * Get a config option (as an object). - * @param {String} key - * @param {Object?} fallback - * @returns {Object|null} - */ - -Config.prototype.obj = function obj(key, fallback) { - const value = this.get(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (typeof value !== 'object' || Array.isArray(value)) - throw new Error(`${fmt(key)} must be an object.`); - - return value; -}; - -/** - * Get a config option (as a function). - * @param {String} key - * @param {Object?} fallback - * @returns {Function|null} - */ - -Config.prototype.func = function func(key, fallback) { - const value = this.get(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (typeof value !== 'function') - throw new Error(`${fmt(key)} must be a function.`); - - return value; -}; - -/** - * Get a config option (as a string). - * @param {String} key - * @param {Object?} fallback - * @returns {String|null} - */ - -Config.prototype.path = function path(key, fallback) { - let value = this.str(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - if (value.length === 0) - return fallback; - - switch (value[0]) { - case '~': // home dir - value = Path.join(HOME, value.substring(1)); - break; - case '@': // prefix - value = Path.join(this.prefix, value.substring(1)); - break; - default: // cwd - break; - } - - return Path.normalize(value); -}; - -/** - * Get a config option (in MB). - * @param {String} key - * @param {Object?} fallback - * @returns {Number|null} - */ - -Config.prototype.mb = function mb(key, fallback) { - const value = this.uint(key); - - if (fallback === undefined) - fallback = null; - - if (value === null) - return fallback; - - return value * 1024 * 1024; -}; - -/** - * Grab suffix from config data. - * @returns {String} - */ - -Config.prototype.getSuffix = function getSuffix() { - if (!this.suffix) - throw new Error('No suffix presented.'); - - const suffix = this.str(this.suffix, this.fallback); - - assert(isAlpha(suffix), 'Bad suffix.'); - - return suffix; -}; - -/** - * Grab prefix from config data. - * @private - * @returns {String} - */ - -Config.prototype.getPrefix = function getPrefix() { - let prefix = this.str('prefix'); - - if (prefix) { - if (prefix[0] === '~') - prefix = Path.join(HOME, prefix.substring(1)); - } else { - prefix = Path.join(HOME, `.${this.module}`); - } - - if (this.suffix) { - const suffix = this.str(this.suffix); - - if (suffix) { - assert(isAlpha(suffix), 'Bad suffix.'); - if (this.fallback && suffix !== this.fallback) - prefix = Path.join(prefix, suffix); - } - } - - return Path.normalize(prefix); -}; - -/** - * Grab config filename from config data. - * @private - * @param {String} file - * @returns {String} - */ - -Config.prototype.getFile = function getFile(file) { - const name = this.str('config'); - - if (name) - return name; - - return Path.join(this.prefix, file); -}; - -/** - * Ensure prefix. - * @returns {Promise} - */ - -Config.prototype.ensure = function ensure() { - if (fs.unsupported) - return Promise.resolve(); - - return fs.mkdirp(this.prefix); -}; - -/** - * Create a file path using `prefix`. - * @param {String} file - * @returns {String} - */ - -Config.prototype.location = function location(file) { - return Path.join(this.prefix, file); -}; - -/** - * Parse config text. - * @private - * @param {String} text - */ - -Config.prototype.parseConfig = function parseConfig(text) { - assert(typeof text === 'string', 'Config must be text.'); - - if (text.charCodeAt(0) === 0xfeff) - text = text.substring(1); - - text = text.replace(/\r\n/g, '\n'); - text = text.replace(/\r/g, '\n'); - text = text.replace(/\\\n/g, ''); - - let num = 0; - - for (const chunk of text.split('\n')) { - const line = chunk.trim(); - - num += 1; - - if (line.length === 0) - continue; - - if (line[0] === '#') - continue; - - const index = line.indexOf(':'); - - if (index === -1) - throw new Error(`Expected ':' on line ${num}: "${line}".`); - - let key = line.substring(0, index).trim(); - - key = key.replace(/\-/g, ''); - - if (!isLowerKey(key)) - throw new Error(`Invalid option on line ${num}: ${key}.`); - - const value = line.substring(index + 1).trim(); - - if (value.length === 0) - continue; - - const alias = this.alias[key]; - - if (alias) - key = alias; - - this.data[key] = value; - } -}; - -/** - * Parse arguments. - * @private - * @param {Array?} argv - */ - -Config.prototype.parseArg = function parseArg(argv) { - if (!argv || typeof argv !== 'object') - argv = process.argv; - - assert(Array.isArray(argv)); - - let last = null; - let pass = false; - - for (let i = 2; i < argv.length; i++) { - const arg = argv[i]; - - assert(typeof arg === 'string'); - - if (arg === '--') { - pass = true; - continue; - } - - if (pass) { - this.pass.push(arg); - continue; - } - - if (arg.length === 0) { - last = null; - continue; - } - - if (arg.indexOf('--') === 0) { - const index = arg.indexOf('='); - - let key = null; - let value = null; - let empty = false; - - if (index !== -1) { - // e.g. --opt=val - key = arg.substring(2, index); - value = arg.substring(index + 1); - last = null; - empty = false; - } else { - // e.g. --opt - key = arg.substring(2); - value = 'true'; - last = null; - empty = true; - } - - key = key.replace(/\-/g, ''); - - if (!isLowerKey(key)) - throw new Error(`Invalid argument: --${key}.`); - - if (value.length === 0) - continue; - - // Do not allow one-letter aliases. - if (key.length > 1) { - const alias = this.alias[key]; - if (alias) - key = alias; - } - - this.args[key] = value; - - if (empty) - last = key; - - continue; - } - - if (arg[0] === '-') { - // e.g. -abc - last = null; - - for (let j = 1; j < arg.length; j++) { - let key = arg[j]; - - if ((key < 'a' || key > 'z') - && (key < 'A' || key > 'Z') - && (key < '0' || key > '9') - && key !== '?') { - throw new Error(`Invalid argument: -${key}.`); - } - - const alias = this.alias[key]; - - if (alias) - key = alias; - - this.args[key] = 'true'; - - last = key; - } - - continue; - } - - // e.g. foo - const value = arg; - - if (value.length === 0) { - last = null; - continue; - } - - if (last) { - this.args[last] = value; - last = null; - } else { - this.argv.push(value); - } - } -}; - -/** - * Parse environment variables. - * @private - * @param {Object?} env - * @returns {Object} - */ - -Config.prototype.parseEnv = function parseEnv(env) { - let prefix = this.module; - - prefix = prefix.toUpperCase(); - prefix = prefix.replace(/-/g, '_'); - prefix += '_'; - - if (!env || typeof env !== 'object') - env = process.env; - - assert(env && typeof env === 'object'); - - for (let key of Object.keys(env)) { - const value = env[key]; - - assert(typeof value === 'string'); - - if (key.indexOf(prefix) !== 0) - continue; - - key = key.substring(prefix.length); - key = key.replace(/_/g, ''); - - if (!isUpperKey(key)) - continue; - - if (value.length === 0) - continue; - - key = key.toLowerCase(); - - // Do not allow one-letter aliases. - if (key.length > 1) { - const alias = this.alias[key]; - if (alias) - key = alias; - } - - this.env[key] = value; - } -}; - -/** - * Parse uri querystring variables. - * @private - * @param {String} query - */ - -Config.prototype.parseQuery = function parseQuery(query) { - if (typeof query !== 'string') { - if (!global.location) - return {}; - - query = global.location.search; - - if (typeof query !== 'string') - return {}; - } - - return this.parseForm(query, this.query); -}; - -/** - * Parse uri hash variables. - * @private - * @param {String} hash - */ - -Config.prototype.parseHash = function parseHash(hash) { - if (typeof hash !== 'string') { - if (!global.location) - return {}; - - hash = global.location.hash; - - if (typeof hash !== 'string') - return {}; - } - - return this.parseForm(hash, this.hash); -}; - -/** - * Parse form-urlencoded variables. - * @private - * @param {String} query - */ - -Config.prototype.parseForm = function parseForm(query, map) { - assert(typeof query === 'string'); - - if (query.length === 0) - return; - - let ch = '?'; - - if (map === this.hash) - ch = '#'; - - if (query[0] === ch) - query = query.substring(1); - - for (const pair of query.split('&')) { - const index = pair.indexOf('='); - - let key, value; - if (index !== -1) { - key = pair.substring(0, index); - value = pair.substring(index + 1); - } else { - key = pair; - value = 'true'; - } - - key = unescape(key); - key = key.replace(/\-/g, ''); - - if (!isLowerKey(key)) - continue; - - value = unescape(value); - - if (value.length === 0) - continue; - - const alias = this.alias[key]; - - if (alias) - key = alias; - - map[key] = value; - } -}; - -/* - * Helpers - */ - -function fmt(key) { - if (Array.isArray(key)) - key = key[0]; - - if (typeof key === 'number') - return `Argument #${key}`; - - return key; -} - -function unescape(str) { - try { - str = decodeURIComponent(str); - str = str.replace(/\+/g, ' '); - } catch (e) { - ; - } - str = str.replace(/\0/g, ''); - return str; -} - -function isAlpha(str) { - return /^[a-z0-9_\-]+$/i.test(str); -} - -function isKey(key) { - return /^[a-zA-Z0-9]+$/.test(key); -} - -function isLowerKey(key) { - if (!isKey(key)) - return false; - - return !/[A-Z]/.test(key); -} - -function isUpperKey(key) { - if (!isKey(key)) - return false; - - return !/[a-z]/.test(key); -} - -/* - * Expose - */ - -module.exports = Config; diff --git a/lib/node/index.js b/lib/node/index.js index ee0edddb..c78e3bb8 100644 --- a/lib/node/index.js +++ b/lib/node/index.js @@ -10,10 +10,8 @@ * @module node */ -exports.Config = require('./config'); exports.FullNode = require('./fullnode'); exports.HTTP = require('./http'); -exports.Logger = require('./logger'); exports.Node = require('./node'); exports.RPC = require('./rpc'); exports.SPVNode = require('./spvnode'); diff --git a/lib/node/logger.js b/lib/node/logger.js deleted file mode 100644 index 781d9117..00000000 --- a/lib/node/logger.js +++ /dev/null @@ -1,995 +0,0 @@ -/*! - * logger.js - basic logger for bcoin - * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License). - * https://github.com/bcoin-org/bcoin - */ - -'use strict'; - -const assert = require('assert'); -const {format, inspect} = require('util'); -const fs = require('bfile'); -const util = require('../utils/util'); -const co = require('../utils/co'); -const Lock = require('../utils/lock'); - -/* - * Constants - */ - -const inspectOptions = { - showHidden: false, - depth: 20, - colors: false, - customInspect: true, - showProxy: false, - maxArrayLength: Infinity, - breakLength: 60 -}; - -/** - * Basic stdout and file logger. - * @alias module:node.Logger - * @constructor - * @param {(String|Object)?} options/level - * @param {String?} options.level - * @param {Boolean} [options.colors=true] - */ - -function Logger(options) { - if (!(this instanceof Logger)) - return new Logger(options); - - this.level = Logger.levels.NONE; - this.colors = Logger.HAS_TTY; - this.console = true; - this.shrink = true; - this.closed = true; - this.closing = false; - this.filename = null; - this.stream = null; - this.contexts = Object.create(null); - this.locker = new Lock(); - - if (options) - this.set(options); -} - -/** - * Whether stdout is a tty FD. - * @const {Boolean} - */ - -Logger.HAS_TTY = Boolean(process.stdout && process.stdout.isTTY); - -/** - * Maximum file size. - * @const {Number} - * @default - */ - -Logger.MAX_FILE_SIZE = 20 << 20; - -/** - * Available log levels. - * @enum {Number} - */ - -Logger.levels = { - NONE: 0, - ERROR: 1, - WARNING: 2, - INFO: 3, - DEBUG: 4, - SPAM: 5 -}; - -/** - * Available log levels. - * @const {String[]} - * @default - */ - -Logger.levelsByVal = [ - 'none', - 'error', - 'warning', - 'info', - 'debug', - 'spam' -]; - -/** - * Available log levels. - * @const {String[]} - * @default - */ - -Logger.prefixByVal = [ - 'N', - 'E', - 'W', - 'I', - 'D', - 'S' -]; - -/** - * Default CSI colors. - * @const {String[]} - * @default - */ - -Logger.styles = [ - '0', - '1;31', - '1;33', - '94', - '90', - '90' -]; - -/** - * Set logger options. - * @param {Object} options - */ - -Logger.prototype.set = function set(options) { - assert(options); - assert(this.closed); - - if (typeof options === 'string') { - this.setLevel(options); - return; - } - - if (options.level != null) { - assert(typeof options.level === 'string'); - this.setLevel(options.level); - } - - if (options.colors != null && Logger.HAS_TTY) { - assert(typeof options.colors === 'boolean'); - this.colors = options.colors; - } - - if (options.console != null) { - assert(typeof options.console === 'boolean'); - this.console = options.console; - } - - if (options.shrink != null) { - assert(typeof options.shrink === 'boolean'); - this.shrink = options.shrink; - } - - if (options.filename != null) { - assert(typeof options.filename === 'string', 'Bad file.'); - this.filename = options.filename; - } -}; - -/** - * Open the logger. - * @method - * @returns {Promise} - */ - -Logger.prototype.open = async function open() { - const unlock = await this.locker.lock(); - try { - return await this._open(); - } finally { - unlock(); - } -}; - -/** - * Open the logger (no lock). - * @method - * @returns {Promise} - */ - -Logger.prototype._open = async function _open() { - if (!this.filename) { - this.closed = false; - return; - } - - if (this.stream) { - this.closed = false; - return; - } - - if (fs.unsupported) { - this.closed = false; - return; - } - - if (this.shrink) - await this.truncate(); - - this.stream = await openStream(this.filename); - this.stream.once('error', this.handleError.bind(this)); - this.closed = false; -}; - -/** - * Destroy the write stream. - * @method - * @returns {Promise} - */ - -Logger.prototype.close = async function close() { - const unlock = await this.locker.lock(); - try { - return await this._close(); - } finally { - unlock(); - } -}; - -/** - * Destroy the write stream (no lock). - * @method - * @returns {Promise} - */ - -Logger.prototype._close = async function _close() { - if (this.timer != null) { - co.clearTimeout(this.timer); - this.timer = null; - } - - if (fs.unsupported) { - this.closed = true; - this.stream = null; - return; - } - - if (this.stream) { - try { - this.closing = true; - await closeStream(this.stream); - } finally { - this.closing = false; - } - this.stream = null; - } - - this.closed = true; -}; - -/** - * Truncate the log file to the last 20mb. - * @method - * @private - * @returns {Promise} - */ - -Logger.prototype.truncate = async function truncate() { - if (!this.filename) - return; - - if (fs.unsupported) - return; - - assert(!this.stream); - - let stat; - try { - stat = await fs.stat(this.filename); - } catch (e) { - if (e.code === 'ENOENT') - return; - throw e; - } - - const maxSize = Logger.MAX_FILE_SIZE; - - if (stat.size <= maxSize + (maxSize / 10)) - return; - - this.debug('Truncating log file to %d bytes.', maxSize); - - const fd = await fs.open(this.filename, 'r+'); - const data = Buffer.allocUnsafe(maxSize); - - await fs.read(fd, data, 0, maxSize, stat.size - maxSize); - await fs.ftruncate(fd, maxSize); - await fs.write(fd, data, 0, maxSize, 0); - await fs.close(fd); -}; - -/** - * Handle write stream error. - * @param {Error} err - */ - -Logger.prototype.handleError = function handleError(err) { - try { - this.stream.close(); - } catch (e) { - ; - } - - this.stream = null; - this.retry(); -}; - -/** - * Try to reopen the logger. - * @method - * @private - * @returns {Promise} - */ - -Logger.prototype.reopen = async function reopen() { - const unlock = await this.locker.lock(); - try { - return await this._reopen(); - } finally { - unlock(); - } -}; - -/** - * Try to reopen the logger (no lock). - * @method - * @private - * @returns {Promise} - */ - -Logger.prototype._reopen = async function _reopen() { - if (this.stream) - return; - - if (this.closed) - return; - - if (fs.unsupported) - return; - - try { - this.stream = await openStream(this.filename); - } catch (e) { - this.retry(); - return; - } - - this.stream.once('error', this.handleError.bind(this)); -}; - -/** - * Try to reopen the logger after a timeout. - * @method - * @private - * @returns {Promise} - */ - -Logger.prototype.retry = function retry() { - assert(this.timer == null); - this.timer = co.setTimeout(() => { - this.timer = null; - this.reopen(); - }, 10000, this); -}; - -/** - * 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 - */ - -Logger.prototype.setLevel = function setLevel(name) { - const level = Logger.levels[name.toUpperCase()]; - assert(level != null, 'Invalid log level.'); - this.level = level; -}; - -/** - * Format log. - * @param {Array} args - * @param {Boolean} colors - * @returns {String} - */ - -Logger.prototype.fmt = function fmt(args, colors) { - if (args.length > 0) { - const [obj] = args; - if (obj && typeof obj === 'object') { - inspectOptions.colors = colors; - return inspect(obj, inspectOptions); - } - } - return format(...args); -}; - -/** - * Output a log to the `error` log level. - * @param {String|Object|Error} err - * @param {...Object} args - */ - -Logger.prototype.error = function error(...args) { - if (this.level < Logger.levels.ERROR) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.ERROR, null, err); - return; - } - - this.log(Logger.levels.ERROR, null, args); -}; - -/** - * Output a log to the `warning` log level. - * @param {String|Object} obj - * @param {...Object} args - */ - -Logger.prototype.warning = function warning(...args) { - if (this.level < Logger.levels.WARNING) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.WARNING, null, err); - return; - } - - this.log(Logger.levels.WARNING, null, args); -}; - -/** - * Output a log to the `info` log level. - * @param {String|Object} obj - * @param {...Object} args - */ - -Logger.prototype.info = function info(...args) { - if (this.level < Logger.levels.INFO) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.INFO, null, err); - return; - } - - this.log(Logger.levels.INFO, null, args); -}; - -/** - * Output a log to the `debug` log level. - * @param {String|Object} obj - * @param {...Object} args - */ - -Logger.prototype.debug = function debug(...args) { - if (this.level < Logger.levels.DEBUG) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.DEBUG, null, err); - return; - } - - this.log(Logger.levels.DEBUG, null, args); -}; - -/** - * Output a log to the `spam` log level. - * @param {String|Object} obj - * @param {...Object} args - */ - -Logger.prototype.spam = function spam(...args) { - if (this.level < Logger.levels.SPAM) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.SPAM, null, err); - return; - } - - this.log(Logger.levels.SPAM, null, args); -}; - -/** - * Output a log to the desired log level. - * Note that this bypasses the level check. - * @param {String} level - * @param {String|null} module - * @param {Object[]} args - */ - -Logger.prototype.log = function log(level, module, args) { - if (this.closed) - return; - - if (this.level < level) - return; - - this.writeConsole(level, module, args); - this.writeStream(level, module, args); -}; - -/** - * Create logger context. - * @param {String} module - * @returns {LoggerContext} - */ - -Logger.prototype.context = function context(module) { - let ctx = this.contexts[module]; - - if (!ctx) { - ctx = new LoggerContext(this, module); - this.contexts[module] = ctx; - } - - return ctx; -}; - -/** - * Write log to the console. - * @param {String} level - * @param {String|null} module - * @param {Object[]} args - */ - -Logger.prototype.writeConsole = function writeConsole(level, module, args) { - const name = Logger.levelsByVal[level]; - - assert(name, 'Invalid log level.'); - - if (!this.console) - return false; - - if (!process.stdout) { - let msg = `[${name}] `; - - if (module) - msg += `(${module}) `; - - if (typeof args[0] === 'object') { - return level === Logger.levels.ERROR - ? console.error(msg, args[0]) - : console.log(msg, args[0]); - } - - msg += this.fmt(args, false); - - if (level === Logger.levels.ERROR) { - console.error(msg); - return true; - } - - console.log(msg); - - return true; - } - - let msg; - if (this.colors) { - const color = Logger.styles[level]; - assert(color); - - msg = `\x1b[${color}m[${name}]\x1b[m `; - } else { - msg = `[${name}] `; - } - - if (module) - msg += `(${module}) `; - - msg += this.fmt(args, this.colors); - msg += '\n'; - - return level === Logger.levels.ERROR - ? process.stderr.write(msg) - : process.stdout.write(msg); -}; - -/** - * Write a string to the output stream (usually a file). - * @param {String} level - * @param {String|null} module - * @param {Object[]} args - */ - -Logger.prototype.writeStream = function writeStream(level, module, args) { - const name = Logger.prefixByVal[level]; - - assert(name, 'Invalid log level.'); - - if (!this.stream) - return; - - if (this.closing) - return; - - let msg = `[${name}:${util.date()}] `; - - if (module) - msg += `(${module}) `; - - msg += this.fmt(args, false); - msg += '\n'; - - this.stream.write(msg); -}; - -/** - * Helper to parse an error into a nicer - * format. Call's `log` internally. - * @private - * @param {Number} level - * @param {String|null} module - * @param {Error} err - */ - -Logger.prototype.logError = function logError(level, module, err) { - if (this.closed) - return; - - if (fs.unsupported && this.console) { - if (level <= Logger.levels.WARNING) - console.error(err); - } - - let msg = String(err.message).replace(/^ *Error: */, ''); - - if (level !== Logger.levels.ERROR) - msg = `Error: ${msg}`; - - this.log(level, module, [msg]); - - if (level <= Logger.levels.WARNING) { - if (this.stream) - this.stream.write(err.stack + '\n'); - } -}; - -/** - * Get the current memory usage. - * @returns {Object} - */ - -Logger.prototype.memoryUsage = function memoryUsage() { - if (!process.memoryUsage) { - return { - total: 0, - jsHeap: 0, - jsHeapTotal: 0, - nativeHeap: 0, - external: 0 - }; - } - - const mem = process.memoryUsage(); - - return { - total: Math.floor(mem.rss / (1 << 20)), - jsHeap: Math.floor(mem.heapUsed / (1 << 20)), - jsHeapTotal: Math.floor(mem.heapTotal / (1 << 20)), - nativeHeap: Math.floor((mem.rss - mem.heapTotal) / (1 << 20)), - external: Math.floor(mem.external / (1 << 20)) - }; -}; - -/** - * Log the current memory usage. - * @param {String|null} module - */ - -Logger.prototype.memory = function memory(module) { - const mem = this.memoryUsage(); - - this.log(Logger.levels.DEBUG, module, [ - 'Memory: rss=%dmb, js-heap=%d/%dmb native-heap=%dmb', - mem.total, - mem.jsHeap, - mem.jsHeapTotal, - mem.nativeHeap - ]); -}; - -/** - * Basic stdout and file logger. - * @constructor - * @ignore - * @param {Logger} logger - * @param {String} module - */ - -function LoggerContext(logger, module) { - if (!(this instanceof LoggerContext)) - return new LoggerContext(logger, module); - - assert(typeof module === 'string'); - - this.logger = logger; - this.module = module; -} - -/** - * Open the logger. - * @returns {Promise} - */ - -LoggerContext.prototype.open = function open() { - return this.logger.open(); -}; - -/** - * Destroy the write stream. - * @returns {Promise} - */ - -LoggerContext.prototype.close = function close() { - return this.logger.close(); -}; - -/** - * Set the log file location. - * @param {String} filename - */ - -LoggerContext.prototype.setFile = function setFile(filename) { - this.logger.setFile(filename); -}; - -/** - * Set or reset the log level. - * @param {String} level - */ - -LoggerContext.prototype.setLevel = function setLevel(name) { - this.logger.setLevel(name); -}; - -/** - * Output a log to the `error` log level. - * @param {String|Object|Error} err - * @param {...Object} args - */ - -LoggerContext.prototype.error = function error(...args) { - if (this.logger.level < Logger.levels.ERROR) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.ERROR, err); - return; - } - - this.log(Logger.levels.ERROR, args); -}; - -/** - * Output a log to the `warning` log level. - * @param {String|Object} obj - * @param {...Object} args - */ - -LoggerContext.prototype.warning = function warning(...args) { - if (this.logger.level < Logger.levels.WARNING) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.WARNING, err); - return; - } - - this.log(Logger.levels.WARNING, args); -}; - -/** - * Output a log to the `info` log level. - * @param {String|Object} obj - * @param {...Object} args - */ - -LoggerContext.prototype.info = function info(...args) { - if (this.logger.level < Logger.levels.INFO) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.INFO, err); - return; - } - - this.log(Logger.levels.INFO, args); -}; - -/** - * Output a log to the `debug` log level. - * @param {String|Object} obj - * @param {...Object} args - */ - -LoggerContext.prototype.debug = function debug(...args) { - if (this.logger.level < Logger.levels.DEBUG) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.DEBUG, err); - return; - } - - this.log(Logger.levels.DEBUG, args); -}; - -/** - * Output a log to the `spam` log level. - * @param {String|Object} obj - * @param {...Object} args - */ - -LoggerContext.prototype.spam = function spam(...args) { - if (this.logger.level < Logger.levels.SPAM) - return; - - const err = args[0]; - - if (err instanceof Error) { - this.logError(Logger.levels.SPAM, err); - return; - } - - this.log(Logger.levels.SPAM, args); -}; - -/** - * Output a log to the desired log level. - * Note that this bypasses the level check. - * @param {String} level - * @param {Object[]} args - */ - -LoggerContext.prototype.log = function log(level, args) { - this.logger.log(level, this.module, args); -}; - -/** - * Create logger context. - * @param {String} module - * @returns {LoggerContext} - */ - -LoggerContext.prototype.context = function context(module) { - return new LoggerContext(this.logger, module); -}; - -/** - * Helper to parse an error into a nicer - * format. Call's `log` internally. - * @private - * @param {Number} level - * @param {Error} err - */ - -LoggerContext.prototype.logError = function logError(level, err) { - this.logger.logError(level, this.module, err); -}; - -/** - * Get the current memory usage. - * @returns {Object} - */ - -LoggerContext.prototype.memoryUsage = function memoryUsage() { - return this.logger.memoryUsage(); -}; - -/** - * Log the current memory usage. - */ - -LoggerContext.prototype.memory = function memory() { - this.logger.memory(this.module); -}; - -/* - * Default - */ - -Logger.global = new Logger(); - -/* - * Helpers - */ - -function openStream(filename) { - return new Promise((resolve, reject) => { - const stream = fs.createWriteStream(filename, { flags: 'a' }); - - const cleanup = () => { - /* eslint-disable */ - stream.removeListener('error', onError); - stream.removeListener('open', onOpen); - /* eslint-enable */ - }; - - const onError = (err) => { - try { - stream.close(); - } catch (e) { - ; - } - cleanup(); - reject(err); - }; - - const onOpen = () => { - cleanup(); - resolve(stream); - }; - - stream.once('error', onError); - stream.once('open', onOpen); - }); -} - -function closeStream(stream) { - return new Promise((resolve, reject) => { - const cleanup = () => { - /* eslint-disable */ - stream.removeListener('error', onError); - stream.removeListener('close', onClose); - /* eslint-enable */ - }; - - const onError = (err) => { - cleanup(); - reject(err); - }; - - const onClose = () => { - cleanup(); - resolve(stream); - }; - - stream.removeAllListeners('error'); - stream.removeAllListeners('close'); - stream.once('error', onError); - stream.once('close', onClose); - - stream.close(); - }); -} - -/* - * Expose - */ - -module.exports = Logger; diff --git a/lib/node/node.js b/lib/node/node.js index f3ee14e3..b0f510f5 100644 --- a/lib/node/node.js +++ b/lib/node/node.js @@ -11,10 +11,10 @@ const assert = require('assert'); const AsyncObject = require('../utils/asyncobject'); const util = require('../utils/util'); const Network = require('../protocol/network'); -const Logger = require('./logger'); +const Logger = require('blgr'); const WorkerPool = require('../workers/workerpool'); const secp256k1 = require('bcrypto/lib/secp256k1'); -const Config = require('./config'); +const Config = require('bcfg'); /** * Base class from which every other diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 83b46a61..4ed7b9b3 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -22,7 +22,7 @@ const common = require('./common'); const Wallet = require('./wallet'); const Account = require('./account'); const BloomFilter = require('bfilter/lib/bloom'); -const Logger = require('../node/logger'); +const Logger = require('blgr'); const Outpoint = require('../primitives/outpoint'); const layouts = require('./layout'); const records = require('./records'); diff --git a/test/util/node-context.js b/test/util/node-context.js index f8b39cd8..984bcdaa 100644 --- a/test/util/node-context.js +++ b/test/util/node-context.js @@ -4,7 +4,7 @@ const assert = require('assert'); const FullNode = require('../../lib/node/fullnode'); const Network = require('../../lib/protocol/network'); const co = require('../../lib/utils/co'); -const Logger = require('../../lib/node/logger'); +const Logger = require('blgr'); function NodeContext(network, size) { if (!(this instanceof NodeContext))