2022-07-29 17:41:09 +02:00
|
|
|
import { ILightningApi } from '../lightning-api.interface';
|
2022-08-02 16:39:34 +02:00
|
|
|
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-07-29 16:33:07 +02:00
|
|
|
|
2022-07-29 17:41:09 +02:00
|
|
|
/**
|
|
|
|
* Convert a clightning "listnode" entry to a lnd node entry
|
|
|
|
*/
|
2022-07-29 16:33:07 +02:00
|
|
|
export function convertNode(clNode: any): ILightningApi.Node {
|
|
|
|
return {
|
|
|
|
alias: clNode.alias ?? '',
|
|
|
|
color: `#${clNode.color ?? ''}`,
|
|
|
|
features: [], // TODO parse and return clNode.feature
|
2022-08-01 19:42:33 +02:00
|
|
|
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}]`;
|
|
|
|
}
|
2022-08-01 19:42:33 +02:00
|
|
|
return {
|
|
|
|
network: addr.type,
|
2022-08-30 20:31:04 +02:00
|
|
|
addr: `${address}:${addr.port}`
|
2022-08-01 19:42:33 +02:00
|
|
|
};
|
2022-08-13 10:24:11 +02:00
|
|
|
}) ?? [],
|
2022-08-01 19:42:33 +02:00
|
|
|
last_update: clNode?.last_timestamp ?? 0,
|
2022-07-29 16:33:07 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-07-29 17:41:09 +02:00
|
|
|
/**
|
2022-08-01 19:42:33 +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[]> {
|
|
|
|
logger.info('Converting clightning nodes and channels to lnd graph format');
|
|
|
|
|
|
|
|
let loggerTimer = new Date().getTime() / 1000;
|
|
|
|
let channelProcessed = 0;
|
|
|
|
|
2022-07-29 16:33:07 +02:00
|
|
|
const consolidatedChannelList: ILightningApi.Channel[] = [];
|
|
|
|
const clChannelsDict = {};
|
|
|
|
const clChannelsDictCount = {};
|
|
|
|
|
2022-08-03 12:13:55 +02:00
|
|
|
for (const clChannel of clChannels) {
|
2022-07-29 16:33:07 +02:00
|
|
|
if (!clChannelsDict[clChannel.short_channel_id]) {
|
|
|
|
clChannelsDict[clChannel.short_channel_id] = clChannel;
|
|
|
|
clChannelsDictCount[clChannel.short_channel_id] = 1;
|
|
|
|
} else {
|
|
|
|
consolidatedChannelList.push(
|
2022-08-02 16:39:34 +02:00
|
|
|
await buildFullChannel(clChannel, clChannelsDict[clChannel.short_channel_id])
|
2022-07-29 16:33:07 +02:00
|
|
|
);
|
|
|
|
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);
|
|
|
|
if (elapsedSeconds > 10) {
|
|
|
|
logger.info(`Building complete channels from clightning output. Channels processed: ${channelProcessed + 1} of ${clChannels.length}`);
|
|
|
|
loggerTimer = new Date().getTime() / 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
++channelProcessed;
|
2022-07-29 16:33:07 +02:00
|
|
|
}
|
2022-08-03 12:13:55 +02:00
|
|
|
|
|
|
|
channelProcessed = 0;
|
|
|
|
const keys = Object.keys(clChannelsDict);
|
|
|
|
for (const short_channel_id of keys) {
|
2022-08-02 16:39:34 +02:00
|
|
|
consolidatedChannelList.push(await buildIncompleteChannel(clChannelsDict[short_channel_id]));
|
2022-08-03 12:13:55 +02:00
|
|
|
|
|
|
|
const elapsedSeconds = Math.round((new Date().getTime() / 1000) - loggerTimer);
|
|
|
|
if (elapsedSeconds > 10) {
|
|
|
|
logger.info(`Building partial channels from clightning output. Channels processed: ${channelProcessed + 1} of ${keys.length}`);
|
|
|
|
loggerTimer = new Date().getTime() / 1000;
|
|
|
|
}
|
2022-07-29 16:33:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return consolidatedChannelList;
|
|
|
|
}
|
|
|
|
|
2022-07-29 17:41:09 +02:00
|
|
|
/**
|
2022-08-01 19:42:33 +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
|
|
|
|
*/
|
2022-08-02 16:39:34 +02:00
|
|
|
async function buildFullChannel(clChannelA: any, clChannelB: any): Promise<ILightningApi.Channel> {
|
2022-07-29 16:33:07 +02:00
|
|
|
const lastUpdate = Math.max(clChannelA.last_update ?? 0, clChannelB.last_update ?? 0);
|
2022-08-02 16:39:34 +02:00
|
|
|
|
|
|
|
const tx = await FundingTxFetcher.$fetchChannelOpenTx(clChannelA.short_channel_id);
|
|
|
|
const parts = clChannelA.short_channel_id.split('x');
|
|
|
|
const outputIdx = parts[2];
|
|
|
|
|
2022-07-29 16:33:07 +02:00
|
|
|
return {
|
2022-08-10 16:58:29 +02:00
|
|
|
channel_id: Common.channelShortIdToIntegerId(clChannelA.short_channel_id),
|
2022-07-29 16:33:07 +02:00
|
|
|
capacity: clChannelA.satoshis,
|
2022-08-01 19:42:33 +02:00
|
|
|
last_update: lastUpdate,
|
|
|
|
node1_policy: convertPolicy(clChannelA),
|
|
|
|
node2_policy: convertPolicy(clChannelB),
|
2022-08-02 16:39:34 +02:00
|
|
|
chan_point: `${tx.txid}:${outputIdx}`,
|
2022-08-01 19:42:33 +02:00
|
|
|
node1_pub: clChannelA.source,
|
|
|
|
node2_pub: clChannelB.source,
|
2022-07-29 16:33:07 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-07-29 17:41:09 +02:00
|
|
|
/**
|
2022-08-01 19:42:33 +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
|
|
|
|
*/
|
2022-08-03 12:13:55 +02:00
|
|
|
async function buildIncompleteChannel(clChannel: any): Promise<ILightningApi.Channel> {
|
2022-08-02 16:39:34 +02:00
|
|
|
const tx = await FundingTxFetcher.$fetchChannelOpenTx(clChannel.short_channel_id);
|
|
|
|
const parts = clChannel.short_channel_id.split('x');
|
|
|
|
const outputIdx = parts[2];
|
|
|
|
|
2022-07-29 16:33:07 +02:00
|
|
|
return {
|
2022-08-10 16:58:29 +02:00
|
|
|
channel_id: Common.channelShortIdToIntegerId(clChannel.short_channel_id),
|
2022-07-29 16:33:07 +02:00
|
|
|
capacity: clChannel.satoshis,
|
2022-08-01 19:42:33 +02:00
|
|
|
last_update: clChannel.last_update ?? 0,
|
|
|
|
node1_policy: convertPolicy(clChannel),
|
|
|
|
node2_policy: null,
|
2022-08-02 16:39:34 +02:00
|
|
|
chan_point: `${tx.txid}:${outputIdx}`,
|
2022-08-01 19:42:33 +02:00
|
|
|
node1_pub: clChannel.source,
|
|
|
|
node2_pub: clChannel.destination,
|
2022-07-29 16:33:07 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
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 {
|
2022-07-29 16:33:07 +02:00
|
|
|
return {
|
2022-08-01 19:42:33 +02:00
|
|
|
time_lock_delta: 0, // TODO
|
|
|
|
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,
|
2022-07-29 16:33:07 +02:00
|
|
|
};
|
|
|
|
}
|