mirror of
https://github.com/Ride-The-Lightning/RTL.git
synced 2026-08-13 12:33:07 +02:00
parent
575e0581f8
commit
3863653a5e
40 changed files with 1257 additions and 49 deletions
|
|
@ -1,6 +1,9 @@
|
|||
import request from 'request-promise';
|
||||
import { Logger } from '../../utils/logger.js';
|
||||
import { Common } from '../../utils/common.js';
|
||||
import { createInvoiceRequestCall, listPendingInvoicesRequestCall } from './invoices.js';
|
||||
import { findRouteBetweenNodesRequestCall } from './network.js';
|
||||
import { getSentInfoFromPaymentRequest, sendPaymentToRouteRequestCall } from './payments.js';
|
||||
let options = null;
|
||||
const logger = Logger;
|
||||
const common = Common;
|
||||
|
|
@ -156,3 +159,52 @@ export const closeChannel = (req, res, next) => {
|
|||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
// options.form = { sourceNodeId: req.params.source, targetNodeId: req.params.target, amountMsat: req.params.amount, ignoreNodeIds: req.params.ignore };
|
||||
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) => {
|
||||
const foundExistingInvoice = callRes.find(inv => inv.description.includes(crInvDescription) && inv.amount === req.body.amountMsat && inv.expiry && inv.timestamp && ((inv.expiry + inv.timestamp) >= tillToday));
|
||||
// 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) => {
|
||||
let routes = values[0]?.routes?.filter(route => {
|
||||
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));
|
||||
});
|
||||
let firstRoute = routes[0].shortChannelIds.join() || '';
|
||||
let shortChannelIds = req.body.sourceShortChannelId + ',' + firstRoute + ',' + req.body.targetShortChannelId;
|
||||
let invoice = (foundExistingInvoice && foundExistingInvoice.serialized ? foundExistingInvoice.serialized : (values[1] ? values[1].serialized : '')) || '';
|
||||
let paymentHash = (foundExistingInvoice && foundExistingInvoice.paymentHash ? foundExistingInvoice.paymentHash : (values[1] ? values[1].paymentHash : '') || '');
|
||||
return sendPaymentToRouteRequestCall(req.session.selectedNode, shortChannelIds, invoice, req.body.amountMsat).then(payToRouteCallRes => {
|
||||
setTimeout(() => {
|
||||
return getSentInfoFromPaymentRequest(req.session.selectedNode, paymentHash).then(sentInfoCallRes => {
|
||||
let payStatus = sentInfoCallRes.length && sentInfoCallRes.length > 0 ? sentInfoCallRes[sentInfoCallRes.length - 1].status : sentInfoCallRes;
|
||||
return res.status(201).json({ flgReusingInvoice: !!foundExistingInvoice, invoice: invoice, paymentHash: paymentHash, paymentDetails: payToRouteCallRes, paymentStatus: payStatus });
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Channels', 'Channel Rebalance From Sent Info Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ flgReusingInvoice: !!foundExistingInvoice, invoice: invoice, paymentHash: paymentHash, paymentDetails: payToRouteCallRes, paymentStatus: { message: err.message, error: err.error } });
|
||||
});
|
||||
}, 3000);
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Channels', 'Channel Rebalance From Send Payment To Route Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ flgReusingInvoice: !!foundExistingInvoice, invoice: invoice, paymentHash: paymentHash, paymentDetails: {}, paymentStatus: { message: err.message, error: err.error } });
|
||||
});
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Channels', 'Channel Rebalance From Find Routes Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ flgReusingInvoice: !!foundExistingInvoice, invoice: (foundExistingInvoice.serialized || ''), paymentHash: '', paymentDetails: {}, paymentStatus: { message: err.message, error: err.error } });
|
||||
});
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Channels', 'Channel Rebalance From List Pending Invoices Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ flgReusingInvoice: false, invoice: '', paymentHash: '', paymentDetails: {}, paymentStatus: { message: err.message, error: err.error } });
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const getReceivedPaymentInfo = (lnServerUrl, invoice) => {
|
|||
invoice.status = response.status.type;
|
||||
if (response.status && response.status.type === 'received') {
|
||||
invoice.amountSettled = response.status.amount ? Math.round(response.status.amount / 1000) : 0;
|
||||
invoice.receivedAt = response.status.receivedAt ? Math.round(response.status.receivedAt / 1000) : 0;
|
||||
invoice.receivedAt = response.status.receivedAt.unix ? response.status.receivedAt.unix : 0;
|
||||
}
|
||||
return invoice;
|
||||
}).catch((err) => {
|
||||
|
|
@ -53,6 +53,20 @@ export const getInvoice = (req, res, next) => {
|
|||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
export const listPendingInvoicesRequestCall = (selectedNode) => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'List Pending Invoices..' });
|
||||
options = selectedNode.options;
|
||||
options.url = selectedNode.ln_server_url + '/listpendinginvoices';
|
||||
options.form = { from: 0, to: (Math.round(new Date(Date.now()).getTime() / 1000)).toString() };
|
||||
return new Promise((resolve, reject) => {
|
||||
request.post(options).then((pendingInvoicesResponse) => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Pending Invoices List ', data: pendingInvoicesResponse });
|
||||
resolve(pendingInvoicesResponse);
|
||||
}).catch((errRes) => {
|
||||
reject(common.handleError(errRes, 'Invoices', 'List Pending Invoices Error', selectedNode));
|
||||
});
|
||||
});
|
||||
};
|
||||
export const listInvoices = (req, res, next) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Getting List Invoices..' });
|
||||
options = common.getOptions(req);
|
||||
|
|
@ -103,22 +117,32 @@ export const listInvoices = (req, res, next) => {
|
|||
});
|
||||
}
|
||||
};
|
||||
export const createInvoiceRequestCall = (selectedNode, description, amount) => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' });
|
||||
options = selectedNode.options;
|
||||
options.url = selectedNode.ln_server_url + '/createinvoice';
|
||||
options.form = { description: description, amountMsat: amount };
|
||||
return new Promise((resolve, reject) => {
|
||||
request.post(options).then((invResponse) => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Created', data: invResponse });
|
||||
if (invResponse.amount) {
|
||||
invResponse.amount = Math.round(invResponse.amount / 1000);
|
||||
}
|
||||
resolve(invResponse);
|
||||
}).catch((errRes) => {
|
||||
reject(common.handleError(errRes, 'Invoices', 'Create Invoice Error', selectedNode));
|
||||
});
|
||||
});
|
||||
};
|
||||
export const createInvoice = (req, res, next) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' });
|
||||
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 + '/createinvoice';
|
||||
options.form = req.body;
|
||||
request.post(options).then((body) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Invoice', msg: 'Invoice Created', data: body });
|
||||
if (body.amount) {
|
||||
body.amount = Math.round(body.amount / 1000);
|
||||
}
|
||||
res.status(201).json(body);
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Invoices', 'Create Invoice Error', req.session.selectedNode);
|
||||
createInvoiceRequestCall(req.session.selectedNode, req.body.description, req.body.amountMsat).then(invRes => {
|
||||
res.status(201).json(invRes);
|
||||
}).catch((err) => {
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,3 +20,28 @@ export const getNodes = (req, res, next) => {
|
|||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
export const findRouteBetweenNodesRequestCall = (selectedNode, amountMsat, sourceNodeId, targetNodeId, ignoreNodeIds = [], format = 'shortChannelId') => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'Network', msg: 'Find Route Between Nodes..' });
|
||||
options = selectedNode.options;
|
||||
options.url = selectedNode.ln_server_url + '/findroutebetweennodes';
|
||||
options.form = { amountMsat: amountMsat, sourceNodeId: sourceNodeId, targetNodeId: targetNodeId, ignoreNodeIds: ignoreNodeIds, format: format };
|
||||
return new Promise((resolve, reject) => {
|
||||
request.post(options).then((body) => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'Network', msg: 'Route Lookup Between Nodes Finished', data: body });
|
||||
resolve(body);
|
||||
}).catch((errRes) => {
|
||||
reject(common.handleError(errRes, 'Network', 'Route Lookup Between Nodes Error', selectedNode));
|
||||
});
|
||||
});
|
||||
};
|
||||
export const findRouteBetweenNodes = (req, res, next) => {
|
||||
options = common.getOptions(req);
|
||||
if (options.error) {
|
||||
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||
}
|
||||
findRouteBetweenNodesRequestCall(req.session.selectedNode, req.body.amountMsat, req.body.sourceNodeId, req.body.targetNodeId, req.body.ignoreNodeIds, req.body.format).then(callRes => {
|
||||
res.status(200).json(callRes);
|
||||
}).catch((err) => {
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -126,3 +126,30 @@ export const getSentPaymentsInformation = (req, res, next) => {
|
|||
return res.status(200).json([]);
|
||||
}
|
||||
};
|
||||
export const sendPaymentToRouteRequestCall = (selectedNode, shortChannelIds, invoice, amountMsat) => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'Invoices', msg: 'Creating Invoice..' });
|
||||
options = selectedNode.options;
|
||||
options.url = selectedNode.ln_server_url + '/sendtoroute';
|
||||
options.form = { shortChannelIds: shortChannelIds, amountMsat: amountMsat, invoice: invoice };
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment To Route Options', data: options.form });
|
||||
request.post(options).then((body) => {
|
||||
logger.log({ selectedNode: selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent To Route', data: body });
|
||||
resolve(body);
|
||||
}).catch((errRes) => {
|
||||
reject(common.handleError(errRes, 'Payments', 'Send Payment To Route Error', selectedNode));
|
||||
});
|
||||
});
|
||||
};
|
||||
export const sendPaymentToRoute = (req, res, next) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Send Payment To Route..' });
|
||||
options = common.getOptions(req);
|
||||
if (options.error) {
|
||||
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||
}
|
||||
sendPaymentToRouteRequestCall(req.session.selectedNode, req.body.shortChannelIds, req.body.invoice, req.body.amountMsat).then(callRes => {
|
||||
res.status(200).json(callRes);
|
||||
}).catch((err) => {
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { getChannels, getChannelStats, openChannel, updateChannelRelayFee, closeChannel } from '../../controllers/eclair/channels.js';
|
||||
import { getChannels, getChannelStats, openChannel, updateChannelRelayFee, closeChannel, circularRebalance } from '../../controllers/eclair/channels.js';
|
||||
const router = Router();
|
||||
router.get('/', isAuthenticated, getChannels);
|
||||
router.get('/stats', isAuthenticated, getChannelStats);
|
||||
router.post('/', isAuthenticated, openChannel);
|
||||
router.post('/updateRelayFee', isAuthenticated, updateChannelRelayFee);
|
||||
router.post('/circularRebalance', circularRebalance);
|
||||
router.delete('/', isAuthenticated, closeChannel);
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { getNodes } from '../../controllers/eclair/network.js';
|
||||
import { getNodes, findRouteBetweenNodes } from '../../controllers/eclair/network.js';
|
||||
const router = Router();
|
||||
router.get('/nodes/:id', isAuthenticated, getNodes);
|
||||
router.get('/routebetweennodes', isAuthenticated, findRouteBetweenNodes);
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { queryPaymentRoute, decodePayment, getSentPaymentsInformation, postPayment } from '../../controllers/eclair/payments.js';
|
||||
import { queryPaymentRoute, decodePayment, getSentPaymentsInformation, postPayment, sendPaymentToRoute } from '../../controllers/eclair/payments.js';
|
||||
const router = Router();
|
||||
router.get('/route/', isAuthenticated, queryPaymentRoute);
|
||||
router.get('/decode/:invoice', isAuthenticated, decodePayment);
|
||||
router.post('/getsentinfos', isAuthenticated, getSentPaymentsInformation);
|
||||
router.post('/sendtoroute', isAuthenticated, sendPaymentToRoute);
|
||||
router.post('/', isAuthenticated, postPayment);
|
||||
export default router;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue