ln-accounting 5.0.2 → 5.0.3
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/esplora/get_esplora_tx.js +88 -0
- package/esplora/get_esplora_vout.js +74 -0
- package/esplora/get_proxy_tx.js +74 -0
- package/esplora/get_proxy_vout.js +62 -0
- package/esplora/index.js +4 -0
- package/package.json +2 -2
- package/records/get_chain_transactions.js +29 -26
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const dateFromEpoch = epoch => new Date(epoch * 1e3).toISOString();
|
|
5
|
+
const {isArray} = Array;
|
|
6
|
+
const url = (api, id) => `${api}tx/${id}`;
|
|
7
|
+
|
|
8
|
+
/** Get transaction details from Esplora-compatible API
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
api: <Esplora API Base String>
|
|
12
|
+
id: <Transaction Id Hex String>
|
|
13
|
+
request: <Request Function>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@returns via cbk or Promise
|
|
17
|
+
{
|
|
18
|
+
[confirmation_height]: <Confirmed In Block At Height Number>
|
|
19
|
+
[created_at]: <Transaction Confirmation Date ISO 8601 Date String>
|
|
20
|
+
[block_id]: <Confirmed In Block With Hash Hex String>
|
|
21
|
+
fee: <Transaction Fee Tokens Number>
|
|
22
|
+
output_addresses: [<Output Address String>]
|
|
23
|
+
}
|
|
24
|
+
*/
|
|
25
|
+
module.exports = ({api, id, request}, cbk) => {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
return asyncAuto({
|
|
28
|
+
// Check argument
|
|
29
|
+
validate: cbk => {
|
|
30
|
+
if (!api) {
|
|
31
|
+
return cbk([400, 'ExpectedBaseEsploraApiToGetEsploraTx']);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!id) {
|
|
35
|
+
return cbk([400, 'ExpectedTransactionIdToGetEsploraTx']);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!request) {
|
|
39
|
+
return cbk([400, 'ExpectedRequestFunctionToGetEsploraTx']);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return cbk();
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Get tx details
|
|
46
|
+
getDetails: ['validate', ({}, cbk) => {
|
|
47
|
+
return request({json: true, url: url(api, id)}, (err, r, body) => {
|
|
48
|
+
if (!!err) {
|
|
49
|
+
return cbk([503, 'UnexpectedErrorGettingEsploraTx', {err}]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!body) {
|
|
53
|
+
return cbk([503, 'ExpectedTxLookupResultForEsploraTx']);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (body.fee === undefined) {
|
|
57
|
+
return cbk([503, 'ExpectedTransactionFeeInResultFromEsplora']);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!body.status) {
|
|
61
|
+
return cbk([503, 'ExpectedStatusOfEsploraTransaction']);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!isArray(body.vout)) {
|
|
65
|
+
return cbk([503, 'ExpectedOutputsInEsploraTransaction']);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Exit early when transaction is not confirmed
|
|
69
|
+
if (!body.status.confirmed) {
|
|
70
|
+
return cbk(null, {
|
|
71
|
+
fee: body.fee,
|
|
72
|
+
output_addresses: body.vout.map(n => n.scriptpubkey_address),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return cbk(null, {
|
|
77
|
+
block_id: body.status.block_hash,
|
|
78
|
+
confirmation_height: body.status.block_height,
|
|
79
|
+
created_at: dateFromEpoch(body.status.block_time),
|
|
80
|
+
fee: body.fee,
|
|
81
|
+
output_addresses: body.vout.map(n => n.scriptpubkey_address),
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}],
|
|
85
|
+
},
|
|
86
|
+
returnResult({reject, resolve, of: 'getDetails'}, cbk));
|
|
87
|
+
});
|
|
88
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const dateFromEpoch = epoch => new Date(epoch * 1e3).toISOString();
|
|
5
|
+
const {isArray} = Array;
|
|
6
|
+
const url = (api, id) => `${api}tx/${id}`;
|
|
7
|
+
|
|
8
|
+
/** Get transaction details from Esplora-compatible API
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
api: <Esplora API Base String>
|
|
12
|
+
id: <Transaction Id Hex String>
|
|
13
|
+
request: <Request Function>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@returns via cbk or Promise
|
|
17
|
+
{
|
|
18
|
+
tokens: <Transaction Output Tokens Number>
|
|
19
|
+
}
|
|
20
|
+
*/
|
|
21
|
+
module.exports = ({api, id, request, vout}, cbk) => {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
return asyncAuto({
|
|
24
|
+
// Check argument
|
|
25
|
+
validate: cbk => {
|
|
26
|
+
if (!api) {
|
|
27
|
+
return cbk([400, 'ExpectedApiPathToGetEsploraVout']);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!id) {
|
|
31
|
+
return cbk([400, 'ExpectedTransactionIdToGetEsploraVout']);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!request) {
|
|
35
|
+
return cbk([400, 'ExpectedRequestFunctionToGetEsploraVout']);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (vout === undefined) {
|
|
39
|
+
return cbk([400, 'ExpectedTransactionOutputIndexToGetEsploraVout']);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return cbk();
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Get tx details
|
|
46
|
+
getDetails: ['validate', ({}, cbk) => {
|
|
47
|
+
return request({json: true, url: url(api, id)}, (err, r, body) => {
|
|
48
|
+
if (!!err) {
|
|
49
|
+
return cbk([503, 'UnexpectedErrorGettingExploraTx', {err}]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!body) {
|
|
53
|
+
return cbk([503, 'ExpectedTxLookupResultForEsploraTx']);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!isArray(body.vout)) {
|
|
57
|
+
return cbk([503, 'ExpectedOutputsArrayForExploraTx']);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!body.vout[vout]) {
|
|
61
|
+
return cbk([503, 'ExpectedOutputInEsploraTxDetails']);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!body.vout[vout].value) {
|
|
65
|
+
return cbk([503, 'ExpectedOutputValueInEsploraTxDetails']);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return cbk(null, {tokens: body.vout[vout].value});
|
|
69
|
+
});
|
|
70
|
+
}],
|
|
71
|
+
},
|
|
72
|
+
returnResult({reject, resolve, of: 'getDetails'}, cbk));
|
|
73
|
+
});
|
|
74
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const getEsploraTx = require('./get_esplora_tx');
|
|
5
|
+
|
|
6
|
+
const apiBlockstreamBtc = 'https://blockstream.info/api/';
|
|
7
|
+
const apiBlockstreamBtcTestnet = 'https://blockstream.info/testnet/api/';
|
|
8
|
+
const apiMempoolSpaceBtc = 'https://mempool.space/api/';
|
|
9
|
+
const btcTestnet = 'btctestnet';
|
|
10
|
+
const random = arr => arr[Math.floor(Math.random() * arr.length)];
|
|
11
|
+
|
|
12
|
+
/** Get a transaction as a proxy for a local transaction
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
id: <Transaction Id Hex String>
|
|
16
|
+
[network]: <Network Name String>
|
|
17
|
+
request: <Request Function>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@returns via cbk or Promise
|
|
21
|
+
{
|
|
22
|
+
[block_id]: <Block Hash Hex String>
|
|
23
|
+
[confirmation_height]: <Transaction Confirmed At Height Number>
|
|
24
|
+
created_at: <Transaction Created At ISO 8601 Date String>>
|
|
25
|
+
fee: <Transaction Fee Tokens Number>
|
|
26
|
+
id: <Transaction Id Hex String>
|
|
27
|
+
is_confirmed: <Transaction Confirmed Bool>
|
|
28
|
+
output_addresses: [<Transaction Output Address String>]
|
|
29
|
+
}
|
|
30
|
+
*/
|
|
31
|
+
module.exports = ({id, network, request}, cbk) => {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
return asyncAuto({
|
|
34
|
+
// Check arguments
|
|
35
|
+
validate: cbk => {
|
|
36
|
+
if (!id) {
|
|
37
|
+
return cbk([400, 'ExpectedPaymentIdToGetProxyTransaction']);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!request) {
|
|
41
|
+
return cbk([400, 'ExpectedRequestFunctionToGetProxyTransaction']);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return cbk();
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Determine the API to use
|
|
48
|
+
api: ['validate', ({}, cbk) => {
|
|
49
|
+
if (network === btcTestnet) {
|
|
50
|
+
return cbk(null, apiBlockstreamBtcTestnet);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return cbk(null, random([apiBlockstreamBtc, apiMempoolSpaceBtc]));
|
|
54
|
+
}],
|
|
55
|
+
|
|
56
|
+
// Get transaction
|
|
57
|
+
getTx: ['api', ({api}, cbk) => getEsploraTx({api, id, request}, cbk)],
|
|
58
|
+
|
|
59
|
+
// Transaction details
|
|
60
|
+
tx: ['getTx', ({api, getTx}, cbk) => {
|
|
61
|
+
return cbk(null, {
|
|
62
|
+
id,
|
|
63
|
+
block_id: getTx.block_id,
|
|
64
|
+
confirmation_height: getTx.confirmation_height,
|
|
65
|
+
created_at: getTx.created_at || new Date().toISOString(),
|
|
66
|
+
fee: getTx.fee,
|
|
67
|
+
is_confirmed: !!getTx.block_id,
|
|
68
|
+
output_addresses: getTx.output_addresses,
|
|
69
|
+
});
|
|
70
|
+
}],
|
|
71
|
+
},
|
|
72
|
+
returnResult({reject, resolve, of: 'tx'}, cbk));
|
|
73
|
+
});
|
|
74
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const getEsploraVout = require('./get_esplora_vout');
|
|
5
|
+
|
|
6
|
+
const apiBlockstreamBtc = 'https://blockstream.info/api/';
|
|
7
|
+
const apiBlockstreamBtcTestnet = 'https://blockstream.info/testnet/api/';
|
|
8
|
+
const apiMempoolSpaceBtc = 'https://mempool.space/api/';
|
|
9
|
+
const btcTestnet = 'btctestnet';
|
|
10
|
+
const random = arr => arr[Math.floor(Math.random() * arr.length)];
|
|
11
|
+
|
|
12
|
+
/** Get a vout from an esplora endpoint
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
id: <Transaction Id Hex String>
|
|
16
|
+
[network]: <Network Name String>
|
|
17
|
+
request: <Request Function>
|
|
18
|
+
vout: <Transaction Output Index Number>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@returns via cbk or Promise
|
|
22
|
+
{
|
|
23
|
+
tokens: <Transaction Output Tokens Number>
|
|
24
|
+
}
|
|
25
|
+
*/
|
|
26
|
+
module.exports = ({id, network, request, vout}, cbk) => {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
return asyncAuto({
|
|
29
|
+
// Check arguments
|
|
30
|
+
validate: cbk => {
|
|
31
|
+
if (!id) {
|
|
32
|
+
return cbk([400, 'ExpectedPaymentIdToGetProxyVout']);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!request) {
|
|
36
|
+
return cbk([400, 'ExpectedRequestFunctionToGetProxyVout']);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (vout === undefined) {
|
|
40
|
+
return cbk([400, 'ExpectedTransactionOutputIndexToGetProxyVout']);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return cbk();
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Determine the API to use
|
|
47
|
+
api: ['validate', ({}, cbk) => {
|
|
48
|
+
if (network === btcTestnet) {
|
|
49
|
+
return cbk(null, apiBlockstreamBtcTestnet);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return cbk(null, random([apiBlockstreamBtc, apiMempoolSpaceBtc]));
|
|
53
|
+
}],
|
|
54
|
+
|
|
55
|
+
// Get transaction vout
|
|
56
|
+
getVout: ['api', ({api}, cbk) => {
|
|
57
|
+
return getEsploraVout({api, id, request, vout}, cbk);
|
|
58
|
+
}],
|
|
59
|
+
},
|
|
60
|
+
returnResult({reject, resolve, of: 'getVout'}, cbk));
|
|
61
|
+
});
|
|
62
|
+
};
|
package/esplora/index.js
ADDED
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"bitcoinjs-lib": "5.2.0",
|
|
13
13
|
"goldengate": "10.4.0",
|
|
14
14
|
"json2csv": "5.0.6",
|
|
15
|
-
"ln-service": "52.
|
|
15
|
+
"ln-service": "52.4.0"
|
|
16
16
|
},
|
|
17
17
|
"description": "lnd accounting reports",
|
|
18
18
|
"devDependencies": {
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"scripts": {
|
|
36
36
|
"test": "tap --branches=1 --functions=1 --lines=1 --statements=1 test/blockstream/*.js test/fiat/*.js test/harmony/*.js test/records/*.js"
|
|
37
37
|
},
|
|
38
|
-
"version": "5.0.
|
|
38
|
+
"version": "5.0.3"
|
|
39
39
|
}
|
|
@@ -8,17 +8,17 @@ const {getSweepTransactions} = require('ln-service');
|
|
|
8
8
|
const {returnResult} = require('asyncjs-util');
|
|
9
9
|
const {Transaction} = require('bitcoinjs-lib');
|
|
10
10
|
|
|
11
|
-
const {
|
|
12
|
-
const {
|
|
11
|
+
const {getProxyTx} = require('./../esplora');
|
|
12
|
+
const {getProxyVout} = require('./../esplora');
|
|
13
13
|
|
|
14
14
|
const dateAsMs = date => new Date(date).getTime();
|
|
15
15
|
const {fromHex} = Transaction;
|
|
16
|
-
const interval =
|
|
16
|
+
const interval = 200;
|
|
17
17
|
const {isArray} = Array;
|
|
18
18
|
const msAsBlocks = ms => Math.ceil(ms / 1000 / 60 / 2.5);
|
|
19
19
|
const {now} = Date;
|
|
20
20
|
const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
|
|
21
|
-
const times =
|
|
21
|
+
const times = 15;
|
|
22
22
|
|
|
23
23
|
/** Get chain transactions, including sweep fees
|
|
24
24
|
|
|
@@ -116,7 +116,7 @@ module.exports = ({after, before, lnd, network, request}, cbk) => {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
return asyncRetry({interval, times}, cbk => {
|
|
119
|
-
return
|
|
119
|
+
return getProxyVout({
|
|
120
120
|
network,
|
|
121
121
|
request,
|
|
122
122
|
id: spend.transaction_id,
|
|
@@ -211,29 +211,32 @@ module.exports = ({after, before, lnd, network, request}, cbk) => {
|
|
|
211
211
|
|
|
212
212
|
// Exit early when the close transaction is missing
|
|
213
213
|
if (hasMissingLocalData) {
|
|
214
|
-
return
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
},
|
|
219
|
-
(err, res) => {
|
|
220
|
-
if (!!err) {
|
|
221
|
-
return cbk(err);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return cbk(null, {
|
|
225
|
-
block_id: res.block_id,
|
|
226
|
-
confirmation_height: res.confirmation_height,
|
|
227
|
-
created_at: res.created_at || new Date().toISOString(),
|
|
228
|
-
description: 'Channel close',
|
|
229
|
-
fee: res.fee,
|
|
214
|
+
return asyncRetry({interval, times}, cbk => {
|
|
215
|
+
return getProxyTx({
|
|
216
|
+
network,
|
|
217
|
+
request,
|
|
230
218
|
id: channel.close_transaction_id,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
219
|
+
},
|
|
220
|
+
(err, res) => {
|
|
221
|
+
if (!!err) {
|
|
222
|
+
return cbk(err);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return cbk(null, {
|
|
226
|
+
block_id: res.block_id,
|
|
227
|
+
confirmation_height: res.confirmation_height,
|
|
228
|
+
created_at: res.created_at,
|
|
229
|
+
description: 'Channel close',
|
|
230
|
+
fee: res.fee,
|
|
231
|
+
id: res.id,
|
|
232
|
+
is_confirmed: res.is_confirmed,
|
|
233
|
+
is_outgoing: true,
|
|
234
|
+
output_addresses: res.output_addresses,
|
|
235
|
+
tokens: res.fee,
|
|
236
|
+
});
|
|
235
237
|
});
|
|
236
|
-
}
|
|
238
|
+
},
|
|
239
|
+
cbk);
|
|
237
240
|
}
|
|
238
241
|
|
|
239
242
|
const inputs = fromHex(tx.transaction).ins.map(({hash, index}) => {
|