Backend Updates

clnrest-migration
ShahanaFarooqui 8 months ago
parent 5b88674d2c
commit 2ea645152c

@ -21,7 +21,7 @@ parameters have `default` values for initial setup and can be updated after RTL
"lnNode": "<Node name to uniquely identify the node in the UI, Required>",
"lnImplementation": "<LNP implementation, Allowed values LND/CLN/ECL, Required>",
"Authentication": {
"macaroonPath": "<Path for the folder containing 'admin.macaroon' (LND)/'access.macaroon' (CLN) file, Required for LND & CLN>",
"macaroonPath": "<Path for the folder containing 'admin.macaroon' (LND)/'.commando' (CLN, contains rune) file, Required for LND & CLN>",
"swapMacaroonPath": "<Path for the folder containing 'loop.macaroon' (LND), Required for LND Loop>",
"boltzMacaroonPath": "<Path for the folder containing 'admin.macaroon' (Boltz), Required for Boltz Swaps>",
"configPath": "<Full path of the lnd.conf/core lightning config/eclair.conf file including the file name, if present locally, Optional, only mandatory for ECL if the lnApiPassword is missing>",
@ -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)<br />
BOLTZ_SERVER_URL (Boltz server URL for REST APIs, default http://127.0.0.1:9003) (Optional)<br />
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)<br />
MACAROON_PATH (Path for the folder containing 'admin.macaroon' (LND)/'access.macaroon' (CLN) file, Required for LND & CLN)<br />
MACAROON_PATH (Path for the folder containing 'admin.macaroon' (LND)//'.commando' (CLN, contains rune) (CLN) file, Required for LND & CLN)<br />
SWAP_MACAROON_PATH (Path for the folder containing Loop's 'loop.macaroon', optional)<br />
BOLTZ_MACAROON_PATH (Path for the folder containing Boltz's 'admin.macaroon', optional)<br />
RTL_SSO (1 - single sign on via an external cookie, 0 - stand alone RTL authentication, Required)<br />

@ -17,8 +17,9 @@ Follow the below steps to install and setup RTL to run on Core Lightning.
### <a name="prereq"></a>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.
### <a name="arch"></a>Architecture
![](../screenshots/RTL-CLN-Arch-2.png)
@ -50,12 +51,12 @@ If there is an error with `upstream dependency conflict` message then replace `n
### <a name="prep"></a>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": "<Modify to include the path of the folder with access.macaroon>",
"macaroonPath": "<Modify to include the path of the folder with .commando including filename>",
"configPath": "<Optional - Config file path for core lightning>"
},
"Settings": {

@ -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;
}

@ -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;
}

@ -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;
}

@ -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 });

@ -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) => {

@ -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) {

@ -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) => {

@ -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) => {

@ -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) => {

@ -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);

@ -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) => {

@ -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;

@ -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));

@ -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') };

@ -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; }

@ -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 });

@ -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);

@ -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 });

@ -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) => {

@ -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) {

@ -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) => {

@ -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) => {

@ -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) => {

@ -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);

@ -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) => {

@ -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..' });

@ -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));

@ -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':

Loading…
Cancel
Save