ln-service 57.23.1 → 57.25.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
CHANGED
package/README.md
CHANGED
|
@@ -613,10 +613,13 @@ permissions
|
|
|
613
613
|
|
|
614
614
|
`max_tokens_per_vbyte` is not supported in LND 0.15.0 and below
|
|
615
615
|
|
|
616
|
+
`is_graceful_close` is not supported in LND 0.17.5 and below
|
|
617
|
+
|
|
616
618
|
{
|
|
617
619
|
[address]: <Request Sending Local Channel Funds To Address String>
|
|
618
620
|
[id]: <Standard Format Channel Id String>
|
|
619
621
|
[is_force_close]: <Is Force Close Bool>
|
|
622
|
+
[is_graceful_close]: <Is Waiting For Pending Payments to Coop Close Bool>
|
|
620
623
|
lnd: <Authenticated LND API Object>
|
|
621
624
|
[max_tokens_per_vbyte]: <Fail Cooperative Close Above Fee Rate Number>
|
|
622
625
|
[public_key]: <Peer Public Key String>
|
package/package.json
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"bolt07": "1.9.4",
|
|
11
|
-
"invoices": "
|
|
12
|
-
"lightning": "10.
|
|
11
|
+
"invoices": "4.0.0",
|
|
12
|
+
"lightning": "10.25.0",
|
|
13
13
|
"macaroon": "3.0.4"
|
|
14
14
|
},
|
|
15
15
|
"description": "Interaction helper for your Lightning Network daemon",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
"bip32": "4.0.0",
|
|
22
22
|
"bip66": "2.0.0",
|
|
23
23
|
"bitcoinjs-lib": "6.1.7",
|
|
24
|
-
"bn.js": "5.2.
|
|
24
|
+
"bn.js": "5.2.2",
|
|
25
25
|
"bs58check": "4.0.0",
|
|
26
|
-
"ecpair": "
|
|
27
|
-
"ln-docker-daemons": "6.0.
|
|
26
|
+
"ecpair": "3.0.0",
|
|
27
|
+
"ln-docker-daemons": "6.0.25",
|
|
28
28
|
"p2tr": "2.0.0",
|
|
29
|
-
"portfinder": "1.0.
|
|
30
|
-
"psbt": "
|
|
29
|
+
"portfinder": "1.0.37",
|
|
30
|
+
"psbt": "4.0.0",
|
|
31
31
|
"rimraf": "6.0.1",
|
|
32
32
|
"tiny-secp256k1": "2.2.3",
|
|
33
|
-
"uuid": "11.0
|
|
33
|
+
"uuid": "11.1.0",
|
|
34
34
|
"varuint-bitcoin": "2.0.0"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
"url": "https://github.com/alexbosworth/ln-service.git"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
|
+
"integration-test-0.18.5": "DOCKER_LND_VERSION=v0.18.5-beta npm run test",
|
|
55
|
+
"integration-test-0.18.4": "DOCKER_LND_VERSION=v0.18.4-beta npm run test",
|
|
56
|
+
"integration-test-0.18.3": "DOCKER_LND_VERSION=v0.18.3-beta npm run test",
|
|
54
57
|
"integration-test-0.18.2": "DOCKER_LND_VERSION=v0.18.2-beta npm run test",
|
|
55
58
|
"integration-test-0.18.1": "DOCKER_LND_VERSION=v0.18.1-beta npm run test",
|
|
56
59
|
"integration-test-0.18.0": "DOCKER_LND_VERSION=v0.18.0-beta npm run test",
|
|
@@ -73,5 +76,5 @@
|
|
|
73
76
|
"integration-test-0.14.4": "DOCKER_LND_VERSION=v0.14.4-beta npm run test",
|
|
74
77
|
"test": "echo $DOCKER_LND_VERSION && node test/runner"
|
|
75
78
|
},
|
|
76
|
-
"version": "57.
|
|
79
|
+
"version": "57.25.0"
|
|
77
80
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const {equal} = require('node:assert').strict;
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
|
|
4
|
+
const asyncAuto = require('async/auto');
|
|
5
|
+
const asyncRetry = require('async/retry');
|
|
6
|
+
const {cancelHodlInvoice} = require('./../../');
|
|
7
|
+
const {createHodlInvoice} = require('./../../');
|
|
8
|
+
const {getInvoice} = require('./../../');
|
|
9
|
+
const {getChannels} = require('./../../');
|
|
10
|
+
const {getClosedChannels} = require('./../../');
|
|
11
|
+
const {pay} = require('./../../');
|
|
12
|
+
const {setupChannel} = require('ln-docker-daemons');
|
|
13
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
14
|
+
|
|
15
|
+
const {closeChannel} = require('./../../');
|
|
16
|
+
|
|
17
|
+
const interval = 100;
|
|
18
|
+
const size = 2;
|
|
19
|
+
const times = 1000;
|
|
20
|
+
const tokens = 10;
|
|
21
|
+
|
|
22
|
+
// Closing a pending payments channel should close the channel after a wait
|
|
23
|
+
test(`Close channel with wait for pending`, async () => {
|
|
24
|
+
const {kill, nodes} = await spawnLightningCluster({size});
|
|
25
|
+
|
|
26
|
+
const [control, target] = nodes;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Open a channel from control to target
|
|
30
|
+
const channelOpen = await setupChannel({
|
|
31
|
+
generate: control.generate,
|
|
32
|
+
lnd: control.lnd,
|
|
33
|
+
to: target,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Create a hold invoice to create the pending state
|
|
37
|
+
const invoice = await createHodlInvoice({tokens, lnd: target.lnd});
|
|
38
|
+
|
|
39
|
+
await asyncAuto({
|
|
40
|
+
makePayment: async () => {
|
|
41
|
+
// Pay to the hold invoice
|
|
42
|
+
try {
|
|
43
|
+
await pay({lnd: control.lnd, request: invoice.request});
|
|
44
|
+
} catch (err) {
|
|
45
|
+
const [, type] = err;
|
|
46
|
+
|
|
47
|
+
// We expect that it would be canceled back
|
|
48
|
+
equal(type, 'PaymentRejectedByDestination');
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// Wait for the payment to be held
|
|
53
|
+
lookForPayment: async () => {
|
|
54
|
+
await asyncRetry({interval, times}, async () => {
|
|
55
|
+
const received = await getInvoice({id: invoice.id, lnd: target.lnd});
|
|
56
|
+
|
|
57
|
+
if (!received.is_held) {
|
|
58
|
+
throw new Error('ExpectedPaymentHeld');
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// Start the closing process
|
|
64
|
+
closeChannel: ['lookForPayment', async () => {
|
|
65
|
+
try {
|
|
66
|
+
await closeChannel({
|
|
67
|
+
id: channelOpen.id,
|
|
68
|
+
is_graceful_close: true,
|
|
69
|
+
lnd: control.lnd,
|
|
70
|
+
});
|
|
71
|
+
} catch (err) {
|
|
72
|
+
const [,, error] = err;
|
|
73
|
+
|
|
74
|
+
const {details} = error.err;
|
|
75
|
+
|
|
76
|
+
// LND 0.17.5 and below do not support graceful closes
|
|
77
|
+
if (details === 'cannot co-op close channel with active htlcs') {
|
|
78
|
+
throw new Error('FeatureUnsupported');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
}],
|
|
84
|
+
|
|
85
|
+
// Wait for channel to be in closing mode
|
|
86
|
+
confirmClosing: ['lookForPayment', async () => {
|
|
87
|
+
await asyncRetry({interval, times}, async () => {
|
|
88
|
+
const {channels} = await getChannels({lnd: control.lnd});
|
|
89
|
+
|
|
90
|
+
if (!channels.filter(n => !n.is_active).length) {
|
|
91
|
+
throw new Error('ExpectedInactiveChannels');
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}],
|
|
95
|
+
|
|
96
|
+
// Stop the payment to void the pending state
|
|
97
|
+
cancelPayment: ['confirmClosing', async () => {
|
|
98
|
+
await cancelHodlInvoice({id: invoice.id, lnd: target.lnd});
|
|
99
|
+
}],
|
|
100
|
+
|
|
101
|
+
// Confirm that the channel closed cleanly
|
|
102
|
+
confirmClosed: ['cancelPayment', async () => {
|
|
103
|
+
await asyncRetry({interval, times}, async () => {
|
|
104
|
+
const {channels} = await getClosedChannels({lnd: control.lnd});
|
|
105
|
+
|
|
106
|
+
await control.generate({});
|
|
107
|
+
|
|
108
|
+
if (!channels.filter(n => !!n.is_cooperative_close).length) {
|
|
109
|
+
throw new Error('ExpectedCooperativelyClosedChannels');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}],
|
|
113
|
+
});
|
|
114
|
+
} catch (err) {
|
|
115
|
+
equal(err.message, 'FeatureUnsupported', 'Expected no error');
|
|
116
|
+
} finally {
|
|
117
|
+
await kill({});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return;
|
|
121
|
+
});
|
|
@@ -194,7 +194,10 @@ test(`Sign a taproot transaction`, async () => {
|
|
|
194
194
|
|
|
195
195
|
const unusedKey = ecp.makeRandom({network: networks.regtest});
|
|
196
196
|
|
|
197
|
-
const witnessScript = compile([
|
|
197
|
+
const witnessScript = compile([
|
|
198
|
+
Buffer.from(unusedKey.publicKey).slice(1),
|
|
199
|
+
OP_CHECKSIG,
|
|
200
|
+
]);
|
|
198
201
|
|
|
199
202
|
const branches = [{script: witnessScript}];
|
|
200
203
|
|
|
@@ -273,6 +276,9 @@ test(`Sign a taproot transaction`, async () => {
|
|
|
273
276
|
}
|
|
274
277
|
});
|
|
275
278
|
} catch (err) {
|
|
279
|
+
console.log("ERR", err);
|
|
280
|
+
await kill({});
|
|
281
|
+
|
|
276
282
|
equal(err, null, 'Expected no error');
|
|
277
283
|
}
|
|
278
284
|
|
|
@@ -128,14 +128,20 @@ test(`Fund PSBT`, async () => {
|
|
|
128
128
|
const keyPair2 = ecp.makeRandom({network: networks.regtest});
|
|
129
129
|
const unusedKey = ecp.makeRandom({network: networks.regtest});
|
|
130
130
|
|
|
131
|
-
const witnessScript = compile([
|
|
131
|
+
const witnessScript = compile([
|
|
132
|
+
from(unusedKey.publicKey).slice(1),
|
|
133
|
+
OP_CHECKSIG,
|
|
134
|
+
]);
|
|
132
135
|
|
|
133
136
|
const branches = [{script: witnessScript}];
|
|
134
137
|
|
|
135
138
|
const {hash} = hashForTree({branches});
|
|
136
139
|
|
|
137
140
|
// Create a combined key using public key material
|
|
138
|
-
const combinedPoint = pointAdd(
|
|
141
|
+
const combinedPoint = pointAdd(
|
|
142
|
+
from(keyPair1.publicKey),
|
|
143
|
+
from(keyPair2.publicKey)
|
|
144
|
+
);
|
|
139
145
|
|
|
140
146
|
const output = v1OutputScript({
|
|
141
147
|
hash,
|
|
@@ -187,7 +193,10 @@ test(`Fund PSBT`, async () => {
|
|
|
187
193
|
});
|
|
188
194
|
|
|
189
195
|
// Ready for private key combining
|
|
190
|
-
const combinedKey = privateAdd(
|
|
196
|
+
const combinedKey = privateAdd(
|
|
197
|
+
from(keyPair1.privateKey),
|
|
198
|
+
from(keyPair2.privateKey)
|
|
199
|
+
);
|
|
191
200
|
|
|
192
201
|
const signedInput = signHash({
|
|
193
202
|
hash,
|
|
@@ -224,7 +233,10 @@ test(`Fund PSBT`, async () => {
|
|
|
224
233
|
|
|
225
234
|
const keyPair = ecp.makeRandom({network: networks.regtest});
|
|
226
235
|
|
|
227
|
-
const witnessScript = compile([
|
|
236
|
+
const witnessScript = compile([
|
|
237
|
+
from(keyPair.publicKey.slice(1)),
|
|
238
|
+
OP_CHECKSIG,
|
|
239
|
+
]);
|
|
228
240
|
|
|
229
241
|
const branches = [{script: witnessScript}];
|
|
230
242
|
|
|
@@ -277,7 +289,7 @@ test(`Fund PSBT`, async () => {
|
|
|
277
289
|
);
|
|
278
290
|
});
|
|
279
291
|
|
|
280
|
-
const signature = from(signSchnorr(hashToSign, keyPair.privateKey));
|
|
292
|
+
const signature = from(signSchnorr(hashToSign, from(keyPair.privateKey)));
|
|
281
293
|
|
|
282
294
|
const {block} = controlBlock({
|
|
283
295
|
external_key: output.external_key,
|
|
@@ -308,7 +320,11 @@ test(`Fund PSBT`, async () => {
|
|
|
308
320
|
}
|
|
309
321
|
});
|
|
310
322
|
} catch (err) {
|
|
323
|
+
await kill({});
|
|
324
|
+
|
|
311
325
|
equal(err, null, 'Expected no error');
|
|
326
|
+
|
|
327
|
+
return;
|
|
312
328
|
}
|
|
313
329
|
|
|
314
330
|
// A Taproot output should be funded for a regular key spend
|
|
@@ -318,7 +334,7 @@ test(`Fund PSBT`, async () => {
|
|
|
318
334
|
const keyPair = ecp.makeRandom({network: networks.regtest});
|
|
319
335
|
|
|
320
336
|
const output = v1OutputScript({
|
|
321
|
-
internal_key: keyPair.publicKey.toString('hex'),
|
|
337
|
+
internal_key: from(keyPair.publicKey).toString('hex'),
|
|
322
338
|
});
|
|
323
339
|
|
|
324
340
|
const outputScript = hexAsBuffer(output.script);
|
|
@@ -368,8 +384,8 @@ test(`Fund PSBT`, async () => {
|
|
|
368
384
|
});
|
|
369
385
|
|
|
370
386
|
const signedInput = signHash({
|
|
371
|
-
private_key: keyPair.privateKey.toString('hex'),
|
|
372
|
-
public_key: keyPair.publicKey.toString('hex'),
|
|
387
|
+
private_key: from(keyPair.privateKey).toString('hex'),
|
|
388
|
+
public_key: from(keyPair.publicKey).toString('hex'),
|
|
373
389
|
sign_hash: hashToSign.toString('hex'),
|
|
374
390
|
});
|
|
375
391
|
|