Delete historical generation code
This commit is contained in:
		
							parent
							
								
									a94403b3a1
								
							
						
					
					
						commit
						eb90434c28
					
				@ -1,4 +1,263 @@
 | 
			
		||||
import config from '../../../config';
 | 
			
		||||
import CLightningClient from './jsonrpc';
 | 
			
		||||
// Imported from https://github.com/shesek/lightning-client-js
 | 
			
		||||
 | 
			
		||||
export default new CLightningClient(config.CLIGHTNING.SOCKET);
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const methods = [
 | 
			
		||||
  'addgossip',
 | 
			
		||||
  'autocleaninvoice',
 | 
			
		||||
  'check',
 | 
			
		||||
  'checkmessage',
 | 
			
		||||
  'close',
 | 
			
		||||
  'connect',
 | 
			
		||||
  'createinvoice',
 | 
			
		||||
  'createinvoicerequest',
 | 
			
		||||
  'createoffer',
 | 
			
		||||
  'createonion',
 | 
			
		||||
  'decode',
 | 
			
		||||
  'decodepay',
 | 
			
		||||
  'delexpiredinvoice',
 | 
			
		||||
  'delinvoice',
 | 
			
		||||
  'delpay',
 | 
			
		||||
  'dev-listaddrs',
 | 
			
		||||
  'dev-rescan-outputs',
 | 
			
		||||
  'disableoffer',
 | 
			
		||||
  'disconnect',
 | 
			
		||||
  'estimatefees',
 | 
			
		||||
  'feerates',
 | 
			
		||||
  'fetchinvoice',
 | 
			
		||||
  'fundchannel',
 | 
			
		||||
  'fundchannel_cancel',
 | 
			
		||||
  'fundchannel_complete',
 | 
			
		||||
  'fundchannel_start',
 | 
			
		||||
  'fundpsbt',
 | 
			
		||||
  'getchaininfo',
 | 
			
		||||
  'getinfo',
 | 
			
		||||
  'getlog',
 | 
			
		||||
  'getrawblockbyheight',
 | 
			
		||||
  'getroute',
 | 
			
		||||
  'getsharedsecret',
 | 
			
		||||
  'getutxout',
 | 
			
		||||
  'help',
 | 
			
		||||
  'invoice',
 | 
			
		||||
  'keysend',
 | 
			
		||||
  'legacypay',
 | 
			
		||||
  'listchannels',
 | 
			
		||||
  'listconfigs',
 | 
			
		||||
  'listforwards',
 | 
			
		||||
  'listfunds',
 | 
			
		||||
  'listinvoices',
 | 
			
		||||
  'listnodes',
 | 
			
		||||
  'listoffers',
 | 
			
		||||
  'listpays',
 | 
			
		||||
  'listpeers',
 | 
			
		||||
  'listsendpays',
 | 
			
		||||
  'listtransactions',
 | 
			
		||||
  'multifundchannel',
 | 
			
		||||
  'multiwithdraw',
 | 
			
		||||
  'newaddr',
 | 
			
		||||
  'notifications',
 | 
			
		||||
  'offer',
 | 
			
		||||
  'offerout',
 | 
			
		||||
  'openchannel_abort',
 | 
			
		||||
  'openchannel_bump',
 | 
			
		||||
  'openchannel_init',
 | 
			
		||||
  'openchannel_signed',
 | 
			
		||||
  'openchannel_update',
 | 
			
		||||
  'pay',
 | 
			
		||||
  'payersign',
 | 
			
		||||
  'paystatus',
 | 
			
		||||
  'ping',
 | 
			
		||||
  'plugin',
 | 
			
		||||
  'reserveinputs',
 | 
			
		||||
  'sendinvoice',
 | 
			
		||||
  'sendonion',
 | 
			
		||||
  'sendonionmessage',
 | 
			
		||||
  'sendpay',
 | 
			
		||||
  'sendpsbt',
 | 
			
		||||
  'sendrawtransaction',
 | 
			
		||||
  'setchannelfee',
 | 
			
		||||
  'signmessage',
 | 
			
		||||
  'signpsbt',
 | 
			
		||||
  'stop',
 | 
			
		||||
  'txdiscard',
 | 
			
		||||
  'txprepare',
 | 
			
		||||
  'txsend',
 | 
			
		||||
  'unreserveinputs',
 | 
			
		||||
  'utxopsbt',
 | 
			
		||||
  'waitanyinvoice',
 | 
			
		||||
  'waitblockheight',
 | 
			
		||||
  'waitinvoice',
 | 
			
		||||
  'waitsendpay',
 | 
			
		||||
  'withdraw'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import EventEmitter from 'events';
 | 
			
		||||
import { existsSync, statSync } from 'fs';
 | 
			
		||||
import { createConnection, Socket } from 'net';
 | 
			
		||||
import { homedir } from 'os';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import { createInterface, Interface } from 'readline';
 | 
			
		||||
import logger from '../../../logger';
 | 
			
		||||
import { AbstractLightningApi } from '../lightning-api-abstract-factory';
 | 
			
		||||
import { ILightningApi } from '../lightning-api.interface';
 | 
			
		||||
import { convertAndmergeBidirectionalChannels, convertNode } from './clightning-convert';
 | 
			
		||||
 | 
			
		||||
class LightningError extends Error {
 | 
			
		||||
  type: string = 'lightning';
 | 
			
		||||
  message: string = 'lightning-client error';
 | 
			
		||||
 | 
			
		||||
  constructor(error) {
 | 
			
		||||
    super();
 | 
			
		||||
    this.type = error.type;
 | 
			
		||||
    this.message = error.message;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const defaultRpcPath = path.join(homedir(), '.lightning')
 | 
			
		||||
  , fStat = (...p) => statSync(path.join(...p))
 | 
			
		||||
  , fExists = (...p) => existsSync(path.join(...p))
 | 
			
		||||
 | 
			
		||||
export default class CLightningClient extends EventEmitter implements AbstractLightningApi {
 | 
			
		||||
  private rpcPath: string;
 | 
			
		||||
  private reconnectWait: number;
 | 
			
		||||
  private reconnectTimeout;
 | 
			
		||||
  private reqcount: number;
 | 
			
		||||
  private client: Socket;
 | 
			
		||||
  private rl: Interface;
 | 
			
		||||
  private clientConnectionPromise: Promise<unknown>;
 | 
			
		||||
 | 
			
		||||
  constructor(rpcPath = defaultRpcPath) {
 | 
			
		||||
    if (!path.isAbsolute(rpcPath)) {
 | 
			
		||||
      throw new Error('The rpcPath must be an absolute path');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!fExists(rpcPath) || !fStat(rpcPath).isSocket()) {
 | 
			
		||||
      // network directory provided, use the lightning-rpc within in
 | 
			
		||||
      if (fExists(rpcPath, 'lightning-rpc')) {
 | 
			
		||||
        rpcPath = path.join(rpcPath, 'lightning-rpc');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // main data directory provided, default to using the bitcoin mainnet subdirectory
 | 
			
		||||
      // to be removed in v0.2.0
 | 
			
		||||
      else if (fExists(rpcPath, 'bitcoin', 'lightning-rpc')) {
 | 
			
		||||
        logger.warn(`[CLightningClient] ${rpcPath}/lightning-rpc is missing, using the bitcoin mainnet subdirectory at ${rpcPath}/bitcoin instead.`)
 | 
			
		||||
        logger.warn(`[CLightningClient] specifying the main lightning data directory is deprecated, please specify the network directory explicitly.\n`)
 | 
			
		||||
        rpcPath = path.join(rpcPath, 'bitcoin', 'lightning-rpc')
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    logger.debug(`[CLightningClient] Connecting to ${rpcPath}`);
 | 
			
		||||
 | 
			
		||||
    super();
 | 
			
		||||
    this.rpcPath = rpcPath;
 | 
			
		||||
    this.reconnectWait = 0.5;
 | 
			
		||||
    this.reconnectTimeout = null;
 | 
			
		||||
    this.reqcount = 0;
 | 
			
		||||
 | 
			
		||||
    const _self = this;
 | 
			
		||||
 | 
			
		||||
    this.client = createConnection(rpcPath);
 | 
			
		||||
    this.rl = createInterface({ input: this.client })
 | 
			
		||||
 | 
			
		||||
    this.clientConnectionPromise = new Promise<void>(resolve => {
 | 
			
		||||
      _self.client.on('connect', () => {
 | 
			
		||||
        logger.info(`[CLightningClient] Lightning client connected`);
 | 
			
		||||
        _self.reconnectWait = 1;
 | 
			
		||||
        resolve();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      _self.client.on('end', () => {
 | 
			
		||||
        logger.err('[CLightningClient] Lightning client connection closed, reconnecting');
 | 
			
		||||
        _self.increaseWaitTime();
 | 
			
		||||
        _self.reconnect();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      _self.client.on('error', error => {
 | 
			
		||||
        logger.err(`[CLightningClient] Lightning client connection error: ${error}`);
 | 
			
		||||
        _self.emit('error', error);
 | 
			
		||||
        _self.increaseWaitTime();
 | 
			
		||||
        _self.reconnect();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.rl.on('line', line => {
 | 
			
		||||
      line = line.trim();
 | 
			
		||||
      if (!line) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      const data = JSON.parse(line);
 | 
			
		||||
      // logger.debug(`[CLightningClient] #${data.id} <-- ${JSON.stringify(data.error || data.result)}`);
 | 
			
		||||
      _self.emit('res:' + data.id, data);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  increaseWaitTime(): void {
 | 
			
		||||
    if (this.reconnectWait >= 16) {
 | 
			
		||||
      this.reconnectWait = 16;
 | 
			
		||||
    } else {
 | 
			
		||||
      this.reconnectWait *= 2;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reconnect(): void {
 | 
			
		||||
    const _self = this;
 | 
			
		||||
 | 
			
		||||
    if (this.reconnectTimeout) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.reconnectTimeout = setTimeout(() => {
 | 
			
		||||
      logger.debug('[CLightningClient] Trying to reconnect...');
 | 
			
		||||
 | 
			
		||||
      _self.client.connect(_self.rpcPath);
 | 
			
		||||
      _self.reconnectTimeout = null;
 | 
			
		||||
    }, this.reconnectWait * 1000);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  call(method, args = []): Promise<any> {
 | 
			
		||||
    const _self = this;
 | 
			
		||||
 | 
			
		||||
    const callInt = ++this.reqcount;
 | 
			
		||||
    const sendObj = {
 | 
			
		||||
      jsonrpc: '2.0',
 | 
			
		||||
      method,
 | 
			
		||||
      params: args,
 | 
			
		||||
      id: '' + callInt
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // logger.debug(`[CLightningClient] #${callInt} --> ${method} ${args}`);
 | 
			
		||||
 | 
			
		||||
    // Wait for the client to connect
 | 
			
		||||
    return this.clientConnectionPromise
 | 
			
		||||
      .then(() => new Promise((resolve, reject) => {
 | 
			
		||||
        // Wait for a response
 | 
			
		||||
        this.once('res:' + callInt, res => res.error == null
 | 
			
		||||
          ? resolve(res.result)
 | 
			
		||||
          : reject(new LightningError(res.error))
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Send the command
 | 
			
		||||
        _self.client.write(JSON.stringify(sendObj));
 | 
			
		||||
      }));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async $getNetworkGraph(): Promise<ILightningApi.NetworkGraph> {
 | 
			
		||||
    const listnodes: any[] = await this.call('listnodes');
 | 
			
		||||
    const listchannels: any[] = await this.call('listchannels');
 | 
			
		||||
    const channelsList = convertAndmergeBidirectionalChannels(listchannels['channels']);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      nodes: listnodes['nodes'].map(node => convertNode(node)),
 | 
			
		||||
      channels: channelsList,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const protify = s => s.replace(/-([a-z])/g, m => m[1].toUpperCase());
 | 
			
		||||
 | 
			
		||||
methods.forEach(k => {
 | 
			
		||||
  CLightningClient.prototype[protify(k)] = function (...args: any) {
 | 
			
		||||
    return this.call(k, args);
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
import logger from "../../../logger";
 | 
			
		||||
import { ILightningApi } from "../lightning-api.interface";
 | 
			
		||||
import { ILightningApi } from '../lightning-api.interface';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert a clightning "listnode" entry to a lnd node entry
 | 
			
		||||
 */
 | 
			
		||||
export function convertNode(clNode: any): ILightningApi.Node {
 | 
			
		||||
  return {
 | 
			
		||||
    alias: clNode.alias ?? '',
 | 
			
		||||
@ -12,7 +14,10 @@ export function convertNode(clNode: any): ILightningApi.Node {
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function convertAndmergeBidirectionalChannels(clChannels: any[]): ILightningApi.Channel[] {
 | 
			
		||||
/**
 | 
			
		||||
 * Convert clightning "listchannels" response to lnd "describegraph.channels" format
 | 
			
		||||
 */
 | 
			
		||||
 export function convertAndmergeBidirectionalChannels(clChannels: any[]): ILightningApi.Channel[] {
 | 
			
		||||
  const consolidatedChannelList: ILightningApi.Channel[] = [];
 | 
			
		||||
  const clChannelsDict = {};
 | 
			
		||||
  const clChannelsDictCount = {};
 | 
			
		||||
@ -23,27 +28,24 @@ export function convertAndmergeBidirectionalChannels(clChannels: any[]): ILightn
 | 
			
		||||
      clChannelsDictCount[clChannel.short_channel_id] = 1;
 | 
			
		||||
    } else {
 | 
			
		||||
      consolidatedChannelList.push(
 | 
			
		||||
        buildBidirectionalChannel(clChannel, clChannelsDict[clChannel.short_channel_id])
 | 
			
		||||
        buildFullChannel(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]));
 | 
			
		||||
    consolidatedChannelList.push(buildIncompleteChannel(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 {
 | 
			
		||||
/**
 | 
			
		||||
 * Convert two clightning "getchannels" entries into a full a lnd "describegraph.channels" format
 | 
			
		||||
 * In this case, clightning knows the channel policy for both nodes
 | 
			
		||||
 */
 | 
			
		||||
function buildFullChannel(clChannelA: any, clChannelB: any): ILightningApi.Channel {
 | 
			
		||||
  const lastUpdate = Math.max(clChannelA.last_update ?? 0, clChannelB.last_update ?? 0);
 | 
			
		||||
  
 | 
			
		||||
  return {
 | 
			
		||||
@ -59,7 +61,11 @@ function buildBidirectionalChannel(clChannelA: any, clChannelB: any): ILightning
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function buildUnidirectionalChannel(clChannel: any): ILightningApi.Channel {
 | 
			
		||||
/**
 | 
			
		||||
 * Convert one clightning "getchannels" entry into a full a lnd "describegraph.channels" format
 | 
			
		||||
 * In this case, clightning knows the channel policy of only one node
 | 
			
		||||
 */
 | 
			
		||||
 function buildIncompleteChannel(clChannel: any): ILightningApi.Channel {
 | 
			
		||||
  return {
 | 
			
		||||
    id: clChannel.short_channel_id,
 | 
			
		||||
    capacity: clChannel.satoshis,
 | 
			
		||||
@ -70,7 +76,10 @@ function buildUnidirectionalChannel(clChannel: any): ILightningApi.Channel {
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function convertPolicy(clChannel: any): ILightningApi.Policy {
 | 
			
		||||
/**
 | 
			
		||||
 * Convert a clightning "listnode" response to a lnd channel policy format
 | 
			
		||||
 */
 | 
			
		||||
 function convertPolicy(clChannel: any): ILightningApi.Policy {
 | 
			
		||||
  return {
 | 
			
		||||
    public_key: clChannel.source,
 | 
			
		||||
    base_fee_mtokens: clChannel.base_fee_millisatoshi,
 | 
			
		||||
@ -82,7 +91,10 @@ function convertPolicy(clChannel: any): ILightningApi.Policy {
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getEmptyPolicy(): ILightningApi.Policy {
 | 
			
		||||
/**
 | 
			
		||||
 * Create an empty channel policy in lnd format
 | 
			
		||||
 */
 | 
			
		||||
 function getEmptyPolicy(): ILightningApi.Policy {
 | 
			
		||||
  return {
 | 
			
		||||
    public_key: 'null',
 | 
			
		||||
    base_fee_mtokens: '0',
 | 
			
		||||
 | 
			
		||||
@ -1,249 +0,0 @@
 | 
			
		||||
// Imported from https://github.com/shesek/lightning-client-js
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
const methods = [
 | 
			
		||||
  'addgossip',
 | 
			
		||||
  'autocleaninvoice',
 | 
			
		||||
  'check',
 | 
			
		||||
  'checkmessage',
 | 
			
		||||
  'close',
 | 
			
		||||
  'connect',
 | 
			
		||||
  'createinvoice',
 | 
			
		||||
  'createinvoicerequest',
 | 
			
		||||
  'createoffer',
 | 
			
		||||
  'createonion',
 | 
			
		||||
  'decode',
 | 
			
		||||
  'decodepay',
 | 
			
		||||
  'delexpiredinvoice',
 | 
			
		||||
  'delinvoice',
 | 
			
		||||
  'delpay',
 | 
			
		||||
  'dev-listaddrs',
 | 
			
		||||
  'dev-rescan-outputs',
 | 
			
		||||
  'disableoffer',
 | 
			
		||||
  'disconnect',
 | 
			
		||||
  'estimatefees',
 | 
			
		||||
  'feerates',
 | 
			
		||||
  'fetchinvoice',
 | 
			
		||||
  'fundchannel',
 | 
			
		||||
  'fundchannel_cancel',
 | 
			
		||||
  'fundchannel_complete',
 | 
			
		||||
  'fundchannel_start',
 | 
			
		||||
  'fundpsbt',
 | 
			
		||||
  'getchaininfo',
 | 
			
		||||
  'getinfo',
 | 
			
		||||
  'getlog',
 | 
			
		||||
  'getrawblockbyheight',
 | 
			
		||||
  'getroute',
 | 
			
		||||
  'getsharedsecret',
 | 
			
		||||
  'getutxout',
 | 
			
		||||
  'help',
 | 
			
		||||
  'invoice',
 | 
			
		||||
  'keysend',
 | 
			
		||||
  'legacypay',
 | 
			
		||||
  'listchannels',
 | 
			
		||||
  'listconfigs',
 | 
			
		||||
  'listforwards',
 | 
			
		||||
  'listfunds',
 | 
			
		||||
  'listinvoices',
 | 
			
		||||
  'listnodes',
 | 
			
		||||
  'listoffers',
 | 
			
		||||
  'listpays',
 | 
			
		||||
  'listpeers',
 | 
			
		||||
  'listsendpays',
 | 
			
		||||
  'listtransactions',
 | 
			
		||||
  'multifundchannel',
 | 
			
		||||
  'multiwithdraw',
 | 
			
		||||
  'newaddr',
 | 
			
		||||
  'notifications',
 | 
			
		||||
  'offer',
 | 
			
		||||
  'offerout',
 | 
			
		||||
  'openchannel_abort',
 | 
			
		||||
  'openchannel_bump',
 | 
			
		||||
  'openchannel_init',
 | 
			
		||||
  'openchannel_signed',
 | 
			
		||||
  'openchannel_update',
 | 
			
		||||
  'pay',
 | 
			
		||||
  'payersign',
 | 
			
		||||
  'paystatus',
 | 
			
		||||
  'ping',
 | 
			
		||||
  'plugin',
 | 
			
		||||
  'reserveinputs',
 | 
			
		||||
  'sendinvoice',
 | 
			
		||||
  'sendonion',
 | 
			
		||||
  'sendonionmessage',
 | 
			
		||||
  'sendpay',
 | 
			
		||||
  'sendpsbt',
 | 
			
		||||
  'sendrawtransaction',
 | 
			
		||||
  'setchannelfee',
 | 
			
		||||
  'signmessage',
 | 
			
		||||
  'signpsbt',
 | 
			
		||||
  'stop',
 | 
			
		||||
  'txdiscard',
 | 
			
		||||
  'txprepare',
 | 
			
		||||
  'txsend',
 | 
			
		||||
  'unreserveinputs',
 | 
			
		||||
  'utxopsbt',
 | 
			
		||||
  'waitanyinvoice',
 | 
			
		||||
  'waitblockheight',
 | 
			
		||||
  'waitinvoice',
 | 
			
		||||
  'waitsendpay',
 | 
			
		||||
  'withdraw'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import EventEmitter from 'events';
 | 
			
		||||
import { existsSync, statSync } from 'fs';
 | 
			
		||||
import { createConnection, Socket } from 'net';
 | 
			
		||||
import { homedir } from 'os';
 | 
			
		||||
import path from 'path';
 | 
			
		||||
import { createInterface, Interface } from 'readline';
 | 
			
		||||
import logger from '../../../logger';
 | 
			
		||||
 | 
			
		||||
class LightningError extends Error {
 | 
			
		||||
  type: string = 'lightning';
 | 
			
		||||
  message: string = 'lightning-client error';
 | 
			
		||||
 | 
			
		||||
  constructor(error) {
 | 
			
		||||
    super();
 | 
			
		||||
    this.type = error.type;
 | 
			
		||||
    this.message = error.message;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const defaultRpcPath = path.join(homedir(), '.lightning')
 | 
			
		||||
  , fStat = (...p) => statSync(path.join(...p))
 | 
			
		||||
  , fExists = (...p) => existsSync(path.join(...p))
 | 
			
		||||
 | 
			
		||||
export default class CLightningClient extends EventEmitter {
 | 
			
		||||
  private rpcPath: string;
 | 
			
		||||
  private reconnectWait: number;
 | 
			
		||||
  private reconnectTimeout;
 | 
			
		||||
  private reqcount: number;
 | 
			
		||||
  private client: Socket;
 | 
			
		||||
  private rl: Interface;
 | 
			
		||||
  private clientConnectionPromise: Promise<unknown>;
 | 
			
		||||
 | 
			
		||||
  constructor(rpcPath = defaultRpcPath) {
 | 
			
		||||
    if (!path.isAbsolute(rpcPath)) {
 | 
			
		||||
      throw new Error('The rpcPath must be an absolute path');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!fExists(rpcPath) || !fStat(rpcPath).isSocket()) {
 | 
			
		||||
      // network directory provided, use the lightning-rpc within in
 | 
			
		||||
      if (fExists(rpcPath, 'lightning-rpc')) {
 | 
			
		||||
        rpcPath = path.join(rpcPath, 'lightning-rpc');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // main data directory provided, default to using the bitcoin mainnet subdirectory
 | 
			
		||||
      // to be removed in v0.2.0
 | 
			
		||||
      else if (fExists(rpcPath, 'bitcoin', 'lightning-rpc')) {
 | 
			
		||||
        logger.warn(`[CLightningClient] ${rpcPath}/lightning-rpc is missing, using the bitcoin mainnet subdirectory at ${rpcPath}/bitcoin instead.`)
 | 
			
		||||
        logger.warn(`[CLightningClient] specifying the main lightning data directory is deprecated, please specify the network directory explicitly.\n`)
 | 
			
		||||
        rpcPath = path.join(rpcPath, 'bitcoin', 'lightning-rpc')
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    logger.debug(`[CLightningClient] Connecting to ${rpcPath}`);
 | 
			
		||||
 | 
			
		||||
    super();
 | 
			
		||||
    this.rpcPath = rpcPath;
 | 
			
		||||
    this.reconnectWait = 0.5;
 | 
			
		||||
    this.reconnectTimeout = null;
 | 
			
		||||
    this.reqcount = 0;
 | 
			
		||||
 | 
			
		||||
    const _self = this;
 | 
			
		||||
 | 
			
		||||
    this.client = createConnection(rpcPath);
 | 
			
		||||
    this.rl = createInterface({ input: this.client })
 | 
			
		||||
 | 
			
		||||
    this.clientConnectionPromise = new Promise<void>(resolve => {
 | 
			
		||||
      _self.client.on('connect', () => {
 | 
			
		||||
        logger.debug(`[CLightningClient] Lightning client connected`);
 | 
			
		||||
        _self.reconnectWait = 1;
 | 
			
		||||
        resolve();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      _self.client.on('end', () => {
 | 
			
		||||
        logger.err('[CLightningClient] Lightning client connection closed, reconnecting');
 | 
			
		||||
        _self.increaseWaitTime();
 | 
			
		||||
        _self.reconnect();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      _self.client.on('error', error => {
 | 
			
		||||
        logger.err(`[CLightningClient] Lightning client connection error: ${error}`);
 | 
			
		||||
        _self.emit('error', error);
 | 
			
		||||
        _self.increaseWaitTime();
 | 
			
		||||
        _self.reconnect();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.rl.on('line', line => {
 | 
			
		||||
      line = line.trim();
 | 
			
		||||
      if (!line) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      const data = JSON.parse(line);
 | 
			
		||||
      // logger.debug(`[CLightningClient] #${data.id} <-- ${JSON.stringify(data.error || data.result)}`);
 | 
			
		||||
      _self.emit('res:' + data.id, data);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  increaseWaitTime(): void {
 | 
			
		||||
    if (this.reconnectWait >= 16) {
 | 
			
		||||
      this.reconnectWait = 16;
 | 
			
		||||
    } else {
 | 
			
		||||
      this.reconnectWait *= 2;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  reconnect(): void {
 | 
			
		||||
    const _self = this;
 | 
			
		||||
 | 
			
		||||
    if (this.reconnectTimeout) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.reconnectTimeout = setTimeout(() => {
 | 
			
		||||
      logger.debug('[CLightningClient] Trying to reconnect...');
 | 
			
		||||
 | 
			
		||||
      _self.client.connect(_self.rpcPath);
 | 
			
		||||
      _self.reconnectTimeout = null;
 | 
			
		||||
    }, this.reconnectWait * 1000);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  call(method, args = []): Promise<any> {
 | 
			
		||||
    const _self = this;
 | 
			
		||||
 | 
			
		||||
    const callInt = ++this.reqcount;
 | 
			
		||||
    const sendObj = {
 | 
			
		||||
      jsonrpc: '2.0',
 | 
			
		||||
      method,
 | 
			
		||||
      params: args,
 | 
			
		||||
      id: '' + callInt
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    logger.debug(`[CLightningClient] #${callInt} --> ${method} ${args}`);
 | 
			
		||||
 | 
			
		||||
    // Wait for the client to connect
 | 
			
		||||
    return this.clientConnectionPromise
 | 
			
		||||
      .then(() => new Promise((resolve, reject) => {
 | 
			
		||||
        // Wait for a response
 | 
			
		||||
        this.once('res:' + callInt, res => res.error == null
 | 
			
		||||
          ? resolve(res.result)
 | 
			
		||||
          : reject(new LightningError(res.error))
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Send the command
 | 
			
		||||
        _self.client.write(JSON.stringify(sendObj));
 | 
			
		||||
      }));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const protify = s => s.replace(/-([a-z])/g, m => m[1].toUpperCase());
 | 
			
		||||
 | 
			
		||||
methods.forEach(k => {
 | 
			
		||||
  CLightningClient.prototype[protify(k)] = function (...args: any) {
 | 
			
		||||
    return this.call(k, args);
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
@ -1,7 +1,5 @@
 | 
			
		||||
import { ILightningApi } from './lightning-api.interface';
 | 
			
		||||
 | 
			
		||||
export interface AbstractLightningApi {
 | 
			
		||||
  $getNetworkInfo(): Promise<ILightningApi.NetworkInfo>;
 | 
			
		||||
  $getNetworkGraph(): Promise<ILightningApi.NetworkGraph>;
 | 
			
		||||
  $getInfo(): Promise<ILightningApi.Info>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,12 @@
 | 
			
		||||
import config from '../../config';
 | 
			
		||||
import CLightningClient from './clightning/clightning-client';
 | 
			
		||||
import { AbstractLightningApi } from './lightning-api-abstract-factory';
 | 
			
		||||
import LndApi from './lnd/lnd-api';
 | 
			
		||||
 | 
			
		||||
function lightningApiFactory(): AbstractLightningApi {
 | 
			
		||||
  switch (config.LIGHTNING.BACKEND) {
 | 
			
		||||
  switch (config.LIGHTNING.ENABLED === true && config.LIGHTNING.BACKEND) {
 | 
			
		||||
    case 'cln':
 | 
			
		||||
      return new CLightningClient(config.CLIGHTNING.SOCKET);
 | 
			
		||||
    case 'lnd':
 | 
			
		||||
    default:
 | 
			
		||||
      return new LndApi();
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,9 @@ import bitcoinClient from '../../api/bitcoin/bitcoin-client';
 | 
			
		||||
import bitcoinApi from '../../api/bitcoin/bitcoin-api-factory';
 | 
			
		||||
import config from '../../config';
 | 
			
		||||
import { IEsploraApi } from '../../api/bitcoin/esplora-api.interface';
 | 
			
		||||
import lightningApi from '../../api/lightning/lightning-api-factory';
 | 
			
		||||
import { ILightningApi } from '../../api/lightning/lightning-api.interface';
 | 
			
		||||
import { $lookupNodeLocation } from './sync-tasks/node-locations';
 | 
			
		||||
import lightningApi from '../../api/lightning/lightning-api-factory';
 | 
			
		||||
 | 
			
		||||
class NodeSyncService {
 | 
			
		||||
  constructor() {}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user