xud
Version:
Exchange Union Daemon
289 lines • 14.5 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 = require("assert");
const events_1 = require("events");
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const bootstrap_1 = __importDefault(require("./bootstrap"));
const Config_1 = __importDefault(require("./Config"));
const enums_1 = require("./constants/enums");
const DB_1 = __importDefault(require("./db/DB"));
const GrpcServer_1 = __importDefault(require("./grpc/GrpcServer"));
const GrpcWebProxyServer_1 = __importDefault(require("./grpc/webproxy/GrpcWebProxyServer"));
const HttpServer_1 = __importDefault(require("./http/HttpServer"));
const Logger_1 = __importDefault(require("./Logger"));
const NodeKey_1 = __importDefault(require("./nodekey/NodeKey"));
const OrderBook_1 = __importDefault(require("./orderbook/OrderBook"));
const Pool_1 = __importDefault(require("./p2p/Pool"));
const InitService_1 = __importDefault(require("./service/InitService"));
const Service_1 = __importDefault(require("./service/Service"));
const SwapClientManager_1 = __importDefault(require("./swaps/SwapClientManager"));
const Swaps_1 = __importDefault(require("./swaps/Swaps"));
const simnet_connext_channels_1 = require("./utils/simnet-connext-channels");
const UnitConverter_1 = require("./utils/UnitConverter");
const version = require('../package.json').version;
bootstrap_1.default();
/** Class representing a complete Exchange Union daemon. */
class Xud extends events_1.EventEmitter {
/**
* Create an Exchange Union daemon.
*/
constructor() {
super();
this.shuttingDown = false;
/**
* Start all processes necessary for the operation of an Exchange Union node.
* @param args optional arguments to override configuration parameters.
*/
this.start = (args) => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
let configFileLoaded;
try {
configFileLoaded = yield this.config.load(args);
}
catch (err) {
if (err instanceof assert_1.AssertionError) {
console.error(err.message);
process.exit(1);
}
else {
throw err;
}
}
const loggers = Logger_1.default.createLoggers(this.config.loglevel, this.config.logpath, this.config.instanceid, this.config.logdateformat);
this.logger = loggers.global;
if (configFileLoaded) {
this.logger.info('config file loaded');
}
try {
if (!this.config.rpc.disable) {
// start rpc server first, it will respond with UNAVAILABLE error
// indicating xud is starting until xud finishes initializing
this.rpcServer = new GrpcServer_1.default(loggers.rpc);
yield this.rpcServer.listen(this.config.rpc.port, this.config.rpc.host, path_1.default.join(this.config.xudir, 'tls.cert'), path_1.default.join(this.config.xudir, 'tls.key'));
if (!this.config.webproxy.disable) {
this.grpcAPIProxy = new GrpcWebProxyServer_1.default(loggers.rpc);
yield this.grpcAPIProxy.listen(this.config.webproxy.port, this.config.rpc.port, this.config.rpc.host, path_1.default.join(this.config.xudir, 'tls.cert'));
}
}
this.db = new DB_1.default(loggers.db, this.config.dbpath);
yield this.db.init(this.config.network, this.config.initdb);
this.unitConverter = new UnitConverter_1.UnitConverter();
this.unitConverter.init();
const nodeKeyPath = NodeKey_1.default.getPath(this.config.xudir, this.config.instanceid);
const nodeKeyExists = yield fs_1.promises.access(nodeKeyPath).then(() => true).catch(() => false);
this.swapClientManager = new SwapClientManager_1.default(this.config, loggers, this.unitConverter, this.db.models);
yield this.swapClientManager.init();
let nodeKey;
if (this.config.noencrypt) {
if (nodeKeyExists) {
nodeKey = yield NodeKey_1.default.fromFile(nodeKeyPath);
}
else {
nodeKey = yield NodeKey_1.default.generate(nodeKeyPath);
yield nodeKey.toFile();
}
// we need to initialize connext every time xud starts, even in noencrypt mode
// the call below is in lieu of the UnlockNode/CreateNode call flow
yield this.swapClientManager.initConnext(nodeKey.childSeed(enums_1.SwapClientType.Connext));
}
else if (this.rpcServer) {
this.rpcServer.grpcService.locked = true;
const initService = new InitService_1.default(this.swapClientManager, nodeKeyPath, nodeKeyExists, this.config.dbpath);
this.rpcServer.grpcInitService.setInitService(initService);
this.logger.info("Node key is encrypted, unlock with 'xucli unlock', 'xucli create', or 'xucli restore'");
nodeKey = yield new Promise((resolve) => {
initService.once('nodekey', resolve);
this.on('shutdown', () => {
// in case we shutdown before unlocking xud
resolve();
initService.removeListener('nodekey', resolve);
});
});
if (!nodeKey) {
return; // we interrupted before unlocking xud
}
this.rpcServer.grpcService.locked = false;
}
else {
throw new Error('rpc server cannot be disabled when xud is locked');
}
if (this.rpcServer) {
this.rpcServer.grpcInitService.disable();
}
this.logger.info(`Local nodePubKey is ${nodeKey.pubKey}`);
this.pool = new Pool_1.default({
nodeKey,
version,
config: this.config.p2p,
xuNetwork: this.config.network,
logger: loggers.p2p,
models: this.db.models,
strict: this.config.strict,
});
const initPromises = [];
this.swaps = new Swaps_1.default({
logger: loggers.swaps,
models: this.db.models,
pool: this.pool,
swapClientManager: this.swapClientManager,
strict: this.config.strict,
});
initPromises.push(this.swaps.init());
this.orderBook = new OrderBook_1.default({
logger: loggers.orderbook,
models: this.db.models,
thresholds: this.config.orderthresholds,
nomatching: this.config.nomatching,
pool: this.pool,
swaps: this.swaps,
nosanityswaps: this.config.nosanityswaps,
nobalancechecks: this.config.nobalancechecks,
strict: this.config.strict,
});
initPromises.push(this.orderBook.init());
// wait for components to initialize in parallel
yield Promise.all(initPromises);
// initialize pool and start listening/connecting only once other components are initialized
yield this.pool.init();
this.service = new Service_1.default({
version,
nodeKey,
orderBook: this.orderBook,
swapClientManager: this.swapClientManager,
pool: this.pool,
swaps: this.swaps,
logger: loggers.service,
shutdown: this.beginShutdown,
});
this.service.on('logLevel', (level) => {
var _a;
(_a = this.swapClientManager) === null || _a === void 0 ? void 0 : _a.setLogLevel(level);
Object.values(loggers).forEach((logger) => {
logger.setLogLevel(level);
});
});
if ((_a = this.swapClientManager.connextClient) === null || _a === void 0 ? void 0 : _a.isOperational()) {
this.httpServer = new HttpServer_1.default(loggers.http, this.service);
yield this.httpServer.listen(this.config.http.port, this.config.http.host);
}
// if we're running in simnet mode and Connext is enabled we'll
// attempt to request funds from the faucet and open a channel
// to the node once we have received the on-chain funds
if (this.config.network === enums_1.XuNetwork.SimNet && ((_b = this.swapClientManager.connextClient) === null || _b === void 0 ? void 0 : _b.isOperational())) {
this.simnetChannels$ = simnet_connext_channels_1.createSimnetChannels({
channels: [
{
currency: 'ETH',
// amount of currency to put in the channel
channelAmount: 1000000000,
// minimum channelBalance threshold
minChannelAmount: 100000000,
},
{
currency: 'USDT',
channelAmount: 100000000000,
minChannelAmount: 100000000,
},
{
currency: 'DAI',
channelAmount: 150000000000,
minChannelAmount: 100000000,
},
],
// we check the channel and on-chain balance every 10 seconds
// and refund from faucet if below the walletAmount
retryInterval: 10000,
}).subscribe({
next: (currency) => {
this.logger.info(`Connext wallet funded and channel opened for ${currency}`);
},
error: (e) => {
this.logger.error(`Failed to fund Connext wallet and open a channel: ${e}`);
},
complete: () => {
this.logger.info('Stopped monitoring Connext balances for automatic funding and channel creation');
},
});
}
// initialize rpc server last
if (this.rpcServer) {
this.rpcServer.grpcService.setService(this.service);
}
else {
this.logger.info('RPC server is disabled.');
}
}
catch (err) {
this.logger.error('Unexpected error during initialization, shutting down...', err);
yield this.shutdown();
}
});
this.shutdown = () => __awaiter(this, void 0, void 0, function* () {
var _c;
if (this.shuttingDown) {
this.logger.info('XUD is already shutting down');
return;
}
this.shuttingDown = true;
this.logger.info('XUD is shutting down');
// TODO: ensure we are not in the middle of executing any trades
const closePromises = [];
(_c = this.simnetChannels$) === null || _c === void 0 ? void 0 : _c.unsubscribe();
if (this.swapClientManager) {
this.swapClientManager.close();
}
if (this.httpServer) {
closePromises.push(this.httpServer.close());
}
if (this.pool) {
closePromises.push(this.pool.disconnect());
}
if (this.rpcServer) {
closePromises.push(this.rpcServer.close());
}
if (this.grpcAPIProxy) {
closePromises.push(this.grpcAPIProxy.close());
}
if (this.swaps) {
this.swaps.close();
}
yield Promise.all(closePromises);
yield this.db.close();
this.logger.info('XUD shutdown gracefully');
this.emit('shutdown');
});
/**
* Initiate graceful shutdown of xud. Emits the `shutdown` event when shutdown is complete.
*/
this.beginShutdown = () => {
// we begin the shutdown process but return a response before it completes.
void (this.shutdown());
};
this.config = new Config_1.default();
process.on('SIGINT', () => {
this.beginShutdown();
});
process.on('SIGTERM', () => {
this.beginShutdown();
});
}
}
if (!module.parent) {
const xud = new Xud();
void xud.start();
}
exports.default = Xud;
//# sourceMappingURL=Xud.js.map