ln-service 57.14.2 → 57.14.4

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,6 +1,6 @@
1
1
  # Versions
2
2
 
3
- ## 57.14.2
3
+ ## 57.14.4
4
4
 
5
5
  - `getChannel`: Add support for specifying `transaction_id` and
6
6
  `transaction_vout` instead of `id`
package/README.md CHANGED
@@ -9,7 +9,7 @@ through npm.
9
9
 
10
10
  Supported LND versions:
11
11
 
12
- - v0.18.0-beta
12
+ - v0.18.0-beta to v0.18.1-beta
13
13
  - v0.17.0-beta to v0.17.5-beta
14
14
  - v0.16.0-beta to v0.16.4-beta
15
15
  - v0.15.2-beta to v0.15.5-beta
package/package.json CHANGED
@@ -7,9 +7,9 @@
7
7
  "url": "https://github.com/alexbosworth/ln-service/issues"
8
8
  },
9
9
  "dependencies": {
10
- "bolt07": "1.9.3",
10
+ "bolt07": "1.9.4",
11
11
  "invoices": "3.0.0",
12
- "lightning": "10.14.1",
12
+ "lightning": "10.14.4",
13
13
  "macaroon": "3.0.4"
14
14
  },
15
15
  "description": "Interaction helper for your Lightning Network daemon",
@@ -19,12 +19,12 @@
19
19
  "async": "3.2.5",
20
20
  "asyncjs-util": "1.2.12",
21
21
  "bip32": "4.0.0",
22
- "bip66": "1.1.5",
22
+ "bip66": "2.0.0",
23
23
  "bitcoinjs-lib": "6.1.6",
24
24
  "bn.js": "5.2.1",
25
- "bs58check": "3.0.1",
25
+ "bs58check": "4.0.0",
26
26
  "ecpair": "2.1.0",
27
- "ln-docker-daemons": "6.0.19",
27
+ "ln-docker-daemons": "6.0.20",
28
28
  "p2tr": "2.0.0",
29
29
  "portfinder": "1.0.32",
30
30
  "psbt": "3.0.0",
@@ -52,6 +52,7 @@
52
52
  "url": "https://github.com/alexbosworth/ln-service.git"
53
53
  },
54
54
  "scripts": {
55
+ "integration-test-0.18.1": "DOCKER_LND_VERSION=v0.18.1-beta npm run test",
55
56
  "integration-test-0.18.0": "DOCKER_LND_VERSION=v0.18.0-beta npm run test",
56
57
  "integration-test-0.17.5": "DOCKER_LND_VERSION=v0.17.5-beta npm run test",
57
58
  "integration-test-0.17.4": "DOCKER_LND_VERSION=v0.17.4-beta npm run test",
@@ -72,5 +73,5 @@
72
73
  "integration-test-0.14.4": "DOCKER_LND_VERSION=v0.14.4-beta npm run test",
73
74
  "test": "echo $DOCKER_LND_VERSION && node test/runner"
74
75
  },
75
- "version": "57.14.2"
76
+ "version": "57.14.4"
76
77
  }
@@ -24,128 +24,130 @@ const tokens = 1000;
24
24
 
25
25
  // Getting a route to a destination should return a route to the destination
26
26
  test(`Get a route to a destination`, async () => {
27
- const {kill, nodes} = await spawnLightningCluster({size});
27
+ await asyncRetry({}, async () => {
28
+ const {kill, nodes} = await spawnLightningCluster({size});
28
29
 
29
- const [control, target, remote] = nodes;
30
+ const [control, target, remote] = nodes;
30
31
 
31
- await control.generate({count: 500});
32
+ await control.generate({count: 200});
32
33
 
33
- await setupChannel({
34
- generate: control.generate,
35
- lnd: control.lnd,
36
- to: target,
37
- });
38
-
39
- await setupChannel({
40
- generate: target.generate,
41
- give_tokens: 1e5,
42
- is_private: true,
43
- lnd: target.lnd,
44
- to: remote,
45
- });
46
-
47
- const invoice = await asyncRetry({interval, times}, async () => {
48
- const invoice = await createInvoice({
49
- tokens,
50
- is_including_private_channels: true,
51
- lnd: remote.lnd,
34
+ await setupChannel({
35
+ generate: control.generate,
36
+ lnd: control.lnd,
37
+ to: target,
52
38
  });
53
39
 
54
- const {routes} = parsePaymentRequest({request: invoice.request});
55
-
56
- // Wait for private routes to get picked up
57
- if (!routes) {
58
- await cancelHodlInvoice({id: invoice.id, lnd: remote.lnd});
40
+ await setupChannel({
41
+ generate: target.generate,
42
+ give_tokens: 1e5,
43
+ is_private: true,
44
+ lnd: target.lnd,
45
+ to: remote,
46
+ });
59
47
 
60
- throw new Error('ExpectedRouteForInvoice');
61
- }
48
+ const invoice = await asyncRetry({interval, times}, async () => {
49
+ const invoice = await createInvoice({
50
+ tokens,
51
+ is_including_private_channels: true,
52
+ lnd: remote.lnd,
53
+ });
62
54
 
63
- return invoice;
64
- });
55
+ const {routes} = parsePaymentRequest({request: invoice.request});
65
56
 
66
- const parsed = parsePaymentRequest({request: invoice.request});
57
+ // Wait for private routes to get picked up
58
+ if (!routes) {
59
+ await cancelHodlInvoice({id: invoice.id, lnd: remote.lnd});
67
60
 
68
- await asyncRetry({interval, times}, async () => {
69
- const {route} = await getRouteToDestination({
70
- destination: parsed.destination,
71
- features: parsed.features,
72
- lnd: control.lnd,
73
- payment: parsed.payment,
74
- routes: parsed.routes,
75
- mtokens: parsed.mtokens,
76
- total_mtokens: parsed.mtokens,
77
- });
61
+ throw new Error('ExpectedRouteForInvoice');
62
+ }
78
63
 
79
- const paid = await payViaRoutes({
80
- id: parsed.id,
81
- lnd: control.lnd,
82
- routes: [route],
64
+ return invoice;
83
65
  });
84
66
 
85
- strictEqual(invoice.secret, paid.secret, 'Paid multi-hop private route');
86
- });
87
-
88
- const inv = await createInvoice({tokens, lnd: target.lnd});
67
+ const parsed = parsePaymentRequest({request: invoice.request});
89
68
 
90
- const invDetails = await decodePaymentRequest({
91
- lnd: control.lnd,
92
- request: inv.request,
93
- });
69
+ await asyncRetry({interval, times}, async () => {
70
+ const {route} = await getRouteToDestination({
71
+ destination: parsed.destination,
72
+ features: parsed.features,
73
+ lnd: control.lnd,
74
+ payment: parsed.payment,
75
+ routes: parsed.routes,
76
+ mtokens: parsed.mtokens,
77
+ total_mtokens: parsed.mtokens,
78
+ });
79
+
80
+ const paid = await payViaRoutes({
81
+ id: parsed.id,
82
+ lnd: control.lnd,
83
+ routes: [route],
84
+ });
94
85
 
95
- const controlToTarget = await getRouteToDestination({
96
- destination: target.id,
97
- features: invDetails.features,
98
- lnd: control.lnd,
99
- messages: [message],
100
- payment: invDetails.payment,
101
- tokens: invDetails.tokens / [control, remote].length,
102
- total_mtokens: invDetails.mtokens,
103
- });
86
+ strictEqual(invoice.secret, paid.secret, 'Paid multi-hop private route');
87
+ });
104
88
 
105
- const remoteToTarget = await getRouteToDestination({
106
- destination: target.id,
107
- features: invDetails.features,
108
- lnd: remote.lnd,
109
- messages: [message],
110
- payment: invDetails.payment,
111
- tokens: invDetails.tokens / [control, remote].length,
112
- total_mtokens: invDetails.mtokens,
113
- });
89
+ const inv = await createInvoice({tokens, lnd: target.lnd});
114
90
 
115
- try {
116
- const [controlPay, remotePay] = await all([
117
- payViaRoutes({
118
- id: invDetails.id,
119
- lnd: control.lnd,
120
- routes: [controlToTarget.route],
121
- }),
122
- payViaRoutes({
123
- id: invDetails.id,
124
- lnd: remote.lnd,
125
- routes: [remoteToTarget.route],
126
- }),
127
- ]);
91
+ const invDetails = await decodePaymentRequest({
92
+ lnd: control.lnd,
93
+ request: inv.request,
94
+ });
128
95
 
129
- strictEqual(controlPay.secret, inv.secret, 'Control paid for secret');
130
- strictEqual(remotePay.secret, inv.secret, 'Remote paid for secret');
96
+ const controlToTarget = await getRouteToDestination({
97
+ destination: target.id,
98
+ features: invDetails.features,
99
+ lnd: control.lnd,
100
+ messages: [message],
101
+ payment: invDetails.payment,
102
+ tokens: invDetails.tokens / [control, remote].length,
103
+ total_mtokens: invDetails.mtokens,
104
+ });
131
105
 
132
- const {payments} = await getInvoice({
133
- id: invDetails.id,
134
- lnd: target.lnd,
106
+ const remoteToTarget = await getRouteToDestination({
107
+ destination: target.id,
108
+ features: invDetails.features,
109
+ lnd: remote.lnd,
110
+ messages: [message],
111
+ payment: invDetails.payment,
112
+ tokens: invDetails.tokens / [control, remote].length,
113
+ total_mtokens: invDetails.mtokens,
135
114
  });
136
115
 
137
- const [payment1, payment2] = payments;
116
+ try {
117
+ const [controlPay, remotePay] = await all([
118
+ payViaRoutes({
119
+ id: invDetails.id,
120
+ lnd: control.lnd,
121
+ routes: [controlToTarget.route],
122
+ }),
123
+ payViaRoutes({
124
+ id: invDetails.id,
125
+ lnd: remote.lnd,
126
+ routes: [remoteToTarget.route],
127
+ }),
128
+ ]);
129
+
130
+ strictEqual(controlPay.secret, inv.secret, 'Control paid for secret');
131
+ strictEqual(remotePay.secret, inv.secret, 'Remote paid for secret');
132
+
133
+ const {payments} = await getInvoice({
134
+ id: invDetails.id,
135
+ lnd: target.lnd,
136
+ });
137
+
138
+ const [payment1, payment2] = payments;
138
139
 
139
- const [message1] = payment1.messages;
140
- const [message2] = payment2.messages;
140
+ const [message1] = payment1.messages;
141
+ const [message2] = payment2.messages;
141
142
 
142
- deepStrictEqual(message1, message, 'Target received message');
143
- deepStrictEqual(message2, message, 'Target received both messages');
144
- } catch (err) {
145
- strictEqual(err, null, 'Unexpected error paying invoice');
146
- }
143
+ deepStrictEqual(message1, message, 'Target received message');
144
+ deepStrictEqual(message2, message, 'Target received both messages');
145
+ } catch (err) {
146
+ strictEqual(err, null, 'Unexpected error paying invoice');
147
+ }
147
148
 
148
- await kill({});
149
+ await kill({});
150
+ });
149
151
 
150
152
  return;
151
153
  });
@@ -0,0 +1,325 @@
1
+ const {equal} = require('node:assert').strict;
2
+ const test = require('node:test');
3
+
4
+ const asyncRetry = require('async/retry');
5
+ const {setupChannel} = require('ln-docker-daemons');
6
+ const {spawnLightningCluster} = require('ln-docker-daemons');
7
+
8
+ const {addPeer} = require('./../../');
9
+ const {createInvoice} = require('./../../');
10
+ const {decodePaymentRequest} = require('./../../');
11
+ const {getHeight} = require('./../../');
12
+ const {getChannel} = require('./../../');
13
+ const {getNode} = require('./../../');
14
+ const {getRouteThroughHops} = require('./../../');
15
+ const {getRouteToDestination} = require('./../../');
16
+ const {getWalletInfo} = require('./../../');
17
+ const {payViaRoutes} = require('./../../');
18
+ const {routeFromChannels} = require('./../../');
19
+ const {updateRoutingFees} = require('./../../');
20
+ const {waitForRoute} = require('./../macros');
21
+
22
+ const baseFee = '1000';
23
+ const confirmationCount = 6;
24
+ const discount = '1000';
25
+ const interval = 10;
26
+ const size = 5;
27
+ const times = 1000;
28
+ const tokens = 100;
29
+
30
+ // Calculating a route from complex channels set should result in a route
31
+ test(`Get route through complex hops`, async () => {
32
+ const {kill, nodes} = await spawnLightningCluster({size});
33
+
34
+ try {
35
+ const [{generate, lnd}, target, remote, far, farthest] = nodes;
36
+
37
+ const controlToTargetChan = await setupChannel({
38
+ generate,
39
+ lnd,
40
+ to: target,
41
+ });
42
+
43
+ await generate({});
44
+
45
+ const targetRemoteChannel = await asyncRetry({
46
+ interval,
47
+ times,
48
+ },
49
+ async () => {
50
+ await generate({});
51
+
52
+ await addPeer({
53
+ lnd: target.lnd,
54
+ public_key: remote.id,
55
+ socket: remote.socket,
56
+ });
57
+
58
+ return await setupChannel({
59
+ generate: target.generate,
60
+ lnd: target.lnd,
61
+ to: remote,
62
+ });
63
+ });
64
+
65
+ await generate({});
66
+
67
+ const remoteFarChannel = await asyncRetry({interval, times}, async () => {
68
+ await generate({});
69
+
70
+ await addPeer({lnd: remote.lnd, public_key: far.id, socket: far.socket});
71
+
72
+ return await setupChannel({
73
+ generate: remote.generate,
74
+ lnd: remote.lnd,
75
+ to: far,
76
+ });
77
+ });
78
+
79
+ await target.generate({count: confirmationCount});
80
+
81
+ const farFarthestChannel = await asyncRetry({
82
+ interval,
83
+ times,
84
+ },
85
+ async () => {
86
+ await generate({});
87
+
88
+ await addPeer({
89
+ lnd: far.lnd,
90
+ public_key: farthest.id,
91
+ socket: farthest.socket,
92
+ });
93
+
94
+ return await setupChannel({
95
+ generate: far.generate,
96
+ lnd: far.lnd,
97
+ to: farthest,
98
+ });
99
+ });
100
+
101
+ await target.generate({count: confirmationCount});
102
+
103
+ await asyncRetry({interval, times}, async () => {
104
+ const wallet = await getWalletInfo({lnd: remote.lnd});
105
+
106
+ await addPeer({lnd, public_key: remote.id, socket: remote.socket});
107
+
108
+ if (!wallet.is_synced_to_chain) {
109
+ throw new Error('ExpectedWalletSyncedToChain');
110
+ }
111
+ });
112
+
113
+ await asyncRetry({interval, times}, async () => {
114
+ const wallet = await getWalletInfo({lnd: far.lnd});
115
+
116
+ await addPeer({lnd, public_key: far.id, socket: far.socket});
117
+
118
+ if (!wallet.is_synced_to_chain) {
119
+ throw new Error('ExpectedWalletSyncedToChain');
120
+ }
121
+ });
122
+
123
+ await asyncRetry({interval, times}, async () => {
124
+ const wallet = await getWalletInfo({lnd: farthest.lnd});
125
+
126
+ await addPeer({lnd, public_key: farthest.id, socket: farthest.socket});
127
+
128
+ if (!wallet.is_synced_to_chain) {
129
+ throw new Error('ExpectedWalletSyncedToChain');
130
+ }
131
+ });
132
+
133
+ await asyncRetry({interval, times}, async () => {
134
+ await getNode({lnd, public_key: remote.id});
135
+ });
136
+
137
+ await asyncRetry({interval, times}, async () => {
138
+ const {features} = await getNode({lnd, public_key: far.id});
139
+
140
+ if (!features.length) {
141
+ throw new Error('ExpectedFarFeatures');
142
+ }
143
+ });
144
+
145
+ await asyncRetry({interval, times}, async () => {
146
+ const {features} = await getNode({lnd, public_key: farthest.id});
147
+
148
+ await addPeer({lnd, public_key: farthest.id, socket: farthest.socket});
149
+
150
+ if (!features.length) {
151
+ throw new Error('ExpectedFarthestFeatures');
152
+ }
153
+ });
154
+
155
+ const invoice = await createInvoice({
156
+ tokens,
157
+ cltv_delta: 50,
158
+ lnd: farthest.lnd,
159
+ });
160
+
161
+ const {id} = invoice;
162
+ const {request} = invoice;
163
+
164
+ const decodedRequest = await decodePaymentRequest({lnd, request});
165
+
166
+ await waitForRoute({
167
+ lnd,
168
+ destination: farthest.id,
169
+ tokens: invoice.tokens,
170
+ });
171
+
172
+ const {route} = await asyncRetry({interval, times}, async () => {
173
+ return await getRouteToDestination({
174
+ lnd,
175
+ cltv_delta: decodedRequest.cltv_delta,
176
+ destination: decodedRequest.destination,
177
+ tokens: invoice.tokens,
178
+ });
179
+ });
180
+
181
+ const policyAStart = await getChannel({lnd, id: controlToTargetChan.id});
182
+ const policyBStart = await getChannel({lnd, id: targetRemoteChannel.id});
183
+ const policyCStart = await getChannel({lnd, id: remoteFarChannel.id});
184
+ const policyDStart = await getChannel({lnd, id: farFarthestChannel.id});
185
+
186
+ await updateRoutingFees({
187
+ cltv_delta: 200,
188
+ lnd: target.lnd,
189
+ transaction_id: targetRemoteChannel.transaction_id,
190
+ transaction_vout: targetRemoteChannel.transaction_vout,
191
+ });
192
+
193
+ await updateRoutingFees({
194
+ cltv_delta: 200,
195
+ lnd: remote.lnd,
196
+ transaction_id: targetRemoteChannel.transaction_id,
197
+ transaction_vout: targetRemoteChannel.transaction_vout,
198
+ });
199
+
200
+ await updateRoutingFees({
201
+ cltv_delta: 100,
202
+ inbound_base_discount_mtokens: discount,
203
+ lnd: target.lnd,
204
+ transaction_id: controlToTargetChan.transaction_id,
205
+ transaction_vout: controlToTargetChan.transaction_vout,
206
+ });
207
+
208
+ await updateRoutingFees({
209
+ cltv_delta: 300,
210
+ lnd: far.lnd,
211
+ transaction_id: remoteFarChannel.transaction_id,
212
+ transaction_vout: remoteFarChannel.transaction_vout,
213
+ });
214
+
215
+ await updateRoutingFees({
216
+ cltv_delta: 400,
217
+ lnd: remote.lnd,
218
+ transaction_id: remoteFarChannel.transaction_id,
219
+ transaction_vout: remoteFarChannel.transaction_vout,
220
+ });
221
+
222
+ // Control updates their routing policy
223
+ await updateRoutingFees({
224
+ lnd,
225
+ cltv_delta: 100,
226
+ transaction_id: controlToTargetChan.transaction_id,
227
+ transaction_vout: controlToTargetChan.transaction_vout,
228
+ });
229
+
230
+ await updateRoutingFees({
231
+ cltv_delta: 500,
232
+ lnd: far.lnd,
233
+ transaction_id: farFarthestChannel.transaction_id,
234
+ transaction_vout: farFarthestChannel.transaction_vout,
235
+ });
236
+
237
+ await updateRoutingFees({
238
+ cltv_delta: 100,
239
+ lnd: farthest.lnd,
240
+ transaction_id: farFarthestChannel.transaction_id,
241
+ transaction_vout: farFarthestChannel.transaction_vout,
242
+ });
243
+
244
+ // Wait for policy to be updated
245
+ const policyA = await asyncRetry({interval, times}, async () => {
246
+ const policy = await getChannel({lnd, id: controlToTargetChan.id});
247
+
248
+ if (policy.updated_at === policyAStart.updated_at) {
249
+ throw new Error('PolicyNotUpdatedYet');
250
+ }
251
+
252
+ return policy;
253
+ });
254
+
255
+ // A discount should be set for traffic from control to remote
256
+ const discountFee = policyA.policies.find(n => n.public_key === target.id);
257
+
258
+ // Wait for policy to be updated
259
+ const policyB = await asyncRetry({interval, times}, async () => {
260
+ const policy = await getChannel({lnd, id: targetRemoteChannel.id});
261
+
262
+ if (policy.updated_at === policyBStart.updated_at) {
263
+ throw new Error('PolicyNotUpdatedYet');
264
+ }
265
+
266
+ return policy;
267
+ });
268
+
269
+ // Wait for policy to be updated
270
+ const policyC = await asyncRetry({interval, times}, async () => {
271
+ const policy = await getChannel({lnd, id: remoteFarChannel.id});
272
+
273
+ if (policy.updated_at === policyCStart.updated_at) {
274
+ throw new Error('PolicyNotUpdatedYet');
275
+ }
276
+
277
+ return policy;
278
+ });
279
+
280
+ const policyD = await asyncRetry({interval, times}, async () => {
281
+ const policy = await getChannel({lnd, id: farFarthestChannel.id});
282
+
283
+ if (policy.updated_at === policyDStart.updated_at) {
284
+ throw new Error('PolicyNotUpdatedYet');
285
+ }
286
+
287
+ return policy;
288
+ });
289
+
290
+ const channelsRoute = routeFromChannels({
291
+ channels: [policyA, policyB, policyC, policyD],
292
+ cltv_delta: decodedRequest.cltv_delta + confirmationCount,
293
+ destination: decodedRequest.destination,
294
+ height: (await getHeight({lnd})).current_block_height,
295
+ mtokens: decodedRequest.mtokens,
296
+ payment: decodedRequest.payment,
297
+ total_mtokens: decodedRequest.mtokens,
298
+ });
299
+
300
+ const lndRoute = await getRouteThroughHops({
301
+ lnd,
302
+ cltv_delta: decodedRequest.cltv_delta + confirmationCount,
303
+ mtokens: decodedRequest.mtokens,
304
+ payment: decodedRequest.payment,
305
+ public_keys: [target.id, remote.id, far.id, farthest.id],
306
+ total_mtokens: decodedRequest.mtokens,
307
+ });
308
+
309
+ const discounted = BigInt(discountFee.inbound_base_discount_mtokens);
310
+
311
+ const gotTotalFee = BigInt(channelsRoute.route.fee_mtokens);
312
+
313
+ await payViaRoutes({lnd, id: invoice.id, routes: [channelsRoute.route]});
314
+
315
+ equal(gotTotalFee, BigInt(baseFee * 2), 'Got expected discount');
316
+
317
+ await kill({});
318
+ } catch (err) {
319
+ await kill({});
320
+
321
+ equal(err, null, 'Expected no error');
322
+ }
323
+
324
+ return;
325
+ });
@@ -64,7 +64,11 @@ test(`Get route through hops`, async () => {
64
64
  }
65
65
  });
66
66
 
67
- const invoice = await createInvoice({tokens, lnd: remote.lnd});
67
+ const invoice = await createInvoice({
68
+ tokens,
69
+ cltv_delta: 60,
70
+ lnd: remote.lnd,
71
+ });
68
72
 
69
73
  const {id} = invoice;
70
74
  const {request} = invoice;
@@ -122,6 +126,17 @@ test(`Get route through hops`, async () => {
122
126
  total_mtokens: decodedRequest.mtokens,
123
127
  });
124
128
 
129
+ const lndRoute = await asyncRetry({interval, times}, async () => {
130
+ return await getRouteThroughHops({
131
+ lnd,
132
+ cltv_delta: decodedRequest.cltv_delta + confirmationCount,
133
+ mtokens: decodedRequest.mtokens,
134
+ payment: decodedRequest.payment,
135
+ public_keys: [target.id, remote.id],
136
+ total_mtokens: decodedRequest.mtokens,
137
+ });
138
+ });
139
+
125
140
  const discounted = BigInt(discountFee.inbound_base_discount_mtokens);
126
141
 
127
142
  const gotTotalFee = BigInt(channelsRoute.route.fee_mtokens);
@@ -133,4 +148,4 @@ test(`Get route through hops`, async () => {
133
148
  equal(gotTotalFee, BigInt(baseFee) - discounted, 'Got expected discount');
134
149
 
135
150
  return;
136
- });
151
+ });
@@ -3,7 +3,7 @@ const {equal} = require('node:assert').strict;
3
3
  const test = require('node:test');
4
4
 
5
5
  const bip32 = require('bip32');
6
- const bs58check = require('bs58check');
6
+ const bs58check = require('bs58check').default;
7
7
  const ecc = require('tiny-secp256k1')
8
8
  const {spawnLightningCluster} = require('ln-docker-daemons');
9
9