xud
Version:
Exchange Union Daemon
331 lines • 15.2 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = __importDefault(require("assert"));
const fs_1 = require("fs");
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const toml_1 = __importDefault(require("toml"));
const enums_1 = require("./constants/enums");
const Logger_1 = require("./Logger");
const utils_1 = require("./utils/utils");
const propAssertions = {
port: (val) => assert_1.default(val >= 0 && val <= 65535, 'port must be between 0 and 65535'),
cltvdelta: (val) => assert_1.default(val > 0, 'cltvdelta must be a positive number'),
discoverminutes: (val) => assert_1.default(val > 0, 'discoverminutes must be a positive number'),
minQuantity: (val) => assert_1.default(val >= 0, 'minQuantity must be 0 or a positive number'),
};
function validateConfig(propVal, defaultVal, propKey, prefix) {
const actualType = typeof propVal;
const expectedType = typeof defaultVal;
if (actualType === 'undefined') {
return; // this is an unspecified property that will use the default value
}
if (expectedType === 'undefined') {
return; // this is a superfluous property that we ignore for now
}
assert_1.default.equal(actualType, expectedType, `${prefix || ''}${propKey} is type ${actualType} but should be ${expectedType}`);
if (actualType === 'object') {
// if this is an object, we recurse
for (const nestedPropKey in propVal) {
const nestedPrefix = propKey ? `${prefix || ''}${propKey}.` : undefined;
validateConfig(propVal[nestedPropKey], defaultVal[nestedPropKey], nestedPropKey, nestedPrefix);
}
}
else {
if (propKey && propKey in propAssertions) {
// shortcoming in typescript 3.6.4 `in` keyword type guard requires manual cast to any below
propAssertions[propKey](propVal);
}
}
}
let Config = /** @class */ (() => {
class Config {
constructor() {
this.lnd = {};
this.instanceid = 0;
/** Whether to intialize a new database with default values. */
this.initdb = true;
/** Whether matching will be disabled */
this.nomatching = false;
/** Whether a password should not be used to encrypt the xud key and underlying wallets. */
this.noencrypt = false;
/**
* Whether to disable sanity swaps that verify that the orders can possibly be swapped
* before adding trading pairs as active.
*/
this.nosanityswaps = true;
/**
* Whether to disable balance checks that verify that the orders can possibly be swapped
* before adding them to the order book.
*/
this.nobalancechecks = false;
/**
* Loads the xud configuration from an optional file and any command line arguments.
* @returns a promise that resolves to `true` if a config file was found and loaded, otherwise `false`
*/
this.load = (args) => __awaiter(this, void 0, void 0, function* () {
if (args) {
if (args.xudir) {
this.xudir = args.xudir;
}
const argNetwork = this.getNetwork(args);
if (argNetwork) {
this.network = argNetwork;
args.network = argNetwork;
}
}
yield this.mkDirIfNotExist(this.xudir);
const configPath = path_1.default.join(this.xudir, 'xud.conf');
const configProps = yield Config.readConfigProps(configPath);
if (configProps) {
validateConfig(configProps, this);
// set the network and xudir props up front because they influence default config values
if (configProps.network && (!args || !args.network)) {
this.network = configProps.network;
if (![enums_1.XuNetwork.MainNet, enums_1.XuNetwork.TestNet, enums_1.XuNetwork.SimNet, enums_1.XuNetwork.RegTest].includes(configProps.network)) {
throw new Error(`Invalid network config: ${configProps.network}`);
}
}
if (configProps.xudir && (!args || !args.xudir)) {
this.xudir = configProps.xudir;
}
if (configProps.thresholds) {
this.orderthresholds = Object.assign(Object.assign({}, this.orderthresholds), configProps.thresholds);
}
}
// update defaults based on the xudir and network from the args or config file
this.logpath = this.getDefaultLogPath();
this.dbpath = this.getDefaultDbPath();
this.p2p.port = this.getDefaultP2pPort();
this.rpc.port = this.getDefaultRpcPort();
this.http.port = this.getDefaultHttpPort();
this.setDefaultMacaroonPaths();
if (configProps) {
// merge parsed json properties from config file to the default config
utils_1.deepMerge(this, configProps);
}
if (args) {
validateConfig(args, this);
// override our config file with command line arguments
utils_1.deepMerge(this, args);
}
if (!Object.values(Logger_1.Level).includes(this.loglevel)) {
this.loglevel = this.getDefaultLogLevel();
}
const logDir = path_1.default.dirname(this.logpath);
yield this.mkDirIfNotExist(logDir);
return !!configProps;
});
/**
* Creates a directory if it does not exist, otherwise does nothing.
*/
this.mkDirIfNotExist = (dirPath) => __awaiter(this, void 0, void 0, function* () {
try {
yield fs_1.promises.mkdir(dirPath);
}
catch (err) {
if (err.code !== 'EEXIST') {
// ignore the error if the directory already exists, otherwise throw
throw err;
}
}
});
this.getNetwork = (args) => {
const networks = {
[enums_1.XuNetwork.MainNet]: args.mainnet,
[enums_1.XuNetwork.TestNet]: args.testnet,
[enums_1.XuNetwork.SimNet]: args.simnet,
[enums_1.XuNetwork.RegTest]: args.regtest,
};
const selected = Object.keys(networks).filter(key => networks[key]);
if (selected.length > 1) {
throw Error('only one network selection is allowed');
}
if (selected.length === 0) {
return undefined;
}
else {
return selected[0];
}
};
this.setDefaultMacaroonPaths = () => {
for (const currency in this.lnd) {
switch (currency) {
case 'LTC':
// litecoin uses a specific folder name for testnet
this.lnd.LTC.macaroonpath = path_1.default.join(this.lnd.LTC.macaroonpath, '..', '..', this.network === enums_1.XuNetwork.TestNet ? 'testnet4' : this.network, 'admin.macaroon');
break;
default:
// by default we want to update the network folder name to the selected network
this.lnd[currency].macaroonpath = path_1.default.join(this.lnd[currency].macaroonpath, '..', '..', this.network, 'admin.macaroon');
break;
}
}
};
this.getDefaultP2pPort = () => {
switch (this.network) {
case enums_1.XuNetwork.MainNet:
return 8885; // X = 88, U = 85 in ASCII
case enums_1.XuNetwork.TestNet:
return 18885;
case enums_1.XuNetwork.SimNet:
return 28885;
case enums_1.XuNetwork.RegTest:
return 38885;
default:
throw new Error('unrecognized network');
}
};
this.getDefaultRpcPort = () => {
switch (this.network) {
case enums_1.XuNetwork.MainNet:
return 8886;
case enums_1.XuNetwork.TestNet:
return 18886;
case enums_1.XuNetwork.SimNet:
return 28886;
case enums_1.XuNetwork.RegTest:
return 38886;
default:
throw new Error('unrecognized network');
}
};
this.getDefaultHttpPort = () => {
switch (this.network) {
case enums_1.XuNetwork.MainNet:
return 8887;
case enums_1.XuNetwork.TestNet:
return 18887;
case enums_1.XuNetwork.SimNet:
return 28887;
case enums_1.XuNetwork.RegTest:
return 38887;
default:
throw new Error('unrecognized network');
}
};
this.getDefaultDbPath = () => {
return path_1.default.join(this.xudir, `xud-${this.network}.db`);
};
this.getDefaultLogPath = () => {
return path_1.default.resolve(this.xudir, 'logs', 'xud.log');
};
this.getDefaultLogLevel = () => {
return process.env.NODE_ENV === 'production' ? Logger_1.Level.Info : Logger_1.Level.Debug;
};
const platform = os_1.default.platform();
let lndDefaultDatadir;
switch (platform) {
case 'win32': { // windows
const homeDir = process.env.LOCALAPPDATA;
this.xudir = path_1.default.join(homeDir, 'Xud');
lndDefaultDatadir = path_1.default.join(homeDir, 'Lnd');
break;
}
case 'darwin': { // mac
const homeDir = process.env.HOME;
this.xudir = path_1.default.join(homeDir, '.xud');
lndDefaultDatadir = path_1.default.join(homeDir, 'Library', 'Application Support', 'Lnd');
break;
}
default: { // linux
const homeDir = process.env.HOME;
this.xudir = path_1.default.join(homeDir, '.xud');
lndDefaultDatadir = path_1.default.join(homeDir, '.lnd');
break;
}
}
// default configuration
this.loglevel = this.getDefaultLogLevel();
this.logpath = this.getDefaultLogPath();
this.logdateformat = 'DD/MM/YYYY HH:mm:ss.SSS';
this.network = enums_1.XuNetwork.SimNet;
this.dbpath = this.getDefaultDbPath();
this.strict = false;
this.p2p = {
listen: true,
discover: true,
tor: false,
torport: 0,
discoverminutes: 60 * 12,
detectexternalip: false,
port: this.getDefaultP2pPort(),
addresses: [],
};
this.rpc = {
disable: false,
host: 'localhost',
port: this.getDefaultRpcPort(),
};
this.http = {
host: 'localhost',
port: this.getDefaultHttpPort(),
};
this.webproxy = {
disable: true,
port: 8080,
};
// TODO: add dynamic max/min price limits
this.orderthresholds = {
minQuantity: 0,
};
this.lnd.BTC = {
disable: false,
certpath: path_1.default.join(lndDefaultDatadir, 'tls.cert'),
macaroonpath: path_1.default.join(lndDefaultDatadir, 'data', 'chain', 'bitcoin', this.network, 'admin.macaroon'),
host: 'localhost',
port: 10009,
nomacaroons: false,
cltvdelta: 40,
};
this.lnd.LTC = {
disable: false,
certpath: path_1.default.join(lndDefaultDatadir, 'tls.cert'),
macaroonpath: path_1.default.join(lndDefaultDatadir, 'data', 'chain', 'litecoin', this.network, 'admin.macaroon'),
host: 'localhost',
port: 10010,
nomacaroons: false,
cltvdelta: 576,
};
this.connext = {
disable: false,
host: 'localhost',
port: 5040,
webhookhost: 'localhost',
webhookport: 8887,
};
}
}
Config.readConfigProps = (configPath) => __awaiter(void 0, void 0, void 0, function* () {
let configText;
try {
configText = yield fs_1.promises.readFile(configPath, 'utf8');
}
catch (err) { }
let configProps;
if (configText) {
try {
configProps = toml_1.default.parse(configText);
}
catch (e) {
throw new Error(`Error parsing config file at ${configPath} on line ${e.line}, column ${e.column}: ${e.message}`);
}
}
return configProps;
});
return Config;
})();
exports.default = Config;
//# sourceMappingURL=Config.js.map