mempool/backend/src/api/lightning/clightning/clightning-convert.ts

159 lines
5.6 KiB
TypeScript
Raw Normal View History

2022-07-29 17:41:09 +02:00
import { ILightningApi } from '../lightning-api.interface';
import FundingTxFetcher from '../../../tasks/lightning/sync-tasks/funding-tx-fetcher';
2022-08-03 12:13:55 +02:00
import logger from '../../../logger';
2022-08-10 16:58:29 +02:00
import { Common } from '../../common';
2022-12-01 15:52:06 +01:00
import config from '../../../config';
2022-07-29 17:41:09 +02:00
/**
* Convert a clightning "listnode" entry to a lnd node entry
*/
export function convertNode(clNode: any): ILightningApi.Node {
let custom_records: { [type: number]: string } | undefined = undefined;
if (clNode.option_will_fund) {
try {
custom_records = { '1': Buffer.from(clNode.option_will_fund.compact_lease || '', 'hex').toString('base64') };
} catch (e) {
logger.err(`Cannot decode option_will_fund compact_lease for ${clNode.nodeid}). Reason: ` + (e instanceof Error ? e.message : e));
custom_records = undefined;
}
}
return {
alias: clNode.alias ?? '',
color: `#${clNode.color ?? ''}`,
features: [], // TODO parse and return clNode.feature
pub_key: clNode.nodeid,
addresses: clNode.addresses?.map((addr) => {
2022-08-30 20:31:04 +02:00
let address = addr.address;
if (addr.type === 'ipv6') {
address = `[${address}]`;
}
return {
network: addr.type,
2022-08-30 20:31:04 +02:00
addr: `${address}:${addr.port}`
};
2022-08-13 10:24:11 +02:00
}) ?? [],
last_update: clNode?.last_timestamp ?? 0,
custom_records
};
}
2022-07-29 17:41:09 +02:00
/**
* Convert clightning "listchannels" response to lnd "describegraph.edges" format
2022-07-29 17:41:09 +02:00
*/
2022-08-03 12:13:55 +02:00
export async function convertAndmergeBidirectionalChannels(clChannels: any[]): Promise<ILightningApi.Channel[]> {
2022-12-01 15:52:06 +01:00
logger.debug(`Converting clightning nodes and channels to lnd graph format`, logger.tags.ln);
2022-08-03 12:13:55 +02:00
let loggerTimer = new Date().getTime() / 1000;
let channelProcessed = 0;
const consolidatedChannelList: ILightningApi.Channel[] = [];
const clChannelsDict = {};
const clChannelsDictCount = {};
2022-08-03 12:13:55 +02:00
for (const clChannel of clChannels) {
if (!clChannelsDict[clChannel.short_channel_id]) {
clChannelsDict[clChannel.short_channel_id] = clChannel;
clChannelsDictCount[clChannel.short_channel_id] = 1;
} else {
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]++;
}
}
2022-08-03 12:13:55 +02:00
const elapsedSeconds = Math.round((new Date().getTime() / 1000) - loggerTimer);
2022-12-01 15:52:06 +01:00
if (elapsedSeconds > config.LIGHTNING.LOGGER_UPDATE_INTERVAL) {
logger.info(`Building complete channels from clightning output. Channels processed: ${channelProcessed + 1} of ${clChannels.length}`, logger.tags.ln);
2022-08-03 12:13:55 +02:00
loggerTimer = new Date().getTime() / 1000;
}
++channelProcessed;
}
2022-08-03 12:13:55 +02:00
channelProcessed = 0;
const keys = Object.keys(clChannelsDict);
for (const short_channel_id of keys) {
const incompleteChannel = await buildIncompleteChannel(clChannelsDict[short_channel_id]);
if (incompleteChannel !== null) {
consolidatedChannelList.push(incompleteChannel);
}
2022-08-03 12:13:55 +02:00
const elapsedSeconds = Math.round((new Date().getTime() / 1000) - loggerTimer);
2022-12-01 15:52:06 +01:00
if (elapsedSeconds > config.LIGHTNING.LOGGER_UPDATE_INTERVAL) {
2022-08-03 12:13:55 +02:00
logger.info(`Building partial channels from clightning output. Channels processed: ${channelProcessed + 1} of ${keys.length}`);
loggerTimer = new Date().getTime() / 1000;
}
2022-09-30 19:10:11 +02:00
channelProcessed++;
}
return consolidatedChannelList;
}
2022-07-29 17:41:09 +02:00
/**
* Convert two clightning "getchannels" entries into a full a lnd "describegraph.edges" format
2022-07-29 17:41:09 +02:00
* In this case, clightning knows the channel policy for both nodes
*/
async function buildFullChannel(clChannelA: any, clChannelB: any): Promise<ILightningApi.Channel | null> {
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];
return {
2022-08-10 16:58:29 +02:00
channel_id: Common.channelShortIdToIntegerId(clChannelA.short_channel_id),
capacity: clChannelA.satoshis,
last_update: lastUpdate,
node1_policy: convertPolicy(clChannelA),
node2_policy: convertPolicy(clChannelB),
chan_point: `${tx.txid}:${outputIdx}`,
node1_pub: clChannelA.source,
node2_pub: clChannelB.source,
};
}
2022-07-29 17:41:09 +02:00
/**
* Convert one clightning "getchannels" entry into a full a lnd "describegraph.edges" format
2022-07-29 17:41:09 +02:00
* In this case, clightning knows the channel policy of only one node
*/
async function buildIncompleteChannel(clChannel: any): Promise<ILightningApi.Channel | null> {
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];
return {
2022-08-10 16:58:29 +02:00
channel_id: Common.channelShortIdToIntegerId(clChannel.short_channel_id),
capacity: clChannel.satoshis,
last_update: clChannel.last_update ?? 0,
node1_policy: convertPolicy(clChannel),
node2_policy: null,
chan_point: `${tx.txid}:${outputIdx}`,
node1_pub: clChannel.source,
node2_pub: clChannel.destination,
};
}
2022-07-29 17:41:09 +02:00
/**
* Convert a clightning "listnode" response to a lnd channel policy format
*/
2022-08-03 12:13:55 +02:00
function convertPolicy(clChannel: any): ILightningApi.RoutingPolicy {
return {
time_lock_delta: clChannel.delay,
min_htlc: clChannel.htlc_minimum_msat.slice(0, -4),
max_htlc_msat: clChannel.htlc_maximum_msat.slice(0, -4),
fee_base_msat: clChannel.base_fee_millisatoshi,
fee_rate_milli_msat: clChannel.fee_per_millionth,
disabled: !clChannel.active,
last_update: clChannel.last_update ?? 0,
};
}