balanceofsatoshis 12.26.4 → 12.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Versions
2
2
 
3
+ ## 12.28.0
4
+
5
+ - `chart-payments-received`: Optimize reporting speed for short times
6
+ - `chart-payments-received`: Add `--count` to report on count of payments
7
+ - `chart-payments-received`: Add `--for` to filter for a matching description
8
+
9
+ ## 12.27.0
10
+
11
+ - `rebalance`: `--in-filter`, `--out-filter`: add `INBOUND_FEE_RATE` variable
12
+ - `rebalance`: Ignore inbound-disabled peers when specifying `--in-filter`
13
+
14
+ ## 12.26.5
15
+
16
+ - `telegram`: Optimize /command speed by reducing duplicate wallet info calls
17
+
3
18
  ## 12.26.4
4
19
 
5
20
  - `telegram`: Fix button push handling and responding to button push queries
package/README.md CHANGED
@@ -627,6 +627,7 @@ And for `--in-filter` and `--out-filter`:
627
627
 
628
628
  - `capacity`: The total capacity with the peer
629
629
  - `heights`: The set of heights of the channels with the peer
630
+ - `inbound_fee_rate`: The fee rate the peer is charging
630
631
  - `inbound_liquidity`: The inbound liquidity with the peer
631
632
  - `outbound_liquidity`: The outbound liquidity with the peer
632
633
  - `pending_payments`: The number of pending payments
package/bos CHANGED
@@ -452,8 +452,10 @@ prog
452
452
  // Chart earnings from payments received
453
453
  .command('chart-payments-received', 'Get a chart of received payments')
454
454
  .help('Show chart for settled invoices from external parties')
455
+ .option('--count', 'Show count of settled instead of amount received')
455
456
  .option('--days <days>', 'Chart over the past number of days', INT)
456
457
  .option('--end <end_date>', 'Final date for chart as YYYY-MM-DD', STRING)
458
+ .option('--for <query>', 'Only consider payments including a specific query')
457
459
  .option('--node <node_name>', 'Get payments from saved node(s)', REPEATABLE)
458
460
  .option('--start <start_date>', 'Start date for chart as YYYY-MM-DD', STRING)
459
461
  .action((args, options, logger) => {
@@ -462,7 +464,9 @@ prog
462
464
  return wallets.getReceivedChart({
463
465
  days: options.days,
464
466
  end_date: options.end,
467
+ is_count: options.count,
465
468
  lnds: (await lnd.getLnds({logger, nodes: options.node})).lnds,
469
+ query: options.for,
466
470
  start_date: options.start,
467
471
  },
468
472
  responses.returnChart({logger, reject, resolve, data: 'data'}));
@@ -1395,6 +1399,7 @@ prog
1395
1399
  .help('--in decreases the inbound liquidity with a specific peer/tag')
1396
1400
  .help('--in-filter/--out-filter vars: CAPACITY/HEIGHTS/INBOUND_LIQUIDITY')
1397
1401
  .help('--in-filter/--out-filter vars: OUTBOUND_LIQUIDITY/PENDING_PAYMENTS')
1402
+ .help('--in-filter/--out-filter vars: INBOUND_FEE_RATE')
1398
1403
  .help('--out increases the inbound liquidity with a specific peer/tag')
1399
1404
  .option('--amount <amount>', 'Maximum amount to rebalance')
1400
1405
  .option('--avoid <pubkey_or_chanid>', 'Avoid forwarding through', REPEATABLE)
package/package.json CHANGED
@@ -36,7 +36,7 @@
36
36
  "ini": "3.0.1",
37
37
  "inquirer": "9.1.0",
38
38
  "ln-accounting": "5.0.7",
39
- "ln-service": "53.20.0",
39
+ "ln-service": "53.21.0",
40
40
  "ln-sync": "3.13.1",
41
41
  "ln-telegram": "3.22.3",
42
42
  "moment": "2.29.4",
@@ -53,7 +53,7 @@
53
53
  "description": "Lightning balance CLI",
54
54
  "devDependencies": {
55
55
  "@alexbosworth/tap": "15.0.11",
56
- "invoices": "2.1.0",
56
+ "invoices": "2.2.0",
57
57
  "ln-docker-daemons": "2.3.5",
58
58
  "mock-lnd": "1.4.4",
59
59
  "tiny-secp256k1": "2.2.1"
@@ -83,5 +83,5 @@
83
83
  "postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
84
84
  "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"
85
85
  },
86
- "version": "12.26.4"
86
+ "version": "12.28.0"
87
87
  }
@@ -1,8 +1,10 @@
1
1
  const {decodeChanId} = require('bolt07');
2
2
 
3
+ const flatten = arr => [].concat(...arr);
3
4
  const {shuffle} = require('./../arrays');
4
5
  const {isMatchingFilters} = require('./../display');
5
6
 
7
+ const {max} = Math;
6
8
  const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
7
9
 
8
10
  /** Find a node for a tag query
@@ -15,6 +17,11 @@ const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
15
17
  remote_balance: <Channel Local Balance Tokens Number>
16
18
  }]
17
19
  [filters]: [<Filter Expression String>]
20
+ policies: {
21
+ [fee_rate]: <Remote Fees Charged in Millitokens Per Million Number>
22
+ [is_disabled]: <Remote Channel Forwarding Is Disabled Bool>
23
+ public_key: <Remote Public Key Hex String>
24
+ }
18
25
  query: <Query String>
19
26
  tags: [{
20
27
  [alias]: <Tag Alias String>
@@ -37,8 +44,12 @@ const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
37
44
  }]
38
45
  }
39
46
  */
40
- module.exports = ({channels, filters, tags, query}) => {
41
- const peerKeys = channels.map(n => n.partner_public_key);
47
+ module.exports = ({channels, filters, policies, tags, query}) => {
48
+ const disabled = policies.filter(n => n.is_disabled).map(n => n.public_key);
49
+
50
+ const peerKeys = channels
51
+ .map(n => n.partner_public_key)
52
+ .filter(n => !disabled.includes(n));
42
53
 
43
54
  // Find tags that match on id or on alias, and also have relevant nodes
44
55
  const matches = tags.filter(tag => {
@@ -78,8 +89,10 @@ module.exports = ({channels, filters, tags, query}) => {
78
89
  return {match: key};
79
90
  }
80
91
 
92
+ const peerPolicies = policies.filter(n => n.public_key === key);
81
93
  const withPeer = channels.filter(n => n.partner_public_key === key);
82
94
 
95
+ const feeRates = peerPolicies.filter(n => n.fee_rate !== undefined);
83
96
  const pendingPayments = withPeer.map(n => n.pending_payments.length);
84
97
 
85
98
  const matching = isMatchingFilters({
@@ -89,6 +102,7 @@ module.exports = ({channels, filters, tags, query}) => {
89
102
  heights: withPeer.map(n => {
90
103
  return decodeChanId({channel: n.id}).block_height;
91
104
  }),
105
+ inbound_fee_rate: max(...feeRates.map(n => n.fee_rate)),
92
106
  inbound_liquidity: sumOf(withPeer.map(n => n.remote_balance)),
93
107
  outbound_liquidity: sumOf(withPeer.map(n => n.local_balance)),
94
108
  pending_payments: sumOf(pendingPayments),
@@ -212,13 +212,26 @@ module.exports = (args, cbk) => {
212
212
 
213
213
  // Find inbound tag public key
214
214
  findInTag: [
215
+ 'getFees',
215
216
  'getInitialLiquidity',
217
+ 'getPublicKey',
216
218
  'getTags',
217
- ({getInitialLiquidity, getTags}, cbk) =>
219
+ ({getFees, getInitialLiquidity, getPublicKey, getTags}, cbk) =>
218
220
  {
221
+ const id = getPublicKey.public_key;
222
+
219
223
  const {failure, match, matches} = findTagMatch({
220
224
  channels: getInitialLiquidity.channels.filter(n => n.is_active),
221
225
  filters: args.in_filters,
226
+ policies: getFees.channels.map(channel => {
227
+ const policy = channel.policies.find(n => n.public_key !== id);
228
+
229
+ return {
230
+ fee_rate: policy.fee_rate,
231
+ is_disabled: policy.is_disabled,
232
+ public_key: policy.public_key,
233
+ };
234
+ }),
222
235
  tags: getTags.tags,
223
236
  query: args.in_through,
224
237
  });
@@ -267,14 +280,31 @@ module.exports = (args, cbk) => {
267
280
  // Find outbound peer key if a name is specified
268
281
  findOutKey: [
269
282
  'findInTag',
283
+ 'getFees',
270
284
  'getInitialLiquidity',
285
+ 'getPublicKey',
271
286
  'getTags',
272
287
  'lnd',
273
- ({findInTag, getInitialLiquidity, getTags, lnd}, cbk) =>
288
+ ({
289
+ findInTag,
290
+ getFees,
291
+ getInitialLiquidity,
292
+ getPublicKey,
293
+ getTags,
294
+ lnd,
295
+ },
296
+ cbk) =>
274
297
  {
298
+ const id = getPublicKey.public_key;
299
+
275
300
  const {failure, match, matches} = findTagMatch({
276
301
  channels: getInitialLiquidity.channels.filter(n => n.is_active),
277
302
  filters: args.out_filters,
303
+ policies: getFees.channels.map(channel => {
304
+ const policy = channel.policies.find(n => n.public_key !== id);
305
+
306
+ return {fee_rate: policy.fee_rate, public_key: policy.public_key};
307
+ }),
278
308
  tags: getTags.tags,
279
309
  query: args.out_through,
280
310
  });
@@ -1,18 +1,19 @@
1
1
  const asyncAuto = require('async/auto');
2
- const asyncMap = require('async/map');
3
2
  const {returnResult} = require('asyncjs-util');
4
- const {getWalletInfo} = require('ln-service');
5
3
 
6
4
  const {getLnds} = require('./../lnd');
7
5
 
8
- const fromName = node => `${node.alias} ${node.public_key.substring(0, 8)}`;
9
6
  const {isArray} = Array;
10
- const sanitize = n => (n || '').replace(/_/g, '\\_').replace(/[*~`]/g, '');
11
7
 
12
8
  /** Get node details for telegram commands
13
9
 
14
10
  {
15
11
  logger: <Winston Logger Object>
12
+ names: [{
13
+ alias: <Node Alias String>
14
+ from: <Node Name String>
15
+ public_key: <Node Identity Public Key Hex String>
16
+ }]
16
17
  nodes: [<Saved Node Name String>]
17
18
  }
18
19
 
@@ -26,7 +27,7 @@ const sanitize = n => (n || '').replace(/_/g, '\\_').replace(/[*~`]/g, '');
26
27
  }]
27
28
  }
28
29
  */
29
- module.exports = ({logger, nodes}, cbk) => {
30
+ module.exports = ({logger, names, nodes}, cbk) => {
30
31
  return new Promise((resolve, reject) => {
31
32
  return asyncAuto({
32
33
  // Check arguments
@@ -45,32 +46,19 @@ module.exports = ({logger, nodes}, cbk) => {
45
46
  // Get associated LNDs
46
47
  getLnds: ['validate', ({}, cbk) => getLnds({logger, nodes}, cbk)],
47
48
 
48
- // Get node info for the nodes
49
- getNodes: ['getLnds', ({getLnds}, cbk) => {
50
- return asyncMap(getLnds.lnds, (lnd, cbk) => {
51
- return getWalletInfo({lnd}, (err, res) => {
52
- if (!!err) {
53
- return cbk([503, 'FailedToGetNodeInfoForTelegramNode', {err}]);
54
- }
55
-
56
- const named = fromName({
57
- alias: res.alias,
58
- public_key: res.public_key,
59
- });
49
+ // Merge node info for the nodes
50
+ nodes: ['getLnds', ({getLnds}, cbk) => {
51
+ const nodes = getLnds.lnds.map((lnd, i) => {
52
+ return {
53
+ lnd,
54
+ alias: names[i].alias,
55
+ from: names[i].from,
56
+ public_key: names[i].public_key,
57
+ };
58
+ });
60
59
 
61
- return cbk(null, {
62
- lnd,
63
- alias: res.alias,
64
- from: sanitize(named),
65
- public_key: res.public_key,
66
- });
67
- });
68
- },
69
- cbk);
60
+ return cbk(null, {nodes});
70
61
  }],
71
-
72
- // List of nodes with details
73
- nodes: ['getNodes', ({getNodes}, cbk) => cbk(null, {nodes: getNodes})],
74
62
  },
75
63
  returnResult({reject, resolve, of: 'nodes'}, cbk));
76
64
  });
@@ -56,7 +56,7 @@ const {version} = require('./../package');
56
56
 
57
57
  const fileAsDoc = file => new InputFile(file.source, file.filename);
58
58
  const fromName = node => `${node.alias} ${node.public_key.substring(0, 8)}`;
59
- const getDetails = (logger, nodes) => getNodeDetails({logger, nodes});
59
+ const getLnds = (x, y, z) => getNodeDetails({logger: x, names: y, nodes: z});
60
60
  const {isArray} = Array;
61
61
  const isHash = n => /^[0-9A-F]{64}$/i.test(n);
62
62
  let isBotInit = false;
@@ -164,6 +164,12 @@ module.exports = (args, cbk) => {
164
164
  return cbk();
165
165
  }
166
166
 
167
+ const names = getNodes.map(node => ({
168
+ alias: node.alias,
169
+ from: node.from,
170
+ public_key: node.public_key,
171
+ }));
172
+
167
173
  args.bot.catch(err => args.logger.error({telegram_error: err}));
168
174
 
169
175
  // Catch message editing
@@ -183,7 +189,7 @@ module.exports = (args, cbk) => {
183
189
  await handleBackupCommand({
184
190
  from: ctx.message.from.id,
185
191
  id: connectedId,
186
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
192
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
187
193
  reply: ctx.reply,
188
194
  send: (n, opts) => ctx.replyWithDocument(fileAsDoc(n), opts),
189
195
  });
@@ -218,7 +224,7 @@ module.exports = (args, cbk) => {
218
224
  await handleCostsCommand({
219
225
  from: ctx.message.from.id,
220
226
  id: connectedId,
221
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
227
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
222
228
  reply: n => ctx.reply(n, markdown),
223
229
  request: args.request,
224
230
  working: () => ctx.replyWithChatAction('typing'),
@@ -234,7 +240,7 @@ module.exports = (args, cbk) => {
234
240
  await handleEarningsCommand({
235
241
  from: ctx.message.from.id,
236
242
  id: connectedId,
237
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
243
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
238
244
  reply: n => ctx.reply(n, markdown),
239
245
  working: () => ctx.replyWithChatAction('typing'),
240
246
  });
@@ -249,7 +255,7 @@ module.exports = (args, cbk) => {
249
255
  await handleGraphCommand({
250
256
  from: ctx.message.from.id,
251
257
  id: connectedId,
252
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
258
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
253
259
  remove: () => ctx.deleteMessage(),
254
260
  reply: (message, options) => ctx.reply(message, options),
255
261
  text: ctx.message.text,
@@ -266,7 +272,7 @@ module.exports = (args, cbk) => {
266
272
  await handleInfoCommand({
267
273
  from: ctx.message.from.id,
268
274
  id: connectedId,
269
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
275
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
270
276
  remove: () => ctx.deleteMessage(),
271
277
  reply: (message, options) => ctx.reply(message, options),
272
278
  });
@@ -281,7 +287,7 @@ module.exports = (args, cbk) => {
281
287
  await handleInvoiceCommand({
282
288
  ctx,
283
289
  id: connectedId,
284
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
290
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
285
291
  });
286
292
  } catch (err) {
287
293
  args.logger.error({err});
@@ -317,7 +323,7 @@ module.exports = (args, cbk) => {
317
323
  await handleLiquidityCommand({
318
324
  from: ctx.message.from.id,
319
325
  id: connectedId,
320
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
326
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
321
327
  reply: (n, opt) => ctx.reply(n, opt),
322
328
  text: ctx.message.text,
323
329
  working: () => ctx.replyWithChatAction('typing'),
@@ -346,7 +352,7 @@ module.exports = (args, cbk) => {
346
352
  budget,
347
353
  from: ctx.message.from.id,
348
354
  id: connectedId,
349
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
355
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
350
356
  reply: message => ctx.reply(message, markdown),
351
357
  request: args.request,
352
358
  text: ctx.message.text,
@@ -365,7 +371,7 @@ module.exports = (args, cbk) => {
365
371
  await handlePendingCommand({
366
372
  from: ctx.message.from.id,
367
373
  id: connectedId,
368
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
374
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
369
375
  reply: (message, options) => ctx.reply(message, options),
370
376
  working: () => ctx.replyWithChatAction('typing'),
371
377
  });
@@ -444,7 +450,7 @@ module.exports = (args, cbk) => {
444
450
  ctx,
445
451
  bot: args.bot,
446
452
  id: connectedId,
447
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
453
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
448
454
  });
449
455
  } catch (err) {
450
456
  args.logger.error({err});
@@ -460,7 +466,7 @@ module.exports = (args, cbk) => {
460
466
  ctx,
461
467
  api: args.bot.api,
462
468
  id: connectedId,
463
- nodes: (await getDetails(args.logger, args.nodes)).nodes,
469
+ nodes: (await getLnds(args.logger, names, args.nodes)).nodes,
464
470
  });
465
471
  } catch (err) {
466
472
  args.logger.error({err});
@@ -11,6 +11,7 @@ const makeArgs = overrides => {
11
11
  pending_payments: [],
12
12
  remote_balance: 2,
13
13
  }],
14
+ policies: [],
14
15
  query: '0000',
15
16
  tags: [{
16
17
  id: Buffer.alloc(32).toString('hex'),
@@ -28,6 +28,7 @@ const parseDate = n => Date.parse(n);
28
28
  [days]: <Received Over Days Count Number>
29
29
  [end_date]: <End Date YYYY-MM-DD String>
30
30
  lnds: [<Authenticated LND API Object>]
31
+ [query]: <Match Description String>
31
32
  [start_date]: <Start Date YYYY-MM-DD String>
32
33
  }
33
34
 
@@ -140,6 +141,7 @@ module.exports = (args, cbk) => {
140
141
  return getAllInvoices({
141
142
  lnd,
142
143
  confirmed_after: start.toISOString(),
144
+ created_after: start.toISOString(),
143
145
  },
144
146
  cbk);
145
147
  },
@@ -149,6 +151,10 @@ module.exports = (args, cbk) => {
149
151
  }
150
152
 
151
153
  const settled = flatten(res.map(n => n.invoices)).filter(invoice => {
154
+ if (!!args.query && !invoice.description.includes(args.query)) {
155
+ return false;
156
+ }
157
+
152
158
  // Exit early when considering all invoices without an end point
153
159
  if (!args.end_date) {
154
160
  return true;
@@ -240,10 +246,16 @@ module.exports = (args, cbk) => {
240
246
 
241
247
  // Total activity
242
248
  data: ['description', 'sum', ({description, sum}, cbk) => {
249
+ const title = [
250
+ !!args.is_count ? 'Received' : 'Payments',
251
+ !!args.query ? `for “${args.query}”` : '',
252
+ !!args.is_count ? 'count' : 'received',
253
+ ];
254
+
243
255
  return cbk(null, {
244
256
  description,
245
257
  data: !args.is_count ? sum.sum : sum.count,
246
- title: !args.is_count ? 'Payments received' : 'Received count',
258
+ title: title.filter(n => !!n).join(' '),
247
259
  });
248
260
  }],
249
261
  },