2021-12-29 23:08:41 +00:00
import request from 'request-promise' ;
import { Logger } from '../../utils/logger.js' ;
import { Common } from '../../utils/common.js' ;
2023-04-26 05:04:29 +00:00
import { createInvoiceRequestCall , listPendingInvoicesRequestCall } from './invoices.js' ;
import { findRouteBetweenNodesRequestCall } from './network.js' ;
import { getSentInfoFromPaymentRequest , sendPaymentToRouteRequestCall } from './payments.js' ;
2021-12-29 23:08:41 +00:00
let options = null ;
const logger = Logger ;
const common = Common ;
2022-01-16 20:55:50 +00:00
export const simplifyAllChannels = ( selNode , channels ) => {
2021-12-29 23:08:41 +00:00
let channelNodeIds = '' ;
const simplifiedChannels = [ ] ;
channels . forEach ( ( channel ) => {
channelNodeIds = channelNodeIds + ',' + channel . nodeId ;
simplifiedChannels . push ( {
nodeId : channel . nodeId ? channel . nodeId : '' ,
channelId : channel . channelId ? channel . channelId : '' ,
state : channel . state ? channel . state : '' ,
2023-09-29 03:18:14 +00:00
announceChannel : channel . data && channel . data . commitments && channel . data . commitments . params && channel . data . commitments . params . channelFlags && channel . data . commitments . params . channelFlags . announceChannel ? channel . data . commitments . params . channelFlags . announceChannel : false ,
toLocal : ( channel . data . commitments . active [ 0 ] . localCommit . spec . toLocal ) ? Math . round ( + channel . data . commitments . active [ 0 ] . localCommit . spec . toLocal / 1000 ) : 0 ,
toRemote : ( channel . data . commitments . active [ 0 ] . localCommit . spec . toRemote ) ? Math . round ( + channel . data . commitments . active [ 0 ] . localCommit . spec . toRemote / 1000 ) : 0 ,
2022-09-10 01:55:56 +00:00
shortChannelId : channel . data && channel . data . channelUpdate && channel . data . channelUpdate . shortChannelId ? channel . data . channelUpdate . shortChannelId : '' ,
2023-09-29 03:18:14 +00:00
isInitiator : channel . data && channel . data . commitments && channel . data . commitments . params && channel . data . commitments . params . localParams && channel . data . commitments . params . localParams . isInitiator ? channel . data . commitments . params . localParams . isInitiator : false ,
2021-12-29 23:08:41 +00:00
feeBaseMsat : channel . data && channel . data . channelUpdate && channel . data . channelUpdate . feeBaseMsat ? channel . data . channelUpdate . feeBaseMsat : 0 ,
feeProportionalMillionths : channel . data && channel . data . channelUpdate && channel . data . channelUpdate . feeProportionalMillionths ? channel . data . channelUpdate . feeProportionalMillionths : 0 ,
alias : ''
} ) ;
} ) ;
channelNodeIds = channelNodeIds . substring ( 1 ) ;
2022-01-16 20:55:50 +00:00
options . url = selNode . ln _server _url + '/nodes' ;
2021-12-29 23:08:41 +00:00
options . form = { nodeIds : channelNodeIds } ;
2022-01-16 20:55:50 +00:00
logger . log ( { selectedNode : selNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Node Ids to find alias' , data : channelNodeIds } ) ;
2021-12-29 23:08:41 +00:00
return request . post ( options ) . then ( ( nodes ) => {
2022-01-16 20:55:50 +00:00
logger . log ( { selectedNode : selNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Filtered Nodes Received' , data : nodes } ) ;
2021-12-29 23:08:41 +00:00
let foundPeer = null ;
2022-11-24 02:20:23 +00:00
simplifiedChannels ? . map ( ( channel ) => {
2021-12-29 23:08:41 +00:00
foundPeer = nodes . find ( ( channelWithAlias ) => channel . nodeId === channelWithAlias . nodeId ) ;
channel . alias = foundPeer ? foundPeer . alias : channel . nodeId . substring ( 0 , 20 ) ;
return channel ;
} ) ;
return simplifiedChannels ;
} ) . catch ( ( err ) => simplifiedChannels ) ;
} ;
export const getChannels = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'List Channels..' } ) ;
options = common . getOptions ( req ) ;
if ( options . error ) {
return res . status ( options . statusCode ) . json ( { message : options . message , error : options . error } ) ;
}
options . url = req . session . selectedNode . ln _server _url + '/channels' ;
options . form = { } ;
if ( req . query && req . query . nodeId ) {
options . form = req . query ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Channels Node Id' , data : options . form } ) ;
}
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Options' , data : options } ) ;
if ( common . read _dummy _data ) {
common . getDummyData ( 'Channels' , req . session . selectedNode . ln _implementation ) . then ( ( data ) => { res . status ( 200 ) . json ( data ) ; } ) ;
}
else {
request . post ( options ) . then ( ( body ) => {
2022-01-16 20:55:50 +00:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Channels List Received' , data : body } ) ;
2021-12-29 23:08:41 +00:00
if ( body && body . length ) {
2022-01-16 20:55:50 +00:00
return simplifyAllChannels ( req . session . selectedNode , body ) . then ( ( simplifiedChannels ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Simplified Channels with Alias Received' , data : simplifiedChannels } ) ;
2021-12-29 23:08:41 +00:00
res . status ( 200 ) . json ( simplifiedChannels ) ;
} ) ;
}
else {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Empty Channels List Received' } ) ;
2022-08-06 06:01:10 +00:00
res . status ( 200 ) . json ( [ ] ) ;
2021-12-29 23:08:41 +00:00
}
} ) .
catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'List Channels Error' , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . message , error : err . error } ) ;
} ) ;
}
} ;
export const getChannelStats = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Getting Channel States..' } ) ;
options = common . getOptions ( req ) ;
if ( options . error ) {
return res . status ( options . statusCode ) . json ( { message : options . message , error : options . error } ) ;
}
options . url = req . session . selectedNode . ln _server _url + '/channelstats' ;
2022-12-20 00:20:02 +00:00
const today = new Date ( Date . now ( ) ) ;
const tillToday = ( Math . round ( today . getTime ( ) / 1000 ) ) . toString ( ) ;
const fromLastMonth = ( Math . round ( new Date ( today . getFullYear ( ) , today . getMonth ( ) - 1 , today . getDate ( ) + 1 , 0 , 0 , 0 ) . getTime ( ) / 1000 ) ) . toString ( ) ;
options . form = {
from : fromLastMonth ,
to : tillToday
} ;
2021-12-29 23:08:41 +00:00
request . post ( options ) . then ( ( body ) => {
2022-01-16 20:55:50 +00:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Channel States Received' , data : body } ) ;
2021-12-29 23:08:41 +00:00
res . status ( 201 ) . json ( body ) ;
} ) . catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'Get Channel Stats Error' , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . message , error : err . error } ) ;
} ) ;
} ;
export const openChannel = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Opening Channel..' } ) ;
options = common . getOptions ( req ) ;
if ( options . error ) {
return res . status ( options . statusCode ) . json ( { message : options . message , error : options . error } ) ;
}
options . url = req . session . selectedNode . ln _server _url + '/open' ;
options . form = req . body ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Open Channel Params' , data : options . form } ) ;
request . post ( options ) . then ( ( body ) => {
2022-01-16 20:55:50 +00:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Channel Opened' , data : body } ) ;
2021-12-29 23:08:41 +00:00
res . status ( 201 ) . json ( body ) ;
} ) . catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'Open Channel Error' , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . message , error : err . error } ) ;
} ) ;
} ;
export const updateChannelRelayFee = ( req , res , next ) => {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Updating Channel Relay Fee..' } ) ;
options = common . getOptions ( req ) ;
if ( options . error ) {
return res . status ( options . statusCode ) . json ( { message : options . message , error : options . error } ) ;
}
options . url = req . session . selectedNode . ln _server _url + '/updaterelayfee' ;
options . form = req . query ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Update Relay Fee Params' , data : options . form } ) ;
request . post ( options ) . then ( ( body ) => {
2022-01-16 20:55:50 +00:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Channel Relay Fee Updated' , data : body } ) ;
2021-12-29 23:08:41 +00:00
res . status ( 201 ) . json ( body ) ;
} ) . catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'Update Relay Fee Error' , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . message , error : err . error } ) ;
} ) ;
} ;
export const closeChannel = ( req , res , next ) => {
options = common . getOptions ( req ) ;
if ( options . error ) {
return res . status ( options . statusCode ) . json ( { message : options . message , error : options . error } ) ;
}
if ( req . query . force !== 'true' ) {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Closing Channel..' } ) ;
options . url = req . session . selectedNode . ln _server _url + '/close' ;
}
else {
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Force Closing Channel..' } ) ;
options . url = req . session . selectedNode . ln _server _url + '/forceclose' ;
}
options . form = { channelId : req . query . channelId } ;
2022-01-16 20:55:50 +00:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Close URL' , data : options . url } ) ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Close Params' , data : options . form } ) ;
2021-12-29 23:08:41 +00:00
request . post ( options ) . then ( ( body ) => {
2022-01-16 20:55:50 +00:00
logger . log ( { selectedNode : req . session . selectedNode , level : 'INFO' , fileName : 'Channels' , msg : 'Channel Closed' , data : body } ) ;
2021-12-29 23:08:41 +00:00
res . status ( 204 ) . json ( body ) ;
} ) . catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'Close Channel Error' , req . session . selectedNode ) ;
return res . status ( err . statusCode ) . json ( { message : err . message , error : err . error } ) ;
} ) ;
} ;
2023-04-26 05:04:29 +00:00
export const circularRebalance = ( req , res , next ) => {
const crInvDescription = 'Circular rebalancing invoice for ' + ( req . body . amountMsat / 1000 ) + ' Sats' ;
options = common . getOptions ( req ) ;
if ( options . error ) {
return res . status ( options . statusCode ) . json ( { message : options . message , error : options . error } ) ;
}
options . form = req . body ;
logger . log ( { selectedNode : req . session . selectedNode , level : 'DEBUG' , fileName : 'Channels' , msg : 'Rebalance Params' , data : options . form } ) ;
const tillToday = ( Math . round ( new Date ( Date . now ( ) ) . getTime ( ) / 1000 ) ) . toString ( ) ;
// Check if unpaid Invoice exists already
listPendingInvoicesRequestCall ( req . session . selectedNode ) . then ( ( callRes ) => {
2023-05-03 00:34:41 +00:00
const foundExistingInvoice = callRes . find ( ( inv ) => inv . description . includes ( crInvDescription ) && inv . amount === req . body . amountMsat && inv . expiry && inv . timestamp && ( ( inv . expiry + inv . timestamp ) >= tillToday ) ) ;
2023-04-26 05:04:29 +00:00
// Create new invoice if doesn't exist already
const requestCalls = foundExistingInvoice && foundExistingInvoice . serialized ?
[ findRouteBetweenNodesRequestCall ( req . session . selectedNode , req . body . amountMsat , req . body . sourceNodeId , req . body . targetNodeId , req . body . ignoreNodeIds , req . body . format ) ] :
[ findRouteBetweenNodesRequestCall ( req . session . selectedNode , req . body . amountMsat , req . body . sourceNodeId , req . body . targetNodeId , req . body . ignoreNodeIds , req . body . format ) , createInvoiceRequestCall ( req . session . selectedNode , crInvDescription , req . body . amountMsat ) ] ;
Promise . all ( requestCalls ) . then ( ( values ) => {
2023-05-03 00:34:41 +00:00
// eslint-disable-next-line arrow-body-style
const routes = values [ 0 ] ? . routes ? . filter ( ( route ) => {
2023-04-26 05:04:29 +00:00
return ! ( ( route . shortChannelIds [ 0 ] === req . body . sourceShortChannelId && route . shortChannelIds [ 1 ] === req . body . targetShortChannelId ) ||
( route . shortChannelIds [ 1 ] === req . body . sourceShortChannelId && route . shortChannelIds [ 0 ] === req . body . targetShortChannelId ) ) ;
} ) ;
2023-05-03 00:34:41 +00:00
const firstRoute = routes [ 0 ] . shortChannelIds . join ( ) || '' ;
const shortChannelIds = req . body . sourceShortChannelId + ',' + firstRoute + ',' + req . body . targetShortChannelId ;
const invoice = ( foundExistingInvoice && foundExistingInvoice . serialized ? foundExistingInvoice . serialized : ( values [ 1 ] ? values [ 1 ] . serialized : '' ) ) || '' ;
const paymentHash = ( foundExistingInvoice && foundExistingInvoice . paymentHash ? foundExistingInvoice . paymentHash : ( values [ 1 ] ? values [ 1 ] . paymentHash : '' ) || '' ) ;
return sendPaymentToRouteRequestCall ( req . session . selectedNode , shortChannelIds , invoice , req . body . amountMsat ) . then ( ( payToRouteCallRes ) => {
// eslint-disable-next-line arrow-body-style
2023-04-26 05:04:29 +00:00
setTimeout ( ( ) => {
2023-05-03 00:34:41 +00:00
return getSentInfoFromPaymentRequest ( req . session . selectedNode , paymentHash ) . then ( ( sentInfoCallRes ) => {
const payStatus = sentInfoCallRes . length && sentInfoCallRes . length > 0 ? sentInfoCallRes [ sentInfoCallRes . length - 1 ] . status : sentInfoCallRes ;
2023-05-30 01:31:14 +00:00
return res . status ( 201 ) . json ( { flgReusingInvoice : ! ! foundExistingInvoice , invoice : invoice , paymentRoute : shortChannelIds , paymentHash : paymentHash , paymentDetails : payToRouteCallRes , paymentStatus : payStatus } ) ;
2023-04-26 05:04:29 +00:00
} ) . catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'Channel Rebalance From Sent Info Error' , req . session . selectedNode ) ;
2023-05-30 01:31:14 +00:00
return res . status ( err . statusCode ) . json ( { flgReusingInvoice : ! ! foundExistingInvoice , invoice : invoice , paymentRoute : shortChannelIds , paymentHash : paymentHash , paymentDetails : payToRouteCallRes , paymentStatus : { error : err . error } } ) ;
2023-04-26 05:04:29 +00:00
} ) ;
} , 3000 ) ;
} ) . catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'Channel Rebalance From Send Payment To Route Error' , req . session . selectedNode ) ;
2023-05-30 01:31:14 +00:00
return res . status ( err . statusCode ) . json ( { flgReusingInvoice : ! ! foundExistingInvoice , invoice : invoice , paymentRoute : shortChannelIds , paymentHash : paymentHash , paymentDetails : { } , paymentStatus : { error : err . error } } ) ;
2023-04-26 05:04:29 +00:00
} ) ;
} ) . catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'Channel Rebalance From Find Routes Error' , req . session . selectedNode ) ;
2023-05-30 01:31:14 +00:00
return res . status ( err . statusCode ) . json ( { flgReusingInvoice : ! ! foundExistingInvoice , invoice : ( foundExistingInvoice . serialized || '' ) , paymentRoute : '' , paymentHash : '' , paymentDetails : { } , paymentStatus : { error : err . error } } ) ;
2023-04-26 05:04:29 +00:00
} ) ;
} ) . catch ( ( errRes ) => {
const err = common . handleError ( errRes , 'Channels' , 'Channel Rebalance From List Pending Invoices Error' , req . session . selectedNode ) ;
2023-05-30 01:31:14 +00:00
return res . status ( err . statusCode ) . json ( { flgReusingInvoice : false , invoice : '' , paymentRoute : '' , paymentHash : '' , paymentDetails : { } , paymentStatus : { error : err . error } } ) ;
2023-04-26 05:04:29 +00:00
} ) ;
} ;