Wrote some utility functions to convert clightning output to our db schema
This commit is contained in:
parent
3f83e517f0
commit
a94403b3a1
@ -0,0 +1,4 @@
|
|||||||
|
import config from '../../../config';
|
||||||
|
import CLightningClient from './jsonrpc';
|
||||||
|
|
||||||
|
export default new CLightningClient(config.CLIGHTNING.SOCKET);
|
95
backend/src/api/lightning/clightning/clightning-convert.ts
Normal file
95
backend/src/api/lightning/clightning/clightning-convert.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import logger from "../../../logger";
|
||||||
|
import { ILightningApi } from "../lightning-api.interface";
|
||||||
|
|
||||||
|
export function convertNode(clNode: any): ILightningApi.Node {
|
||||||
|
return {
|
||||||
|
alias: clNode.alias ?? '',
|
||||||
|
color: `#${clNode.color ?? ''}`,
|
||||||
|
features: [], // TODO parse and return clNode.feature
|
||||||
|
public_key: clNode.nodeid,
|
||||||
|
sockets: clNode.addresses?.map(addr => `${addr.address}:${addr.port}`) ?? [],
|
||||||
|
updated_at: new Date((clNode?.last_timestamp ?? 0) * 1000).toUTCString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertAndmergeBidirectionalChannels(clChannels: any[]): ILightningApi.Channel[] {
|
||||||
|
const consolidatedChannelList: ILightningApi.Channel[] = [];
|
||||||
|
const clChannelsDict = {};
|
||||||
|
const clChannelsDictCount = {};
|
||||||
|
|
||||||
|
for (const clChannel of clChannels) {
|
||||||
|
if (!clChannelsDict[clChannel.short_channel_id]) {
|
||||||
|
clChannelsDict[clChannel.short_channel_id] = clChannel;
|
||||||
|
clChannelsDictCount[clChannel.short_channel_id] = 1;
|
||||||
|
} else {
|
||||||
|
consolidatedChannelList.push(
|
||||||
|
buildBidirectionalChannel(clChannel, clChannelsDict[clChannel.short_channel_id])
|
||||||
|
);
|
||||||
|
delete clChannelsDict[clChannel.short_channel_id];
|
||||||
|
clChannelsDictCount[clChannel.short_channel_id]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const bidirectionalChannelsCount = consolidatedChannelList.length;
|
||||||
|
|
||||||
|
for (const short_channel_id of Object.keys(clChannelsDict)) {
|
||||||
|
consolidatedChannelList.push(buildUnidirectionalChannel(clChannelsDict[short_channel_id]));
|
||||||
|
}
|
||||||
|
const unidirectionalChannelsCount = consolidatedChannelList.length - bidirectionalChannelsCount;
|
||||||
|
|
||||||
|
logger.debug(`clightning knows ${clChannels.length} channels. ` +
|
||||||
|
`We found ${bidirectionalChannelsCount} bidirectional channels ` +
|
||||||
|
`and ${unidirectionalChannelsCount} unidirectional channels.`);
|
||||||
|
|
||||||
|
return consolidatedChannelList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBidirectionalChannel(clChannelA: any, clChannelB: any): ILightningApi.Channel {
|
||||||
|
const lastUpdate = Math.max(clChannelA.last_update ?? 0, clChannelB.last_update ?? 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: clChannelA.short_channel_id,
|
||||||
|
capacity: clChannelA.satoshis,
|
||||||
|
transaction_id: '', // TODO
|
||||||
|
transaction_vout: 0, // TODO
|
||||||
|
updated_at: new Date(lastUpdate * 1000).toUTCString(),
|
||||||
|
policies: [
|
||||||
|
convertPolicy(clChannelA),
|
||||||
|
convertPolicy(clChannelB)
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildUnidirectionalChannel(clChannel: any): ILightningApi.Channel {
|
||||||
|
return {
|
||||||
|
id: clChannel.short_channel_id,
|
||||||
|
capacity: clChannel.satoshis,
|
||||||
|
policies: [convertPolicy(clChannel), getEmptyPolicy()],
|
||||||
|
transaction_id: '', // TODO
|
||||||
|
transaction_vout: 0, // TODO
|
||||||
|
updated_at: new Date((clChannel.last_update ?? 0) * 1000).toUTCString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertPolicy(clChannel: any): ILightningApi.Policy {
|
||||||
|
return {
|
||||||
|
public_key: clChannel.source,
|
||||||
|
base_fee_mtokens: clChannel.base_fee_millisatoshi,
|
||||||
|
fee_rate: clChannel.fee_per_millionth,
|
||||||
|
is_disabled: !clChannel.active,
|
||||||
|
max_htlc_mtokens: clChannel.htlc_maximum_msat.slice(0, -4),
|
||||||
|
min_htlc_mtokens: clChannel.htlc_minimum_msat.slice(0, -4),
|
||||||
|
updated_at: new Date((clChannel.last_update ?? 0) * 1000).toUTCString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEmptyPolicy(): ILightningApi.Policy {
|
||||||
|
return {
|
||||||
|
public_key: 'null',
|
||||||
|
base_fee_mtokens: '0',
|
||||||
|
fee_rate: 0,
|
||||||
|
is_disabled: true,
|
||||||
|
max_htlc_mtokens: '0',
|
||||||
|
min_htlc_mtokens: '0',
|
||||||
|
updated_at: new Date(0).toUTCString(),
|
||||||
|
};
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
// Imported from https://github.com/shesek/lightning-client-js
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const methods = [
|
const methods = [
|
||||||
@ -96,7 +98,7 @@ import { createConnection, Socket } from 'net';
|
|||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { createInterface, Interface } from 'readline';
|
import { createInterface, Interface } from 'readline';
|
||||||
import logger from '../../logger';
|
import logger from '../../../logger';
|
||||||
|
|
||||||
class LightningError extends Error {
|
class LightningError extends Error {
|
||||||
type: string = 'lightning';
|
type: string = 'lightning';
|
||||||
@ -113,7 +115,7 @@ const defaultRpcPath = path.join(homedir(), '.lightning')
|
|||||||
, fStat = (...p) => statSync(path.join(...p))
|
, fStat = (...p) => statSync(path.join(...p))
|
||||||
, fExists = (...p) => existsSync(path.join(...p))
|
, fExists = (...p) => existsSync(path.join(...p))
|
||||||
|
|
||||||
class CLightningClient extends EventEmitter {
|
export default class CLightningClient extends EventEmitter {
|
||||||
private rpcPath: string;
|
private rpcPath: string;
|
||||||
private reconnectWait: number;
|
private reconnectWait: number;
|
||||||
private reconnectTimeout;
|
private reconnectTimeout;
|
||||||
@ -182,7 +184,7 @@ class CLightningClient extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = JSON.parse(line);
|
const data = JSON.parse(line);
|
||||||
logger.debug(`[CLightningClient] #${data.id} <-- ${JSON.stringify(data.error || data.result)}`);
|
// logger.debug(`[CLightningClient] #${data.id} <-- ${JSON.stringify(data.error || data.result)}`);
|
||||||
_self.emit('res:' + data.id, data);
|
_self.emit('res:' + data.id, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -210,7 +212,7 @@ class CLightningClient extends EventEmitter {
|
|||||||
}, this.reconnectWait * 1000);
|
}, this.reconnectWait * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
call(method, args = []): Promise<unknown> {
|
call(method, args = []): Promise<any> {
|
||||||
const _self = this;
|
const _self = this;
|
||||||
|
|
||||||
const callInt = ++this.reqcount;
|
const callInt = ++this.reqcount;
|
||||||
@ -245,5 +247,3 @@ methods.forEach(k => {
|
|||||||
return this.call(k, args);
|
return this.call(k, args);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export default new CLightningClient();
|
|
@ -38,6 +38,9 @@ interface IConfig {
|
|||||||
MACAROON_PATH: string;
|
MACAROON_PATH: string;
|
||||||
REST_API_URL: string;
|
REST_API_URL: string;
|
||||||
};
|
};
|
||||||
|
CLIGHTNING: {
|
||||||
|
SOCKET: string;
|
||||||
|
};
|
||||||
ELECTRUM: {
|
ELECTRUM: {
|
||||||
HOST: string;
|
HOST: string;
|
||||||
PORT: number;
|
PORT: number;
|
||||||
@ -186,6 +189,9 @@ const defaults: IConfig = {
|
|||||||
'MACAROON_PATH': '',
|
'MACAROON_PATH': '',
|
||||||
'REST_API_URL': 'https://localhost:8080',
|
'REST_API_URL': 'https://localhost:8080',
|
||||||
},
|
},
|
||||||
|
'CLIGHTNING': {
|
||||||
|
'SOCKET': '',
|
||||||
|
},
|
||||||
'SOCKS5PROXY': {
|
'SOCKS5PROXY': {
|
||||||
'ENABLED': false,
|
'ENABLED': false,
|
||||||
'USE_ONION': true,
|
'USE_ONION': true,
|
||||||
@ -226,6 +232,7 @@ class Config implements IConfig {
|
|||||||
BISQ: IConfig['BISQ'];
|
BISQ: IConfig['BISQ'];
|
||||||
LIGHTNING: IConfig['LIGHTNING'];
|
LIGHTNING: IConfig['LIGHTNING'];
|
||||||
LND: IConfig['LND'];
|
LND: IConfig['LND'];
|
||||||
|
CLIGHTNING: IConfig['CLIGHTNING'];
|
||||||
SOCKS5PROXY: IConfig['SOCKS5PROXY'];
|
SOCKS5PROXY: IConfig['SOCKS5PROXY'];
|
||||||
PRICE_DATA_SERVER: IConfig['PRICE_DATA_SERVER'];
|
PRICE_DATA_SERVER: IConfig['PRICE_DATA_SERVER'];
|
||||||
EXTERNAL_DATA_SERVER: IConfig['EXTERNAL_DATA_SERVER'];
|
EXTERNAL_DATA_SERVER: IConfig['EXTERNAL_DATA_SERVER'];
|
||||||
@ -244,6 +251,7 @@ class Config implements IConfig {
|
|||||||
this.BISQ = configs.BISQ;
|
this.BISQ = configs.BISQ;
|
||||||
this.LIGHTNING = configs.LIGHTNING;
|
this.LIGHTNING = configs.LIGHTNING;
|
||||||
this.LND = configs.LND;
|
this.LND = configs.LND;
|
||||||
|
this.CLIGHTNING = configs.CLIGHTNING;
|
||||||
this.SOCKS5PROXY = configs.SOCKS5PROXY;
|
this.SOCKS5PROXY = configs.SOCKS5PROXY;
|
||||||
this.PRICE_DATA_SERVER = configs.PRICE_DATA_SERVER;
|
this.PRICE_DATA_SERVER = configs.PRICE_DATA_SERVER;
|
||||||
this.EXTERNAL_DATA_SERVER = configs.EXTERNAL_DATA_SERVER;
|
this.EXTERNAL_DATA_SERVER = configs.EXTERNAL_DATA_SERVER;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user