balanceofsatoshis
Version:
Lightning balance CLI
176 lines (148 loc) • 4.84 kB
JavaScript
const asyncAuto = require('async/auto');
const asyncMap = require('async/map');
const asyncUntil = require('async/until');
const {getChannels} = require('ln-service');
const {getClosedChannels} = require('ln-service');
const {getForwards} = require('ln-service');
const {getNode} = require('ln-service');
const {returnResult} = require('asyncjs-util');
const forwardsViaPeer = require('./forwards_via_peer');
const flatten = arr => [].concat(...arr);
const {isArray} = Array;
const isPublicKey = n => /^[0-9A-F]{66}$/i.test(n);
const pageLimit = 1e3;
/** Get forwards
{
after: <After Date ISO 8601 String>
[before]: <Before Date ISO 8601 String>
lnd: <Authenticated LND API Object>
[via]: [<Via Public Key Hex String>]
}
@returns via cbk or Promise
{
forwards: [{
created_at: <Forward Record Created At ISO 8601 Date String>
fee: <Fee Tokens Charged Number>
fee_mtokens: <Approximated Fee Millitokens Charged String>
incoming_channel: <Incoming Standard Format Channel Id String>
[mtokens]: <Forwarded Millitokens String>
outgoing_channel: <Outgoing Standard Format Channel Id String>
tokens: <Forwarded Tokens Number>
}]
}
*/
module.exports = ({after, before, lnd, via}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!after) {
return cbk([400, 'ExpectedAfterDateToGetForwardsForNode']);
}
if (!lnd) {
return cbk([400, 'ExpectedAuthenticatedLndToGetForwardsForNode']);
}
if (!!via && !isArray(via)) {
return cbk([400, 'ExpectedArrayOfPublicKeysForForwardsViaNodes']);
}
if (!!via && !!via.filter(n => !isPublicKey(n)).length) {
return cbk([400, 'ExpectedPublicKeyForViaFilterOfForwardsForNode']);
}
return cbk();
},
// Get closed channels with via peer
getClosedChannels: ['validate', ({}, cbk) => {
// Exit early when there is no via node specified
if (!via) {
return cbk();
}
return getClosedChannels({lnd}, cbk);
}],
// Get forwards
getForwards: ['validate', ({}, cbk) => {
const forwards = [];
let token;
return asyncUntil(
cbk => cbk(null, token === false),
cbk => {
return getForwards({
after,
lnd,
token,
before: before || new Date().toISOString(),
limit: !token ? pageLimit : undefined,
},
(err, res) => {
if (!!err) {
return cbk(err);
}
limit = null;
token = res.next || false;
res.forwards.forEach(n => forwards.push(n));
return cbk();
});
},
err => {
if (!!err) {
return cbk(err);
}
return cbk(null, forwards);
}
);
}],
// Get private channels
getPrivateChannels: ['validate', ({}, cbk) => {
// Exit early when there is no via node specified
if (!via) {
return cbk();
}
return getChannels({lnd, is_private: true}, (err, res) => {
if (!!err) {
return cbk(err);
}
const channels = res.channels.map(channel => {
return via.includes(channel.partner_public_key);
});
return cbk(null, {channels});
});
}],
// Get node details
getNode: ['validate', ({}, cbk) => {
// Exit early when there is no via node specified
if (!via) {
return cbk();
}
return asyncMap(via, (key, cbk) => {
return getNode({lnd, public_key: key}, cbk);
},
(err, res) => {
if (!!err && err.slice().shift() === 404) {
return cbk(null, {channels: []});
}
if (!!err) {
return cbk(err);
}
return cbk(null, {channels: flatten(res.map(n => n.channels))});
});
}],
// Full set of forwards
forwards: [
'getClosedChannels',
'getForwards',
'getNode',
'getPrivateChannels',
({getClosedChannels, getForwards, getNode, getPrivateChannels}, cbk) =>
{
const {forwards} = forwardsViaPeer({
via,
closed_channels: !!via ? getClosedChannels.channels : [],
forwards: getForwards,
private_channels: !!via ? getPrivateChannels.channels : [],
public_channels: !!via ? getNode.channels : [],
});
return cbk(null, {forwards});
}],
},
returnResult({reject, resolve, of: 'forwards'}, cbk));
});
};