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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Versions
2
2
 
3
+ ## Version 5.0.3
4
+
5
+ - `getChainTransactions`: Add mempool space tx data lookup method
6
+
3
7
  ## Version 5.0.2
4
8
 
5
9
  - `getChainTransactions`: Optimize lookup speed when an after date is specified
@@ -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
+ };
@@ -0,0 +1,4 @@
1
+ const getProxyTx = require('./get_proxy_tx');
2
+ const getProxyVout = require('./get_proxy_vout');
3
+
4
+ module.exports = {getProxyTx, getProxyVout};
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.3.0"
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.2"
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 {getBlockstreamTx} = require('./../blockstream');
12
- const {getBlockstreamVout} = require('./../blockstream');
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 = retryCount => 100 * Math.pow(2, retryCount);
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 = 10;
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 getBlockstreamVout({
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 getBlockstreamTx({
215
- network,
216
- request,
217
- id: channel.close_transaction_id,
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
- is_confirmed: !!res.block_id,
232
- is_outgoing: true,
233
- output_addresses: res.output_addresses,
234
- tokens: res.fee,
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}) => {