Bound remaining unbounded alias-resolution fan-outs in LND graph.ts and channels.ts

Fixes #1630
This commit is contained in:
Osuji 2026-07-24 20:25:03 +01:00 committed by saubyk
parent 785f485019
commit 3491a92300
No known key found for this signature in database
GPG key ID: 00C9E2BC2E45666F
4 changed files with 135 additions and 104 deletions

View file

@ -30,18 +30,24 @@ export const getAllChannels = (req, res, next) => {
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels List Received', data: body });
if (body.channels) {
return Promise.all(body.channels?.map((channel) => {
body.channels.forEach((channel) => {
local = (channel.local_balance) ? +channel.local_balance : 0;
remote = (channel.remote_balance) ? +channel.remote_balance : 0;
total = local + remote;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return getAliasForChannel(req.session.selectedNode, channel);
})).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get All Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
const getChannelAliasesTasks = body.channels.map((channel) => () => getAliasForChannel(req.session.selectedNode, channel));
common.runWithConcurrencyLimit(getChannelAliasesTasks, 20, () => {
try {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body });
return res.status(200).json(body);
}
catch (e) {
const err = common.handleError(e, 'Channels', 'Get All Channel Aliases Error', req.session.selectedNode);
if (!res.headersSent) {
res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
});
}
else {
@ -66,26 +72,30 @@ export const getPendingChannels = (req, res, next) => {
if (!body.total_limbo_balance) {
body.total_limbo_balance = 0;
}
const promises = [];
const getPendingAliasesTasks = [];
if (body.pending_open_channels && body.pending_open_channels.length > 0) {
body.pending_open_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
body.pending_open_channels?.map((channel) => getPendingAliasesTasks.push(() => getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) {
body.pending_force_closing_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
body.pending_force_closing_channels?.map((channel) => getPendingAliasesTasks.push(() => getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.pending_closing_channels && body.pending_closing_channels.length > 0) {
body.pending_closing_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
body.pending_closing_channels?.map((channel) => getPendingAliasesTasks.push(() => getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.waiting_close_channels && body.waiting_close_channels.length > 0) {
body.waiting_close_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
body.waiting_close_channels?.map((channel) => getPendingAliasesTasks.push(() => getAliasForChannel(req.session.selectedNode, channel.channel)));
}
return Promise.all(promises).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Pending Channels List Received', data: body });
return res.status(200).json(body);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get Pending Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
common.runWithConcurrencyLimit(getPendingAliasesTasks, 20, () => {
try {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Pending Channels List Received', data: body });
return res.status(200).json(body);
}
catch (e) {
const err = common.handleError(e, 'Channels', 'Get Pending Channel Aliases Error', req.session.selectedNode);
if (!res.headersSent) {
res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Pending Channels Error', req.session.selectedNode);
@ -102,15 +112,21 @@ export const getClosedChannels = (req, res, next) => {
options.qs = req.query;
request(options).then((body) => {
if (body.channels && body.channels.length > 0) {
return Promise.all(body.channels?.map((channel) => {
body.channels.forEach((channel) => {
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
return getAliasForChannel(req.session.selectedNode, channel);
})).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get Closed Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
const getClosedAliasesTasks = body.channels.map((channel) => () => getAliasForChannel(req.session.selectedNode, channel));
common.runWithConcurrencyLimit(getClosedAliasesTasks, 20, () => {
try {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body });
return res.status(200).json(body);
}
catch (e) {
const err = common.handleError(e, 'Channels', 'Get Closed Channel Aliases Error', req.session.selectedNode);
if (!res.headersSent) {
res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
});
}
else {

View file

@ -83,19 +83,23 @@ export const getQueryRoutes = (req, res, next) => {
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes Received', data: body });
if (body.routes && body.routes.length && body.routes.length > 0 && body.routes[0].hops && body.routes[0].hops.length && body.routes[0].hops.length > 0) {
return Promise.all(body.routes[0].hops?.map((hop) => getAliasFromPubkey(req.session.selectedNode, hop.pub_key))).
then((values) => {
body.routes[0].hops?.map((hop, i) => {
hop.hop_sequence = i + 1;
hop.pubkey_alias = values[i];
return hop;
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes with Alias Received', data: body });
res.status(200).json(body);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
const getRouteAliasesTasks = body.routes[0].hops.map((hop) => () => getAliasFromPubkey(req.session.selectedNode, hop.pub_key));
common.runWithConcurrencyLimit(getRouteAliasesTasks, 20, (values) => {
try {
body.routes[0].hops?.map((hop, i) => {
hop.hop_sequence = i + 1;
hop.pubkey_alias = values[i]?.error ? 'Unknown' : values[i];
return hop;
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes with Alias Received', data: body });
res.status(200).json(body);
}
catch (e) {
const err = common.handleError(e, 'Graph', 'Get Query Routes Error', req.session.selectedNode);
if (!res.headersSent) {
res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
});
}
else {
@ -145,14 +149,19 @@ export const getAliasesForPubkeys = (req, res, next) => {
}
if (req.query.pubkeys) {
const pubkeyArr = req.query.pubkeys.split(',');
return Promise.all(pubkeyArr?.map((pubkey) => getAliasFromPubkey(req.session.selectedNode, pubkey))).
then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Node Alias', data: values });
res.status(200).json(values);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Aliases for Pubkeys Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
const getAliasesTasks = pubkeyArr.map((pubkey) => () => getAliasFromPubkey(req.session.selectedNode, pubkey));
common.runWithConcurrencyLimit(getAliasesTasks, 20, (values) => {
try {
const safeValues = values.map((v) => (v?.error ? 'Unknown' : v));
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Node Alias', data: safeValues });
res.status(200).json(safeValues);
}
catch (e) {
const err = common.handleError(e, 'Graph', 'Get Aliases for Pubkeys Error', req.session.selectedNode);
if (!res.headersSent) {
res.status(err.statusCode).json({ message: err.message, error: err.error });
}
}
});
}
else {

View file

@ -31,20 +31,21 @@ export const getAllChannels = (req, res, next) => {
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Channels List Received', data: body });
if (body.channels) {
return Promise.all(
body.channels?.map((channel) => {
local = (channel.local_balance) ? +channel.local_balance : 0;
remote = (channel.remote_balance) ? +channel.remote_balance : 0;
total = local + remote;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
return getAliasForChannel(req.session.selectedNode, channel);
})
).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get All Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
body.channels.forEach((channel) => {
local = (channel.local_balance) ? +channel.local_balance : 0;
remote = (channel.remote_balance) ? +channel.remote_balance : 0;
total = local + remote;
channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3);
});
const getChannelAliasesTasks = body.channels.map((channel) => () => getAliasForChannel(req.session.selectedNode, channel));
common.runWithConcurrencyLimit(getChannelAliasesTasks, 20, () => {
try {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sorted Channels List Received', data: body });
return res.status(200).json(body);
} catch (e) {
const err = common.handleError(e, 'Channels', 'Get All Channel Aliases Error', req.session.selectedNode);
if (!res.headersSent) { res.status(err.statusCode).json({ message: err.message, error: err.error }); }
}
});
} else {
body.channels = [];
@ -67,27 +68,28 @@ export const getPendingChannels = (req, res, next) => {
if (!body.total_limbo_balance) {
body.total_limbo_balance = 0;
}
const promises = [];
const getPendingAliasesTasks = [];
if (body.pending_open_channels && body.pending_open_channels.length > 0) {
body.pending_open_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
body.pending_open_channels?.map((channel) => getPendingAliasesTasks.push(() => getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.pending_force_closing_channels && body.pending_force_closing_channels.length > 0) {
body.pending_force_closing_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
body.pending_force_closing_channels?.map((channel) => getPendingAliasesTasks.push(() => getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.pending_closing_channels && body.pending_closing_channels.length > 0) {
body.pending_closing_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
body.pending_closing_channels?.map((channel) => getPendingAliasesTasks.push(() => getAliasForChannel(req.session.selectedNode, channel.channel)));
}
if (body.waiting_close_channels && body.waiting_close_channels.length > 0) {
body.waiting_close_channels?.map((channel) => promises.push(getAliasForChannel(req.session.selectedNode, channel.channel)));
body.waiting_close_channels?.map((channel) => getPendingAliasesTasks.push(() => getAliasForChannel(req.session.selectedNode, channel.channel)));
}
return Promise.all(promises).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Pending Channels List Received', data: body });
return res.status(200).json(body);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get Pending Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
common.runWithConcurrencyLimit(getPendingAliasesTasks, 20, () => {
try {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Pending Channels List Received', data: body });
return res.status(200).json(body);
} catch (e) {
const err = common.handleError(e, 'Channels', 'Get Pending Channel Aliases Error', req.session.selectedNode);
if (!res.headersSent) { res.status(err.statusCode).json({ message: err.message, error: err.error }); }
}
});
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'List Pending Channels Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
@ -102,17 +104,18 @@ export const getClosedChannels = (req, res, next) => {
options.qs = req.query;
request(options).then((body) => {
if (body.channels && body.channels.length > 0) {
return Promise.all(
body.channels?.map((channel) => {
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
return getAliasForChannel(req.session.selectedNode, channel);
})
).then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body });
return res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Channels', 'Get Closed Channel Aliases Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
body.channels.forEach((channel) => {
channel.close_type = (!channel.close_type) ? 'COOPERATIVE_CLOSE' : channel.close_type;
});
const getClosedAliasesTasks = body.channels.map((channel) => () => getAliasForChannel(req.session.selectedNode, channel));
common.runWithConcurrencyLimit(getClosedAliasesTasks, 20, () => {
try {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closed Channels List Received', data: body });
return res.status(200).json(body);
} catch (e) {
const err = common.handleError(e, 'Channels', 'Get Closed Channel Aliases Error', req.session.selectedNode);
if (!res.headersSent) { res.status(err.statusCode).json({ message: err.message, error: err.error }); }
}
});
} else {
body.channels = [];

View file

@ -80,20 +80,21 @@ export const getQueryRoutes = (req, res, next) => {
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Graph', msg: 'Query Routes Received', data: body });
if (body.routes && body.routes.length && body.routes.length > 0 && body.routes[0].hops && body.routes[0].hops.length && body.routes[0].hops.length > 0) {
return Promise.all(body.routes[0].hops?.map((hop) => getAliasFromPubkey(req.session.selectedNode, hop.pub_key))).
then((values) => {
const getRouteAliasesTasks = body.routes[0].hops.map((hop) => () => getAliasFromPubkey(req.session.selectedNode, hop.pub_key));
common.runWithConcurrencyLimit(getRouteAliasesTasks, 20, (values) => {
try {
body.routes[0].hops?.map((hop, i) => {
hop.hop_sequence = i + 1;
hop.pubkey_alias = values[i];
hop.pubkey_alias = values[i]?.error ? 'Unknown' : values[i];
return hop;
});
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes with Alias Received', data: body });
res.status(200).json(body);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Query Routes Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
} catch (e) {
const err = common.handleError(e, 'Graph', 'Get Query Routes Error', req.session.selectedNode);
if (!res.headersSent) { res.status(err.statusCode).json({ message: err.message, error: err.error }); }
}
});
} else {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Graph Routes Received', data: body });
return res.status(200).json(body);
@ -138,15 +139,17 @@ export const getAliasesForPubkeys = (req, res, next) => {
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
if (req.query.pubkeys) {
const pubkeyArr = req.query.pubkeys.split(',');
return Promise.all(pubkeyArr?.map((pubkey) => getAliasFromPubkey(req.session.selectedNode, pubkey))).
then((values) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Node Alias', data: values });
res.status(200).json(values);
}).
catch((errRes) => {
const err = common.handleError(errRes, 'Graph', 'Get Aliases for Pubkeys Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
const getAliasesTasks = pubkeyArr.map((pubkey) => () => getAliasFromPubkey(req.session.selectedNode, pubkey));
common.runWithConcurrencyLimit(getAliasesTasks, 20, (values) => {
try {
const safeValues = values.map((v) => (v?.error ? 'Unknown' : v));
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Graph', msg: 'Node Alias', data: safeValues });
res.status(200).json(safeValues);
} catch (e) {
const err = common.handleError(e, 'Graph', 'Get Aliases for Pubkeys Error', req.session.selectedNode);
if (!res.headersSent) { res.status(err.statusCode).json({ message: err.message, error: err.error }); }
}
});
} else {
return res.status(200).json([]);
}