UNPKG

@raiden_network/raiden-cli

Version:

Raiden Light Client standalone app with a REST API via HTTP

167 lines (166 loc) 6.76 kB
"use strict"; 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); });