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 +4 -0
- package/balances/conflicting_balances.js +93 -0
- package/balances/detailed_balances.js +8 -0
- package/balances/get_detailed_balance.js +17 -2
- package/bos +1 -4
- package/package.json +9 -9
- package/test/balances/test_detailed_balance.js +4 -0
- package/test/balances/test_get_accounting_report.js +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
20
|
-
"asyncjs-util": "1.2.
|
|
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.
|
|
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.
|
|
36
|
+
"invoices": "2.0.3",
|
|
37
37
|
"ln-accounting": "5.0.5",
|
|
38
|
-
"ln-service": "53.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|