diff --git a/backend/src/api/lightning/clightning/clightning-client.ts b/backend/src/api/lightning/clightning/clightning-client.ts index f5643ed01..15f472f2e 100644 --- a/backend/src/api/lightning/clightning/clightning-client.ts +++ b/backend/src/api/lightning/clightning/clightning-client.ts @@ -245,7 +245,7 @@ export default class CLightningClient extends EventEmitter implements AbstractLi async $getNetworkGraph(): Promise { const listnodes: any[] = await this.call('listnodes'); const listchannels: any[] = await this.call('listchannels'); - const channelsList = convertAndmergeBidirectionalChannels(listchannels['channels']); + const channelsList = await convertAndmergeBidirectionalChannels(listchannels['channels']); return { nodes: listnodes['nodes'].map(node => convertNode(node)), diff --git a/backend/src/api/lightning/clightning/clightning-convert.ts b/backend/src/api/lightning/clightning/clightning-convert.ts index 008094bf5..1a267bc65 100644 --- a/backend/src/api/lightning/clightning/clightning-convert.ts +++ b/backend/src/api/lightning/clightning/clightning-convert.ts @@ -1,4 +1,5 @@ import { ILightningApi } from '../lightning-api.interface'; +import FundingTxFetcher from '../../../tasks/lightning/sync-tasks/funding-tx-fetcher'; /** * Convert a clightning "listnode" entry to a lnd node entry @@ -22,7 +23,7 @@ export function convertNode(clNode: any): ILightningApi.Node { /** * Convert clightning "listchannels" response to lnd "describegraph.edges" format */ - export function convertAndmergeBidirectionalChannels(clChannels: any[]): ILightningApi.Channel[] { + export async function convertAndmergeBidirectionalChannels(clChannels: any[]): Promise { const consolidatedChannelList: ILightningApi.Channel[] = []; const clChannelsDict = {}; const clChannelsDictCount = {}; @@ -33,14 +34,14 @@ export function convertNode(clNode: any): ILightningApi.Node { clChannelsDictCount[clChannel.short_channel_id] = 1; } else { consolidatedChannelList.push( - buildFullChannel(clChannel, clChannelsDict[clChannel.short_channel_id]) + await buildFullChannel(clChannel, clChannelsDict[clChannel.short_channel_id]) ); delete clChannelsDict[clChannel.short_channel_id]; clChannelsDictCount[clChannel.short_channel_id]++; } } for (const short_channel_id of Object.keys(clChannelsDict)) { - consolidatedChannelList.push(buildIncompleteChannel(clChannelsDict[short_channel_id])); + consolidatedChannelList.push(await buildIncompleteChannel(clChannelsDict[short_channel_id])); } return consolidatedChannelList; @@ -55,16 +56,20 @@ export function convertChannelId(channelId): string { * Convert two clightning "getchannels" entries into a full a lnd "describegraph.edges" format * In this case, clightning knows the channel policy for both nodes */ -function buildFullChannel(clChannelA: any, clChannelB: any): ILightningApi.Channel { +async function buildFullChannel(clChannelA: any, clChannelB: any): Promise { const lastUpdate = Math.max(clChannelA.last_update ?? 0, clChannelB.last_update ?? 0); - + + const tx = await FundingTxFetcher.$fetchChannelOpenTx(clChannelA.short_channel_id); + const parts = clChannelA.short_channel_id.split('x'); + const outputIdx = parts[2]; + return { channel_id: clChannelA.short_channel_id, capacity: clChannelA.satoshis, last_update: lastUpdate, node1_policy: convertPolicy(clChannelA), node2_policy: convertPolicy(clChannelB), - chan_point: ':0', // TODO + chan_point: `${tx.txid}:${outputIdx}`, node1_pub: clChannelA.source, node2_pub: clChannelB.source, }; @@ -74,14 +79,18 @@ function buildFullChannel(clChannelA: any, clChannelB: any): ILightningApi.Chann * Convert one clightning "getchannels" entry into a full a lnd "describegraph.edges" format * In this case, clightning knows the channel policy of only one node */ - function buildIncompleteChannel(clChannel: any): ILightningApi.Channel { + async function buildIncompleteChannel(clChannel: any): Promise { + const tx = await FundingTxFetcher.$fetchChannelOpenTx(clChannel.short_channel_id); + const parts = clChannel.short_channel_id.split('x'); + const outputIdx = parts[2]; + return { channel_id: clChannel.short_channel_id, capacity: clChannel.satoshis, last_update: clChannel.last_update ?? 0, node1_policy: convertPolicy(clChannel), node2_policy: null, - chan_point: ':0', // TODO + chan_point: `${tx.txid}:${outputIdx}`, node1_pub: clChannel.source, node2_pub: clChannel.destination, }; diff --git a/backend/src/index.ts b/backend/src/index.ts index 0f7cc7aa7..976ec12df 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -34,6 +34,7 @@ import miningRoutes from './api/mining/mining-routes'; import bisqRoutes from './api/bisq/bisq.routes'; import liquidRoutes from './api/liquid/liquid.routes'; import bitcoinRoutes from './api/bitcoin/bitcoin.routes'; +import fundingTxFetcher from "./tasks/lightning/sync-tasks/funding-tx-fetcher"; class Server { private wss: WebSocket.Server | undefined; @@ -136,7 +137,8 @@ class Server { } if (config.LIGHTNING.ENABLED) { - networkSyncService.$startService() + fundingTxFetcher.$init() + .then(() => networkSyncService.$startService()) .then(() => lightningStatsUpdater.$startService()); } diff --git a/backend/src/tasks/lightning/sync-tasks/funding-tx-fetcher.ts b/backend/src/tasks/lightning/sync-tasks/funding-tx-fetcher.ts index 9da721876..926d20c91 100644 --- a/backend/src/tasks/lightning/sync-tasks/funding-tx-fetcher.ts +++ b/backend/src/tasks/lightning/sync-tasks/funding-tx-fetcher.ts @@ -1,8 +1,6 @@ import { existsSync, promises } from 'fs'; -import bitcoinApiFactory from '../../../api/bitcoin/bitcoin-api-factory'; import bitcoinClient from '../../../api/bitcoin/bitcoin-client'; import config from '../../../config'; -import DB from '../../../database'; import logger from '../../../logger'; const fsPromises = promises; @@ -16,12 +14,7 @@ class FundingTxFetcher { private channelNewlyProcessed = 0; public fundingTxCache = {}; - async $fetchChannelsFundingTxs(channelIds: string[]): Promise { - if (this.running) { - return; - } - this.running = true; - + async $init(): Promise { // Load funding tx disk cache if (Object.keys(this.fundingTxCache).length === 0 && existsSync(CACHE_FILE_NAME)) { try { @@ -32,6 +25,13 @@ class FundingTxFetcher { } logger.debug(`Imported ${Object.keys(this.fundingTxCache).length} funding tx amount from the disk cache`); } + } + + async $fetchChannelsFundingTxs(channelIds: string[]): Promise { + if (this.running) { + return; + } + this.running = true; const globalTimer = new Date().getTime() / 1000; let cacheTimer = new Date().getTime() / 1000;