Websocket, runePath and Invoices

clnrest-migration
ShahanaFarooqui 7 months ago
parent 1e68cdbab0
commit cee77e61db

5
.github/README.md vendored

@ -96,11 +96,12 @@ Example RTL-Config.json:
"lnNode": "LND Testnet",
"lnImplementation": "LND",
"Authentication": {
"macaroonPath": "<Complete path of the folder containing LND's admin.macaroon for the node # 1>",
"macaroonPath": "<Complete path of the folder containing LND admin.macaroon for the node>",
"runePath": "<Complete path including filename for CLN rune for the node, rune format 'LIGHTNING_RUNE="your-rune"'>",
"lnApiPassword": "<Can be used to provide password in ECL implementation>",
"swapMacaroonPath": "<Complete path of the folder containing Loop's loop.macaroon for the node>",
"boltzMacaroonPath": "<Complete path of the folder containing Boltz admin.macaroon for the node>",
"configPath": "<Optional:Path of the .conf if present locally or empty>",
"lnApiPassword": "<Optional:Can be used to provide password in ECL implementation>"
},
"Settings": {
"userPersona": "OPERATOR",

@ -21,11 +21,12 @@ 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)/'.commando' (CLN, contains rune) file, Required for LND & CLN>",
"macaroonPath": "<Path for the folder containing 'admin.macaroon' for LND node, Required for LND>",
"runePath": "<Complete path including filename for CLN rune for the node, Required for CLN>",
"lnApiPassword": "<Password to be used for ECL API authentication. Mandatory only for ECL if the configPath is missing>"
"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>",
"lnApiPassword": "<Password to be used for ECL API authentication. Mandatory only for ECL if the configPath is missing>"
},
"Settings": {
"userPersona": "<User persona to tailor the data on UI. Allowed values MERCHANT/OPERATOR. Default MERCHANT, Optional>",
@ -59,7 +60,8 @@ 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)//'.commando' (CLN, contains rune) (CLN) file, Required for LND & CLN)<br />
MACAROON_PATH (Path for the folder containing 'admin.macaroon' for LND, Required for LND)<br />
RUNE_PATH (Complete path for the file containing 'rune' for CLN where the file should define the rune in 'LIGHTNING_RUNE="your-rune"' format, Required for 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 />

@ -56,7 +56,7 @@ RTL requires its own config file `RTL-Config.json`, to start the server and prov
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 `.commando` file including the file name.
* `runePath` - Path of the folder including filename which contains `rune` for the node. This rune in the file should be saved in `LIGHTNING_RUNE="your-rune"` format.
* `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.
@ -78,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 .commando including filename>",
"runePath": "<Modify to include the path of the folder including filename which contains `rune`>",
"configPath": "<Optional - Config file path for core lightning>"
},
"Settings": {

@ -30,7 +30,8 @@ This step is only required to configure the nodes, which will be remotely connec
12. `swapServerUrl` must be set to the swap service url. e.g. https://127.0.0.1:8081.
13. `boltzServerUrl` must be set to the boltz service url. e.g. https://127.0.0.1:9003.
14. `configPath` and `bitcoindConfigPath` are optional parameters which can be set only if the RTL is running locally on the same node. Else it can be set to "" or removed from the conf file all together.
15. `lnApiPassword` is mandatory in the ln implementation is ECL and configPath is missing. It is used to provide password for API authentication. It will be ignored in other ln implementations.
15. `lnApiPassword` is mandatory if the ln implementation is ECL and configPath is missing. It is used to provide password for API authentication. It will be ignored in other ln implementations.
16. `runePath` is mandatory for CLN implementation. It should be set to the local path of the folder including filename containing rune value. This rune value in the file should be saved in `LIGHTNING_RUNE="your-rune"` format.
#### 3. Restart RTL

@ -59,7 +59,6 @@ export const listNodes = (req, res, next) => {
return res.status(options.statusCode).json({ message: options.message, error: options.error });
}
options.url = req.session.selectedNode.ln_server_url + '/v1/listnodes';
options.body = req.params.id ? { id: req.params.id } : null;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'List Nodes URL' + options.url });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'List Nodes Finished', data: body });

@ -1,4 +1,4 @@
import WebSocket from 'ws';
import socketIOClient from 'socket.io-client';
import { Logger } from '../../utils/logger.js';
import { Common } from '../../utils/common.js';
import { WSServer } from '../../utils/webSocketServer.js';
@ -34,7 +34,7 @@ export class CLWebSocketClient {
}
}
else {
if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) {
if ((!clientExists.webSocketClient || !clientExists.webSocketClient.connected) && selectedNode.ln_server_url) {
clientExists.reConnect = true;
this.connectWithClient(clientExists);
}
@ -47,38 +47,37 @@ 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..' });
try {
if (!clWsClt.selectedNode.macaroon_value) {
clWsClt.selectedNode.macaroon_value = this.common.getMacaroonValue(clWsClt.selectedNode.macaroon_path);
if (!clWsClt.selectedNode.rune_value) {
clWsClt.selectedNode.rune_value = this.common.getRuneValue(clWsClt.selectedNode.rune_path);
}
clWsClt.webSocketClient = new WebSocket(clWsClt.selectedNode.ln_server_url, {
headers: { rune: clWsClt.selectedNode.macaroon_value },
clWsClt.webSocketClient = socketIOClient(clWsClt.selectedNode.ln_server_url, {
extraHeaders: { rune: '9ISqFS53IFIfBS0yhwgM_XaNHFAUoFU_Bzfyhe-s8u49MA==' },
transports: ['websocket'],
secure: true,
rejectUnauthorized: false
});
}
catch (err) {
throw new Error(err);
}
clWsClt.webSocketClient.onopen = () => {
clWsClt.webSocketClient.on('connect', () => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connected to the Core Lightning\'s Websocket Server..' });
this.waitTime = 0.5;
};
clWsClt.webSocketClient.onclose = (e) => {
});
clWsClt.webSocketClient.on('disconnect', (reason) => {
if (clWsClt && clWsClt.selectedNode && clWsClt.selectedNode.ln_implementation === 'CLN') {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Web socket disconnected, will reconnect again...' });
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Web socket disconnected, will reconnect again...', data: reason });
clWsClt.webSocketClient.close();
if (clWsClt.reConnect) {
this.reconnet(clWsClt);
}
}
};
clWsClt.webSocketClient.onmessage = (msg) => {
});
clWsClt.webSocketClient.on('message', (msg) => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'DEBUG', fileName: 'CLWebSocket', msg: 'Received message from the server..', data: msg.data });
msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data;
msg['source'] = 'CLN';
const msgStr = JSON.stringify(msg);
this.wsServer.sendEventsToAllLNClients(msgStr, clWsClt.selectedNode);
};
clWsClt.webSocketClient.onerror = (err) => {
this.wsServer.sendEventsToAllLNClients(JSON.stringify({ source: 'CLN', data: msg }), clWsClt.selectedNode);
});
clWsClt.webSocketClient.on('error', (err) => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'ERROR', fileName: 'CLWebSocket', msg: 'Web socket error', error: err });
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }'));
this.wsServer.sendErrorToAllLNClients(errStr, clWsClt.selectedNode);
@ -86,11 +85,11 @@ export class CLWebSocketClient {
if (clWsClt.reConnect) {
this.reconnet(clWsClt);
}
};
});
};
this.disconnect = (selectedNode) => {
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) {
if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.connected) {
this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the Core Lightning\'s Websocket Server..' });
clientExists.reConnect = false;
clientExists.webSocketClient.close();

@ -1,9 +1,11 @@
export class CommonSelectedNode {
constructor(options, ln_server_url, macaroon_path, macaroon_value, ln_api_password, swap_server_url, boltz_server_url, config_path, rtl_conf_file_path, swap_macaroon_path, boltz_macaroon_path, bitcoind_config_path, channel_backup_path, log_level, log_file, index, ln_node, ln_implementation, user_persona, theme_mode, theme_color, unannounced_channels, fiat_conversion, currency_unit, ln_version, api_version, enable_offers, enable_peerswap) {
constructor(options, ln_server_url, macaroon_path, macaroon_value, rune_path, rune_value, ln_api_password, swap_server_url, boltz_server_url, config_path, rtl_conf_file_path, swap_macaroon_path, boltz_macaroon_path, bitcoind_config_path, channel_backup_path, log_level, log_file, index, ln_node, ln_implementation, user_persona, theme_mode, theme_color, unannounced_channels, fiat_conversion, currency_unit, ln_version, api_version, enable_offers, enable_peerswap) {
this.options = options;
this.ln_server_url = ln_server_url;
this.macaroon_path = macaroon_path;
this.macaroon_value = macaroon_value;
this.rune_path = rune_path;
this.rune_value = rune_value;
this.ln_api_password = ln_api_password;
this.swap_server_url = swap_server_url;
this.boltz_server_url = boltz_server_url;

@ -6,5 +6,5 @@ const router = Router();
router.get('/getRoute/:destPubkey/:amount', isAuthenticated, getRoute);
router.get('/listChannels/:channelShortId', isAuthenticated, listChannels);
router.get('/feeRates/:feeRateStyle', isAuthenticated, feeRates);
router.get('/listNodes/:id', isAuthenticated, listNodes);
router.get('/listNodes', isAuthenticated, listNodes);
export default router;

@ -91,10 +91,10 @@ export class CommonService {
switch (req.session.selectedNode.ln_implementation.toUpperCase()) {
case 'CLN':
try {
if (!req.session.selectedNode.macaroon_value) {
req.session.selectedNode.macaroon_value = this.getMacaroonValue(req.session.selectedNode.macaroon_path);
if (!req.session.selectedNode.rune_value) {
req.session.selectedNode.rune_value = this.getRuneValue(req.session.selectedNode.rune_path);
}
req.session.selectedNode.options.headers = { rune: req.session.selectedNode.macaroon_value };
req.session.selectedNode.options.headers = { rune: req.session.selectedNode.rune_value };
}
catch (err) {
throw new Error(err);
@ -124,8 +124,8 @@ export class CommonService {
return { status: 502, message: err };
}
};
this.getMacaroonValue = (macaroon_path) => {
const data = fs.readFileSync(macaroon_path, 'utf8');
this.getRuneValue = (rune_path) => {
const data = fs.readFileSync(rune_path, 'utf8');
const pattern = /LIGHTNING_RUNE="(?<runeValue>[^"]+)"/;
const match = data.match(pattern);
if (match.groups.runeValue) {
@ -152,10 +152,10 @@ export class CommonService {
switch (node.ln_implementation.toUpperCase()) {
case 'CLN':
try {
if (!node.macaroon_value) {
node.macaroon_value = this.getMacaroonValue(node.macaroon_path);
if (!node.rune_value) {
node.rune_value = this.getRuneValue(node.rune_path);
}
node.options.headers = { rune: node.macaroon_value };
node.options.headers = { rune: node.rune_value };
}
catch (err) {
throw new Error(err);

@ -148,25 +148,40 @@ export class ConfigService {
if (this.common.nodes[idx].ln_implementation === 'CLT') {
this.common.nodes[idx].ln_implementation = 'CLN';
}
if (this.common.nodes[idx].ln_implementation !== 'ECL' && process?.env?.MACAROON_PATH && process?.env?.MACAROON_PATH.trim() !== '') {
this.common.nodes[idx].macaroon_path = process?.env?.MACAROON_PATH;
}
else if (this.common.nodes[idx].ln_implementation !== 'ECL' && node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') {
this.common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
}
else if (this.common.nodes[idx].ln_implementation !== 'ECL') {
this.errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Config.json!';
}
if (this.common.nodes[idx].ln_implementation === 'ECL') {
if (process?.env?.LN_API_PASSWORD) {
this.common.nodes[idx].ln_api_password = process?.env?.LN_API_PASSWORD;
}
else if (node.Authentication && node.Authentication.lnApiPassword) {
this.common.nodes[idx].ln_api_password = node.Authentication.lnApiPassword;
}
else {
this.common.nodes[idx].ln_api_password = '';
}
switch (this.common.nodes[idx].ln_implementation) {
case 'CLN':
if (process?.env?.RUNE_PATH && process?.env?.RUNE_PATH.trim() !== '') {
this.common.nodes[idx].rune_path = process?.env?.RUNE_PATH;
}
else if (node.Authentication && node.Authentication.runePath && node.Authentication.runePath.trim() !== '') {
this.common.nodes[idx].rune_path = node.Authentication.runePath;
}
else {
this.errMsg = 'Please set rune path for node index ' + node.index + ' in RTL-Config.json!';
}
break;
case 'ECL':
if (process?.env?.LN_API_PASSWORD) {
this.common.nodes[idx].ln_api_password = process?.env?.LN_API_PASSWORD;
}
else if (node.Authentication && node.Authentication.lnApiPassword) {
this.common.nodes[idx].ln_api_password = node.Authentication.lnApiPassword;
}
else {
this.common.nodes[idx].ln_api_password = '';
}
break;
default:
if (process?.env?.MACAROON_PATH && process?.env?.MACAROON_PATH.trim() !== '') {
this.common.nodes[idx].macaroon_path = process?.env?.MACAROON_PATH;
}
else if (node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') {
this.common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
}
else {
this.errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Config.json!';
}
break;
}
if (process?.env?.CONFIG_PATH) {
this.common.nodes[idx].config_path = process?.env?.CONFIG_PATH;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],s=!0,l=0;l<t.length;l++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[l]))?t.splice(l--,1):(s=!1,o<a&&(a=o));if(s){e.splice(n--,1);var d=i();void 0!==d&&(f=d)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>e+"."+{125:"37eafd5d4474e084",456:"16212571f65d0b5f",570:"a719a189ca60c55b",758:"2801e2da6f8bba94"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="RTLApp:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,s;if(void 0!==o)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==f+o){a=u;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+o),a.src=r.tu(t)),e[t]=[i];var c=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:f=>f},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((u,c)=>n=e[i]=[u,c]);o.push(n[2]=a);var s=r.p+r.u(i),l=new Error;r.l(s,u=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var c=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;l.message="Loading chunk "+i+" failed.\n("+c+": "+p+")",l.name="ChunkLoadError",l.type=c,l.request=p,n[1](l)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var l,d,[n,a,s]=o,u=0;if(n.some(p=>0!==e[p])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(s)var c=s(r)}for(i&&i(o);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[d]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],s=!0,l=0;l<t.length;l++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[l]))?t.splice(l--,1):(s=!1,o<a&&(a=o));if(s){e.splice(n--,1);var d=i();void 0!==d&&(f=d)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>e+"."+{125:"dbd1b465c25f616b",456:"16212571f65d0b5f",570:"a719a189ca60c55b",758:"2801e2da6f8bba94"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="RTLApp:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,s;if(void 0!==o)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==f+o){a=u;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+o),a.src=r.tu(t)),e[t]=[i];var c=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:f=>f},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((u,c)=>n=e[i]=[u,c]);o.push(n[2]=a);var s=r.p+r.u(i),l=new Error;r.l(s,u=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var c=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;l.message="Loading chunk "+i+" failed.\n("+c+": "+p+")",l.name="ChunkLoadError",l.type=c,l.request=p,n[1](l)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var l,d,[n,a,s]=o,u=0;if(n.some(p=>0!==e[p])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(s)var c=s(r)}for(i&&i(o);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[d]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();

61
package-lock.json generated

@ -30,6 +30,7 @@
"request-promise": "^4.2.6",
"rxjs": "~7.8.0",
"sha256": "^0.2.0",
"socket.io-client": "^4.7.2",
"stream-browserify": "^3.0.0",
"tslib": "^2.3.0",
"ws": "^8.14.2",
@ -4832,8 +4833,7 @@
"node_modules/@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
"dev": true
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
},
"node_modules/@swimlane/ngx-charts": {
"version": "20.4.1",
@ -8331,7 +8331,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
@ -8926,11 +8925,42 @@
"node": ">=10.2.0"
}
},
"node_modules/engine.io-client": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz",
"integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.11.0",
"xmlhttprequest-ssl": "~2.0.0"
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
"integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==",
"dev": true,
"engines": {
"node": ">=10.0.0"
}
@ -16581,11 +16611,24 @@
}
}
},
"node_modules/socket.io-client": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz",
"integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.5.2",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"dev": true,
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
@ -18931,6 +18974,14 @@
"sax": "^1.2.4"
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

@ -42,6 +42,7 @@
"request-promise": "^4.2.6",
"rxjs": "~7.8.0",
"sha256": "^0.2.0",
"socket.io-client": "^4.7.2",
"stream-browserify": "^3.0.0",
"tslib": "^2.3.0",
"ws": "^8.14.2",
@ -57,9 +58,9 @@
"@angular/animations": "^16.2.0",
"@angular/cdk": "^16.2.7",
"@angular/cli": "^16.2.5",
"@angular/compiler-cli": "^16.2.0",
"@angular/common": "^16.2.0",
"@angular/compiler": "^16.2.0",
"@angular/compiler-cli": "^16.2.0",
"@angular/core": "^16.2.0",
"@angular/flex-layout": "^15.0.0-beta.42",
"@angular/forms": "^16.2.0",

@ -57,7 +57,6 @@ export const listNodes = (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/listnodes';
options.body = req.params.id ? { id: req.params.id } : null;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Network', msg: 'List Nodes URL' + options.url });
request.post(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Network', msg: 'List Nodes Finished', data: body });

@ -1,6 +1,4 @@
import * as fs from 'fs';
import WebSocket from 'ws';
import socketIOClient from 'socket.io-client';
import { Logger, LoggerService } from '../../utils/logger.js';
import { Common, CommonService } from '../../utils/common.js';
import { WSServer } from '../../utils/webSocketServer.js';
@ -46,7 +44,7 @@ export class CLWebSocketClient {
this.webSocketClients.push(newWebSocketClient);
}
} else {
if ((!clientExists.webSocketClient || clientExists.webSocketClient.readyState !== WebSocket.OPEN) && selectedNode.ln_server_url) {
if ((!clientExists.webSocketClient || !clientExists.webSocketClient.connected) && selectedNode.ln_server_url) {
clientExists.reConnect = true;
this.connectWithClient(clientExists);
}
@ -59,49 +57,49 @@ 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..' });
try {
if (!clWsClt.selectedNode.macaroon_value) {
clWsClt.selectedNode.macaroon_value = this.common.getMacaroonValue(clWsClt.selectedNode.macaroon_path);
if (!clWsClt.selectedNode.rune_value) {
clWsClt.selectedNode.rune_value = this.common.getRuneValue(clWsClt.selectedNode.rune_path);
}
clWsClt.webSocketClient = new WebSocket(clWsClt.selectedNode.ln_server_url, {
headers: { rune: clWsClt.selectedNode.macaroon_value },
clWsClt.webSocketClient = socketIOClient(clWsClt.selectedNode.ln_server_url, {
extraHeaders: { rune: '9ISqFS53IFIfBS0yhwgM_XaNHFAUoFU_Bzfyhe-s8u49MA==' },
transports: ['websocket'],
secure: true,
rejectUnauthorized: false
});
} catch (err) {
throw new Error(err);
}
clWsClt.webSocketClient.onopen = () => {
clWsClt.webSocketClient.on('connect', () => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Connected to the Core Lightning\'s Websocket Server..' });
this.waitTime = 0.5;
};
});
clWsClt.webSocketClient.onclose = (e) => {
clWsClt.webSocketClient.on('disconnect', (reason) => {
if (clWsClt && clWsClt.selectedNode && clWsClt.selectedNode.ln_implementation === 'CLN') {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Web socket disconnected, will reconnect again...' });
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Web socket disconnected, will reconnect again...', data: reason });
clWsClt.webSocketClient.close();
if (clWsClt.reConnect) { this.reconnet(clWsClt); }
}
};
});
clWsClt.webSocketClient.onmessage = (msg) => {
clWsClt.webSocketClient.on('message', (msg) => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'DEBUG', fileName: 'CLWebSocket', msg: 'Received message from the server..', data: msg.data });
msg = (typeof msg.data === 'string') ? JSON.parse(msg.data) : msg.data;
msg['source'] = 'CLN';
const msgStr = JSON.stringify(msg);
this.wsServer.sendEventsToAllLNClients(msgStr, clWsClt.selectedNode);
};
this.wsServer.sendEventsToAllLNClients(JSON.stringify({ source: 'CLN', data: msg }), clWsClt.selectedNode);
});
clWsClt.webSocketClient.onerror = (err) => {
clWsClt.webSocketClient.on('error', (err) => {
this.logger.log({ selectedNode: clWsClt.selectedNode, level: 'ERROR', fileName: 'CLWebSocket', msg: 'Web socket error', error: err });
const errStr = ((typeof err === 'object' && err.message) ? JSON.stringify({ error: err.message }) : (typeof err === 'object') ? JSON.stringify({ error: err }) : ('{ "error": ' + err + ' }'));
this.wsServer.sendErrorToAllLNClients(errStr, clWsClt.selectedNode);
clWsClt.webSocketClient.close();
if (clWsClt.reConnect) { this.reconnet(clWsClt); }
};
});
};
public disconnect = (selectedNode: CommonSelectedNode) => {
const clientExists = this.webSocketClients.find((wsc) => wsc.selectedNode.index === selectedNode.index);
if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.readyState === WebSocket.OPEN) {
if (clientExists && clientExists.webSocketClient && clientExists.webSocketClient.connected) {
this.logger.log({ selectedNode: clientExists.selectedNode, level: 'INFO', fileName: 'CLWebSocket', msg: 'Disconnecting from the Core Lightning\'s Websocket Server..' });
clientExists.reConnect = false;
clientExists.webSocketClient.close();

@ -5,6 +5,8 @@ export class CommonSelectedNode {
public ln_server_url?: string,
public macaroon_path?: string,
public macaroon_value?: string,
public rune_path?: string,
public rune_value?: string,
public ln_api_password?: string,
public swap_server_url?: string,
public boltz_server_url?: string,

@ -8,6 +8,6 @@ const router = Router();
router.get('/getRoute/:destPubkey/:amount', isAuthenticated, getRoute);
router.get('/listChannels/:channelShortId', isAuthenticated, listChannels);
router.get('/feeRates/:feeRateStyle', isAuthenticated, feeRates);
router.get('/listNodes/:id', isAuthenticated, listNodes);
router.get('/listNodes', isAuthenticated, listNodes);
export default router;

@ -97,10 +97,10 @@ export class CommonService {
switch (req.session.selectedNode.ln_implementation.toUpperCase()) {
case 'CLN':
try {
if (!req.session.selectedNode.macaroon_value) {
req.session.selectedNode.macaroon_value = this.getMacaroonValue(req.session.selectedNode.macaroon_path);
if (!req.session.selectedNode.rune_value) {
req.session.selectedNode.rune_value = this.getRuneValue(req.session.selectedNode.rune_path);
}
req.session.selectedNode.options.headers = { rune: req.session.selectedNode.macaroon_value };
req.session.selectedNode.options.headers = { rune: req.session.selectedNode.rune_value };
} catch (err) {
throw new Error(err);
}
@ -131,8 +131,8 @@ export class CommonService {
}
};
public getMacaroonValue = (macaroon_path) => {
const data = fs.readFileSync(macaroon_path, 'utf8');
public getRuneValue = (rune_path) => {
const data = fs.readFileSync(rune_path, 'utf8');
const pattern = /LIGHTNING_RUNE="(?<runeValue>[^"]+)"/;
const match = data.match(pattern);
if (match.groups.runeValue) {
@ -157,10 +157,10 @@ export class CommonService {
switch (node.ln_implementation.toUpperCase()) {
case 'CLN':
try {
if (!node.macaroon_value) {
node.macaroon_value = this.getMacaroonValue(node.macaroon_path);
if (!node.rune_value) {
node.rune_value = this.getRuneValue(node.rune_path);
}
node.options.headers = { rune: node.macaroon_value };
node.options.headers = { rune: node.rune_value };
} catch (err) {
throw new Error(err);
}

@ -150,22 +150,36 @@ export class ConfigService {
this.common.nodes[idx].ln_node = node.lnNode;
this.common.nodes[idx].ln_implementation = (process?.env?.LN_IMPLEMENTATION) ? process?.env?.LN_IMPLEMENTATION : node.lnImplementation ? node.lnImplementation : 'LND';
if (this.common.nodes[idx].ln_implementation === 'CLT') { this.common.nodes[idx].ln_implementation = 'CLN'; }
if (this.common.nodes[idx].ln_implementation !== 'ECL' && process?.env?.MACAROON_PATH && process?.env?.MACAROON_PATH.trim() !== '') {
this.common.nodes[idx].macaroon_path = process?.env?.MACAROON_PATH;
} else if (this.common.nodes[idx].ln_implementation !== 'ECL' && node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') {
this.common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
} else if (this.common.nodes[idx].ln_implementation !== 'ECL') {
this.errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Config.json!';
}
switch (this.common.nodes[idx].ln_implementation) {
case 'CLN':
if (process?.env?.RUNE_PATH && process?.env?.RUNE_PATH.trim() !== '') {
this.common.nodes[idx].rune_path = process?.env?.RUNE_PATH;
} else if (node.Authentication && node.Authentication.runePath && node.Authentication.runePath.trim() !== '') {
this.common.nodes[idx].rune_path = node.Authentication.runePath;
} else {
this.errMsg = 'Please set rune path for node index ' + node.index + ' in RTL-Config.json!';
}
break;
if (this.common.nodes[idx].ln_implementation === 'ECL') {
if (process?.env?.LN_API_PASSWORD) {
this.common.nodes[idx].ln_api_password = process?.env?.LN_API_PASSWORD;
} else if (node.Authentication && node.Authentication.lnApiPassword) {
this.common.nodes[idx].ln_api_password = node.Authentication.lnApiPassword;
} else {
this.common.nodes[idx].ln_api_password = '';
}
case 'ECL':
if (process?.env?.LN_API_PASSWORD) {
this.common.nodes[idx].ln_api_password = process?.env?.LN_API_PASSWORD;
} else if (node.Authentication && node.Authentication.lnApiPassword) {
this.common.nodes[idx].ln_api_password = node.Authentication.lnApiPassword;
} else {
this.common.nodes[idx].ln_api_password = '';
}
break;
default:
if (process?.env?.MACAROON_PATH && process?.env?.MACAROON_PATH.trim() !== '') {
this.common.nodes[idx].macaroon_path = process?.env?.MACAROON_PATH;
} else if (node.Authentication && node.Authentication.macaroonPath && node.Authentication.macaroonPath.trim() !== '') {
this.common.nodes[idx].macaroon_path = node.Authentication.macaroonPath;
} else {
this.errMsg = 'Please set macaroon path for node index ' + node.index + ' in RTL-Config.json!';
}
break;
}
if (process?.env?.CONFIG_PATH) {
this.common.nodes[idx].config_path = process?.env?.CONFIG_PATH;

@ -85,7 +85,7 @@ export const fetchInvoices = createAction(CLNActions.FETCH_INVOICES_CLN, props<{
export const setInvoices = createAction(CLNActions.SET_INVOICES_CLN, props<{ payload: ListInvoices }>());
export const saveNewInvoice = createAction(CLNActions.SAVE_NEW_INVOICE_CLN, props<{ payload: { amount: number, label: string, description: string, expiry: number, private: boolean } }>());
export const saveNewInvoice = createAction(CLNActions.SAVE_NEW_INVOICE_CLN, props<{ payload: { amount_msat: number, label: string, description: string, expiry: number, exposeprivatechannels: boolean } }>());
export const addInvoice = createAction(CLNActions.ADD_INVOICE_CLN, props<{ payload: Invoice }>());

@ -58,23 +58,9 @@ export class CLNEffects implements OnDestroy {
takeUntil(this.unSubs[1])).
subscribe((newMessage) => {
this.logger.info('Received new message from the service: ' + JSON.stringify(newMessage));
if (newMessage) {
switch (newMessage.event) {
case CLNWSEventTypeEnum.INVOICE:
this.logger.info(newMessage);
if (newMessage && newMessage.data && newMessage.data.label) {
this.store.dispatch(updateInvoice({ payload: newMessage.data }));
}
break;
case CLNWSEventTypeEnum.SEND_PAYMENT:
this.logger.info(newMessage);
break;
case CLNWSEventTypeEnum.BLOCK_HEIGHT:
this.logger.info(newMessage);
break;
default:
this.logger.info('Received Event from WS: ' + JSON.stringify(newMessage));
break;
if (newMessage && newMessage.data) {
if (newMessage.data[CLNWSEventTypeEnum.INVOICE_PAYMENT] && newMessage.data.label) {
this.store.dispatch(updateInvoice({ payload: newMessage.data }));
}
}
});
@ -692,18 +678,16 @@ export class CLNEffects implements OnDestroy {
saveNewInvoiceCL = createEffect(() => this.actions.pipe(
ofType(CLNActions.SAVE_NEW_INVOICE_CLN),
mergeMap((action: { type: string, payload: { amount: number, label: string, description: string, expiry: number, private: boolean } }) => {
mergeMap((action: { type: string, payload: { amount_msat: number, label: string, description: string, expiry: number, exposeprivatechannels: boolean } }) => {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.ADD_INVOICE }));
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewInvoice', status: APICallStatusEnum.INITIATED } }));
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.INVOICES_API, {
label: action.payload.label, amount: action.payload.amount, description: action.payload.description, expiry: action.payload.expiry, private: action.payload.private
}).
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.INVOICES_API, action.payload).
pipe(
map((postRes: Invoice) => {
this.logger.info(postRes);
this.store.dispatch(updateCLNAPICallStatus({ payload: { action: 'SaveNewInvoice', status: APICallStatusEnum.COMPLETED } }));
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.ADD_INVOICE }));
postRes.amount_msat = action.payload.amount;
postRes.amount_msat = action.payload.amount_msat;
postRes.label = action.payload.label;
postRes.expires_at = Math.round((new Date().getTime() / 1000) + action.payload.expiry);
postRes.description = action.payload.description;

@ -77,7 +77,7 @@ export class CLNCreateInvoiceComponent implements OnInit, OnDestroy {
}
this.store.dispatch(saveNewInvoice({
payload: {
label: ('ulbl' + Math.random().toString(36).slice(2) + Date.now()), amount: this.invoiceValue * 1000, description: this.description, expiry: expiryInSecs, private: this.private
label: ('ulbl' + Math.random().toString(36).slice(2) + Date.now()), amount_msat: this.invoiceValue * 1000, description: this.description, expiry: expiryInSecs, exposeprivatechannels: this.private
}
}));
}

@ -49,8 +49,8 @@
<div *ngIf="!flgInvoicePaid">{{invoice?.amount_received_msat/1000 | number}} Sats</div>
</ng-container>
<ng-container *ngIf="invoice?.status !== 'paid'">
<span *ngIf="invoice?.status !== 'unpaid' || !flgVersionCompatible">-</span>
<mat-spinner *ngIf="invoice?.status === 'unpaid' && flgVersionCompatible" [diameter]="20" />
<span *ngIf="invoice?.status !== 'unpaid'">-</span>
<mat-spinner *ngIf="invoice?.status === 'unpaid'" [diameter]="20" />
</ng-container>
</span>
</div>

@ -11,9 +11,9 @@ import { CommonService } from '../../../../shared/services/common.service';
import { CLNInvoiceInformation } from '../../../../shared/models/alertData';
import { ScreenSizeEnum } from '../../../../shared/services/consts-enums-functions';
import { GetInfo, Invoice, ListInvoices } from '../../../../shared/models/clnModels';
import { Invoice, ListInvoices } from '../../../../shared/models/clnModels';
import { RTLState } from '../../../../store/rtl.state';
import { clnNodeInformation, listInvoices } from '../../../store/cln.selector';
import { listInvoices } from '../../../store/cln.selector';
import { ApiCallStatusPayload } from '../../../../shared/models/apiCallsPayload';
@Component({

@ -152,7 +152,7 @@ export class CLNLightningInvoicesTableComponent implements OnInit, AfterViewInit
this.newlyAddedInvoiceValue = this.invoiceValue;
this.store.dispatch(saveNewInvoice({
payload: {
label: this.newlyAddedInvoiceMemo, amount: this.invoiceValue * 1000, description: this.description, expiry: expiryInSecs, private: this.private
label: this.newlyAddedInvoiceMemo, amount_msat: this.invoiceValue * 1000, description: this.description, expiry: expiryInSecs, exposeprivatechannels: this.private
}
}));
this.resetData();

@ -109,9 +109,20 @@ export enum ECLWSEventTypeEnum {
}
export enum CLNWSEventTypeEnum {
INVOICE = 'invoice',
BLOCK_HEIGHT = 'block-height',
SEND_PAYMENT = 'send-payment'
CONNECT = 'connect',
DISCONNECT = 'disconnect',
WARNING = 'warning',
INVOICE_PAYMENT = 'invoice_payment',
INVOICE_CREATION = 'invoice_creation',
CHANNEL_OPENED = 'channel_opened',
CHANNEL_STATE_CHANGED = 'channel_state_changed',
SENDPAY_SUCCESS = 'sendpay_success',
SENDPAY_FAILURE = 'sendpay_failure',
COIN_MOVEMENT = 'coin_movement',
BALANCE_SNAPSHOT = 'balance_snapshot',
BLOCK_ADDED = 'block_added',
OPENCHANNEL_PEER_SIGS = 'openchannel_peer_sigs',
CHANNEL_OPEN_FAILED = 'channel_open_failed'
}
export enum LNDWSEventTypeEnum {

Loading…
Cancel
Save