balanceofsatoshis 11.41.0 → 11.45.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,25 @@
1
1
  # Versions
2
2
 
3
+ ## 11.45.0
4
+
5
+ - `increase-inbound-liquidity`: Add support for formulas in `--amount`
6
+
7
+ ## 11.44.0
8
+
9
+ - `send`: Add support for `--max-fee-rate` to limit fees paid via PPM measure
10
+
11
+ ## 11.43.0
12
+
13
+ - `limit-forwards`: Add `--min-channel-confirmations` for custom channel ages
14
+ - `limit-forwards`: Add `--only-allow` to restrict forwards to allowed edges
15
+ - `open-balanced-channel`: Disallow fractional fee rate entry
16
+
17
+ ## 11.42.0
18
+
19
+ - `call`: Add support for `fundPsbt` to create a funded PSBT ready to sign
20
+ - `call`: Add support for `partiallySignPsbt` to add a partial sig to a PSBT
21
+ - `call`: Add support for `signPsbt` to sign and finalize a PSBT for broadcast
22
+
3
23
  ## 11.41.0
4
24
 
5
25
  - `remove-peer`: Add interactive mode to select channels to close
@@ -0,0 +1,72 @@
1
+ # Contribution Guidelines
2
+
3
+ - Feel free to open issues or pull requests
4
+ - They may not be addressed or merged
5
+ - You can ignore coding styles if you want
6
+
7
+ ## Coding Style
8
+
9
+ If you want to help with style, here are some rough guidelines on style ideas:
10
+
11
+ ### Formatting
12
+
13
+ - Spaces not tabs, 2 spaces
14
+ - Arguments to methods are snake_case
15
+ - Regular variables are camelCase
16
+ - Returned attributes are snake_case
17
+ - Minimize function nesting, make new files if nesting is required
18
+ - No extraneous whitespace
19
+ - A single newline should appear at the end of a file
20
+ - Don't bother with () in functions when not needed: `const a = b => c`
21
+ - If there are multiple things together, alphabetize them
22
+ - Don't split up long strings over multiple lines
23
+ - Lines should be terminated by explicit semicolons
24
+ - Logic like ternary operators should not extend beyond a single line
25
+ - Don't let any lines linger when they don't do anything
26
+ - Tightly space objects, like `{attribute: value}` not `{ attribute : value }`
27
+ - Use single '' quotes not double "" quotes, except when `` is required
28
+ - If conditions should avoid spanning multiple lines
29
+ - Avoid double specifying an attribute and value, `{type: type}` vs `{type}`
30
+
31
+ ### Control Flow
32
+
33
+ - Async functions should support both cbk and Promise style
34
+ - Use async.js methods for asynchronous control flow
35
+ - Try to exit early from functions when possible, and note this exit in comment
36
+ - Prefer cbk over Promise style, aside from in tests or in Promise libs
37
+ - Use `asyncAuto` for asynchronous control flow dependency management
38
+ - Callbacks should generally be named cbk even when redefinining in inner scope
39
+ - Avoid mixing non-async complex logic and async control flows in the same file
40
+ - Minimize async nesting in returned attributes and in method arguments
41
+ - Methods should always document their arguments and their output
42
+
43
+ ### Variables
44
+
45
+ - Generally use undefined rather than null when defining nil types
46
+ - Prefer variable assignments on new lines rather than on a single line
47
+ - Reduce the usage of . property access, like isArray instead of Array.isArray
48
+ - When there is a newline in an object, always put a comma at the end of a line
49
+ - Short properties always go first in objects: `{short, longer: type}`
50
+ - If a statement relies on a statement above it, it should have a newline above
51
+ - Avoid including scalar values such as strings or numbers in the code itself
52
+
53
+ ### JS Features
54
+
55
+ - Limit usage of `let` and never use `var`, prefer `const`
56
+ - Limit use of npm dependencies when possible, only use good dependencies
57
+ - Target support of the oldest node.js LTS release still being supported
58
+ - Never use class or prototype
59
+ - Do not import more methods from an import than you actually use
60
+ - Try to avoid passing objects in arguments as much as possible
61
+ - Prefer using function iteration like map and forEach over for and while
62
+ - Use arrow functions and not `function` functions whenever possible
63
+
64
+ ### Errors
65
+
66
+ - Never ignore an error case, always deal with it as soon as possible
67
+ - In the case of errors, do include the error string in the code
68
+ - Add simple validations to help target simple calling mistakes
69
+ - When throwing or returning error messages, use PascalCase for the message
70
+ - Use HTTP status codes as a guideline: 4** is a local issue, 5** is remote
71
+ - Return async errors as arrays: `[typeNumber, errorMsgString, extraDetails]`
72
+ - Try to be very specific with error messages and try to not repeat one
package/bos CHANGED
@@ -25,6 +25,7 @@ const {accountingCategories} = commandConstants;
25
25
  const balances = importLazy('./balances');
26
26
  const chain = importLazy('./chain');
27
27
  const commands = importLazy('./commands');
28
+ const display = importLazy('./display');
28
29
  const encryption = importLazy('./encryption');
29
30
  const lnd = importLazy('./lnd');
30
31
  const network = importLazy('./network');
@@ -54,7 +55,6 @@ const months = [...Array(12).keys()].map(n => ++n);
54
55
  const {REPEATABLE} = prog;
55
56
  const {STRING} = prog;
56
57
  const yearMatch = /^\d{4}$/;
57
-
58
58
  prog
59
59
  .version(version)
60
60
 
@@ -826,10 +826,14 @@ prog
826
826
 
827
827
  // Intercept forwarding requests, enforce requirements on acceptance
828
828
  .command('limit-forwarding', 'Enforce rules for routing payments')
829
+ .help('Setting --only-allow will disable all forwards except only allowed')
830
+ .help('--only-allow option can be repeated for multiple forwards')
829
831
  .option('--node <node_name>', 'Saved node to enforce rules on')
830
832
  .option('--disable-forwards', 'Disable all forwards')
831
833
  .option('--max-hours-since-last-block <h>', 'Require fresh blocks', INT, 5)
832
834
  .option('--max-new-pending-per-hour <h>', 'Limit held HTLCs', INT)
835
+ .option('--min-channel-confirmations <confs>', 'Minimum channel confs', INT)
836
+ .option('--only-allow <pair>', 'only forward fromKey/toKey', REPEATABLE)
833
837
  .action((args, options, logger) => {
834
838
  return new Promise(async (resolve, reject) => {
835
839
  try {
@@ -839,6 +843,8 @@ prog
839
843
  lnd: (await lndForNode(logger, options.node)).lnd,
840
844
  max_hours_since_last_block: options.maxHoursSinceLastBlock,
841
845
  max_new_pending_per_hour: options.maxNewPendingPerHour,
846
+ min_channel_confirmations: options.minChannelConfirmations,
847
+ only_allow: flatten([options.onlyAllow].filter(n => !!n)),
842
848
  });
843
849
  } catch (err) {
844
850
  return reject(logger.error({err}));
@@ -878,10 +884,11 @@ prog
878
884
  .command('increase-inbound-liquidity', 'Increase node inbound liquidity')
879
885
  .help('Spend down a channel to get inbound. Fee is an estimate, may be more')
880
886
  .help('If you want to control chain fee increases, use show-raw-recoveries')
887
+ .help('Formulas supported for --amount like 5*m or 0.05*BTC for 5 million')
881
888
  .option('--address <out_address>', 'Out chain address to send funds out to')
882
889
  .option('--api-key <api_key>', 'Pre-paid API key to use')
890
+ .option('--amount <amount>', 'Amount to increase inbound', STRING, '500*k')
883
891
  .option('--avoid <pubkey/chan/tag>', 'Avoid forwarding through', REPEATABLE)
884
- .option('--amount <amount>', 'Amount to increase liquidity', INT, 5e5)
885
892
  .option('--confs <confs>', 'Confs to consider reorg safe', INT, 1)
886
893
  .option('--dryrun', 'Only show cost estimate for increase')
887
894
  .option('--fast', 'Request swap server avoid batching delay')
@@ -924,11 +931,11 @@ prog
924
931
  spend_address: options.spendAddress || undefined,
925
932
  spend_tokens: options.spendAmount || undefined,
926
933
  timeout: 1000 * 60 * 60 * 10,
927
- tokens: options.amount,
934
+ tokens: display.parseAmount({amount: options.amount}).tokens,
928
935
  },
929
936
  responses.returnObject({exit, logger, reject, resolve}));
930
937
  } catch (err) {
931
- return reject(err);
938
+ return reject(logger.error({err}));
932
939
  }
933
940
  });
934
941
  })
@@ -1458,6 +1465,7 @@ prog
1458
1465
  .option('--dryrun', 'Avoid actually sending funds')
1459
1466
  .option('--in <pubkey_or_alias>', 'Route in through a specific node')
1460
1467
  .option('--max-fee <fee>', 'Maximum fee tokens', INT, 1337)
1468
+ .option('--max-fee-rate <max_fee_rate>', 'Max fee rate in PPM to pay', INT)
1461
1469
  .option('--message <message>', 'Message to include with payment')
1462
1470
  .option('--message-omit-from-key', 'Leave out the from key on messages')
1463
1471
  .option('--no-color', 'Mute all colors')
@@ -1478,6 +1486,7 @@ prog
1478
1486
  is_omitting_message_from: options.messageOmitFromKey,
1479
1487
  lnd: (await lndForNode(logger, options.node)).lnd,
1480
1488
  max_fee: options.maxFee,
1489
+ max_fee_rate: options.maxFeeRate,
1481
1490
  message: options.message,
1482
1491
  quiz_answers: flatten([options.quiz].filter(n => !!n)),
1483
1492
  out_through: options.out,
package/commands/api.json CHANGED
@@ -242,6 +242,20 @@
242
242
  ],
243
243
  "method": "enableChannel"
244
244
  },
245
+ {
246
+ "arguments": [
247
+ {
248
+ "description": "Base PSBT to fund",
249
+ "named": "psbt"
250
+ },
251
+ {
252
+ "description": "Fee rate per vbyte to use for funding",
253
+ "named": "fee_tokens_per_vbyte",
254
+ "optional": true
255
+ }
256
+ ],
257
+ "method": "fundPsbt"
258
+ },
245
259
  {
246
260
  "method": "getAccessIds"
247
261
  },
@@ -582,6 +596,13 @@
582
596
  ],
583
597
  "method": "openChannel"
584
598
  },
599
+ {
600
+ "arguments": [{
601
+ "description": "Funded PSBT Hex String",
602
+ "named": "psbt"
603
+ }],
604
+ "method": "partiallySignPsbt"
605
+ },
585
606
  {
586
607
  "arguments": [
587
608
  {
@@ -689,6 +710,13 @@
689
710
  }],
690
711
  "method": "signMessage"
691
712
  },
713
+ {
714
+ "arguments": [{
715
+ "description": "Funded PSBT to sign and finalize",
716
+ "named": "psbt"
717
+ }],
718
+ "method": "signPsbt"
719
+ },
692
720
  {
693
721
  "method": "stopDaemon"
694
722
  },
@@ -16,6 +16,7 @@ const probeDestination = require('./probe_destination');
16
16
 
17
17
  const coins = ['BTC', 'LTC'];
18
18
  const defaultFiatRateProvider = 'coingecko';
19
+ const feeTokensForFeeRate = (tokens, rate) => Math.floor(rate * tokens / 1e6);
19
20
  const fiats = ['EUR', 'USD'];
20
21
  const hasFiat = n => /(eur|usd)/gim.test(n);
21
22
  const {isArray} = Array;
@@ -23,6 +24,7 @@ const isPublicKey = n => /^[0-9A-F]{66}$/i.test(n);
23
24
  const maxQuizLength = 10;
24
25
  const rateAsTokens = rate => 1e8 / rate;
25
26
  const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
27
+ const {min} = Math;
26
28
  const minQuiz = 2;
27
29
  const minTokens = 1;
28
30
  const networks = {btc: 'BTC', btctestnet: 'BTC', ltc: 'LTC'};
@@ -45,6 +47,7 @@ const utf8AsHex = n => Buffer.from(n, 'utf8').toString('hex');
45
47
  lnd: <Authenticated LND API Object>
46
48
  logger: <Winston Logger Object>
47
49
  max_fee: <Maximum Fee Tokens Number>
50
+ [max_fee_rate]: <Max Fee Rate Tokens Per Million Number>
48
51
  [message]: <Message to Include With Payment String>
49
52
  [out_through]: <Pay Out Through Peer String>
50
53
  quiz_answers: [<Quiz Answer String>]
@@ -346,7 +349,18 @@ module.exports = (args, cbk) => {
346
349
  };
347
350
 
348
351
  try {
349
- return cbk(null, parseAmount({variables, amount: args.amount}));
352
+ const {tokens} = parseAmount({variables, amount: args.amount});
353
+
354
+ // Exit early when there is no max fee rate to compute max fee for
355
+ if (!!args.max_fee_rate === undefined) {
356
+ return cbk(null, {tokens, max_fee: args.mage_fee});
357
+ }
358
+
359
+ // Compute the max potential fee given the max fee rate constraint
360
+ const maxFeeByRate = feeTokensForFeeRate(tokens, args.max_fee_rate);
361
+
362
+ // The maximum fee to pay is the lower of the fee limit, fee by rate
363
+ return cbk(null, {tokens, max_fee: min(args.max_fee, maxFeeByRate)});
350
364
  } catch (err) {
351
365
  return cbk([400, 'FailedToParsePushAmount', err]);
352
366
  }
@@ -384,7 +398,7 @@ module.exports = (args, cbk) => {
384
398
  is_omitting_message_from: args.is_omitting_message_from,
385
399
  is_push: payment.is_push,
386
400
  is_real_payment: true,
387
- max_fee: args.max_fee,
401
+ max_fee: parseAmount.max_fee,
388
402
  message: args.message,
389
403
  messages: args.quiz_answers.map((answer, i) => ({
390
404
  type: (quizStart + i).toString(),
package/package.json CHANGED
@@ -28,7 +28,7 @@
28
28
  "crypto-js": "4.1.1",
29
29
  "csv-parse": "5.0.4",
30
30
  "goldengate": "11.0.0",
31
- "grammy": "1.6.2",
31
+ "grammy": "1.7.0",
32
32
  "hot-formula-parser": "4.0.0",
33
33
  "import-lazy": "4.0.0",
34
34
  "ini": "2.0.0",
@@ -36,7 +36,7 @@
36
36
  "invoices": "2.0.3",
37
37
  "ln-accounting": "5.0.5",
38
38
  "ln-service": "53.6.0",
39
- "ln-sync": "3.8.0",
39
+ "ln-sync": "3.9.0",
40
40
  "ln-telegram": "3.12.0",
41
41
  "moment": "2.29.1",
42
42
  "paid-services": "3.11.0",
@@ -52,7 +52,7 @@
52
52
  "description": "Lightning balance CLI",
53
53
  "devDependencies": {
54
54
  "@alexbosworth/tap": "15.0.10",
55
- "ln-docker-daemons": "2.2.1",
55
+ "ln-docker-daemons": "2.2.3",
56
56
  "mock-lnd": "1.4.1",
57
57
  "secp256k1": "4.0.3"
58
58
  },
@@ -81,5 +81,5 @@
81
81
  "postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
82
82
  "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/wallets/*.js"
83
83
  },
84
- "version": "11.41.0"
84
+ "version": "11.45.0"
85
85
  }
@@ -4,6 +4,8 @@ const {returnResult} = require('asyncjs-util');
4
4
 
5
5
  const disableAllForwards = 0;
6
6
  const hoursAsSeconds = hours => hours * 60 * 60;
7
+ const {isArray} = Array;
8
+ const isEdge = n => !!n && /^[0-9A-F]{66}\/[0-9A-F]{66}$/i.test(n);
7
9
 
8
10
  /** Limit forwarding requests
9
11
 
@@ -11,8 +13,10 @@ const hoursAsSeconds = hours => hours * 60 * 60;
11
13
  lnd: (await lndForNode(logger, options.node)).lnd,
12
14
  logger: <Winston Logger Object>
13
15
  [is_disabling_all_forwards]: <All Forwards Are Disabled Bool>
14
- [max_hours_since_last_block]: options.maxHoursSinceLastBlock,
15
- [max_new_pending_per_hour]: options.maxNewPendingPerHour,
16
+ [max_hours_since_last_block]: <Maximum Hours Since Last Block Number>
17
+ [max_new_pending_per_hour]: <Maximum Outstanding New HTLCs Per Hour Number>
18
+ [min_channel_confirmations]: <Minimum Required Channel Confs Number>
19
+ only_allow: [<In Public Key / Out Public Key String>]
16
20
  }
17
21
 
18
22
  @returns via cbk or Promise
@@ -22,6 +26,14 @@ module.exports = (args, cbk) => {
22
26
  return asyncAuto({
23
27
  // Check arguments
24
28
  validate: cbk => {
29
+ if (!isArray(args.only_allow)) {
30
+ return cbk([400, 'ExpectedOnlyAllowArrayToLimitForwarding']);
31
+ }
32
+
33
+ if (!!args.only_allow.filter(n => !isEdge(n)).length) {
34
+ return cbk([400, 'ExpectedOnlyAllowAsPublicKeyPairs']);
35
+ }
36
+
25
37
  if (!args.logger) {
26
38
  return cbk([400, 'ExpectedWinstonLoggerToLimitForwarding']);
27
39
  }
@@ -55,11 +67,25 @@ module.exports = (args, cbk) => {
55
67
  return cbk(null, hoursAsSeconds(args.max_hours_since_last_block));
56
68
  }],
57
69
 
70
+ // Only allow pairs
71
+ onlyAllow: ['validate', ({}, cbk) => {
72
+ if (!args.only_allow.length) {
73
+ return cbk();
74
+ }
75
+
76
+ const allow = args.only_allow.map(([inKey, outKey]) => {
77
+ return {inbound_peer: inKey, outbound_peer: outKey};
78
+ });
79
+
80
+ return cbk(null, allow);
81
+ }],
82
+
58
83
  // Limit forward requests
59
84
  limit: [
60
85
  'maxPendingPerHour',
61
86
  'maxSecondsSinceLastBlock',
62
- ({maxPendingPerHour, maxSecondsSinceLastBlock}, cbk) =>
87
+ 'onlyAllow',
88
+ ({maxPendingPerHour, maxSecondsSinceLastBlock, onlyAllow}, cbk) =>
63
89
  {
64
90
  args.logger.info({limiting_forwards: true});
65
91
 
@@ -67,6 +93,8 @@ module.exports = (args, cbk) => {
67
93
  lnd: args.lnd,
68
94
  max_new_pending_per_hour: maxPendingPerHour,
69
95
  max_seconds_since_last_block: maxSecondsSinceLastBlock,
96
+ min_activation_age: args.min_channel_confirmations || undefined,
97
+ only_allow: onlyAllow,
70
98
  });
71
99
 
72
100
  sub.on('error', err => {
@@ -41,6 +41,7 @@ const giveTokens = capacity => Math.ceil(capacity / 2);
41
41
  const hasInbound = channels => !!channels.find(n => !!n.remote_balance);
42
42
  const hexAsBuffer = hex => Buffer.from(hex, 'hex');
43
43
  const idAsHash = id => Buffer.from(id, 'hex').reverse();
44
+ const {isInteger} = Number;
44
45
  const isNumber = n => !isNaN(n);
45
46
  const isOdd = n => n % 2;
46
47
  const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
@@ -237,11 +238,11 @@ module.exports = (args, cbk) => {
237
238
  default: round(getChainFee.tokens_per_vbyte),
238
239
  message: 'Fee rate per vbyte for the joint funding transaction?',
239
240
  name: 'rate',
240
- type: 'number',
241
+ validate: n => !!isNumber(n) && !!Number(n) && isInteger(Number(n)),
241
242
  };
242
243
 
243
244
  return args.ask(feeRate, ({rate}) => {
244
- if (!isNumber(rate)) {
245
+ if (!isNumber(Number(rate))) {
245
246
  return cbk([400, 'ExpectedFeeRatePerVirtualByteToProposeChannel']);
246
247
  }
247
248