From 3a9f605aa9df21c82101d022a250f8c1354a7ebd Mon Sep 17 00:00:00 2001 From: Alex Bosworth Date: Sun, 13 Mar 2022 16:42:00 -0700 Subject: [PATCH] add in and out constraints to fees paid chart --- CHANGELOG.md | 5 +++ CONTRIBUTING.md | 1 + bos | 4 ++ package-lock.json | 4 +- package.json | 2 +- routing/get_fees_paid.js | 88 ++++++++++++++++++++++++++++++++++++---- 6 files changed, 93 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 282f166..30af64d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Versions +## 11.59.0 + +- `chart-fees-paid`: Add `--in` to filter on routes in specified node +- `chart-fees-paid`: Add `--out` to filter on routes out specified peer + ## 11.58.0 - `trade-secret`: Add experimental channel sales feature diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7e8a9b2..b6b7d90 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,7 @@ - Feel free to open issues or pull requests - They may not be addressed or merged - You can ignore coding styles if you want +- Readability trumps almost everything else ## Coding Style diff --git a/bos b/bos index fbe1471..803b7fd 100755 --- a/bos +++ b/bos @@ -396,10 +396,12 @@ prog .help('Show the routing fees paid to forwarding nodes') .help('--rebalances can return results much more quickly') .option('--days ', 'Chart fees over the past number of days', INT, 60) + .option('--in ', 'Fees paid on routes in node with public key') .option('--most-fees', 'View table of fees paid per node') .option('--most-forwarded', 'View table of forwarded per node') .option('--network', 'Show only non-peers in table view') .option('--node ', 'Get fees chart for saved node(s)', REPEATABLE) + .option('--out ', 'Fees paid on routes out peer with public key') .option('--peers', 'Show only peers in table view') .option('--rebalances', 'Only consider fees paid in self-to-self transfers') .action((args, options, logger) => { @@ -414,12 +416,14 @@ prog return routing.getFeesPaid({ days: options.days, fs: {getFile: readFile}, + in: options.in, is_most_fees_table: options.mostFees, is_most_forwarded_table: options.mostForwarded, is_network: options.network, is_peer: options.peers, is_rebalances_only: options.rebalances, lnds: (await lnd.getLnds({logger, nodes: options.node})).lnds, + out: options.out, }, (options.mostFees || options.mostForwarded) ? asTable : chart); } catch (err) { diff --git a/package-lock.json b/package-lock.json index f1f38d6..6f88782 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "balanceofsatoshis", - "version": "11.58.0", + "version": "11.59.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "balanceofsatoshis", - "version": "11.58.0", + "version": "11.59.0", "license": "MIT", "dependencies": { "@alexbosworth/caporal": "1.4.0", diff --git a/package.json b/package.json index 8fa2aa5..447f5af 100644 --- a/package.json +++ b/package.json @@ -81,5 +81,5 @@ "postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .", "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js" }, - "version": "11.58.0" + "version": "11.59.0" } diff --git a/routing/get_fees_paid.js b/routing/get_fees_paid.js index 3591a58..74f113b 100644 --- a/routing/get_fees_paid.js +++ b/routing/get_fees_paid.js @@ -1,6 +1,7 @@ const asyncAuto = require('async/auto'); const asyncMap = require('async/map'); const {getNode} = require('ln-service'); +const {getNodeAlias} = require('ln-sync'); const {getChannels} = require('ln-service'); const {getPayments} = require('ln-sync'); const {getRebalancePayments} = require('ln-sync'); @@ -24,6 +25,7 @@ const minChartDays = 4; const maxChartDays = 90; const mtokensAsBigUnit = n => (Number(n / BigInt(1e3)) / 1e8).toFixed(8); const mtokensAsTokens = mtokens => Number(mtokens / BigInt(1e3)); +const title = 'Routing fees paid'; const tokensAsBigUnit = tokens => (tokens / 1e8).toFixed(8); /** Get routing fees paid @@ -135,9 +137,63 @@ module.exports = (args, cbk) => { // Filter the payments forwards: ['getPayments', 'start', ({getPayments, start}, cbk) => { - const payments = getPayments.filter(payment => { - return payment.confirmed_at > start.toISOString(); - }); + const payments = getPayments + .filter(payment => { + return payment.confirmed_at > start.toISOString(); + }) + .map(payment => { + const attempts = payment.attempts.filter(({route}) => { + const keys = route.hops.map(n => n.public_key); + + const [outHop] = keys; + + const [, inHop] = keys.slice().reverse(); + + if (!outHop) { + return false; + } + + // Ignore attempts that do not include the specified out hop + if (!!args.out && outHop !== args.out) { + return false; + } + + if (!!args.in && !inHop) { + return false; + } + + // Ignore attempts that do not include the specified in hop + if (!!args.in && inHop !== args.in) { + return false; + } + + return true; + }); + + if (!attempts.length) { + return; + } + + const totalFees = attempts.reduce((sum, attempt) => { + return sum + BigInt(attempt.route.fee_mtokens); + }, + BigInt(Number())); + + const totalTokens = attempts.reduce((sum, attempt) => { + return sum + BigInt(attempt.route.mtokens); + }, + BigInt(Number())); + + return { + attempts, + confirmed_at: payment.confirmed_at, + created_at: payment.created_at, + fee: mtokensAsTokens(totalFees), + fee_mtokens: totalFees.toString(), + mtokens: totalTokens.toString(), + }; + }) + .filter(n => !!n); return cbk(null, payments); }], @@ -315,12 +371,28 @@ module.exports = (args, cbk) => { return cbk(null, `${duration} ${since}. Total: ${paid}`); }], - // Fees paid - data: ['description', 'rows', 'sum', ({description, rows, sum}, cbk) => { - const data = sum.fees; - const title = 'Routing fees paid'; + // Title for fees paid + title: ['validate', async ({}) => { + const [lnd] = args.lnds; - return cbk(null, {data, description, rows, title}); + const into = !args.in ? null : await getNodeAlias({lnd, id: args.in}); + const out = !args.out ? null : await getNodeAlias({lnd, id: args.out}); + + const inPeer = !!args.in ? `in ${into.alias || into.id}` : ''; + const outPeer = !!args.out ? `out ${out.alias || out.id}` : ''; + + return [title, outPeer, inPeer].filter(n => !!n).join(' '); + }], + + // Fees paid + data: [ + 'description', + 'rows', + 'sum', + 'title', + ({description, rows, sum, title}, cbk) => + { + return cbk(null, {description, rows, title, data: sum.fees}); }], }, returnResult({reject, resolve, of: 'data'}, cbk));