ln-service 54.0.0 → 54.1.1

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
+ ## 54.1.1
4
+
5
+ - `subscribeToPayments`: Add method to listen to all outgoing payments
6
+
3
7
  ## 54.0.0
4
8
 
5
9
  ### Breaking Changes
package/README.md CHANGED
@@ -242,6 +242,7 @@ for `unlocker` methods.
242
242
  - [subscribeToPayViaDetails](#subscribetopayviadetails) - Pay using details
243
243
  - [subscribeToPayViaRequest](#subscribetopayviarequest) - Pay using a request
244
244
  - [subscribeToPayViaRoutes](#subscribetopayviaroutes) - Pay using routes
245
+ - [subscribeToPayments](#subscribetopayments) - Subscribe to outgoing payments
245
246
  - [subscribeToPeerMessages](#subscribetopeermessages) - Listen to incoming
246
247
  custom messages
247
248
  - [subscribeToPeers](#subscribetopeers) - Subscribe to peers connectivity
@@ -6254,6 +6255,111 @@ const sub = subscribeToPayViaRoutes({lnd, routes: [route]});
6254
6255
  const [success] = await once(sub, 'success');
6255
6256
  ```
6256
6257
 
6258
+ ### subscribeToPayments
6259
+
6260
+ Subscribe to outgoing payments
6261
+
6262
+ Requires `offchain:read` permission
6263
+
6264
+ Note: Method not supported on LND 0.15.2 and below
6265
+
6266
+ {
6267
+ lnd: <Authenticated LND API Object>
6268
+ }
6269
+
6270
+ @throws
6271
+ <Error>
6272
+
6273
+
6274
+ @returns
6275
+ <Subscription EventEmitter Object>
6276
+
6277
+ @event 'confirmed'
6278
+ {
6279
+ confirmed_at: <Payment Confirmed At ISO 8601 Date String>
6280
+ created_at: <Payment Created At ISO 8601 Date String>
6281
+ destination: <Payment Destination Hex String>
6282
+ fee: <Total Fee Tokens Paid Rounded Down Number>
6283
+ fee_mtokens: <Total Fee Millitokens Paid String>
6284
+ id: <Payment Hash Hex String>
6285
+ mtokens: <Total Millitokens Paid String>
6286
+ paths: [{
6287
+ fee: <Total Fee Tokens Paid Number>
6288
+ fee_mtokens: <Total Fee Millitokens Paid String>
6289
+ hops: [{
6290
+ channel: <Standard Format Channel Id String>
6291
+ channel_capacity: <Channel Capacity Tokens Number>
6292
+ fee: <Fee Tokens Rounded Down Number>
6293
+ fee_mtokens: <Fee Millitokens String>
6294
+ forward: <Forward Tokens Number>
6295
+ forward_mtokens: <Forward Millitokens String>
6296
+ public_key: <Public Key Hex String>
6297
+ timeout: <Timeout Block Height Number>
6298
+ }]
6299
+ mtokens: <Total Millitokens Paid String>
6300
+ safe_fee: <Total Fee Tokens Paid Rounded Up Number>
6301
+ safe_tokens: <Total Tokens Paid, Rounded Up Number>
6302
+ timeout: <Expiration Block Height Number>
6303
+ }]
6304
+ [request]: <BOLT 11 Encoded Payment Request String>
6305
+ safe_fee: <Total Fee Tokens Paid Rounded Up Number>
6306
+ safe_tokens: <Total Tokens Paid, Rounded Up Number>
6307
+ secret: <Payment Preimage Hex String>
6308
+ timeout: <Expiration Block Height Number>
6309
+ tokens: <Total Tokens Paid Rounded Down Number>
6310
+ }
6311
+
6312
+ @event 'failed'
6313
+ {
6314
+ is_insufficient_balance: <Failed Due To Lack of Balance Bool>
6315
+ is_invalid_payment: <Failed Due to Payment Rejected At Destination Bool>
6316
+ is_pathfinding_timeout: <Failed Due to Pathfinding Timeout Bool>
6317
+ is_route_not_found: <Failed Due to Absence of Path Through Graph Bool>
6318
+ }
6319
+
6320
+ @event 'paying'
6321
+ {
6322
+ created_at: <Payment Created At ISO 8601 Date String>
6323
+ destination: <Payment Destination Hex String>
6324
+ id: <Payment Hash Hex String>
6325
+ mtokens: <Total Millitokens Pending String>
6326
+ paths: [{
6327
+ fee: <Total Fee Tokens Pending Number>
6328
+ fee_mtokens: <Total Fee Millitokens Pending String>
6329
+ hops: [{
6330
+ channel: <Standard Format Channel Id String>
6331
+ channel_capacity: <Channel Capacity Tokens Number>
6332
+ fee: <Fee Tokens Rounded Down Number>
6333
+ fee_mtokens: <Fee Millitokens String>
6334
+ forward: <Forward Tokens Number>
6335
+ forward_mtokens: <Forward Millitokens String>
6336
+ public_key: <Public Key Hex String>
6337
+ timeout: <Timeout Block Height Number>
6338
+ }]
6339
+ mtokens: <Total Millitokens Pending String>
6340
+ safe_fee: <Total Fee Tokens Pending Rounded Up Number>
6341
+ safe_tokens: <Total Tokens Pending, Rounded Up Number>
6342
+ timeout: <Expiration Block Height Number>
6343
+ }]
6344
+ [request]: <BOLT 11 Encoded Payment Request String>
6345
+ safe_tokens: <Total Tokens Pending, Rounded Up Number>
6346
+ [timeout]: <Expiration Block Height Number>
6347
+ tokens: <Total Tokens Pending Rounded Down Number>
6348
+ }
6349
+
6350
+ Example:
6351
+
6352
+ ```node
6353
+ const {subscribeToPayments} = require('ln-service');
6354
+
6355
+ const sub = subscribeToPayments({lnd});
6356
+
6357
+ const payments = [];
6358
+
6359
+ // Collect sent payments
6360
+ sub.on('confirmed', n => payments.push(n));
6361
+ ```
6362
+
6257
6363
  ### subscribeToPeerMessages
6258
6364
 
6259
6365
  Subscribe to incoming peer messages
package/index.js CHANGED
@@ -125,6 +125,7 @@ const {subscribeToPastPayments} = require('lightning');
125
125
  const {subscribeToPayViaDetails} = require('lightning');
126
126
  const {subscribeToPayViaRequest} = require('lightning');
127
127
  const {subscribeToPayViaRoutes} = require('lightning');
128
+ const {subscribeToPayments} = require('lightning');
128
129
  const {subscribeToPeerMessages} = require('lightning');
129
130
  const {subscribeToPeers} = require('lightning');
130
131
  const {subscribeToProbeForRoute} = require('lightning');
@@ -275,6 +276,7 @@ module.exports = {
275
276
  subscribeToPayViaDetails,
276
277
  subscribeToPayViaRequest,
277
278
  subscribeToPayViaRoutes,
279
+ subscribeToPayments,
278
280
  subscribeToPeerMessages,
279
281
  subscribeToPeers,
280
282
  subscribeToProbeForRoute,
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "cors": "2.8.5",
12
12
  "express": "4.18.1",
13
13
  "invoices": "2.2.0",
14
- "lightning": "6.0.0",
14
+ "lightning": "6.1.1",
15
15
  "macaroon": "3.0.4",
16
16
  "morgan": "1.10.0",
17
17
  "ws": "8.9.0"
@@ -27,8 +27,8 @@
27
27
  "bitcoinjs-lib": "6.0.2",
28
28
  "bn.js": "5.2.1",
29
29
  "bs58check": "2.1.2",
30
- "ecpair": "2.0.1",
31
- "ln-docker-daemons": "3.0.0",
30
+ "ecpair": "2.1.0",
31
+ "ln-docker-daemons": "3.0.1",
32
32
  "p2tr": "1.3.2",
33
33
  "portfinder": "1.0.32",
34
34
  "psbt": "2.7.1",
@@ -71,5 +71,5 @@
71
71
  "integration-test-0.12.0": "DOCKER_LND_VERSION=v0.12.0-beta npm run test",
72
72
  "test": "echo $DOCKER_LND_VERSION && tap -j 2 --branches=1 --functions=1 --lines=1 --statements=1 -t 200 test/autopilotrpc-integration/*.js test/chainrpc-integration/*.js test/integration/*.js test/invoicesrpc-integration/*.js test/peersrpc-integration/*.js test/routerrpc-integration/*.js test/signerrpc-integration/*.js test/tower_clientrpc-integration/*.js test/tower_serverrpc-integration/*.js test/walletrpc-integration/*.js"
73
73
  },
74
- "version": "54.0.0"
74
+ "version": "54.1.1"
75
75
  }
@@ -0,0 +1,93 @@
1
+ const {spawnLightningCluster} = require('ln-docker-daemons');
2
+ const {test} = require('@alexbosworth/tap');
3
+
4
+ const {createChainAddress} = require('./../../');
5
+ const {delay} = require('./../macros');
6
+ const {grantAccess} = require('./../../');
7
+ const {restrictMacaroon} = require('./../../');
8
+
9
+ const format = 'p2wpkh';
10
+ const ip = '127.0.0.1';
11
+ const methods = ['createChainAddress'];
12
+ const wrongIp = '203.0.113.0';
13
+
14
+ // Restricting the macaroon credentials should result in access limitation
15
+ test(`Restricted macaroons restrict access`, async ({end, equal, rejects}) => {
16
+ const [{lnd, kill, rpc}] = (await spawnLightningCluster({})).nodes;
17
+
18
+ try {
19
+ const create = await grantAccess({lnd, methods});
20
+
21
+ // A regular macaroon can create a chain address
22
+ await createChainAddress({
23
+ format,
24
+ lnd: rpc({macaroon: create.macaroon}).lnd,
25
+ });
26
+
27
+ // A macaroon that is ip limited to the wrong ip is rejected
28
+ {
29
+ const {macaroon} = restrictMacaroon({
30
+ ip: wrongIp,
31
+ macaroon: create.macaroon,
32
+ });
33
+
34
+ await rejects(
35
+ createChainAddress({format, lnd: rpc({macaroon}).lnd}),
36
+ [503, 'UnexpectedErrorCreatingAddress'],
37
+ 'Does not allow incorrect ip'
38
+ );
39
+
40
+ const addRestriction = restrictMacaroon({ip, macaroon});
41
+
42
+ const addedLnd = rpc({macaroon: addRestriction.macaroon});
43
+
44
+ await rejects(
45
+ createChainAddress({format, lnd: addedLnd.lnd}),
46
+ [503, 'UnexpectedErrorCreatingAddress'],
47
+ 'Does not allow extended ip'
48
+ );
49
+ }
50
+
51
+ // A macaroon that is time limited is allowed at first
52
+ {
53
+ const {macaroon} = restrictMacaroon({
54
+ expires_at: new Date(Date.now() + 1000).toISOString(),
55
+ macaroon: create.macaroon,
56
+ });
57
+
58
+ // A time-limited macaroon should work within its time frame
59
+ await createChainAddress({format, lnd: rpc({macaroon}).lnd});
60
+
61
+ // Wait until the macaroon expires
62
+ await delay(2000);
63
+
64
+ // A macaroon that is expired is not allowed
65
+ await rejects(
66
+ createChainAddress({format, lnd: rpc({macaroon}).lnd}),
67
+ [503, 'UnexpectedErrorCreatingAddress'],
68
+ 'Does not allow expired macaroon'
69
+ );
70
+
71
+ // Try extending the time of the macaroon to get around the timer
72
+ const addTime = restrictMacaroon({
73
+ macaroon,
74
+ expires_at: new Date(Date.now() + 1000).toISOString(),
75
+ });
76
+
77
+ const addedLnd = rpc({macaroon: addTime.macaroon});
78
+
79
+ // Adding time is also not allowed
80
+ await rejects(
81
+ createChainAddress({format, lnd: addedLnd.lnd}),
82
+ [503, 'UnexpectedErrorCreatingAddress'],
83
+ 'Does not allow extending expired macaroon'
84
+ );
85
+ }
86
+ } catch (err) {
87
+ equal(err, null, 'Expected no error restricting macaroon');
88
+ }
89
+
90
+ await kill({});
91
+
92
+ return end();
93
+ });
@@ -0,0 +1,101 @@
1
+ const asyncRetry = require('async/retry');
2
+ const {setupChannel} = require('ln-docker-daemons');
3
+ const {spawnLightningCluster} = require('ln-docker-daemons');
4
+ const {test} = require('@alexbosworth/tap');
5
+
6
+ const {createInvoice} = require('./../../');
7
+ const {delay} = require('./../macros');
8
+ const {getPayment} = require('./../../');
9
+ const {payViaPaymentRequest} = require('./../../');
10
+ const {subscribeToForwards} = require('./../../');
11
+ const {subscribeToPayments} = require('./../../');
12
+ const {waitForRoute} = require('./../macros');
13
+
14
+ const unsupported = 'unknown method TrackPayments for service routerrpc.Router';
15
+ const size = 2;
16
+ const tokens = 100;
17
+
18
+ // Subscribing to payments should notify on a payment
19
+ test(`Subscribe to payments`, async ({end, rejects, strictSame}) => {
20
+ const {kill, nodes} = await spawnLightningCluster({size});
21
+
22
+ const [{generate, lnd}, target] = nodes;
23
+
24
+ try {
25
+ const forwards = [];
26
+ const isLegacy = [];
27
+ const isPaying = [];
28
+ const payments = [];
29
+
30
+ const invoice = await createInvoice({tokens, lnd: target.lnd});
31
+
32
+ const {id} = invoice;
33
+
34
+ await setupChannel({generate, lnd, to: target});
35
+
36
+ const sub = subscribeToPayments({lnd});
37
+ const sub2 = subscribeToForwards({lnd});
38
+
39
+ sub2.on('forward', forward => forwards.push(forward));
40
+ sub.on('confirmed', payment => payments.push(payment));
41
+ sub.on('paying', payment => isPaying.push(payment));
42
+
43
+ sub.on('error', error => {
44
+ const [,, {err}] = error;
45
+
46
+ // subscribeToPayments is not supported on LND 0.15.2 and below
47
+ if (err.details === unsupported) {
48
+ return isLegacy.push(error);
49
+ }
50
+
51
+ return strictSame(error, null, 'Expected no error');
52
+ });
53
+
54
+ await payViaPaymentRequest({lnd, request: invoice.request});
55
+
56
+ const {payment} = await getPayment({id, lnd});
57
+
58
+ await asyncRetry({interval: 10, times: 1000}, async () => {
59
+ if (forwards.length !== 2) {
60
+ throw new Error('ExpectedForwardsEvents');
61
+ }
62
+
63
+ return;
64
+ });
65
+
66
+ // Exit early when this is a legacy LND
67
+ if (!!isLegacy.length) {
68
+ [sub, sub2].forEach(n => n.removeAllListeners());
69
+
70
+ await kill({});
71
+
72
+ return end();
73
+ }
74
+
75
+ const [got] = payments;
76
+
77
+ const sent = forwards.find(n => n.is_confirmed && n.is_send);
78
+
79
+ [sub, sub2].forEach(n => n.removeAllListeners());
80
+
81
+ strictSame(got, payment, 'Payment subscription notifies of payment');
82
+
83
+ const [pending] = isPaying;
84
+
85
+ strictSame(pending.created_at, payment.created_at, 'Got date');
86
+ strictSame(pending.destination, payment.destination, 'Got destination');
87
+ strictSame(pending.id, payment.id, 'Got id');
88
+ strictSame(pending.mtokens, payment.mtokens, 'Got mtokens');
89
+ strictSame(pending.paths.length, payment.paths.length, 'Got path');
90
+ strictSame(pending.request, payment.request, 'Got request');
91
+ strictSame(pending.safe_tokens, payment.safe_tokens, 'Got safe tokens');
92
+ strictSame(pending.timeout, payment.timeout, 'Got timeout');
93
+ strictSame(pending.tokens, payment.tokens, 'Got tokens');
94
+ } catch (err) {
95
+ strictSame(err, null, 'Expected no error');
96
+ }
97
+
98
+ await kill({});
99
+
100
+ return end();
101
+ });
@@ -1,122 +0,0 @@
1
- const {address} = require('bitcoinjs-lib');
2
- const {test} = require('@alexbosworth/tap');
3
-
4
- const {authenticatedLndGrpc} = require('./../../');
5
- const {createChainAddress} = require('./../../');
6
- const {delay} = require('./../macros');
7
- const {grantAccess} = require('./../../');
8
- const {restrictMacaroon} = require('./../../');
9
- const {spawnLnd} = require('./../macros');
10
- const {waitForTermination} = require('./../macros');
11
-
12
- const format = 'np2wpkh';
13
- const ip = '127.0.0.1';
14
- const p2shAddressVersion = 196;
15
- const pkHashByteLength = 20;
16
- const regtestBech32AddressHrp = 'bcrt';
17
-
18
- // Restricting the macaroon credentials should result in access limitation
19
- test(`Restricted macaroons restrict access`, async ({end, equal, rejects}) => {
20
- const spawned = await spawnLnd({});
21
-
22
- const {lnd, kill} = spawned;
23
-
24
- const cert = spawned.lnd_cert;
25
- const socket = spawned.lnd_socket;
26
-
27
- // A regular macaroon can create a chain address
28
- {
29
- const macaroon = spawned.lnd_macaroon;
30
-
31
- const {lnd} = authenticatedLndGrpc({cert, macaroon, socket});
32
-
33
- await createChainAddress({format, lnd});
34
- }
35
-
36
- // A macaroon that is ip limited to the wrong ip is rejected
37
- {
38
- const {macaroon} = restrictMacaroon({
39
- ip: '203.0.113.0',
40
- macaroon: spawned.lnd_macaroon,
41
- });
42
-
43
- const {lnd} = authenticatedLndGrpc({cert, macaroon, socket});
44
-
45
- rejects(
46
- createChainAddress({format, lnd}),
47
- [503, 'UnexpectedErrorCreatingAddress'],
48
- 'Does not allow incorrect ip'
49
- );
50
-
51
- const addRestriction = restrictMacaroon({macaroon, ip: '127.0.0.1'});
52
-
53
- const addedLnd = authenticatedLndGrpc({
54
- cert,
55
- socket,
56
- macaroon: addRestriction.macaroon,
57
- });
58
-
59
- rejects(
60
- createChainAddress({format, lnd: addedLnd.lnd}),
61
- [503, 'UnexpectedErrorCreatingAddress'],
62
- 'Does not allow extended ip'
63
- );
64
- }
65
-
66
- // A macaroon that is ip limited to the right ip is allowed
67
- {
68
- const {macaroon} = restrictMacaroon({
69
- ip: '127.0.0.1',
70
- macaroon: spawned.lnd_macaroon,
71
- });
72
-
73
- const {lnd} = authenticatedLndGrpc({cert, macaroon, socket});
74
-
75
- await createChainAddress({format, lnd});
76
- }
77
-
78
- // A macaroon that is time limited is allowed at first
79
- {
80
- const {macaroon} = restrictMacaroon({
81
- expires_at: new Date(Date.now() + 1000).toISOString(),
82
- macaroon: spawned.lnd_macaroon,
83
- });
84
-
85
- const {lnd} = authenticatedLndGrpc({cert, macaroon, socket});
86
-
87
- await createChainAddress({format, lnd});
88
-
89
- await delay(2000);
90
-
91
- // A macaroon that is expired is not allowed
92
- rejects(
93
- createChainAddress({format, lnd}),
94
- [503, 'UnexpectedErrorCreatingAddress'],
95
- 'Does not allow expired macaroon'
96
- );
97
-
98
- const addTime = restrictMacaroon({
99
- macaroon,
100
- expires_at: new Date(Date.now() + 1000).toISOString(),
101
- });
102
-
103
- const addedLnd = authenticatedLndGrpc({
104
- cert,
105
- socket,
106
- macaroon: addTime.macaroon,
107
- });
108
-
109
- // Adding time is also not allowed
110
- rejects(
111
- createChainAddress({format, lnd: addedLnd.lnd}),
112
- [503, 'UnexpectedErrorCreatingAddress'],
113
- 'Does not allow extending expired macaroon'
114
- );
115
- }
116
-
117
- kill();
118
-
119
- await waitForTermination({lnd});
120
-
121
- return end();
122
- });