From 3a7dffbe09e77570b22d7a8ed4ca3f67a6b14bcc Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Tue, 6 Dec 2022 10:51:01 +0100 Subject: [PATCH] Fix crash when channel short id is not valid --- .../clightning/clightning-convert.ts | 26 +++++++++++++------ .../tasks/lightning/network-sync.service.ts | 3 +++ .../sync-tasks/funding-tx-fetcher.ts | 7 ++++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/backend/src/api/lightning/clightning/clightning-convert.ts b/backend/src/api/lightning/clightning/clightning-convert.ts index 92ae1f0a7..817a36e1f 100644 --- a/backend/src/api/lightning/clightning/clightning-convert.ts +++ b/backend/src/api/lightning/clightning/clightning-convert.ts @@ -54,11 +54,12 @@ export async function convertAndmergeBidirectionalChannels(clChannels: any[]): P clChannelsDict[clChannel.short_channel_id] = clChannel; clChannelsDictCount[clChannel.short_channel_id] = 1; } else { - consolidatedChannelList.push( - await buildFullChannel(clChannel, clChannelsDict[clChannel.short_channel_id]) - ); - delete clChannelsDict[clChannel.short_channel_id]; - clChannelsDictCount[clChannel.short_channel_id]++; + const fullChannel = await buildFullChannel(clChannel, clChannelsDict[clChannel.short_channel_id]); + if (fullChannel !== null) { + consolidatedChannelList.push(fullChannel); + delete clChannelsDict[clChannel.short_channel_id]; + clChannelsDictCount[clChannel.short_channel_id]++; + } } const elapsedSeconds = Math.round((new Date().getTime() / 1000) - loggerTimer); @@ -73,7 +74,10 @@ export async function convertAndmergeBidirectionalChannels(clChannels: any[]): P channelProcessed = 0; const keys = Object.keys(clChannelsDict); for (const short_channel_id of keys) { - consolidatedChannelList.push(await buildIncompleteChannel(clChannelsDict[short_channel_id])); + const incompleteChannel = await buildIncompleteChannel(clChannelsDict[short_channel_id]); + if (incompleteChannel !== null) { + consolidatedChannelList.push(incompleteChannel); + } const elapsedSeconds = Math.round((new Date().getTime() / 1000) - loggerTimer); if (elapsedSeconds > 10) { @@ -91,10 +95,13 @@ export async function convertAndmergeBidirectionalChannels(clChannels: any[]): P * Convert two clightning "getchannels" entries into a full a lnd "describegraph.edges" format * In this case, clightning knows the channel policy for both nodes */ -async function buildFullChannel(clChannelA: any, clChannelB: any): Promise { +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); + if (!tx) { + return null; + } const parts = clChannelA.short_channel_id.split('x'); const outputIdx = parts[2]; @@ -114,8 +121,11 @@ async function buildFullChannel(clChannelA: any, clChannelB: any): Promise { +async function buildIncompleteChannel(clChannel: any): Promise { const tx = await FundingTxFetcher.$fetchChannelOpenTx(clChannel.short_channel_id); + if (!tx) { + return null; + } const parts = clChannel.short_channel_id.split('x'); const outputIdx = parts[2]; diff --git a/backend/src/tasks/lightning/network-sync.service.ts b/backend/src/tasks/lightning/network-sync.service.ts index c5e5a102d..3f66df9d2 100644 --- a/backend/src/tasks/lightning/network-sync.service.ts +++ b/backend/src/tasks/lightning/network-sync.service.ts @@ -208,6 +208,9 @@ class NetworkSyncService { const channels = await channelsApi.$getChannelsWithoutCreatedDate(); for (const channel of channels) { const transaction = await fundingTxFetcher.$fetchChannelOpenTx(channel.short_id); + if (!transaction) { + continue; + } await DB.query(` UPDATE channels SET created = FROM_UNIXTIME(?) WHERE channels.id = ?`, [transaction.timestamp, channel.id] 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 76865dc40..2de5b7628 100644 --- a/backend/src/tasks/lightning/sync-tasks/funding-tx-fetcher.ts +++ b/backend/src/tasks/lightning/sync-tasks/funding-tx-fetcher.ts @@ -70,7 +70,7 @@ class FundingTxFetcher { this.running = false; } - public async $fetchChannelOpenTx(channelId: string): Promise<{timestamp: number, txid: string, value: number}> { + public async $fetchChannelOpenTx(channelId: string): Promise<{timestamp: number, txid: string, value: number} | null> { channelId = Common.channelIntegerIdToShortId(channelId); if (this.fundingTxCache[channelId]) { @@ -101,6 +101,11 @@ class FundingTxFetcher { const rawTx = await bitcoinClient.getRawTransaction(txid); const tx = await bitcoinClient.decodeRawTransaction(rawTx); + if (!tx || !tx.vout || tx.vout.length < parseInt(outputIdx, 10) + 1 || tx.vout[outputIdx].value === undefined) { + logger.err(`Cannot find blockchain funding tx for channel id ${channelId}. Possible reasons are: bitcoin backend timeout or the channel shortId is not valid`); + return null; + } + this.fundingTxCache[channelId] = { timestamp: block.time, txid: txid,