From 2ea645152cdf4177b4398617a1e42e22879d4c20 Mon Sep 17 00:00:00 2001 From: ShahanaFarooqui Date: Tue, 17 Oct 2023 19:22:08 -0700 Subject: [PATCH] Backend Updates --- .github/docs/Application_configurations.md | 4 +- .github/docs/Core_lightning_setup.md | 11 ++-- backend/controllers/cln/balance.js | 2 +- backend/controllers/cln/channels.js | 6 +- backend/controllers/cln/fees.js | 2 +- backend/controllers/cln/getInfo.js | 69 +++++++++------------- backend/controllers/cln/invoices.js | 2 +- backend/controllers/cln/network.js | 10 ++-- backend/controllers/cln/offers.js | 2 +- backend/controllers/cln/onchain.js | 4 +- backend/controllers/cln/payments.js | 2 +- backend/controllers/cln/peers.js | 4 +- backend/controllers/cln/utility.js | 6 +- backend/controllers/cln/webSocketClient.js | 9 +-- backend/utils/app.js | 3 +- backend/utils/common.js | 4 +- server/controllers/cln/balance.ts | 2 +- server/controllers/cln/channels.ts | 6 +- server/controllers/cln/fees.ts | 2 +- server/controllers/cln/getInfo.ts | 64 +++++++++----------- server/controllers/cln/invoices.ts | 2 +- server/controllers/cln/network.ts | 10 ++-- server/controllers/cln/offers.ts | 2 +- server/controllers/cln/onchain.ts | 4 +- server/controllers/cln/payments.ts | 2 +- server/controllers/cln/peers.ts | 4 +- server/controllers/cln/utility.ts | 6 +- server/controllers/cln/webSocketClient.ts | 9 +-- server/utils/app.ts | 2 +- server/utils/common.ts | 4 +- 30 files changed, 121 insertions(+), 138 deletions(-) diff --git a/.github/docs/Application_configurations.md b/.github/docs/Application_configurations.md index 2402bdc0..96940b3c 100644 --- a/.github/docs/Application_configurations.md +++ b/.github/docs/Application_configurations.md @@ -21,7 +21,7 @@ parameters have `default` values for initial setup and can be updated after RTL "lnNode": "", "lnImplementation": "", "Authentication": { - "macaroonPath": "", + "macaroonPath": "", "swapMacaroonPath": "", "boltzMacaroonPath": "", "configPath": "", @@ -59,7 +59,7 @@ LN_SERVER_URL (LN server URL for LNP REST APIs, default https://127.0.0.1:8080) SWAP_SERVER_URL (Swap server URL for REST APIs, default http://127.0.0.1:8081) (Optional)
BOLTZ_SERVER_URL (Boltz server URL for REST APIs, default http://127.0.0.1:9003) (Optional)
CONFIG_PATH (Full path of the LNP .conf file including the file name) (Optional for LND & CLN, Mandatory for ECL if LN_API_PASSWORD is undefined)
-MACAROON_PATH (Path for the folder containing 'admin.macaroon' (LND)/'access.macaroon' (CLN) file, Required for LND & CLN)
+MACAROON_PATH (Path for the folder containing 'admin.macaroon' (LND)//'.commando' (CLN, contains rune) (CLN) file, Required for LND & CLN)
SWAP_MACAROON_PATH (Path for the folder containing Loop's 'loop.macaroon', optional)
BOLTZ_MACAROON_PATH (Path for the folder containing Boltz's 'admin.macaroon', optional)
RTL_SSO (1 - single sign on via an external cookie, 0 - stand alone RTL authentication, Required)
diff --git a/.github/docs/Core_lightning_setup.md b/.github/docs/Core_lightning_setup.md index 7313842d..9c51093f 100644 --- a/.github/docs/Core_lightning_setup.md +++ b/.github/docs/Core_lightning_setup.md @@ -17,8 +17,9 @@ Follow the below steps to install and setup RTL to run on Core Lightning. ### Pre-requisites: 1. Functioning Core Lightning node. Follow install instructions on their [github](https://github.com/ElementsProject/lightning) 2. NodeJS - Can be downloaded [here](https://nodejs.org/en/download) -3. Cl-REST - Ensure that `cl-rest` API server is installed and running. Install instructions [here](https://github.com/Ride-The-Lightning/c-lightning-REST) -4. Copy the `access.macaroon` file from `cl-rest` to the device, on which RTL will be installed +3. clnrest - Ensure that core lightning's `clnrest` API server is configured. Configuration instructions [here](https://docs.corelightning.org/docs/rest#configuration) +4. Create/reuse a rune created by core-lightning. Check [`createrune`](https://docs.corelightning.org/reference/lightning-createrune) and [`showrunes`](https://docs.corelightning.org/reference/lightning-showrunes) commands documentation for more details. +4. Copy the `rune` and save it in a file named `.commando`, on which RTL will be installed. ### Architecture ![](../screenshots/RTL-CLN-Arch-2.png) @@ -50,12 +51,12 @@ If there is an error with `upstream dependency conflict` message then replace `n ### Prep for Execution RTL requires its own config file `RTL-Config.json`, to start the server and provide user authentication on the app. * Rename the file `Sample-RTL-Config.json` to `RTL-Config.json` located at`./RTL`.. -* Locate the complete path of the readable `access.macaroon` from `cl-rest` on your node. +* Locate the complete path of the readable `.commando` file on your node. * Modify the RTL conf file per the example file below Ensure that the follow values are correct per your config: * `lnImplementation` - This should be `CLN`, indicating that RTL is connecting to a core lightning node. -* `macaroonPath` - Path of the folder containing `access.macaroon` file from cl-rest server. +* `macaroonPath` - Path of the folder containing `.commando` file including the file name. * `lnServerUrl` - complete url with ip address and port of the cl-rest server. * `multiPass` - Specify the password (in plain text) to access RTL. This password will be hashed and not stored as plain text. * `configPath` (optional) - File path of the core lightning config file, if RTL server is local to the core lightning server. @@ -77,7 +78,7 @@ Ensure that the follow values are correct per your config: "lnNode": "Core Lightning Testnet # 1", "lnImplementation": "CLN", "Authentication": { - "macaroonPath": "", + "macaroonPath": "", "configPath": "" }, "Settings": { diff --git a/backend/controllers/cln/balance.js b/backend/controllers/cln/balance.js index c621d31c..fe3cc81f 100644 --- a/backend/controllers/cln/balance.js +++ b/backend/controllers/cln/balance.js @@ -11,7 +11,7 @@ export const getBalance = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/getBalance'; - request(options).then((body) => { + request.post(options).then((body) => { if (!body.totalBalance) { body.totalBalance = 0; } diff --git a/backend/controllers/cln/channels.js b/backend/controllers/cln/channels.js index d48e4d33..d89cab74 100644 --- a/backend/controllers/cln/channels.js +++ b/backend/controllers/cln/channels.js @@ -11,7 +11,7 @@ export const listPeerChannels = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels'; - request(options).then((body) => { + request.post(options).then((body) => { body?.map((channel) => { if (!channel.alias || channel.alias === '') { channel.alias = channel.peer_id.substring(0, 20); @@ -37,7 +37,7 @@ export const listChannels = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels'; - request(options).then((body) => { + request.post(options).then((body) => { body?.map((channel) => { if (!channel.alias || channel.alias === '') { channel.alias = channel.channel_id.substring(0, 20); @@ -115,7 +115,7 @@ export const getLocalRemoteBalance = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/localremotebal'; - request(options).then((body) => { + request.post(options).then((body) => { if (!body.localBalance) { body.localBalance = 0; } diff --git a/backend/controllers/cln/fees.js b/backend/controllers/cln/fees.js index 07f4ad82..290df6f9 100644 --- a/backend/controllers/cln/fees.js +++ b/backend/controllers/cln/fees.js @@ -11,7 +11,7 @@ export const getFees = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/getFees'; - request(options).then((body) => { + request.post(options).then((body) => { if (!body.feeCollected) { body.feeCollected = 0; } diff --git a/backend/controllers/cln/getInfo.js b/backend/controllers/cln/getInfo.js index 871b017c..bfb66ab2 100644 --- a/backend/controllers/cln/getInfo.js +++ b/backend/controllers/cln/getInfo.js @@ -19,52 +19,41 @@ export const getInfo = (req, res, next) => { options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo'; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from Core Lightning server url ' + options.url }); - if (!options.headers || !options.headers.macaroon) { - const errMsg = 'Core lightning get info failed due to bad or missing macaroon!'; - const err = common.handleError({ statusCode: 502, message: 'Bad Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); + if (!options.headers || !options.headers.rune) { + const errMsg = 'Core lightning get info failed due to blacklisted or missing rune!'; + const err = common.handleError({ statusCode: 502, message: 'Bad rune', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { - return request(options).then((body) => { - const body_str = (!body) ? '' : JSON.stringify(body); - const search_idx = (!body) ? -1 : body_str.search('Not Found'); - if (!body || search_idx > -1 || body.error) { - if (body && !body.error) { - body.error = 'Error From Server!'; - } - const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + return request.post(options).then((body) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Node Information Before Update', data: body }); + body.lnImplementation = 'Core Lightning'; + const chainObj = { chain: '', network: '' }; + if (body.network.includes('litecoin') || body.network.includes('feathercoin')) { + chainObj.chain = ''; + chainObj.network = ''; + } + else if (body.network.includes('liquid')) { + chainObj.chain = 'Liquid'; + chainObj.network = common.titleCase(body.network); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Node Information Before Update', data: body }); - body.lnImplementation = 'Core Lightning'; - const chainObj = { chain: '', network: '' }; - if (body.network.includes('litecoin') || body.network.includes('feathercoin')) { - chainObj.chain = ''; - chainObj.network = ''; - } - else if (body.network.includes('liquid')) { - chainObj.chain = 'Liquid'; - chainObj.network = common.titleCase(body.network); - } - else { - chainObj.chain = 'Bitcoin'; - chainObj.network = common.titleCase(body.network); - } - body.chains = [chainObj]; - body.uris = []; - if (body.address && body.address.length > 0) { - body.address.forEach((addr) => { - body.uris.push(body.id + '@' + addr.address + ':' + addr.port); - }); - } - req.session.selectedNode.api_version = body.api_version || ''; - req.session.selectedNode.ln_version = body.version || ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); - clWsClient.updateSelectedNode(req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); - return res.status(200).json(body); + chainObj.chain = 'Bitcoin'; + chainObj.network = common.titleCase(body.network); + } + body.chains = [chainObj]; + body.uris = []; + if (body.address && body.address.length > 0) { + body.address.forEach((addr) => { + body.uris.push(body.id + '@' + addr.address + ':' + addr.port); + }); } + req.session.selectedNode.api_version = body.api_version || ''; + req.session.selectedNode.ln_version = body.version || ''; + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); + clWsClient.updateSelectedNode(req.session.selectedNode); + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); + return res.status(200).json(body); }).catch((errRes) => { const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); diff --git a/backend/controllers/cln/invoices.js b/backend/controllers/cln/invoices.js index 770cec12..c33f7f2a 100644 --- a/backend/controllers/cln/invoices.js +++ b/backend/controllers/cln/invoices.js @@ -29,7 +29,7 @@ export const listInvoices = (req, res, next) => { const labelQuery = req.query.label ? '?label=' + req.query.label : ''; options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/listInvoices' + labelQuery; logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url }); - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); res.status(200).json(body); }).catch((errRes) => { diff --git a/backend/controllers/cln/network.js b/backend/controllers/cln/network.js index b65d0c35..2454b773 100644 --- a/backend/controllers/cln/network.js +++ b/backend/controllers/cln/network.js @@ -11,7 +11,7 @@ export const getRoute = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/getRoute/' + req.params.destPubkey + '/' + req.params.amount; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Routes Received', data: body }); res.status(200).json({ routes: body }); }).catch((errRes) => { @@ -26,7 +26,7 @@ export const listNode = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNode/' + req.params.id; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished', data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -41,7 +41,7 @@ export const listChannel = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/listChannel/' + req.params.channelShortId; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup Finished', data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -56,7 +56,7 @@ export const feeRates = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/feeRates/' + req.params.feeRateStyle; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Fee Rates Received for ' + req.params.feeRateStyle, data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -73,7 +73,7 @@ export const listNodes = (req, res, next) => { const queryStr = req.query.liquidity_ads ? '?liquidity_ads=' + req.query.liquidity_ads : ''; options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNodes' + queryStr; logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'List Nodes URL' + options.url }); - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'List Nodes Finished', data: body }); body.forEach((node) => { if (node.option_will_fund) { diff --git a/backend/controllers/cln/offers.js b/backend/controllers/cln/offers.js index ad158bcb..86353123 100644 --- a/backend/controllers/cln/offers.js +++ b/backend/controllers/cln/offers.js @@ -41,7 +41,7 @@ export const listOffers = (req, res, next) => { options.url = options.url + '?active_only=' + req.query.active_only; } logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offers List URL', data: options.url }); - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offers List Received', data: body }); res.status(200).json(body); }).catch((errRes) => { diff --git a/backend/controllers/cln/onchain.js b/backend/controllers/cln/onchain.js index fac8756a..be3ac6dc 100644 --- a/backend/controllers/cln/onchain.js +++ b/backend/controllers/cln/onchain.js @@ -11,7 +11,7 @@ export const getNewAddress = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/newaddr?addrType=' + req.query.type; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated', data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -43,7 +43,7 @@ export const getUTXOs = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds'; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body }); res.status(200).json(body); }).catch((errRes) => { diff --git a/backend/controllers/cln/payments.js b/backend/controllers/cln/payments.js index cfe73423..ea93016e 100644 --- a/backend/controllers/cln/payments.js +++ b/backend/controllers/cln/payments.js @@ -70,7 +70,7 @@ export const listPayments = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments'; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments }); res.status(200).json(groupBy(body.payments)); }).catch((errRes) => { diff --git a/backend/controllers/cln/peers.js b/backend/controllers/cln/peers.js index 21bac143..253ae491 100644 --- a/backend/controllers/cln/peers.js +++ b/backend/controllers/cln/peers.js @@ -11,7 +11,7 @@ export const getPeers = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; - request(options).then((body) => { + request.post(options).then((body) => { body.forEach((peer) => { if (!peer.alias || peer.alias === '') { peer.alias = peer.id.substring(0, 20); @@ -35,7 +35,7 @@ export const postPeer = (req, res, next) => { request.post(options).then((connectRes) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: connectRes }); options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; - request(options).then((listPeersRes) => { + request.post(options).then((listPeersRes) => { const peers = listPeersRes ? common.newestOnTop(listPeersRes, 'id', req.body.id) : []; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers }); res.status(201).json(peers); diff --git a/backend/controllers/cln/utility.js b/backend/controllers/cln/utility.js index f8e866d9..ec52a0a2 100644 --- a/backend/controllers/cln/utility.js +++ b/backend/controllers/cln/utility.js @@ -6,7 +6,7 @@ const logger = Logger; const common = Common; export const decodePaymentFromPaymentRequest = (selNode, payment) => { options.url = selNode.ln_server_url + '/v1/utility/decode/' + payment; - return request(options).then((res) => { + return request.post(options).then((res) => { logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: res }); return res; }).catch((err) => { }); @@ -41,7 +41,7 @@ export const decodePayment = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/utility/decode/' + req.params.payReq; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded', data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -87,7 +87,7 @@ export const listConfigs = (req, res, next) => { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/utility/listConfigs'; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Utility', msg: 'List Configs Received', data: body }); res.status(200).json(body); }).catch((errRes) => { diff --git a/backend/controllers/cln/webSocketClient.js b/backend/controllers/cln/webSocketClient.js index 8513fdde..a8c1bebd 100644 --- a/backend/controllers/cln/webSocketClient.js +++ b/backend/controllers/cln/webSocketClient.js @@ -1,5 +1,4 @@ import * as fs from 'fs'; -import { join } from 'path'; import WebSocket from 'ws'; import { Logger } from '../../utils/logger.js'; import { Common } from '../../utils/common.js'; @@ -48,9 +47,11 @@ export class CLWebSocketClient { }; this.connectWithClient = (clWsClt) => { this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connecting to the Core Lightning\'s Websocket Server..' }); - const WS_LINK = (clWsClt.selectedNode.ln_server_url)?.replace(/^http/, 'ws') + '/v1/ws'; - const mcrnHexEncoded = Buffer.from(fs.readFileSync(join(clWsClt.selectedNode.macaroon_path, 'access.macaroon'))).toString('hex'); - clWsClt.webSocketClient = new WebSocket(WS_LINK, [mcrnHexEncoded, 'hex'], { rejectUnauthorized: false }); + const mcrnHexEncoded = Buffer.from(fs.readFileSync(clWsClt.selectedNode.macaroon_path)).toString().replace('\n', ''); + clWsClt.webSocketClient = new WebSocket(clWsClt.selectedNode.ln_server_url, { + headers: { rune: mcrnHexEncoded }, + rejectUnauthorized: false + }); clWsClt.webSocketClient.onopen = () => { this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connected to the Core Lightning\'s Websocket Server..' }); this.waitTime = 0.5; diff --git a/backend/utils/app.js b/backend/utils/app.js index 8974a44c..8df632ac 100644 --- a/backend/utils/app.js +++ b/backend/utils/app.js @@ -13,7 +13,6 @@ import eclRoutes from '../routes/eclair/index.js'; import { Database } from './database.js'; import { Common } from './common.js'; import { Logger } from './logger.js'; -import { CLWSClient } from '../controllers/cln/webSocketClient.js'; import { ECLWSClient } from '../controllers/eclair/webSocketClient.js'; import { LNDWSClient } from '../controllers/lnd/webSocketClient.js'; const ONE_DAY = 1000 * 60 * 60 * 24; @@ -23,7 +22,7 @@ export class ExpressApplication { this.logger = Logger; this.common = Common; this.eclWsClient = ECLWSClient; - this.clWsClient = CLWSClient; + // public clWsClient: CLWebSocketClient = CLWSClient; this.lndWsClient = LNDWSClient; this.databaseService = Database; this.directoryName = dirname(fileURLToPath(import.meta.url)); diff --git a/backend/utils/common.js b/backend/utils/common.js index 7b7f1d2a..062f7b97 100644 --- a/backend/utils/common.js +++ b/backend/utils/common.js @@ -89,7 +89,7 @@ export class CommonService { if (req.session.selectedNode && req.session.selectedNode.ln_implementation) { switch (req.session.selectedNode.ln_implementation.toUpperCase()) { case 'CLN': - req.session.selectedNode.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'access.macaroon'))).toString('base64') }; + req.session.selectedNode.options.headers = { rune: Buffer.from(fs.readFileSync(req.session.selectedNode.macaroon_path)).toString().replace('\n', '') }; break; case 'ECL': req.session.selectedNode.options.headers = { authorization: 'Basic ' + Buffer.from(':' + req.session.selectedNode.ln_api_password).toString('base64') }; @@ -131,7 +131,7 @@ export class CommonService { if (node.ln_implementation) { switch (node.ln_implementation.toUpperCase()) { case 'CLN': - node.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(node.macaroon_path, 'access.macaroon'))).toString('base64') }; + node.options.headers = { rune: Buffer.from(fs.readFileSync(node.macaroon_path)).toString().replace('\n', '') }; break; case 'ECL': node.options.headers = { authorization: 'Basic ' + Buffer.from(':' + node.ln_api_password).toString('base64') }; diff --git a/server/controllers/cln/balance.ts b/server/controllers/cln/balance.ts index 786bfe04..383e5a4d 100644 --- a/server/controllers/cln/balance.ts +++ b/server/controllers/cln/balance.ts @@ -10,7 +10,7 @@ export const getBalance = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/getBalance'; - request(options).then((body) => { + request.post(options).then((body) => { if (!body.totalBalance) { body.totalBalance = 0; } if (!body.confBalance) { body.confBalance = 0; } if (!body.unconfBalance) { body.unconfBalance = 0; } diff --git a/server/controllers/cln/channels.ts b/server/controllers/cln/channels.ts index 38476a4d..7701b418 100644 --- a/server/controllers/cln/channels.ts +++ b/server/controllers/cln/channels.ts @@ -10,7 +10,7 @@ export const listPeerChannels = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels'; - request(options).then((body) => { + request.post(options).then((body) => { body?.map((channel) => { if (!channel.alias || channel.alias === '') { channel.alias = channel.peer_id.substring(0, 20); } const local = channel.to_us_msat || 0; @@ -33,7 +33,7 @@ export const listChannels = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels'; - request(options).then((body) => { + request.post(options).then((body) => { body?.map((channel) => { if (!channel.alias || channel.alias === '') { channel.alias = channel.channel_id.substring(0, 20); } const local = channel.to_us_msat || 0; @@ -105,7 +105,7 @@ export const getLocalRemoteBalance = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/channel/localremotebal'; - request(options).then((body) => { + request.post(options).then((body) => { if (!body.localBalance) { body.localBalance = 0; } if (!body.remoteBalance) { body.remoteBalance = 0; } logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Local Remote Balance Received', data: body }); diff --git a/server/controllers/cln/fees.ts b/server/controllers/cln/fees.ts index fd86e73a..c7866a38 100644 --- a/server/controllers/cln/fees.ts +++ b/server/controllers/cln/fees.ts @@ -10,7 +10,7 @@ export const getFees = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/getFees'; - request(options).then((body) => { + request.post(options).then((body) => { if (!body.feeCollected) { body.feeCollected = 0; } logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Fees', msg: 'Fee Received', data: body }); res.status(200).json(body); diff --git a/server/controllers/cln/getInfo.ts b/server/controllers/cln/getInfo.ts index 501d6fea..71c25452 100644 --- a/server/controllers/cln/getInfo.ts +++ b/server/controllers/cln/getInfo.ts @@ -19,46 +19,38 @@ export const getInfo = (req, res, next) => { options.url = req.session.selectedNode.ln_server_url + '/v1/getinfo'; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Selected Node ' + req.session.selectedNode.ln_node }); logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Calling Info from Core Lightning server url ' + options.url }); - if (!options.headers || !options.headers.macaroon) { - const errMsg = 'Core lightning get info failed due to bad or missing macaroon!'; - const err = common.handleError({ statusCode: 502, message: 'Bad Macaroon', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); + if (!options.headers || !options.headers.rune) { + const errMsg = 'Core lightning get info failed due to blacklisted or missing rune!'; + const err = common.handleError({ statusCode: 502, message: 'Bad rune', error: errMsg }, 'GetInfo', errMsg, req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); } else { - return request(options).then((body) => { - const body_str = (!body) ? '' : JSON.stringify(body); - const search_idx = (!body) ? -1 : body_str.search('Not Found'); - if (!body || search_idx > -1 || body.error) { - if (body && !body.error) { body.error = 'Error From Server!'; } - const err = common.handleError(body, 'GetInfo', 'Get Info Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); + return request.post(options).then((body) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Node Information Before Update', data: body }); + body.lnImplementation = 'Core Lightning'; + const chainObj = { chain: '', network: '' }; + if (body.network.includes('litecoin') || body.network.includes('feathercoin')) { + chainObj.chain = ''; + chainObj.network = ''; + } else if (body.network.includes('liquid')) { + chainObj.chain = 'Liquid'; + chainObj.network = common.titleCase(body.network); } else { - logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'GetInfo', msg: 'Node Information Before Update', data: body }); - body.lnImplementation = 'Core Lightning'; - const chainObj = { chain: '', network: '' }; - if (body.network.includes('litecoin') || body.network.includes('feathercoin')) { - chainObj.chain = ''; - chainObj.network = ''; - } else if (body.network.includes('liquid')) { - chainObj.chain = 'Liquid'; - chainObj.network = common.titleCase(body.network); - } else { - chainObj.chain = 'Bitcoin'; - chainObj.network = common.titleCase(body.network); - } - body.chains = [chainObj]; - body.uris = []; - if (body.address && body.address.length > 0) { - body.address.forEach((addr) => { - body.uris.push(body.id + '@' + addr.address + ':' + addr.port); - }); - } - req.session.selectedNode.api_version = body.api_version || ''; - req.session.selectedNode.ln_version = body.version || ''; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); - clWsClient.updateSelectedNode(req.session.selectedNode); - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); - return res.status(200).json(body); + chainObj.chain = 'Bitcoin'; + chainObj.network = common.titleCase(body.network); } + body.chains = [chainObj]; + body.uris = []; + if (body.address && body.address.length > 0) { + body.address.forEach((addr) => { + body.uris.push(body.id + '@' + addr.address + ':' + addr.port); + }); + } + req.session.selectedNode.api_version = body.api_version || ''; + req.session.selectedNode.ln_version = body.version || ''; + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); + clWsClient.updateSelectedNode(req.session.selectedNode); + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Node Information Received', data: body }); + return res.status(200).json(body); }).catch((errRes) => { const err = common.handleError(errRes, 'GetInfo', 'Get Info Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); diff --git a/server/controllers/cln/invoices.ts b/server/controllers/cln/invoices.ts index 32d3ce60..7d7a9b22 100644 --- a/server/controllers/cln/invoices.ts +++ b/server/controllers/cln/invoices.ts @@ -27,7 +27,7 @@ export const listInvoices = (req, res, next) => { const labelQuery = req.query.label ? '?label=' + req.query.label : ''; options.url = req.session.selectedNode.ln_server_url + '/v1/invoice/listInvoices' + labelQuery; logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url }); - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); res.status(200).json(body); }).catch((errRes) => { diff --git a/server/controllers/cln/network.ts b/server/controllers/cln/network.ts index 81e5f1a4..05c1dcf6 100644 --- a/server/controllers/cln/network.ts +++ b/server/controllers/cln/network.ts @@ -10,7 +10,7 @@ export const getRoute = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/getRoute/' + req.params.destPubkey + '/' + req.params.amount; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Routes Received', data: body }); res.status(200).json({ routes: body }); }).catch((errRes) => { @@ -24,7 +24,7 @@ export const listNode = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNode/' + req.params.id; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Node Lookup Finished', data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -38,7 +38,7 @@ export const listChannel = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/listChannel/' + req.params.channelShortId; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Channel Lookup Finished', data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -52,7 +52,7 @@ export const feeRates = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/network/feeRates/' + req.params.feeRateStyle; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'Network Fee Rates Received for ' + req.params.feeRateStyle, data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -68,7 +68,7 @@ export const listNodes = (req, res, next) => { const queryStr = req.query.liquidity_ads ? '?liquidity_ads=' + req.query.liquidity_ads : ''; options.url = req.session.selectedNode.ln_server_url + '/v1/network/listNodes' + queryStr; logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'List Nodes URL' + options.url }); - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'List Nodes Finished', data: body }); body.forEach((node) => { if (node.option_will_fund) { diff --git a/server/controllers/cln/offers.ts b/server/controllers/cln/offers.ts index bf6c8ae0..dc0bcb81 100644 --- a/server/controllers/cln/offers.ts +++ b/server/controllers/cln/offers.ts @@ -43,7 +43,7 @@ export const listOffers = (req, res, next) => { options.url = options.url + '?active_only=' + req.query.active_only; } logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Offers', msg: 'Offers List URL', data: options.url }); - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Offers', msg: 'Offers List Received', data: body }); res.status(200).json(body); }).catch((errRes) => { diff --git a/server/controllers/cln/onchain.ts b/server/controllers/cln/onchain.ts index 8003be78..0e54d147 100644 --- a/server/controllers/cln/onchain.ts +++ b/server/controllers/cln/onchain.ts @@ -10,7 +10,7 @@ export const getNewAddress = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/newaddr?addrType=' + req.query.type; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'New Address Generated', data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -40,7 +40,7 @@ export const getUTXOs = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/listFunds'; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'OnChain', msg: 'Funds List Received', data: body }); res.status(200).json(body); }).catch((errRes) => { diff --git a/server/controllers/cln/payments.ts b/server/controllers/cln/payments.ts index 29804258..3abab878 100644 --- a/server/controllers/cln/payments.ts +++ b/server/controllers/cln/payments.ts @@ -61,7 +61,7 @@ export const listPayments = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/pay/listPayments'; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments }); res.status(200).json(groupBy(body.payments)); }).catch((errRes) => { diff --git a/server/controllers/cln/peers.ts b/server/controllers/cln/peers.ts index fb247b9b..032974d3 100644 --- a/server/controllers/cln/peers.ts +++ b/server/controllers/cln/peers.ts @@ -10,7 +10,7 @@ export const getPeers = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; - request(options).then((body) => { + request.post(options).then((body) => { body.forEach((peer) => { if (!peer.alias || peer.alias === '') { peer.alias = peer.id.substring(0, 20); @@ -33,7 +33,7 @@ export const postPeer = (req, res, next) => { request.post(options).then((connectRes) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peer Connected', data: connectRes }); options.url = req.session.selectedNode.ln_server_url + '/v1/peer/listPeers'; - request(options).then((listPeersRes) => { + request.post(options).then((listPeersRes) => { const peers = listPeersRes ? common.newestOnTop(listPeersRes, 'id', req.body.id) : []; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Peers List after Connect Received', data: peers }); res.status(201).json(peers); diff --git a/server/controllers/cln/utility.ts b/server/controllers/cln/utility.ts index a2c10984..0bae7119 100644 --- a/server/controllers/cln/utility.ts +++ b/server/controllers/cln/utility.ts @@ -9,7 +9,7 @@ const common: CommonService = Common; export const decodePaymentFromPaymentRequest = (selNode: CommonSelectedNode, payment) => { options.url = selNode.ln_server_url + '/v1/utility/decode/' + payment; - return request(options).then((res) => { + return request.post(options).then((res) => { logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: res }); return res; }).catch((err) => { }); @@ -41,7 +41,7 @@ export const decodePayment = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/utility/decode/' + req.params.payReq; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Decoded', data: body }); res.status(200).json(body); }).catch((errRes) => { @@ -84,7 +84,7 @@ export const listConfigs = (req, res, next) => { options = common.getOptions(req); if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); } options.url = req.session.selectedNode.ln_server_url + '/v1/utility/listConfigs'; - request(options).then((body) => { + request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Utility', msg: 'List Configs Received', data: body }); res.status(200).json(body); }).catch((errRes) => { diff --git a/server/controllers/cln/webSocketClient.ts b/server/controllers/cln/webSocketClient.ts index 4212b69d..b032b002 100644 --- a/server/controllers/cln/webSocketClient.ts +++ b/server/controllers/cln/webSocketClient.ts @@ -1,5 +1,4 @@ import * as fs from 'fs'; -import { join } from 'path'; import WebSocket from 'ws'; import { Logger, LoggerService } from '../../utils/logger.js'; @@ -59,9 +58,11 @@ export class CLWebSocketClient { public connectWithClient = (clWsClt) => { this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connecting to the Core Lightning\'s Websocket Server..' }); - const WS_LINK = (clWsClt.selectedNode.ln_server_url)?.replace(/^http/, 'ws') + '/v1/ws'; - const mcrnHexEncoded = Buffer.from(fs.readFileSync(join(clWsClt.selectedNode.macaroon_path, 'access.macaroon'))).toString('hex'); - clWsClt.webSocketClient = new WebSocket(WS_LINK, [mcrnHexEncoded, 'hex'], { rejectUnauthorized: false }); + const mcrnHexEncoded = Buffer.from(fs.readFileSync(clWsClt.selectedNode.macaroon_path)).toString().replace('\n', ''); + clWsClt.webSocketClient = new WebSocket(clWsClt.selectedNode.ln_server_url, { + headers: { rune: mcrnHexEncoded }, + rejectUnauthorized: false + }); clWsClt.webSocketClient.onopen = () => { this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connected to the Core Lightning\'s Websocket Server..' }); diff --git a/server/utils/app.ts b/server/utils/app.ts index 114fdf14..0a4a0e35 100644 --- a/server/utils/app.ts +++ b/server/utils/app.ts @@ -26,7 +26,7 @@ export class ExpressApplication { public logger: LoggerService = Logger; public common: CommonService = Common; public eclWsClient: ECLWebSocketClient = ECLWSClient; - public clWsClient: CLWebSocketClient = CLWSClient; + // public clWsClient: CLWebSocketClient = CLWSClient; public lndWsClient: LNDWebSocketClient = LNDWSClient; public databaseService: DatabaseService = Database; public directoryName = dirname(fileURLToPath(import.meta.url)); diff --git a/server/utils/common.ts b/server/utils/common.ts index e2da0616..182253b6 100644 --- a/server/utils/common.ts +++ b/server/utils/common.ts @@ -95,7 +95,7 @@ export class CommonService { if (req.session.selectedNode && req.session.selectedNode.ln_implementation) { switch (req.session.selectedNode.ln_implementation.toUpperCase()) { case 'CLN': - req.session.selectedNode.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(req.session.selectedNode.macaroon_path, 'access.macaroon'))).toString('base64') }; + req.session.selectedNode.options.headers = { rune: Buffer.from(fs.readFileSync(req.session.selectedNode.macaroon_path)).toString().replace('\n', '') }; break; case 'ECL': @@ -137,7 +137,7 @@ export class CommonService { if (node.ln_implementation) { switch (node.ln_implementation.toUpperCase()) { case 'CLN': - node.options.headers = { macaroon: Buffer.from(fs.readFileSync(join(node.macaroon_path, 'access.macaroon'))).toString('base64') }; + node.options.headers = { rune: Buffer.from(fs.readFileSync(node.macaroon_path)).toString().replace('\n', '') }; break; case 'ECL':