balanceofsatoshis 11.22.4 → 11.23.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,9 @@
1
1
  # Versions
2
2
 
3
+ ## 11.23.0
4
+
5
+ - `balance`: `--detailed`: Support unconfirmed/invalid/conflicting balances
6
+
3
7
  ## 11.22.4
4
8
 
5
9
  - `telegram`: Fix issue when moving a created invoice to a saved node
@@ -0,0 +1,93 @@
1
+ const {Transaction} = require('bitcoinjs-lib');
2
+
3
+ const inputAsOutpoint = (txId, outputIndex) => `${txId}:${outputIndex}`;
4
+ const {fromHex} = Transaction;
5
+ const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
6
+ const txIdFromHash = hash => hash.slice().reverse().toString('hex');
7
+ const uniq = arr => Array.from(new Set(arr));
8
+
9
+ /** Derive conflicted on-chain pending balances where funds are double spent or
10
+ multiple versions of the spend exist
11
+
12
+ {
13
+ transactions: [{
14
+ is_confirmed: <Transaction is Confirmed Bool>
15
+ transaction: <Raw Transaction Hex String>
16
+ }]
17
+ utxos: [{
18
+ confirmation_count: <UTXO Confirmation Count Number>
19
+ tokens: <UTXO Tokens Number>
20
+ transaction_id: <Outpoint Transaction Id Hex String>
21
+ }]
22
+ }
23
+
24
+ @returns
25
+ {
26
+ conflicting_pending_balance: <Conflicting Pending Balance Tokens Number>
27
+ invalid_pending_balance: <Invalid Pending Balance Tokens Number>
28
+ }
29
+ */
30
+ module.exports = ({transactions, utxos}) => {
31
+ const conflictingUtxos = [];
32
+ const invalidUtxos = [];
33
+ const spends = {}
34
+
35
+ // Look at unconfirmed UTXOs and collect spends of outpoints
36
+ utxos.filter(n => !n.confirmation_count).forEach((utxo, i) => {
37
+ const tx = transactions.find(n => n.id === utxo.transaction_id);
38
+
39
+ // Exit early when the raw transaction is not known
40
+ if (!tx || !tx.transaction) {
41
+ return;
42
+ }
43
+
44
+ // Register all the inputs into the spends map
45
+ return fromHex(tx.transaction).ins.forEach(input => {
46
+ const outpoint = inputAsOutpoint(txIdFromHash(input.hash), input.index);
47
+
48
+ const existing = spends[outpoint];
49
+
50
+ // When existing UTXO spends the same input this is a conflict
51
+ if (!!existing) {
52
+ conflictingUtxos.push(i);
53
+ }
54
+
55
+ // Collect spends of the outpoint
56
+ const spending = !existing ? [i] : [].concat(existing).concat(i);
57
+
58
+ return spends[outpoint] = spending;
59
+ });
60
+ });
61
+
62
+ // Look at confirmed txs and see if any unspents spend a confirmed outpoint
63
+ transactions.forEach(tx => {
64
+ // Exit early when there is no confirmed tx
65
+ if (!tx.transaction || !tx.is_confirmed) {
66
+ return;
67
+ }
68
+
69
+ // Look for pending inputs that are conflicted with a confirmed tx
70
+ return fromHex(tx.transaction).ins.forEach(input => {
71
+ const outpoint = inputAsOutpoint(txIdFromHash(input.hash), input.index);
72
+
73
+ // Exit early when nothing pending spends this outpoint
74
+ if (!spends[outpoint]) {
75
+ return;
76
+ }
77
+
78
+ // Pending things that spend a confirmed input are invalid
79
+ return spends[outpoint].forEach(n => invalidUtxos.push(n));
80
+ });
81
+ });
82
+
83
+ const conflictingTokens = uniq(conflictingUtxos)
84
+ .filter(utxoIndex => !invalidUtxos.includes(utxoIndex))
85
+ .map(n => utxos[n].tokens);
86
+
87
+ const invalidTokens = uniq(invalidUtxos).map(n => utxos[n].tokens);
88
+
89
+ return {
90
+ conflicting_pending_balance: sumOf(conflictingTokens),
91
+ invalid_pending_balance: sumOf(invalidTokens),
92
+ };
93
+ };
@@ -1,3 +1,5 @@
1
+ const conflictingBalances = require('./conflicting_balances');
2
+
1
3
  const {ceil} = Math;
2
4
  const flatten = arr => [].concat(...arr);
3
5
  const inputsCounterVBytesLength = 3;
@@ -61,6 +63,8 @@ const witnessSizeCounterVByteLength = 0.25;
61
63
  @returns
62
64
  {
63
65
  closing_balance: <Balance of Tokens Moving Out Of Channels Tokens Number>
66
+ conflicted_pending: <Conflicting Pending Balance Tokens Number>
67
+ invalid_pending: <Invalid Pending Balance Tokens Number>
64
68
  offchain_balance: <Balance of Owned Tokens In Channels Tokens Number>
65
69
  offchain_pending: <Total Pending Local Balance Tokens Number>
66
70
  onchain_balance: <Balance of Transaction Outputs Number>
@@ -170,8 +174,12 @@ module.exports = ({channels, locked, pending, transactions, utxos}) => {
170
174
  .concat(transactionLockTimeVBytesLength)
171
175
  .concat(inputElements));
172
176
 
177
+ const conflicts = conflictingBalances({transactions, utxos});
178
+
173
179
  return {
174
180
  closing_balance: sumOf(closing),
181
+ conflicted_pending: conflicts.conflicting_pending_balance,
182
+ invalid_pending: conflicts.invalid_pending_balance,
175
183
  offchain_balance: channelBalance,
176
184
  offchain_pending: pendingBalance,
177
185
  onchain_balance: chainBalance,
@@ -15,6 +15,7 @@ const {fromHex} = Transaction;
15
15
  /** Get a detailed balance that categorizes balance of tokens on the node
16
16
 
17
17
  {
18
+ [is_confirmed]: <Only Consider Confirmed Transactions Bool>
18
19
  lnd: <Authenticated LND API Object>
19
20
  }
20
21
 
@@ -113,12 +114,26 @@ module.exports = (args, cbk) => {
113
114
  locked,
114
115
  channels: getChannels.channels,
115
116
  pending: getPending.pending_channels,
116
- transactions: getTx.transactions,
117
- utxos: getUtxos.utxos,
117
+ transactions: getTx.transactions.filter(tx => {
118
+ if (!!args.is_confirmed && !tx.is_confirmed) {
119
+ return false;
120
+ }
121
+
122
+ return true;
123
+ }),
124
+ utxos: getUtxos.utxos.filter(utxo => utxo => {
125
+ if (!!args.is_confirmed && !utxo.confirmation_count) {
126
+ return false;
127
+ }
128
+
129
+ return true;
130
+ }),
118
131
  });
119
132
 
120
133
  return cbk(null, {
121
134
  closing_balance: format(balances.closing_balance) || undefined,
135
+ conflicted_pending: format(balances.conflicted_pending) || undefined,
136
+ invalid_pending: format(balances.invalid_pending) || undefined,
122
137
  offchain_balance: format(balances.offchain_balance) || undefined,
123
138
  offchain_pending: format(balances.offchain_pending) || undefined,
124
139
  onchain_balance: format(balances.onchain_balance) || undefined,
package/bos CHANGED
@@ -167,11 +167,7 @@ prog
167
167
  if (!!options.detailed) {
168
168
  return balances.getDetailedBalance({
169
169
  lnd,
170
- above: options.above,
171
- below: options.below,
172
170
  is_confirmed: options.confirmed,
173
- is_offchain_only: options.offchain,
174
- is_onchain_only: options.onchain,
175
171
  },
176
172
  responses.returnObject({logger, reject, resolve}));
177
173
  }
@@ -1627,6 +1623,7 @@ prog
1627
1623
  logger,
1628
1624
  ask: (n, cbk) => inquirer.prompt([n]).then(res => cbk(null, res)),
1629
1625
  lnd: (await lndForNode(logger, options.node)).lnd,
1626
+ separator: () => new inquirer.Separator(),
1630
1627
  },
1631
1628
  responses.returnObject({exit, logger, reject, resolve}));
1632
1629
  } catch (err) {
package/package.json CHANGED
@@ -11,17 +11,17 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@alexbosworth/caporal": "1.4.0",
14
- "@alexbosworth/fiat": "1.0.0",
14
+ "@alexbosworth/fiat": "1.0.1",
15
15
  "@alexbosworth/html2unicode": "1.1.5",
16
16
  "@alexbosworth/node-fetch": "2.6.2",
17
17
  "abort-controller": "3.0.0",
18
18
  "asciichart": "1.5.25",
19
- "async": "3.2.2",
20
- "asyncjs-util": "1.2.7",
19
+ "async": "3.2.3",
20
+ "asyncjs-util": "1.2.8",
21
21
  "bip66": "1.1.5",
22
22
  "bitcoinjs-lib": "6.0.1",
23
23
  "bolt01": "1.2.3",
24
- "bolt03": "1.2.12",
24
+ "bolt03": "1.2.13",
25
25
  "bolt07": "1.8.0",
26
26
  "cbor": "8.1.0",
27
27
  "colorette": "2.0.16",
@@ -33,14 +33,14 @@
33
33
  "import-lazy": "4.0.0",
34
34
  "ini": "2.0.0",
35
35
  "inquirer": "8.2.0",
36
- "invoices": "2.0.2",
36
+ "invoices": "2.0.3",
37
37
  "ln-accounting": "5.0.5",
38
- "ln-service": "53.2.0",
38
+ "ln-service": "53.4.0",
39
39
  "ln-sync": "3.6.1",
40
40
  "ln-telegram": "3.5.1",
41
41
  "moment": "2.29.1",
42
42
  "paid-services": "3.5.1",
43
- "probing": "2.0.1",
43
+ "probing": "2.0.2",
44
44
  "psbt": "1.1.10",
45
45
  "qrcode-terminal": "0.12.0",
46
46
  "sanitize-filename": "1.6.3",
@@ -51,7 +51,7 @@
51
51
  "description": "Lightning balance CLI",
52
52
  "devDependencies": {
53
53
  "@alexbosworth/tap": "15.0.10",
54
- "ln-docker-daemons": "2.1.0",
54
+ "ln-docker-daemons": "2.1.1",
55
55
  "mock-lnd": "1.4.1",
56
56
  "secp256k1": "4.0.3"
57
57
  },
@@ -90,5 +90,5 @@
90
90
  "postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
91
91
  "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"
92
92
  },
93
- "version": "11.22.4"
93
+ "version": "11.23.0"
94
94
  }
@@ -93,6 +93,8 @@ const tests = [
93
93
  description: 'Balance totals are calculated',
94
94
  expected: {
95
95
  closing_balance: 1,
96
+ conflicted_pending: 0,
97
+ invalid_pending: 0,
96
98
  offchain_balance: 14,
97
99
  offchain_pending: 35,
98
100
  onchain_balance: 4,
@@ -104,6 +106,8 @@ const tests = [
104
106
  description: 'Balance totals are calculated when there are no funds',
105
107
  expected: {
106
108
  closing_balance: 0,
109
+ conflicted_pending: 0,
110
+ invalid_pending: 0,
107
111
  offchain_balance: 0,
108
112
  offchain_pending: 0,
109
113
  onchain_balance: 0,
@@ -98,6 +98,10 @@ const tests = [
98
98
  'income',
99
99
  ],
100
100
  ],
101
+ rows_summary: [
102
+ ['Total', 'Asset', 'Report Date', 'Total Fiat'],
103
+ [1, 'BTC', '', '0.00'],
104
+ ],
101
105
  },
102
106
  },
103
107
  {
@@ -114,6 +118,10 @@ tests.forEach(({args, description, error, expected}) => {
114
118
  } else {
115
119
  const res = await getAccountingReport(args);
116
120
 
121
+ if (!args.is_csv) {
122
+ res.rows_summary[1][2] = '';
123
+ }
124
+
117
125
  strictSame(res, expected, 'Got expected response');
118
126
  }
119
127