@raiden_network/raiden-cli
Version:
Raiden Light Client standalone app with a REST API via HTTP
167 lines (166 loc) • 6.76 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable no-console */
const ethers_1 = require("ethers");
const fs_1 = require("fs");
const inquirer_1 = __importDefault(require("inquirer"));
const loglevel_1 = require("loglevel");
const path = __importStar(require("path"));
const raiden_ts_1 = require("raiden-ts");
const app_1 = require("./app");
const options_1 = require("./options");
const logging_1 = require("./utils/logging");
if (!('RTCPeerConnection' in globalThis)) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
Object.assign(globalThis, require('@koush/wrtc'));
}
async function getKeystoreAccounts(keystorePath) {
const keys = {};
for (const filename of await fs_1.promises.readdir(keystorePath)) {
try {
const json = await fs_1.promises.readFile(path.join(keystorePath, filename), 'utf-8');
const address = ethers_1.ethers.utils.getAddress(JSON.parse(json)['address']);
if (!(address in keys))
keys[address] = [];
keys[address].push(json);
}
catch (e) { }
}
return keys;
}
async function getWallet(keystoreDir, address, passwordFile) {
const keys = await getKeystoreAccounts(keystoreDir);
if (!Object.keys(keys).length)
throw new Error(`No account found on keystore directory "${keystoreDir}"`);
else if (!address)
({ address } = await inquirer_1.default.prompt([
{ type: 'list', name: 'address', message: 'Account:', choices: Object.keys(keys) },
]));
else if (!(address in keys))
throw new Error(`Could not find keystore file for "${address}"`);
let password;
if (passwordFile)
password = (await fs_1.promises.readFile(passwordFile, 'utf-8')).split('\n').shift();
else
({ password } = await inquirer_1.default.prompt([
{ type: 'password', name: 'password', message: `[${address}] Password:`, mask: '*' },
]));
let lastError;
for (const json of keys[address]) {
try {
return await ethers_1.Wallet.fromEncryptedJson(json, password);
}
catch (err) {
lastError = err;
if (err instanceof Error && err.message.includes('invalid counter bytes size')) {
const parsed = JSON.parse(json);
// try to fix non-16-bytes [crypto.cipherparams.iv]
keys[address].push(JSON.stringify({
...parsed,
crypto: {
...parsed.crypto,
cipherparams: {
...parsed.crypto.cipherparams,
iv: parsed.crypto.cipherparams.iv.padStart(32, '0'),
},
},
}));
}
}
}
throw (lastError ?? new Error(`Could not decrypt keystore for "${address}" with provided password`));
}
async function createDataDirectory(path) {
await fs_1.promises.mkdir(path, { recursive: true });
}
function unrefTimeout(timeout) {
if (typeof timeout === 'number')
return;
timeout.unref();
}
function shutdownServer() {
if (this.server.listening) {
this.log.info('Closing server...');
this.server.close();
}
unrefTimeout(setTimeout(() => process.exit(0), 10000));
}
function shutdownRaiden() {
if (this.raiden.started) {
this.log.info('Stopping raiden...');
this.raiden.stop();
}
else {
process.exit(1);
}
}
function registerShutdownHooks() {
// raiden shutdown triggers server shutdown
if ('server' in this)
this.raiden.state$.subscribe({
error: shutdownServer.bind(this),
complete: shutdownServer.bind(this),
});
process.on('SIGINT', shutdownRaiden.bind(this));
process.on('SIGTERM', shutdownRaiden.bind(this));
// 'beforeExit' is emitted after all background tasks are finished;
// we need to call process.exit explicitly in order to avoid 'wrtc'
// cleanup from segfaulting the process
process.on('beforeExit', (code) => {
this.log.info('Exiting', code);
process.exit(code);
});
process.on('unhandledRejection', (reason) => this.log.warn('Unhandled rejection:', reason));
}
async function main() {
const argv = await (0, options_1.parseArguments)();
const wallet = await getWallet(argv.keystorePath, argv.address, argv.passwordFile);
(0, logging_1.setupLoglevel)(argv.logFile);
await createDataDirectory(argv.datadir);
const config = await (0, options_1.createRaidenConfig)(argv);
const raiden = await raiden_ts_1.Raiden.create(argv.ethRpcEndpoint, wallet.privateKey, {
prefix: argv.datadir.endsWith('/') ? argv.datadir : argv.datadir + '/',
state: argv.loadState ? JSON.parse(await fs_1.promises.readFile(argv.loadState, 'utf-8')) : undefined,
}, argv.userDepositContractAddress, config);
const log = (0, loglevel_1.getLogger)(`cli:${raiden.address}`);
const cli = { log, raiden };
if (argv.apiAddress) {
const [host, port] = argv.apiAddress;
const app = app_1.makeApp.call(cli, argv.rpccorsdomain, argv.webUi);
const server = app.listen(port, host, () => cli.log.info(`Server started at: http://${host}:${port}`));
server.setTimeout(3.6e6); // 1h
Object.assign(cli, { app, server });
}
registerShutdownHooks.call(cli);
await raiden.start();
}
main().catch((err) => {
console.error('Main error:', err);
process.exit(2);
});