From 4c9e4c9b2d011696e196ccb456d43d4085a0c18d Mon Sep 17 00:00:00 2001 From: nitesh Date: Fri, 7 Jun 2024 11:05:42 -0400 Subject: [PATCH 01/17] initial working of the new plugin --- backends/CLightningREST.ts | 3 +- backends/CoreLightningRestApi.ts | 342 +++++++++++++++++++++++++++ backends/LND.ts | 3 + locales/en.json | 3 +- stores/SettingsStore.ts | 21 +- utils/BackendUtils.ts | 5 + views/Settings/NodeConfiguration.tsx | 103 +++++++- 7 files changed, 463 insertions(+), 17 deletions(-) create mode 100644 backends/CoreLightningRestApi.ts diff --git a/backends/CLightningREST.ts b/backends/CLightningREST.ts index eace30738..d834d9a05 100644 --- a/backends/CLightningREST.ts +++ b/backends/CLightningREST.ts @@ -10,8 +10,7 @@ import BigNumber from 'bignumber.js'; export default class CLightningREST extends LND { getHeaders = (macaroonHex: string): any => { return { - macaroon: macaroonHex, - encodingtype: 'hex' + macaroon: macaroonHex }; }; diff --git a/backends/CoreLightningRestApi.ts b/backends/CoreLightningRestApi.ts new file mode 100644 index 000000000..99233fd2e --- /dev/null +++ b/backends/CoreLightningRestApi.ts @@ -0,0 +1,342 @@ +import stores from '../stores/Stores'; +import LND from './LND'; +import TransactionRequest from '../models/TransactionRequest'; +import OpenChannelRequest from '../models/OpenChannelRequest'; +import VersionUtils from '../utils/VersionUtils'; +import Base64Utils from '../utils/Base64Utils'; +import { Hash as sha256Hash } from 'fast-sha256'; +import BigNumber from 'bignumber.js'; + +export default class CoreLightningRestApi extends LND { + getHeaders = (rune: string): any => { + return { + Rune: rune + }; + }; + + supports = ( + minVersion: string, + eosVersion?: string, + minApiVersion?: string + ) => { + const { nodeInfo } = stores.nodeInfoStore; + const { version, api_version } = nodeInfo; + const { isSupportedVersion } = VersionUtils; + if (minApiVersion) { + return ( + isSupportedVersion(version, minVersion, eosVersion) && + isSupportedVersion(api_version, minApiVersion) + ); + } + return isSupportedVersion(version, minVersion, eosVersion); + }; + + request = (route: string, method: string, data?: any, params?: any) => { + const { host, port, rune, certVerification, enableTor } = + stores.settingsStore; + + if (params) { + route = `${route}?${Object.keys(params) + .map((key: string) => key + '=' + params[key]) + .join('&')}`; + } + + const headers: any = this.getHeaders(rune); + headers['Content-Type'] = 'application/json'; + + const url = this.getURL(host, port, route); + + return this.restReq( + headers, + url, + method, + data, + certVerification, + enableTor + ); + }; + + getRequest = (route: string, data?: any) => + this.request(route, 'get', null, data); + postRequest = (route: string, data?: any) => + this.request(route, 'post', data); + deleteRequest = (route: string) => this.request(route, 'delete', null); + + getTransactions = () => + this.getRequest('/v1/listFunds').then((data: any) => ({ + transactions: data.outputs + })); + getChannels = () => + this.postRequest('/v1/listpeers').then((data: any) => { + const formattedChannels: any[] = []; + data.filter((peer: any) => peer.channels.length).map( + (peer: any) => { + peer.channels.forEach((channel: any) => { + if ( + channel.state === 'ONCHAIN' || + channel.state === 'CLOSED' || + channel.state === 'CHANNELD_AWAITING_LOCKIN' + ) + return; + + // CLN v23.05 msat deprecations + const to_us_msat = + channel.to_us || + channel.to_us_msat || + channel.msatoshi_to_us || + 0; + const total_msat = + channel.total || + channel.total_msat || + channel.msatoshi_total || + 0; + const out_fulfilled_msat = + channel.out_fulfilled || + channel.out_fulfilled_msat || + channel.out_msatoshi_fulfilled || + 0; + const in_fulfilled_msat = + channel.in_fulfilled || + channel.in_fulfilled_msat || + channel.in_msatoshi_fulfilled || + 0; + const our_reserve_msat = + channel.our_reserve || + channel.our_reserve_msat || + channel.our_channel_reserve_satoshis || + 0; + const their_reserve_msat = + channel.their_reserve || + channel.their_reserve_msat || + channel.their_channel_reserve_satoshi || + 0; + + formattedChannels.push({ + active: peer.connected, + remote_pubkey: peer.id, + channel_point: channel.funding_txid, + chan_id: channel.channel_id, + alias: peer.alias, + capacity: Number(total_msat / 1000).toString(), + local_balance: Number(to_us_msat / 1000).toString(), + remote_balance: Number( + (total_msat - to_us_msat) / 1000 + ).toString(), + total_satoshis_sent: Number( + out_fulfilled_msat / 1000 + ).toString(), + total_satoshis_received: Number( + in_fulfilled_msat / 1000 + ).toString(), + num_updates: ( + channel.in_payments_offered + + channel.out_payments_offered + ).toString(), + csv_delay: channel.our_to_self_delay, + private: channel.private, + local_chan_reserve_sat: Number( + our_reserve_msat / 1000 + ).toString(), + remote_chan_reserve_sat: Number( + their_reserve_msat / 1000 + ).toString(), + close_address: channel.close_to_addr + }); + }); + } + ); + + return { + channels: formattedChannels + }; + }); + getBlockchainBalance = () => + this.getRequest('/v1/getBalance').then( + ({ totalBalance, confBalance, unconfBalance }: any) => ({ + total_balance: totalBalance, + confirmed_balance: confBalance, + unconfirmed_balance: unconfBalance + }) + ); + getLightningBalance = () => + this.getRequest('/v1/channel/localremotebal').then( + ({ localBalance, pendingBalance }: any) => ({ + balance: localBalance, + pending_open_balance: pendingBalance + }) + ); + sendCoins = (data: TransactionRequest) => { + let request: any; + if (data.utxos) { + request = { + address: data.addr, + feeRate: `${Number(data.sat_per_vbyte) * 1000}perkb`, + satoshis: data.amount, + utxos: data.utxos + }; + } else { + request = { + address: data.addr, + feeRate: `${Number(data.sat_per_vbyte) * 1000}perkb`, + satoshis: data.amount + }; + } + return this.postRequest('/v1/withdraw', request); + }; + getMyNodeInfo = () => this.postRequest('/v1/getinfo'); + getInvoices = () => this.getRequest('/v1/invoice/listInvoices/'); + createInvoice = (data: any) => + this.postRequest('/v1/invoice/genInvoice/', { + description: data.memo, + label: 'zeus.' + Math.random() * 1000000, + amount: Number(data.value) * 1000, + expiry: Number(data.expiry), + private: true + }); + getPayments = () => + this.getRequest('/v1/pay/listPays').then((data: any) => ({ + payments: data.pays + })); + getNewAddress = () => this.getRequest('/v1/newaddr?addrType=bech32'); + openChannelSync = (data: OpenChannelRequest) => { + let request: any; + const feeRate = `${new BigNumber(data.sat_per_vbyte) + .times(1000) + .toString()}perkb`; + if (data.utxos && data.utxos.length > 0) { + request = { + id: data.id, + satoshis: data.satoshis, + feeRate, + announce: !data.privateChannel ? 'true' : 'false', + minfConf: data.min_confs, + utxos: data.utxos + }; + } else { + request = { + id: data.id, + satoshis: data.satoshis, + feeRate, + announce: !data.privateChannel ? 'true' : 'false', + minfConf: data.min_confs + }; + } + + return this.postRequest('/v1/channel/openChannel/', request); + }; + connectPeer = (data: any) => + this.postRequest('/v1/peer/connect', { + id: `${data.addr.pubkey}@${data.addr.host}` + }); + decodePaymentRequest = (urlParams?: Array) => + this.getRequest(`/v1/utility/decode/${urlParams && urlParams[0]}`); + payLightningInvoice = (data: any) => + this.postRequest('/v1/pay', { + invoice: data.payment_request, + amount: Number(data.amt && data.amt * 1000), + maxfeepercent: data.max_fee_percent + }); + sendKeysend = (data: any) => + this.postRequest('/v1/pay/keysend', { + pubkey: data.pubkey, + amount: Number(data.amt && data.amt * 1000), + maxfeepercent: data.max_fee_percent + }); + closeChannel = (urlParams?: Array) => + this.deleteRequest( + `/v1/channel/closeChannel/${urlParams && urlParams[0]}/` + ); + getNodeInfo = () => this.getRequest('N/A'); + getFees = () => + this.getRequest('/v1/getFees/').then(({ feeCollected }: any) => ({ + total_fee_sum: feeCollected / 1000 + })); + setFees = (data: any) => + this.postRequest('/v1/channel/setChannelFee/', { + id: data.global ? 'all' : data.channelId, + base: data.base_fee_msat, + ppm: data.fee_rate + }); + getRoutes = () => this.getRequest('N/A'); + getUTXOs = () => this.getRequest('/v1/listFunds'); + signMessage = (message: string) => + this.postRequest('/v1/utility/signMessage', { + message + }); + verifyMessage = (data: any) => + this.getRequest( + `/v1/utility/checkMessage/${data.msg}/${data.signature}` + ); + lnurlAuth = async (r_hash: string) => { + const signed = await this.signMessage(r_hash); + return { + signature: new sha256Hash() + .update(Base64Utils.stringToUint8Array(signed.signature)) + .digest() + }; + }; + + // BOLT 12 / Offers + listOffers = () => this.getRequest('/v1/offers/listOffers'); + createOffer = ({ + description, + label, + singleUse + }: { + description?: string; + label?: string; + singleUse?: boolean; + }) => + this.postRequest('/v1/offers/offer', { + amount: 'any', + description, + label, + single_use: singleUse || false + }); + disableOffer = ({ offer_id }: { offer_id: string }) => + this.deleteRequest(`/v1/offers/disableOffer/${offer_id}`); + fetchInvoiceFromOffer = async (bolt12: string, amountSatoshis: string) => { + return await this.postRequest('/v1/offers/fetchInvoice', { + offer: bolt12, + msatoshi: Number(amountSatoshis) * 1000, + timeout: 60 + }); + }; + + supportsMessageSigning = () => true; + supportsLnurlAuth = () => true; + supportsOnchainSends = () => true; + supportsOnchainReceiving = () => true; + supportsLightningSends = () => true; + supportsKeysend = () => true; + supportsChannelManagement = () => true; + supportsPendingChannels = () => false; + supportsMPP = () => false; + supportsAMP = () => false; + supportsCoinControl = () => this.supports('v0.8.2', undefined, 'v0.4.0'); + supportsChannelCoinControl = () => + this.supports('v0.8.2', undefined, 'v0.4.0'); + supportsHopPicking = () => false; + supportsAccounts = () => false; + supportsRouting = () => true; + supportsNodeInfo = () => true; + singleFeesEarnedTotal = () => true; + supportsAddressTypeSelection = () => false; + supportsTaproot = () => false; + supportsBumpFee = () => false; + supportsLSPs = () => false; + supportsNetworkInfo = () => false; + supportsSimpleTaprootChannels = () => false; + supportsCustomPreimages = () => false; + supportsSweep = () => true; + supportsOnchainBatching = () => false; + supportsChannelBatching = () => false; + supportsLSPS1customMessage = () => false; + supportsLSPS1rest = () => true; + supportsOffers = async () => { + const res = await this.getRequest('/v1/utility/listConfigs'); + const supportsOffers: boolean = res['experimental-offers'] || false; + return supportsOffers; + }; + isLNDBased = () => false; +} diff --git a/backends/LND.ts b/backends/LND.ts index b0a0a58c5..79742cd6d 100644 --- a/backends/LND.ts +++ b/backends/LND.ts @@ -99,6 +99,9 @@ export default class LND { } } }) + .catch((e) => { + console.error('Fetch threw an error', e); + }) ); } diff --git a/locales/en.json b/locales/en.json index 07cc9a1a9..9da9a055d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -234,6 +234,7 @@ "views.Settings.AddEditNode.accessKey": "Access Key", "views.Settings.AddEditNode.restPort": "REST Port", "views.Settings.AddEditNode.macaroon": "Macaroon (Hex format)", + "views.Settings.AddEditNode.rune": "Rune", "views.Settings.AddEditNode.certificateVerification": "Certificate Verification", "views.Settings.AddEditNode.createLndhub": "Create LNDHub account", "views.Settings.AddEditNode.saveNode": "Save Node Config", @@ -1202,4 +1203,4 @@ "views.PendingHTLCs.expirationHeight": "Expiration height", "views.PendingHTLCs.recommendationIOS": "It's recommended to leave ZEUS running while there are pending HTLCs to prevent force closes.", "views.PendingHTLCs.recommendationAndroid": "It's recommended to enable Persistent LND or leave ZEUS running while there are pending HTLCs to prevent force closes." -} \ No newline at end of file +} diff --git a/stores/SettingsStore.ts b/stores/SettingsStore.ts index 187e1d53f..1c7d4c9e2 100644 --- a/stores/SettingsStore.ts +++ b/stores/SettingsStore.ts @@ -16,6 +16,7 @@ export interface Node { port?: string; url?: string; macaroonHex?: string; + rune?: string; accessKey?: string; implementation?: string; certVerification?: boolean; @@ -205,12 +206,26 @@ export const INTERFACE_KEYS = [ { key: 'Embedded LND', value: 'embedded-lnd' }, { key: 'LND (REST)', value: 'lnd' }, { key: 'LND (Lightning Node Connect)', value: 'lightning-node-connect' }, - { key: 'Core Lightning (c-lightning-REST)', value: 'c-lightning-REST' }, + { key: 'Core Lightning REST API', value: 'core-lightning-rest-api' }, { key: 'LNDHub', value: 'lndhub' }, + { + key: '[DEPRECATED] Core Lightning (c-lightning-REST)', + value: 'c-lightning-REST' + }, { key: '[DEPRECATED] Core Lightning (Sparko)', value: 'spark' }, { key: '[DEPRECATED] Eclair', value: 'eclair' } ]; +export type Implementations = + | 'embedded-lnd' + | 'lnd' + | 'lightning-node-connect' + | 'core-lightning-rest-api' + | 'lndhub' + | 'c-lightning-REST' + | 'spark' + | 'eclair'; + export const EMBEDDED_NODE_NETWORK_KEYS = [ { key: 'Mainnet', translateKey: 'network.mainnet', value: 'mainnet' }, { key: 'Testnet', translateKey: 'network.testnet', value: 'testnet' } @@ -1105,8 +1120,9 @@ export default class SettingsStore { @observable port: string; @observable url: string; @observable macaroonHex: string; + @observable rune: string; @observable accessKey: string; - @observable implementation: string; + @observable implementation: Implementations; @observable certVerification: boolean | undefined; @observable public loggedIn = false; @observable public connecting = true; @@ -1450,6 +1466,7 @@ export default class SettingsStore { this.password = node.password; this.lndhubUrl = node.lndhubUrl; this.macaroonHex = node.macaroonHex; + this.rune = node.rune; this.accessKey = node.accessKey; this.implementation = node.implementation || 'lnd'; this.certVerification = node.certVerification || false; diff --git a/utils/BackendUtils.ts b/utils/BackendUtils.ts index dae5ea476..1a26eb44f 100644 --- a/utils/BackendUtils.ts +++ b/utils/BackendUtils.ts @@ -5,6 +5,7 @@ import LightningNodeConnect from '../backends/LightningNodeConnect'; import EmbeddedLND from '../backends/EmbeddedLND'; // Core Lightning import CLightningREST from '../backends/CLightningREST'; +import CoreLightningRestApi from '../backends/CoreLightningRestApi'; import Spark from '../backends/Spark'; // Eclair import Eclair from '../backends/Eclair'; @@ -16,6 +17,7 @@ class BackendUtils { lightningNodeConnect: LightningNodeConnect; embeddedLND: EmbeddedLND; clightningREST: CLightningREST; + coreLightningRestApi: CoreLightningRestApi; spark: Spark; eclair: Eclair; lndHub: LndHub; @@ -24,6 +26,7 @@ class BackendUtils { this.lightningNodeConnect = new LightningNodeConnect(); this.embeddedLND = new EmbeddedLND(); this.clightningREST = new CLightningREST(); + this.coreLightningRestApi = new CoreLightningRestApi(); this.spark = new Spark(); this.eclair = new Eclair(); this.lndHub = new LndHub(); @@ -40,6 +43,8 @@ class BackendUtils { return this.embeddedLND; case 'c-lightning-REST': return this.clightningREST; + case 'core-lightning-rest-api': + return this.coreLightningRestApi; case 'spark': return this.spark; case 'eclair': diff --git a/views/Settings/NodeConfiguration.tsx b/views/Settings/NodeConfiguration.tsx index d8ac02cfe..99ad94a83 100644 --- a/views/Settings/NodeConfiguration.tsx +++ b/views/Settings/NodeConfiguration.tsx @@ -44,7 +44,8 @@ import SettingsStore, { LNC_MAILBOX_KEYS, EMBEDDED_NODE_NETWORK_KEYS, Settings, - Node + Node, + Implementations } from '../../stores/SettingsStore'; import Scan from '../../assets/images/SVG/Scan.svg'; @@ -79,13 +80,14 @@ interface NodeConfigurationState { host: string; // lnd, c-lightning-REST port: string; // lnd, c-lightning-REST macaroonHex: string; // lnd, c-lightning-REST + rune: string; // c-lightning-REST url: string; // spark, eclair accessKey: string; // spark lndhubUrl: string; // lndhub username: string | undefined; // lndhub password: string | undefined; // lndhub, eclair existingAccount: boolean; // lndhub - implementation: string; + implementation: Implementations; certVerification: boolean; saved: boolean; active: boolean; @@ -133,6 +135,7 @@ export default class NodeConfiguration extends React.Component< host: '', port: '', macaroonHex: '', + rune: '', saved: false, index: null as number | null, active: false, @@ -309,6 +312,7 @@ export default class NodeConfiguration extends React.Component< host, port, macaroonHex, + rune, url, lndhubUrl, existingAccount, @@ -336,6 +340,7 @@ export default class NodeConfiguration extends React.Component< host, port, macaroonHex, + rune, url, lndhubUrl, existingAccount, @@ -381,6 +386,7 @@ export default class NodeConfiguration extends React.Component< lndhubUrl, existingAccount, macaroonHex, + rune, accessKey, username, password, @@ -413,6 +419,7 @@ export default class NodeConfiguration extends React.Component< lndhubUrl, existingAccount, macaroonHex, + rune, accessKey, username, password, @@ -493,6 +500,7 @@ export default class NodeConfiguration extends React.Component< lndhubUrl, existingAccount, macaroonHex, + rune, accessKey, username, password, @@ -513,6 +521,7 @@ export default class NodeConfiguration extends React.Component< lndhubUrl, existingAccount, macaroonHex, + rune, accessKey, username, password, @@ -640,6 +649,7 @@ export default class NodeConfiguration extends React.Component< url, lndhubUrl, macaroonHex, + rune, accessKey, username, password, @@ -705,7 +715,7 @@ export default class NodeConfiguration extends React.Component< { + onValueChange={(value: Implementations) => { this.setState({ implementation: value, saved: false, @@ -1347,8 +1357,7 @@ export default class NodeConfiguration extends React.Component< )} )} - {(implementation === 'lnd' || - implementation === 'c-lightning-REST') && ( + {implementation === 'core-lightning-rest-api' && ( <> {localeString( - 'views.Settings.AddEditNode.macaroon' + 'views.Settings.AddEditNode.rune' )} this.setState({ - macaroonHex: text.replace( - /\s+/g, - '' - ), + rune: text.trim(), saved: false }) } @@ -1420,6 +1426,79 @@ export default class NodeConfiguration extends React.Component< )} + {implementation === 'lnd' || + (implementation === 'c-lightning-REST' && ( + <> + + {localeString( + 'views.Settings.AddEditNode.host' + )} + + + this.setState({ + host: text.trim(), + saved: false + }) + } + locked={loading} + /> + + + {localeString( + 'views.Settings.AddEditNode.macaroon' + )} + + + this.setState({ + macaroonHex: text.replace( + /\s+/g, + '' + ), + saved: false + }) + } + locked={loading} + /> + + + {localeString( + 'views.Settings.AddEditNode.restPort' + )} + + + this.setState({ + port: text.trim(), + saved: false + }) + } + locked={loading} + /> + + ))} + {implementation === 'lightning-node-connect' && ( <> From c6ea92cc3eceeb40dac7fb0b4febf2b806a1618b Mon Sep 17 00:00:00 2001 From: nitesh Date: Sat, 8 Jun 2024 11:18:32 -0400 Subject: [PATCH 02/17] add payments, invoices, receiving onchain, offchain, txs and balances --- backends/CoreLightningRequestHandler.ts | 130 +++++++++++++++++++++++ backends/CoreLightningRestApi.ts | 135 +++++------------------- 2 files changed, 159 insertions(+), 106 deletions(-) create mode 100644 backends/CoreLightningRequestHandler.ts diff --git a/backends/CoreLightningRequestHandler.ts b/backends/CoreLightningRequestHandler.ts new file mode 100644 index 000000000..fd5ba2541 --- /dev/null +++ b/backends/CoreLightningRequestHandler.ts @@ -0,0 +1,130 @@ +import CoreLightningRestApi from './CoreLightningRestApi'; + +const api = new CoreLightningRestApi(); + +// Returns onchain balance of core-lightning node +export const getBalance = (data: any) => { + const opArray = data.outputs; + let confBalance = 0; + let unconfBalance = 0; + let totalBalance = 0; + + for (let i = 0; i < opArray.length; i++) { + if (opArray[i].status === 'confirmed') + confBalance = confBalance + opArray[i].amount_msat / 1000; + else if (opArray[i].status === 'unconfirmed') + unconfBalance = unconfBalance + opArray[i].amount_msat / 1000; + } + totalBalance = confBalance + unconfBalance; + return { + total_balance: totalBalance, + confirmed_balance: confBalance, + unconfirmed_balance: unconfBalance + }; +}; + +// Returns offchain balances of core-lightning node +export const getOffchainBalance = (data: any) => { + const chanArray = data.channels; + let localBalance = 0; + let remoteBalance = 0; + let pendingBalance = 0; + let inactiveBalance = 0; + + for (let i = 0; i < chanArray.length; i++) { + if ( + chanArray[i].state === 'CHANNELD_NORMAL' && + chanArray[i].connected === true + ) { + localBalance = localBalance + chanArray[i].our_amount_msat; + + remoteBalance = + remoteBalance + + chanArray[i].amount_msat - + chanArray[i].our_amount_msat; + } else if ( + chanArray[i].state === 'CHANNELD_NORMAL' && + chanArray[i].connected === false + ) { + inactiveBalance = inactiveBalance + chanArray[i].our_amount_msat; + } else if ( + chanArray[i].state === 'CHANNELD_AWAITING_LOCKIN' || + chanArray[i].state === 'DUALOPEND_AWAITING_LOCKIN' + ) { + pendingBalance = pendingBalance + chanArray[i].our_amount_msat; + } + } + + localBalance = localBalance / 1000; + remoteBalance = remoteBalance / 1000; + inactiveBalance = inactiveBalance / 1000; + pendingBalance = pendingBalance / 1000; + + return { + balance: localBalance, + remote_balance: remoteBalance, + inactive_balance: inactiveBalance, + pending_balance: pendingBalance + }; +}; + +// Get your peers and the channel info for core lightning node +export const listPeers = async (data: any) => { + const formattedChannels = data.channels + .map((peer: any) => { + if ( + peer.state === 'ONCHAIN' || + peer.state === 'CLOSED' || + peer.state === 'CHANNELD_AWAITING_LOCKIN' + ) { + return; + } + + return { + active: peer.peer_connected, + remote_pubkey: peer.peer_id, + channel_point: peer.funding_txid, + chan_id: peer.channel_id, + capacity: Number(peer.total_msat / 1000).toString(), + local_balance: Number(peer.to_us_msat / 1000).toString(), + remote_balance: Number( + (peer.total_msat - peer.to_us_msat) / 1000 + ).toString(), + total_satoshis_sent: Number( + peer.out_fulfilled_msat / 1000 + ).toString(), + total_satoshis_received: Number( + peer.in_fulfilled_msat / 1000 + ).toString(), + num_updates: ( + peer.in_payments_offered + peer.out_payments_offered + ).toString(), + csv_delay: peer.our_to_self_delay, + private: peer.private, + local_chan_reserve_sat: Number( + peer.our_reserve_msat / 1000 + ).toString(), + remote_chan_reserve_sat: Number( + peer.their_reserve_msat / 1000 + ).toString(), + close_address: peer.close_to_addr + }; + }) + .filter((n: any) => !!n); + + const channelsWithAliases = await Promise.all( + formattedChannels.map(async (n: any) => { + const { nodes } = await api.getNode({ id: n.remote_pubkey }); + + if (nodes.length) { + n.alias = nodes[0].alias || ''; + } else { + n.alias = ''; + } + + return n; + }) + ); + + return { channels: channelsWithAliases }; +}; diff --git a/backends/CoreLightningRestApi.ts b/backends/CoreLightningRestApi.ts index 99233fd2e..eeec102b0 100644 --- a/backends/CoreLightningRestApi.ts +++ b/backends/CoreLightningRestApi.ts @@ -6,6 +6,11 @@ import VersionUtils from '../utils/VersionUtils'; import Base64Utils from '../utils/Base64Utils'; import { Hash as sha256Hash } from 'fast-sha256'; import BigNumber from 'bignumber.js'; +import { + getBalance, + getOffchainBalance, + listPeers +} from './CoreLightningRequestHandler'; export default class CoreLightningRestApi extends LND { getHeaders = (rune: string): any => { @@ -62,109 +67,26 @@ export default class CoreLightningRestApi extends LND { this.request(route, 'post', data); deleteRequest = (route: string) => this.request(route, 'delete', null); + getNode = (data: any) => + this.postRequest('/v1/listnodes', { id: data.id }).then((res) => { + return res; + }); getTransactions = () => - this.getRequest('/v1/listFunds').then((data: any) => ({ - transactions: data.outputs + this.postRequest('/v1/listfunds').then((res) => ({ + transactions: res.outputs })); - getChannels = () => - this.postRequest('/v1/listpeers').then((data: any) => { - const formattedChannels: any[] = []; - data.filter((peer: any) => peer.channels.length).map( - (peer: any) => { - peer.channels.forEach((channel: any) => { - if ( - channel.state === 'ONCHAIN' || - channel.state === 'CLOSED' || - channel.state === 'CHANNELD_AWAITING_LOCKIN' - ) - return; - - // CLN v23.05 msat deprecations - const to_us_msat = - channel.to_us || - channel.to_us_msat || - channel.msatoshi_to_us || - 0; - const total_msat = - channel.total || - channel.total_msat || - channel.msatoshi_total || - 0; - const out_fulfilled_msat = - channel.out_fulfilled || - channel.out_fulfilled_msat || - channel.out_msatoshi_fulfilled || - 0; - const in_fulfilled_msat = - channel.in_fulfilled || - channel.in_fulfilled_msat || - channel.in_msatoshi_fulfilled || - 0; - const our_reserve_msat = - channel.our_reserve || - channel.our_reserve_msat || - channel.our_channel_reserve_satoshis || - 0; - const their_reserve_msat = - channel.their_reserve || - channel.their_reserve_msat || - channel.their_channel_reserve_satoshi || - 0; - - formattedChannels.push({ - active: peer.connected, - remote_pubkey: peer.id, - channel_point: channel.funding_txid, - chan_id: channel.channel_id, - alias: peer.alias, - capacity: Number(total_msat / 1000).toString(), - local_balance: Number(to_us_msat / 1000).toString(), - remote_balance: Number( - (total_msat - to_us_msat) / 1000 - ).toString(), - total_satoshis_sent: Number( - out_fulfilled_msat / 1000 - ).toString(), - total_satoshis_received: Number( - in_fulfilled_msat / 1000 - ).toString(), - num_updates: ( - channel.in_payments_offered + - channel.out_payments_offered - ).toString(), - csv_delay: channel.our_to_self_delay, - private: channel.private, - local_chan_reserve_sat: Number( - our_reserve_msat / 1000 - ).toString(), - remote_chan_reserve_sat: Number( - their_reserve_msat / 1000 - ).toString(), - close_address: channel.close_to_addr - }); - }); - } - ); - - return { - channels: formattedChannels - }; - }); + getChannels = async () => { + const channels = await this.postRequest('/v1/listpeerchannels'); + return await listPeers(channels); + }; getBlockchainBalance = () => - this.getRequest('/v1/getBalance').then( - ({ totalBalance, confBalance, unconfBalance }: any) => ({ - total_balance: totalBalance, - confirmed_balance: confBalance, - unconfirmed_balance: unconfBalance - }) - ); + this.postRequest('/v1/listfunds').then((res) => { + return getBalance(res); + }); getLightningBalance = () => - this.getRequest('/v1/channel/localremotebal').then( - ({ localBalance, pendingBalance }: any) => ({ - balance: localBalance, - pending_open_balance: pendingBalance - }) - ); + this.postRequest('/v1/listfunds').then((res) => { + return getOffchainBalance(res); + }); sendCoins = (data: TransactionRequest) => { let request: any; if (data.utxos) { @@ -184,20 +106,21 @@ export default class CoreLightningRestApi extends LND { return this.postRequest('/v1/withdraw', request); }; getMyNodeInfo = () => this.postRequest('/v1/getinfo'); - getInvoices = () => this.getRequest('/v1/invoice/listInvoices/'); + getInvoices = () => this.postRequest('/v1/listinvoices'); createInvoice = (data: any) => - this.postRequest('/v1/invoice/genInvoice/', { + this.postRequest('/v1/invoice', { description: data.memo, label: 'zeus.' + Math.random() * 1000000, - amount: Number(data.value) * 1000, + amount_msat: Number(data.value) * 1000, expiry: Number(data.expiry), - private: true + exposeprivatechannels: true }); + getPayments = () => - this.getRequest('/v1/pay/listPays').then((data: any) => ({ + this.postRequest('/v1/listpays').then((data: any) => ({ payments: data.pays })); - getNewAddress = () => this.getRequest('/v1/newaddr?addrType=bech32'); + getNewAddress = () => this.postRequest('/v1/newaddr'); openChannelSync = (data: OpenChannelRequest) => { let request: any; const feeRate = `${new BigNumber(data.sat_per_vbyte) @@ -258,7 +181,7 @@ export default class CoreLightningRestApi extends LND { ppm: data.fee_rate }); getRoutes = () => this.getRequest('N/A'); - getUTXOs = () => this.getRequest('/v1/listFunds'); + getUTXOs = () => this.postRequest('/v1/listfunds'); signMessage = (message: string) => this.postRequest('/v1/utility/signMessage', { message From cd4af6f9bf55b2f5e246b5252293120cc37966bc Mon Sep 17 00:00:00 2001 From: nitesh Date: Sat, 8 Jun 2024 15:35:13 -0400 Subject: [PATCH 03/17] add open channel, add peer, decode, keysend, pay, closechannel --- backends/CLightningREST.ts | 10 +++-- backends/CoreLightningRestApi.ts | 66 ++++++++++++++++++++------------ stores/ChannelsStore.ts | 13 +++++-- stores/TransactionsStore.ts | 2 + 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/backends/CLightningREST.ts b/backends/CLightningREST.ts index d834d9a05..f97eecd3b 100644 --- a/backends/CLightningREST.ts +++ b/backends/CLightningREST.ts @@ -211,10 +211,14 @@ export default class CLightningREST extends LND { amount: Number(data.amt && data.amt * 1000), maxfeepercent: data.max_fee_percent }); - closeChannel = (urlParams?: Array) => - this.deleteRequest( - `/v1/channel/closeChannel/${urlParams && urlParams[0]}/` + closeChannel = (urlParams?: Array) => { + const id = urlParams && urlParams[0]; + const unilateralTimeout = urlParams && urlParams[1] ? 2 : 0; + + return this.deleteRequest( + `/v1/channel/closeChannel/${id}?unilateralTimeout=${unilateralTimeout}` ); + }; getNodeInfo = () => this.getRequest('N/A'); getFees = () => this.getRequest('/v1/getFees/').then(({ feeCollected }: any) => ({ diff --git a/backends/CoreLightningRestApi.ts b/backends/CoreLightningRestApi.ts index eeec102b0..708508211 100644 --- a/backends/CoreLightningRestApi.ts +++ b/backends/CoreLightningRestApi.ts @@ -129,46 +129,58 @@ export default class CoreLightningRestApi extends LND { if (data.utxos && data.utxos.length > 0) { request = { id: data.id, - satoshis: data.satoshis, - feeRate, - announce: !data.privateChannel ? 'true' : 'false', - minfConf: data.min_confs, + amount: data.satoshis, + feerate: feeRate, + announce: !data.privateChannel ? true : false, + minconf: data.min_confs, utxos: data.utxos }; } else { request = { id: data.id, - satoshis: data.satoshis, - feeRate, - announce: !data.privateChannel ? 'true' : 'false', - minfConf: data.min_confs + amount: data.satoshis, + feerate: feeRate, + announce: !data.privateChannel ? true : false, + minconf: data.min_confs }; } - return this.postRequest('/v1/channel/openChannel/', request); + return this.postRequest('/v1/fundchannel', request); }; - connectPeer = (data: any) => - this.postRequest('/v1/peer/connect', { - id: `${data.addr.pubkey}@${data.addr.host}` + connectPeer = (data: any) => { + const [host, port] = data.addr.host.split(':'); + + return this.postRequest('/v1/connect', { + id: data.addr.pubkey, + host, + port }); + }; decodePaymentRequest = (urlParams?: Array) => - this.getRequest(`/v1/utility/decode/${urlParams && urlParams[0]}`); + this.postRequest('/v1/decode', { + string: urlParams && urlParams[0] + }); + payLightningInvoice = (data: any) => this.postRequest('/v1/pay', { - invoice: data.payment_request, - amount: Number(data.amt && data.amt * 1000), + bolt11: data.payment_request, + amount_msat: Number(data.amt && data.amt * 1000), maxfeepercent: data.max_fee_percent }); - sendKeysend = (data: any) => - this.postRequest('/v1/pay/keysend', { - pubkey: data.pubkey, - amount: Number(data.amt && data.amt * 1000), + sendKeysend = (data: any) => { + return this.postRequest('/v1/keysend', { + destination: data.pubkey, + amount_msat: Number(data.amt && data.amt * 1000), maxfeepercent: data.max_fee_percent }); - closeChannel = (urlParams?: Array) => - this.deleteRequest( - `/v1/channel/closeChannel/${urlParams && urlParams[0]}/` - ); + }; + closeChannel = (urlParams?: Array) => { + const request = { + id: urlParams && urlParams[0], + unilateraltimeout: urlParams && urlParams[1] ? 2 : 0 + }; + return this.postRequest('/v1/close', request); + }; getNodeInfo = () => this.getRequest('N/A'); getFees = () => this.getRequest('/v1/getFees/').then(({ feeCollected }: any) => ({ @@ -257,8 +269,12 @@ export default class CoreLightningRestApi extends LND { supportsLSPS1customMessage = () => false; supportsLSPS1rest = () => true; supportsOffers = async () => { - const res = await this.getRequest('/v1/utility/listConfigs'); - const supportsOffers: boolean = res['experimental-offers'] || false; + const { configs } = await this.postRequest('/v1/listconfigs'); + + const supportsOffers: boolean = configs['experimental-offers'] + ? true + : false; + return supportsOffers; }; isLNDBased = () => false; diff --git a/stores/ChannelsStore.ts b/stores/ChannelsStore.ts index d08994e0f..0f14ca074 100644 --- a/stores/ChannelsStore.ts +++ b/stores/ChannelsStore.ts @@ -486,10 +486,17 @@ export default class ChannelsStore { this.closingChannel = true; let urlParams: Array = []; - if (channelId && !channelPoint) { + const implementation = this.settingsStore.implementation; + + if ( + implementation === 'c-lightning-REST' || + implementation === 'core-lightning-rest-api' || + implementation === 'eclair' || + implementation === 'spark' + ) { // c-lightning, eclair urlParams = [channelId, forceClose]; - } else if (channelPoint) { + } else { // lnd const { funding_txid_str, output_index } = channelPoint; @@ -502,7 +509,7 @@ export default class ChannelsStore { ]; } - if (this.settingsStore.implementation === 'lightning-node-connect') { + if (implementation === 'lightning-node-connect') { return BackendUtils.closeChannel(urlParams); } else { let resolved = false; diff --git a/stores/TransactionsStore.ts b/stores/TransactionsStore.ts index f4605802f..e21d23dd8 100644 --- a/stores/TransactionsStore.ts +++ b/stores/TransactionsStore.ts @@ -460,6 +460,8 @@ export default class TransactionsStore { const payFunc = (this.settingsStore.implementation === 'c-lightning-REST' || + this.settingsStore.implementation === + 'core-lightning-rest-api' || this.settingsStore.implementation === 'embedded-lnd') && pubkey ? BackendUtils.sendKeysend From 82595e6ef63e815c482b98da35012c91bc216519 Mon Sep 17 00:00:00 2001 From: nitesh Date: Sat, 8 Jun 2024 18:18:48 -0400 Subject: [PATCH 04/17] add fees and bolt12 --- backends/CoreLightningRestApi.ts | 31 +++--- backends/LND.ts | 3 - components/SetFeesForm.tsx | 7 +- views/Settings/NodeConfiguration.tsx | 140 +++++++++++++-------------- 4 files changed, 91 insertions(+), 90 deletions(-) diff --git a/backends/CoreLightningRestApi.ts b/backends/CoreLightningRestApi.ts index 708508211..7e0bc1f8f 100644 --- a/backends/CoreLightningRestApi.ts +++ b/backends/CoreLightningRestApi.ts @@ -181,27 +181,27 @@ export default class CoreLightningRestApi extends LND { }; return this.postRequest('/v1/close', request); }; - getNodeInfo = () => this.getRequest('N/A'); getFees = () => - this.getRequest('/v1/getFees/').then(({ feeCollected }: any) => ({ - total_fee_sum: feeCollected / 1000 + this.getRequest('/v1/getinfo').then((res: any) => ({ + total_fee_sum: res.fees_collected_msat / 1000 })); setFees = (data: any) => - this.postRequest('/v1/channel/setChannelFee/', { + this.postRequest('/v1/setchannel', { id: data.global ? 'all' : data.channelId, - base: data.base_fee_msat, - ppm: data.fee_rate + feebase: data.base_fee_msat, + feeppm: data.fee_rate }); getRoutes = () => this.getRequest('N/A'); getUTXOs = () => this.postRequest('/v1/listfunds'); signMessage = (message: string) => - this.postRequest('/v1/utility/signMessage', { + this.postRequest('/v1/signmessage', { message }); verifyMessage = (data: any) => - this.getRequest( - `/v1/utility/checkMessage/${data.msg}/${data.signature}` - ); + this.postRequest('/v1/checkmessage', { + message: data.msg, + zbase: data.signature + }); lnurlAuth = async (r_hash: string) => { const signed = await this.signMessage(r_hash); return { @@ -212,7 +212,8 @@ export default class CoreLightningRestApi extends LND { }; // BOLT 12 / Offers - listOffers = () => this.getRequest('/v1/offers/listOffers'); + listOffers = () => + this.postRequest('/v1/listoffers', { active_only: true }); createOffer = ({ description, label, @@ -222,18 +223,18 @@ export default class CoreLightningRestApi extends LND { label?: string; singleUse?: boolean; }) => - this.postRequest('/v1/offers/offer', { + this.postRequest('/v1/offer', { amount: 'any', description, label, single_use: singleUse || false }); disableOffer = ({ offer_id }: { offer_id: string }) => - this.deleteRequest(`/v1/offers/disableOffer/${offer_id}`); + this.postRequest('/v1/disableoffer', { offer_id }); fetchInvoiceFromOffer = async (bolt12: string, amountSatoshis: string) => { - return await this.postRequest('/v1/offers/fetchInvoice', { + return await this.postRequest('/v1/fetchinvoice', { offer: bolt12, - msatoshi: Number(amountSatoshis) * 1000, + amount_msat: Number(amountSatoshis) * 1000, timeout: 60 }); }; diff --git a/backends/LND.ts b/backends/LND.ts index 79742cd6d..b0a0a58c5 100644 --- a/backends/LND.ts +++ b/backends/LND.ts @@ -99,9 +99,6 @@ export default class LND { } } }) - .catch((e) => { - console.error('Fetch threw an error', e); - }) ); } diff --git a/components/SetFeesForm.tsx b/components/SetFeesForm.tsx index b290495be..e882f2804 100644 --- a/components/SetFeesForm.tsx +++ b/components/SetFeesForm.tsx @@ -123,7 +123,8 @@ export default class SetFeesForm extends React.Component< }} > {`${localeString('components.SetFeesForm.feeRate')} (${ - implementation === 'c-lightning-REST' + implementation === 'c-lightning-REST' || + implementation === 'core-lightning-rest-api' ? localeString( 'components.SetFeesForm.ppmMilliMsat' ) @@ -133,7 +134,9 @@ export default class SetFeesForm extends React.Component< )} - {implementation === 'lnd' || - (implementation === 'c-lightning-REST' && ( - <> - - {localeString( - 'views.Settings.AddEditNode.host' - )} - - - this.setState({ - host: text.trim(), - saved: false - }) - } - locked={loading} - /> + {(implementation === 'lnd' || + implementation === 'c-lightning-REST') && ( + <> + + {localeString( + 'views.Settings.AddEditNode.host' + )} + + + this.setState({ + host: text.trim(), + saved: false + }) + } + locked={loading} + /> - - {localeString( - 'views.Settings.AddEditNode.macaroon' - )} - - - this.setState({ - macaroonHex: text.replace( - /\s+/g, - '' - ), - saved: false - }) - } - locked={loading} - /> + + {localeString( + 'views.Settings.AddEditNode.macaroon' + )} + + + this.setState({ + macaroonHex: text.replace( + /\s+/g, + '' + ), + saved: false + }) + } + locked={loading} + /> - - {localeString( - 'views.Settings.AddEditNode.restPort' - )} - - - this.setState({ - port: text.trim(), - saved: false - }) - } - locked={loading} - /> - - ))} + + {localeString( + 'views.Settings.AddEditNode.restPort' + )} + + + this.setState({ + port: text.trim(), + saved: false + }) + } + locked={loading} + /> + + )} {implementation === 'lightning-node-connect' && ( <> From 4088cc6b3e59111ed4877502ded2a96c7dba6799 Mon Sep 17 00:00:00 2001 From: nitesh Date: Sun, 9 Jun 2024 23:21:52 -0400 Subject: [PATCH 05/17] fix nits --- backends/{CoreLightningRestApi.ts => CLNRest.ts} | 2 +- backends/CoreLightningRequestHandler.ts | 4 ++-- components/SetFeesForm.tsx | 4 ++-- stores/ChannelsStore.ts | 2 +- stores/SettingsStore.ts | 4 ++-- stores/TransactionsStore.ts | 3 +-- utils/BackendUtils.ts | 10 +++++----- views/Settings/NodeConfiguration.tsx | 2 +- 8 files changed, 15 insertions(+), 16 deletions(-) rename backends/{CoreLightningRestApi.ts => CLNRest.ts} (99%) diff --git a/backends/CoreLightningRestApi.ts b/backends/CLNRest.ts similarity index 99% rename from backends/CoreLightningRestApi.ts rename to backends/CLNRest.ts index 7e0bc1f8f..139d6a18d 100644 --- a/backends/CoreLightningRestApi.ts +++ b/backends/CLNRest.ts @@ -12,7 +12,7 @@ import { listPeers } from './CoreLightningRequestHandler'; -export default class CoreLightningRestApi extends LND { +export default class CLNRest extends LND { getHeaders = (rune: string): any => { return { Rune: rune diff --git a/backends/CoreLightningRequestHandler.ts b/backends/CoreLightningRequestHandler.ts index fd5ba2541..f42058e74 100644 --- a/backends/CoreLightningRequestHandler.ts +++ b/backends/CoreLightningRequestHandler.ts @@ -1,6 +1,6 @@ -import CoreLightningRestApi from './CoreLightningRestApi'; +import CLNRest from './CLNRest'; -const api = new CoreLightningRestApi(); +const api = new CLNRest(); // Returns onchain balance of core-lightning node export const getBalance = (data: any) => { diff --git a/components/SetFeesForm.tsx b/components/SetFeesForm.tsx index e882f2804..4b3032b43 100644 --- a/components/SetFeesForm.tsx +++ b/components/SetFeesForm.tsx @@ -124,7 +124,7 @@ export default class SetFeesForm extends React.Component< > {`${localeString('components.SetFeesForm.feeRate')} (${ implementation === 'c-lightning-REST' || - implementation === 'core-lightning-rest-api' + implementation === 'cln-rest' ? localeString( 'components.SetFeesForm.ppmMilliMsat' ) @@ -136,7 +136,7 @@ export default class SetFeesForm extends React.Component< placeholder={ feeRate || implementation === 'c-lightning-REST' || - implementation === 'core-lightning-rest-api' + implementation === 'cln-rest' ? '1' : '0.001' } diff --git a/stores/ChannelsStore.ts b/stores/ChannelsStore.ts index 0f14ca074..8f65ebe02 100644 --- a/stores/ChannelsStore.ts +++ b/stores/ChannelsStore.ts @@ -490,7 +490,7 @@ export default class ChannelsStore { if ( implementation === 'c-lightning-REST' || - implementation === 'core-lightning-rest-api' || + implementation === 'cln-rest' || implementation === 'eclair' || implementation === 'spark' ) { diff --git a/stores/SettingsStore.ts b/stores/SettingsStore.ts index 1c7d4c9e2..2da1c8c10 100644 --- a/stores/SettingsStore.ts +++ b/stores/SettingsStore.ts @@ -206,7 +206,7 @@ export const INTERFACE_KEYS = [ { key: 'Embedded LND', value: 'embedded-lnd' }, { key: 'LND (REST)', value: 'lnd' }, { key: 'LND (Lightning Node Connect)', value: 'lightning-node-connect' }, - { key: 'Core Lightning REST API', value: 'core-lightning-rest-api' }, + { key: 'Core Lightning (CLNRest)', value: 'cln-rest' }, { key: 'LNDHub', value: 'lndhub' }, { key: '[DEPRECATED] Core Lightning (c-lightning-REST)', @@ -220,7 +220,7 @@ export type Implementations = | 'embedded-lnd' | 'lnd' | 'lightning-node-connect' - | 'core-lightning-rest-api' + | 'cln-rest' | 'lndhub' | 'c-lightning-REST' | 'spark' diff --git a/stores/TransactionsStore.ts b/stores/TransactionsStore.ts index e21d23dd8..6f2151ba4 100644 --- a/stores/TransactionsStore.ts +++ b/stores/TransactionsStore.ts @@ -460,8 +460,7 @@ export default class TransactionsStore { const payFunc = (this.settingsStore.implementation === 'c-lightning-REST' || - this.settingsStore.implementation === - 'core-lightning-rest-api' || + this.settingsStore.implementation === 'cln-rest' || this.settingsStore.implementation === 'embedded-lnd') && pubkey ? BackendUtils.sendKeysend diff --git a/utils/BackendUtils.ts b/utils/BackendUtils.ts index 1a26eb44f..f0c80fc67 100644 --- a/utils/BackendUtils.ts +++ b/utils/BackendUtils.ts @@ -5,7 +5,7 @@ import LightningNodeConnect from '../backends/LightningNodeConnect'; import EmbeddedLND from '../backends/EmbeddedLND'; // Core Lightning import CLightningREST from '../backends/CLightningREST'; -import CoreLightningRestApi from '../backends/CoreLightningRestApi'; +import CLNRest from '../backends/CLNRest'; import Spark from '../backends/Spark'; // Eclair import Eclair from '../backends/Eclair'; @@ -17,7 +17,7 @@ class BackendUtils { lightningNodeConnect: LightningNodeConnect; embeddedLND: EmbeddedLND; clightningREST: CLightningREST; - coreLightningRestApi: CoreLightningRestApi; + clnRest: CLNRest; spark: Spark; eclair: Eclair; lndHub: LndHub; @@ -26,7 +26,7 @@ class BackendUtils { this.lightningNodeConnect = new LightningNodeConnect(); this.embeddedLND = new EmbeddedLND(); this.clightningREST = new CLightningREST(); - this.coreLightningRestApi = new CoreLightningRestApi(); + this.clnRest = new CLNRest(); this.spark = new Spark(); this.eclair = new Eclair(); this.lndHub = new LndHub(); @@ -43,8 +43,8 @@ class BackendUtils { return this.embeddedLND; case 'c-lightning-REST': return this.clightningREST; - case 'core-lightning-rest-api': - return this.coreLightningRestApi; + case 'cln-rest': + return this.clnRest; case 'spark': return this.spark; case 'eclair': diff --git a/views/Settings/NodeConfiguration.tsx b/views/Settings/NodeConfiguration.tsx index 779c59a0b..cff07d449 100644 --- a/views/Settings/NodeConfiguration.tsx +++ b/views/Settings/NodeConfiguration.tsx @@ -1357,7 +1357,7 @@ export default class NodeConfiguration extends React.Component< )} )} - {implementation === 'core-lightning-rest-api' && ( + {implementation === 'cln-rest' && ( <> Date: Mon, 10 Jun 2024 11:22:18 -0400 Subject: [PATCH 06/17] fixed some nits --- backends/CLNRest.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backends/CLNRest.ts b/backends/CLNRest.ts index 139d6a18d..0004c333d 100644 --- a/backends/CLNRest.ts +++ b/backends/CLNRest.ts @@ -61,11 +61,8 @@ export default class CLNRest extends LND { ); }; - getRequest = (route: string, data?: any) => - this.request(route, 'get', null, data); postRequest = (route: string, data?: any) => this.request(route, 'post', data); - deleteRequest = (route: string) => this.request(route, 'delete', null); getNode = (data: any) => this.postRequest('/v1/listnodes', { id: data.id }).then((res) => { @@ -182,7 +179,7 @@ export default class CLNRest extends LND { return this.postRequest('/v1/close', request); }; getFees = () => - this.getRequest('/v1/getinfo').then((res: any) => ({ + this.postRequest('/v1/getinfo').then((res: any) => ({ total_fee_sum: res.fees_collected_msat / 1000 })); setFees = (data: any) => From 3e67c1bd8246eba025a006dadacc9102ac67f57e Mon Sep 17 00:00:00 2001 From: nitesh Date: Mon, 10 Jun 2024 11:31:48 -0400 Subject: [PATCH 07/17] mark coin controls as default --- backends/CLNRest.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backends/CLNRest.ts b/backends/CLNRest.ts index 0004c333d..d33a38b2b 100644 --- a/backends/CLNRest.ts +++ b/backends/CLNRest.ts @@ -246,9 +246,8 @@ export default class CLNRest extends LND { supportsPendingChannels = () => false; supportsMPP = () => false; supportsAMP = () => false; - supportsCoinControl = () => this.supports('v0.8.2', undefined, 'v0.4.0'); - supportsChannelCoinControl = () => - this.supports('v0.8.2', undefined, 'v0.4.0'); + supportsCoinControl = () => true; + supportsChannelCoinControl = () => true; supportsHopPicking = () => false; supportsAccounts = () => false; supportsRouting = () => true; From f5e2c275dd66e4ca2c5f36b8ac0a042c34515a2d Mon Sep 17 00:00:00 2001 From: nitesh Date: Mon, 10 Jun 2024 11:57:26 -0400 Subject: [PATCH 08/17] decouple cln and lnd backends --- backends/CLNRest.ts | 114 ++++++++++++++++++++++++++++++++++++++-- stores/ChannelsStore.ts | 4 +- 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/backends/CLNRest.ts b/backends/CLNRest.ts index d33a38b2b..a6a1a40e5 100644 --- a/backends/CLNRest.ts +++ b/backends/CLNRest.ts @@ -1,5 +1,4 @@ import stores from '../stores/Stores'; -import LND from './LND'; import TransactionRequest from '../models/TransactionRequest'; import OpenChannelRequest from '../models/OpenChannelRequest'; import VersionUtils from '../utils/VersionUtils'; @@ -11,8 +10,13 @@ import { getOffchainBalance, listPeers } from './CoreLightningRequestHandler'; +import { localeString } from '../utils/LocaleUtils'; +import ReactNativeBlobUtil from 'react-native-blob-util'; +import { doTorRequest, RequestMethod } from '../utils/TorUtils'; -export default class CLNRest extends LND { +const calls = new Map>(); + +export default class CLNRest { getHeaders = (rune: string): any => { return { Rune: rune @@ -36,6 +40,91 @@ export default class CLNRest extends LND { return isSupportedVersion(version, minVersion, eosVersion); }; + clearCachedCalls = () => calls.clear(); + + restReq = async ( + headers: Headers | any, + url: string, + method: any, + data?: any, + certVerification?: boolean, + useTor?: boolean + ) => { + // use body data as an identifier too, we don't want to cancel when we + // are making multiples calls to get all the node names, for example + const id = data ? `${url}${JSON.stringify(data)}` : url; + if (calls.has(id)) { + return calls.get(id); + } + // API is a bit of a mess but + // If tor enabled in setting, start up the daemon here + if (useTor === true) { + calls.set( + id, + doTorRequest( + url, + method as RequestMethod, + JSON.stringify(data), + headers + ).then((response: any) => { + calls.delete(id); + return response; + }) + ); + } else { + calls.set( + id, + ReactNativeBlobUtil.config({ + trusty: !certVerification + }) + .fetch( + method, + url, + headers, + data ? JSON.stringify(data) : data + ) + .then((response: any) => { + calls.delete(id); + if (response.info().status < 300) { + // handle ws responses + if (response.data.includes('\n')) { + const split = response.data.split('\n'); + const length = split.length; + // last instance is empty + return JSON.parse(split[length - 2]); + } + return response.json(); + } else { + try { + const errorInfo = response.json(); + throw new Error( + (errorInfo.error && + errorInfo.error.message) || + errorInfo.message || + errorInfo.error + ); + } catch (e) { + if ( + response.data && + typeof response.data === 'string' + ) { + throw new Error(response.data); + } else { + throw new Error( + localeString( + 'backends.LND.restReq.connectionError' + ) + ); + } + } + } + }) + ); + } + + return await calls.get(id); + }; + request = (route: string, method: string, data?: any, params?: any) => { const { host, port, rune, certVerification, enableTor } = stores.settingsStore; @@ -61,6 +150,26 @@ export default class CLNRest extends LND { ); }; + getURL = ( + host: string, + port: string | number, + route: string, + ws?: boolean + ) => { + const hostPath = host.includes('://') ? host : `https://${host}`; + let baseUrl = `${hostPath}${port ? ':' + port : ''}`; + + if (ws) { + baseUrl = baseUrl.replace('https', 'wss').replace('http', 'ws'); + } + + if (baseUrl[baseUrl.length - 1] === '/') { + baseUrl = baseUrl.slice(0, -1); + } + + return `${baseUrl}${route}`; + }; + postRequest = (route: string, data?: any) => this.request(route, 'post', data); @@ -188,7 +297,6 @@ export default class CLNRest extends LND { feebase: data.base_fee_msat, feeppm: data.fee_rate }); - getRoutes = () => this.getRequest('N/A'); getUTXOs = () => this.postRequest('/v1/listfunds'); signMessage = (message: string) => this.postRequest('/v1/signmessage', { diff --git a/stores/ChannelsStore.ts b/stores/ChannelsStore.ts index 8f65ebe02..224e9a779 100644 --- a/stores/ChannelsStore.ts +++ b/stores/ChannelsStore.ts @@ -324,7 +324,9 @@ export default class ChannelsStore { publicKeysOfToBeLoadedNodeInfos.map( async (remotePubKey: string) => { if ( - this.settingsStore.implementation !== 'c-lightning-REST' + this.settingsStore.implementation !== + 'c-lightning-REST' && + this.settingsStore.implementation !== 'cln-rest' ) { const nodeInfo = await this.getNodeInfo(remotePubKey); if (!nodeInfo) return; From 194f994a5449eea10c5e54d862927d67691d6143 Mon Sep 17 00:00:00 2001 From: nitesh Date: Mon, 10 Jun 2024 14:34:27 -0400 Subject: [PATCH 09/17] minor compatibility fixes --- components/FeeLimit.tsx | 4 +- stores/TransactionsStore.ts | 3 +- views/OpenChannel.tsx | 6 ++- views/PaymentRequest.tsx | 4 +- views/Send.tsx | 60 +++++++++++++++-------------- views/Settings/PaymentsSettings.tsx | 3 +- views/Sweep.tsx | 3 +- 7 files changed, 47 insertions(+), 36 deletions(-) diff --git a/components/FeeLimit.tsx b/components/FeeLimit.tsx index 8fc291fe9..f6e6449f5 100644 --- a/components/FeeLimit.tsx +++ b/components/FeeLimit.tsx @@ -187,7 +187,9 @@ export default class FeeLimit extends React.Component< const { implementation } = SettingsStore; const isLnd: boolean = BackendUtils.isLNDBased(); - const isCLightning: boolean = implementation === 'c-lightning-REST'; + const isCLightning: boolean = + implementation === 'c-lightning-REST' || + implementation === 'cln-rest'; if (hide) return; diff --git a/stores/TransactionsStore.ts b/stores/TransactionsStore.ts index 6f2151ba4..6c2cd0da6 100644 --- a/stores/TransactionsStore.ts +++ b/stores/TransactionsStore.ts @@ -448,7 +448,8 @@ export default class TransactionsStore { // max fee percent for c-lightning if ( max_fee_percent && - this.settingsStore.implementation === 'c-lightning-REST' + (this.settingsStore.implementation === 'c-lightning-REST' || + this.settingsStore.implementation === 'cln-rest') ) { data.max_fee_percent = max_fee_percent; } diff --git a/views/OpenChannel.tsx b/views/OpenChannel.tsx index c22dacefe..5cfcf9455 100644 --- a/views/OpenChannel.tsx +++ b/views/OpenChannel.tsx @@ -565,8 +565,10 @@ export default class OpenChannel extends React.Component< fundMax: newValue, local_funding_amount: newValue && - implementation === - 'c-lightning-REST' + (implementation === + 'c-lightning-REST' || + implementation === + 'cln-rest') ? 'all' : '' }); diff --git a/views/PaymentRequest.tsx b/views/PaymentRequest.tsx index b02a9d903..0fe127c45 100644 --- a/views/PaymentRequest.tsx +++ b/views/PaymentRequest.tsx @@ -347,7 +347,9 @@ export default class PaymentRequest extends React.Component< const { enableTor, implementation } = SettingsStore; const isLnd: boolean = BackendUtils.isLNDBased(); - const isCLightning: boolean = implementation === 'c-lightning-REST'; + const isCLightning: boolean = + implementation === 'c-lightning-REST' || + implementation === 'cln-rest'; const isNoAmountInvoice: boolean = !requestAmount || requestAmount === 0; diff --git a/views/Send.tsx b/views/Send.tsx index c2aef6895..d8156a00c 100644 --- a/views/Send.tsx +++ b/views/Send.tsx @@ -343,7 +343,8 @@ export default class Send extends React.Component { utxos, utxoBalance, amount: - implementation === 'c-lightning-REST' + implementation === 'c-lightning-REST' || + implementation === 'cln-rest' ? 'all' : prevState.amount, account @@ -1242,34 +1243,35 @@ export default class Send extends React.Component { }} /> - {implementation !== 'c-lightning-REST' && ( - <> - - {`${localeString( - 'views.Send.message' - )} (${localeString( - 'general.optional' - )})`} - - - this.setState({ - message: text - }) - } - /> - - )} + {implementation !== 'c-lightning-REST' && + implementation !== 'cln-rest' && ( + <> + + {`${localeString( + 'views.Send.message' + )} (${localeString( + 'general.optional' + )})`} + + + this.setState({ + message: text + }) + } + /> + + )} )} - {implementation === 'c-lightning-REST' && ( + {(implementation === 'c-lightning-REST' || + implementation === 'cln-rest') && ( { let request; request = - implementation === 'c-lightning-REST' + implementation === 'c-lightning-REST' || + implementation === 'cln-rest' ? { addr: destination, sat_per_vbyte: fee, From f47ce5427d7677e9a7521f150cc5f57797813db0 Mon Sep 17 00:00:00 2001 From: nitesh Date: Tue, 11 Jun 2024 09:55:46 -0400 Subject: [PATCH 10/17] fix chain withdrawal --- backends/CLNRest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/CLNRest.ts b/backends/CLNRest.ts index a6a1a40e5..2784052f2 100644 --- a/backends/CLNRest.ts +++ b/backends/CLNRest.ts @@ -197,14 +197,14 @@ export default class CLNRest { let request: any; if (data.utxos) { request = { - address: data.addr, + destination: data.addr, feeRate: `${Number(data.sat_per_vbyte) * 1000}perkb`, satoshis: data.amount, utxos: data.utxos }; } else { request = { - address: data.addr, + destination: data.addr, feeRate: `${Number(data.sat_per_vbyte) * 1000}perkb`, satoshis: data.amount }; From 49a4c8b82148a9695489e820bbec72a751149910 Mon Sep 17 00:00:00 2001 From: nitesh Date: Tue, 11 Jun 2024 10:17:53 -0400 Subject: [PATCH 11/17] fix chain withdrawal --- backends/CLNRest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backends/CLNRest.ts b/backends/CLNRest.ts index 2784052f2..ae88984e6 100644 --- a/backends/CLNRest.ts +++ b/backends/CLNRest.ts @@ -198,15 +198,15 @@ export default class CLNRest { if (data.utxos) { request = { destination: data.addr, - feeRate: `${Number(data.sat_per_vbyte) * 1000}perkb`, - satoshis: data.amount, + feerate: `${Number(data.sat_per_vbyte) * 1000}perkb`, + satoshi: data.amount, utxos: data.utxos }; } else { request = { destination: data.addr, - feeRate: `${Number(data.sat_per_vbyte) * 1000}perkb`, - satoshis: data.amount + feerate: `${Number(data.sat_per_vbyte) * 1000}perkb`, + satoshi: data.amount }; } return this.postRequest('/v1/withdraw', request); From eb360e7cb8c14ee5782833df3c7ee1cf52aba9d8 Mon Sep 17 00:00:00 2001 From: nitesh Date: Tue, 11 Jun 2024 10:55:37 -0400 Subject: [PATCH 12/17] fix chain transactions history --- backends/CLNRest.ts | 6 ++-- backends/CoreLightningRequestHandler.ts | 45 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/backends/CLNRest.ts b/backends/CLNRest.ts index ae88984e6..deadece7d 100644 --- a/backends/CLNRest.ts +++ b/backends/CLNRest.ts @@ -7,6 +7,7 @@ import { Hash as sha256Hash } from 'fast-sha256'; import BigNumber from 'bignumber.js'; import { getBalance, + getChainTransactions, getOffchainBalance, listPeers } from './CoreLightningRequestHandler'; @@ -177,10 +178,7 @@ export default class CLNRest { this.postRequest('/v1/listnodes', { id: data.id }).then((res) => { return res; }); - getTransactions = () => - this.postRequest('/v1/listfunds').then((res) => ({ - transactions: res.outputs - })); + getTransactions = async () => await getChainTransactions(); getChannels = async () => { const channels = await this.postRequest('/v1/listpeerchannels'); return await listPeers(channels); diff --git a/backends/CoreLightningRequestHandler.ts b/backends/CoreLightningRequestHandler.ts index f42058e74..27aa0d2a5 100644 --- a/backends/CoreLightningRequestHandler.ts +++ b/backends/CoreLightningRequestHandler.ts @@ -128,3 +128,48 @@ export const listPeers = async (data: any) => { return { channels: channelsWithAliases }; }; + +// Get all chain transactions from your core-lightnig node +export const getChainTransactions = async () => { + const [transactions, getinfo] = await Promise.all([ + api.postRequest('/v1/bkpr-listaccountevents'), + api.postRequest('/v1/getinfo') + ]); + + const formattedTxs: any[] = []; + + console.log('chain txs', transactions); + + transactions.events + .filter((tx: any) => tx.type !== 'onchain_fee') + .map((tx: any) => { + let amount = 0; + let txid; + + if (tx.tag === 'deposit') { + amount = tx.credit_msat; + txid = tx.outpoint.split(':')[0]; + } else if (tx.tag === 'withdrawal') { + amount = -Math.abs(tx.debit_msat); + txid = tx.txid; + } else if (tx.tag === 'channel_open') { + amount = -Math.abs(tx.credit_msat); + txid = tx.outpoint.split(':')[0]; + } else if (tx.tag === 'channel_close') { + amount = tx.debit_msat; + txid = tx.txid; + } + + formattedTxs.push({ + amount: amount / 1000, + block_height: tx.blockheight, + num_confirmations: getinfo.blockheight - tx.blockheight, + time_stamp: tx.timestamp, + txid + }); + }); + + return { + transactions: formattedTxs + }; +}; From 800a071deddfcf048bb8a563eba0f52798ff232c Mon Sep 17 00:00:00 2001 From: nitesh Date: Tue, 11 Jun 2024 11:14:17 -0400 Subject: [PATCH 13/17] make chain txs more readable --- backends/CoreLightningRequestHandler.ts | 8 +++++++- models/Transaction.ts | 1 + views/Transaction.tsx | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/backends/CoreLightningRequestHandler.ts b/backends/CoreLightningRequestHandler.ts index 27aa0d2a5..18c91613a 100644 --- a/backends/CoreLightningRequestHandler.ts +++ b/backends/CoreLightningRequestHandler.ts @@ -145,19 +145,24 @@ export const getChainTransactions = async () => { .map((tx: any) => { let amount = 0; let txid; + let note: string | null = null; if (tx.tag === 'deposit') { amount = tx.credit_msat; txid = tx.outpoint.split(':')[0]; + note = 'on-chain deposit'; } else if (tx.tag === 'withdrawal') { amount = -Math.abs(tx.debit_msat); txid = tx.txid; + note = 'on-chain withdrawal'; } else if (tx.tag === 'channel_open') { amount = -Math.abs(tx.credit_msat); txid = tx.outpoint.split(':')[0]; + note = 'channel-open'; } else if (tx.tag === 'channel_close') { amount = tx.debit_msat; txid = tx.txid; + note = 'channel-close'; } formattedTxs.push({ @@ -165,7 +170,8 @@ export const getChainTransactions = async () => { block_height: tx.blockheight, num_confirmations: getinfo.blockheight - tx.blockheight, time_stamp: tx.timestamp, - txid + txid, + note }); }); diff --git a/models/Transaction.ts b/models/Transaction.ts index d47bd5c07..0b8e9294d 100644 --- a/models/Transaction.ts +++ b/models/Transaction.ts @@ -43,6 +43,7 @@ export default class Transaction extends BaseModel { public txid: string; public outputs: number; public address: string; + public note: string | null; @computed public get model(): string { return localeString('general.transaction'); diff --git a/views/Transaction.tsx b/views/Transaction.tsx index 2ba2370f9..14ac25cb8 100644 --- a/views/Transaction.tsx +++ b/views/Transaction.tsx @@ -82,6 +82,7 @@ export default class TransactionView extends React.Component< num_confirmations, time_stamp, destAddresses, + note, getFee, getFeePercentage, status, @@ -311,6 +312,21 @@ export default class TransactionView extends React.Component< {!!destAddresses && ( {addresses} )} + {!!note && ( + + {note} + + } + /> + )} {raw_tx_hex && ( <> From 110440009f9f6f29edc212f7896299c93f08d311 Mon Sep 17 00:00:00 2001 From: nitesh Date: Tue, 11 Jun 2024 11:15:10 -0400 Subject: [PATCH 14/17] push translations --- locales/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/en.json b/locales/en.json index 9da9a055d..706c4e4ed 100644 --- a/locales/en.json +++ b/locales/en.json @@ -910,6 +910,7 @@ "views.Transaction.title": "Transaction", "views.Transaction.totalFees": "Total Fees", "views.Transaction.transactionHash": "Transaction Hash", + "views.Transaction.note": "Transaction Note", "views.Transaction.blockHash": "Block Hash", "views.Transaction.blockHeight": "Block Height", "views.Transaction.numConf": "Number of Confirmations", From e192c9df35323316af8a4266ac96d7a9e92d727e Mon Sep 17 00:00:00 2001 From: nitesh Date: Thu, 13 Jun 2024 19:44:22 -0400 Subject: [PATCH 15/17] fix transactions list --- backends/CoreLightningRequestHandler.ts | 114 ++++++++++++++++-------- 1 file changed, 78 insertions(+), 36 deletions(-) diff --git a/backends/CoreLightningRequestHandler.ts b/backends/CoreLightningRequestHandler.ts index 18c91613a..c66bef4e1 100644 --- a/backends/CoreLightningRequestHandler.ts +++ b/backends/CoreLightningRequestHandler.ts @@ -131,51 +131,93 @@ export const listPeers = async (data: any) => { // Get all chain transactions from your core-lightnig node export const getChainTransactions = async () => { - const [transactions, getinfo] = await Promise.all([ - api.postRequest('/v1/bkpr-listaccountevents'), + const results = await Promise.allSettled([ + api.postRequest('/v1/bkpr-listaccountevents', { account: 'wallet' }), + api.postRequest('/v1/bkpr-listaccountevents', { account: 'external' }), + api.postRequest('/v1/listtransactions'), api.postRequest('/v1/getinfo') ]); - const formattedTxs: any[] = []; + const [walletTxsResult, externalTxsResult, listTxsResult, getinfoResult] = + results; - console.log('chain txs', transactions); + // If getinfo fails, return blank txs + if (getinfoResult.status !== 'fulfilled') { + return { transactions: [] }; + } + + const getinfo = getinfoResult.value; + + const walletTxs = + walletTxsResult.status === 'fulfilled' + ? walletTxsResult.value.events.filter( + (tx: any) => tx.tag === 'deposit' + ) + : { events: [] }; + + const externalTxs = + externalTxsResult.status === 'fulfilled' + ? externalTxsResult.value.events.filter( + (tx: any) => tx.tag === 'deposit' + ) + : { events: [] }; + + const listTxs = + listTxsResult.status === 'fulfilled' + ? listTxsResult.value + : { transactions: [] }; - transactions.events - .filter((tx: any) => tx.type !== 'onchain_fee') + console.log('wallet txs', walletTxs); + console.log('external txs', externalTxs); + console.log('list txs are', JSON.stringify(listTxs)); + + const transactions = listTxs.transactions .map((tx: any) => { - let amount = 0; - let txid; - let note: string | null = null; - - if (tx.tag === 'deposit') { - amount = tx.credit_msat; - txid = tx.outpoint.split(':')[0]; - note = 'on-chain deposit'; - } else if (tx.tag === 'withdrawal') { - amount = -Math.abs(tx.debit_msat); - txid = tx.txid; - note = 'on-chain withdrawal'; - } else if (tx.tag === 'channel_open') { - amount = -Math.abs(tx.credit_msat); - txid = tx.outpoint.split(':')[0]; - note = 'channel-open'; - } else if (tx.tag === 'channel_close') { - amount = tx.debit_msat; - txid = tx.txid; - note = 'channel-close'; - } + const withdrawal = externalTxs.find((n: any) => { + const txid = n.outpoint ? n.outpoint.split(':')[0] : null; + + console.log(tx.hash, txid); + return txid === tx.hash; + }); - formattedTxs.push({ - amount: amount / 1000, - block_height: tx.blockheight, - num_confirmations: getinfo.blockheight - tx.blockheight, - time_stamp: tx.timestamp, - txid, - note + const deposit = walletTxs.find((n: any) => { + const txid = n.outpoint ? n.outpoint.split(':')[0] : null; + + return txid === tx.hash; }); - }); + + if (withdrawal) { + return { + amount: -Math.abs(withdrawal.credit_msat) / 1000, + block_height: withdrawal.blockheight, + num_confirmations: + getinfo.blockheight - withdrawal.blockheight, + time_stamp: withdrawal.timestamp, + txid: tx.hash, + note: 'on-chain withdrawal' + }; + } + + if (deposit) { + return { + amount: deposit.credit_msat / 1000, + block_height: deposit.blockheight, + num_confirmations: + getinfo.blockheight - deposit.blockheight, + time_stamp: deposit.timestamp, + txid: tx.hash, + note: 'on-chain deposit' + }; + } + + return null; + }) + .filter((n: any) => !!n); + + // Sort the transactions array by the time_stamp field (descending order) + transactions.sort((a: any, b: any) => b.time_stamp - a.time_stamp); return { - transactions: formattedTxs + transactions }; }; From 68f5c0f50f306288d6ee4b66aac41289666b1dd0 Mon Sep 17 00:00:00 2001 From: nitesh Date: Thu, 13 Jun 2024 20:03:53 -0400 Subject: [PATCH 16/17] actually fix transactions list --- backends/CoreLightningRequestHandler.ts | 57 +++++++++++++++++++------ 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/backends/CoreLightningRequestHandler.ts b/backends/CoreLightningRequestHandler.ts index c66bef4e1..25272a449 100644 --- a/backends/CoreLightningRequestHandler.ts +++ b/backends/CoreLightningRequestHandler.ts @@ -132,14 +132,12 @@ export const listPeers = async (data: any) => { // Get all chain transactions from your core-lightnig node export const getChainTransactions = async () => { const results = await Promise.allSettled([ - api.postRequest('/v1/bkpr-listaccountevents', { account: 'wallet' }), - api.postRequest('/v1/bkpr-listaccountevents', { account: 'external' }), + api.postRequest('/v1/bkpr-listaccountevents'), api.postRequest('/v1/listtransactions'), api.postRequest('/v1/getinfo') ]); - const [walletTxsResult, externalTxsResult, listTxsResult, getinfoResult] = - results; + const [walletTxsResult, listTxsResult, getinfoResult] = results; // If getinfo fails, return blank txs if (getinfoResult.status !== 'fulfilled') { @@ -148,17 +146,24 @@ export const getChainTransactions = async () => { const getinfo = getinfoResult.value; + const allTxs = + walletTxsResult.status === 'fulfilled' + ? walletTxsResult.value.events.filter( + (tx: any) => tx.type !== 'onchain_fee' + ) + : { events: [] }; + const walletTxs = walletTxsResult.status === 'fulfilled' ? walletTxsResult.value.events.filter( - (tx: any) => tx.tag === 'deposit' + (tx: any) => tx.tag === 'deposit' && tx.account === 'wallet' ) : { events: [] }; const externalTxs = - externalTxsResult.status === 'fulfilled' - ? externalTxsResult.value.events.filter( - (tx: any) => tx.tag === 'deposit' + walletTxsResult.status === 'fulfilled' + ? walletTxsResult.value.events.filter( + (tx: any) => tx.tag === 'deposit' && tx.account === 'external' ) : { events: [] }; @@ -167,22 +172,48 @@ export const getChainTransactions = async () => { ? listTxsResult.value : { transactions: [] }; - console.log('wallet txs', walletTxs); - console.log('external txs', externalTxs); - console.log('list txs are', JSON.stringify(listTxs)); - const transactions = listTxs.transactions .map((tx: any) => { const withdrawal = externalTxs.find((n: any) => { const txid = n.outpoint ? n.outpoint.split(':')[0] : null; - console.log(tx.hash, txid); + // Check if the deposit is associated with a channel open or close + const isChannelChange = allTxs.find((c: any) => { + if (!c.outpoint) { + return undefined; + } + + return ( + c.outpoint.split(':')[0] === tx.hash && + (c.tag === 'channel_open' || c.tag === 'channel_close') + ); + }); + + if (isChannelChange) { + return undefined; + } + return txid === tx.hash; }); const deposit = walletTxs.find((n: any) => { const txid = n.outpoint ? n.outpoint.split(':')[0] : null; + // Check if the deposit is associated with a channel open or close + const isChannelChange = allTxs.find((c: any) => { + if (!c.outpoint) { + return undefined; + } + return ( + c.outpoint.split(':')[0] === tx.hash && + (c.tag === 'channel_open' || c.tag === 'channel_close') + ); + }); + + if (isChannelChange) { + return undefined; + } + return txid === tx.hash; }); From 34ce4872fd034d8866252baa77f88615faa4e114 Mon Sep 17 00:00:00 2001 From: Nitesh Balusu Date: Mon, 8 Jul 2024 11:32:03 -0400 Subject: [PATCH 17/17] revert old changes --- backends/CLightningREST.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/CLightningREST.ts b/backends/CLightningREST.ts index f97eecd3b..85a4ca87d 100644 --- a/backends/CLightningREST.ts +++ b/backends/CLightningREST.ts @@ -10,7 +10,8 @@ import BigNumber from 'bignumber.js'; export default class CLightningREST extends LND { getHeaders = (macaroonHex: string): any => { return { - macaroon: macaroonHex + macaroon: macaroonHex, + encodingtype: 'hex' }; };