ln-service 53.16.1 → 53.17.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
|
@@ -4781,6 +4781,7 @@ Requires LND built with `signrpc` build tag
|
|
|
4781
4781
|
|
|
4782
4782
|
Requires `signer:generate` permission
|
|
4783
4783
|
|
|
4784
|
+
`root_hash` is not supported in LND 0.14.3 and below
|
|
4784
4785
|
`spending` is not supported in LND 0.14.3 and below
|
|
4785
4786
|
|
|
4786
4787
|
{
|
|
@@ -4789,9 +4790,10 @@ Requires `signer:generate` permission
|
|
|
4789
4790
|
key_index: <Key Index Number>
|
|
4790
4791
|
output_script: <Output Script Hex String>
|
|
4791
4792
|
output_tokens: <Output Tokens Number>
|
|
4793
|
+
[root_hash]: <Taproot Root Hash Hex String>
|
|
4792
4794
|
sighash: <Sighash Type Number>
|
|
4793
4795
|
vin: <Input Index To Sign Number>
|
|
4794
|
-
witness_script: <Witness Script Hex String>
|
|
4796
|
+
[witness_script]: <Witness Script Hex String>
|
|
4795
4797
|
}]
|
|
4796
4798
|
lnd: <Authenticated LND API Object>
|
|
4797
4799
|
[spending]: [{
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"cors": "2.8.5",
|
|
12
12
|
"express": "4.18.1",
|
|
13
13
|
"invoices": "2.0.6",
|
|
14
|
-
"lightning": "5.
|
|
14
|
+
"lightning": "5.16.0",
|
|
15
15
|
"macaroon": "3.0.4",
|
|
16
16
|
"morgan": "1.10.0",
|
|
17
17
|
"ws": "8.6.0"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"bn.js": "5.2.0",
|
|
29
29
|
"bs58check": "2.1.2",
|
|
30
30
|
"ecpair": "2.0.1",
|
|
31
|
-
"ln-docker-daemons": "2.2.
|
|
31
|
+
"ln-docker-daemons": "2.2.11",
|
|
32
32
|
"p2tr": "1.3.1",
|
|
33
33
|
"portfinder": "1.0.28",
|
|
34
34
|
"psbt": "2.0.1",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"integration-test-0.12.0": "DOCKER_LND_VERSION=v0.12.0-beta npm run test",
|
|
71
71
|
"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"
|
|
72
72
|
},
|
|
73
|
-
"version": "53.
|
|
73
|
+
"version": "53.17.0"
|
|
74
74
|
}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
const asyncRetry = require('async/retry');
|
|
2
|
+
const {address} = require('bitcoinjs-lib');
|
|
3
|
+
const {controlBlock} = require('p2tr');
|
|
4
|
+
const {createPsbt} = require('psbt');
|
|
5
|
+
const {hashForTree} = require('p2tr');
|
|
6
|
+
const {networks} = require('bitcoinjs-lib');
|
|
7
|
+
const {script} = require('bitcoinjs-lib');
|
|
8
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
9
|
+
const {test} = require('@alexbosworth/tap');
|
|
10
|
+
const tinysecp = require('tiny-secp256k1');
|
|
11
|
+
const {Transaction} = require('bitcoinjs-lib');
|
|
12
|
+
const {v1OutputScript} = require('p2tr');
|
|
13
|
+
|
|
14
|
+
const {beginGroupSigningSession} = require('./../../');
|
|
15
|
+
const {broadcastChainTransaction} = require('./../../');
|
|
16
|
+
const {createChainAddress} = require('./../../');
|
|
17
|
+
const {fundPsbt} = require('./../../');
|
|
18
|
+
const {getPublicKey} = require('./../../');
|
|
19
|
+
const {getUtxos} = require('./../../');
|
|
20
|
+
const {signPsbt} = require('./../../');
|
|
21
|
+
const {signTransaction} = require('./../../');
|
|
22
|
+
|
|
23
|
+
const {compile} = script;
|
|
24
|
+
const count = 100;
|
|
25
|
+
const defaultInternalKey = '0350929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0';
|
|
26
|
+
const {fromHex} = Transaction;
|
|
27
|
+
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
28
|
+
const interval = retryCount => 10 * Math.pow(2, retryCount);
|
|
29
|
+
const OP_CHECKSIG = 172;
|
|
30
|
+
const smallTokens = 2e5;
|
|
31
|
+
const times = 20;
|
|
32
|
+
const {toOutputScript} = address;
|
|
33
|
+
const tokens = 1e6;
|
|
34
|
+
|
|
35
|
+
// Signing a taproot transaction should result in a valid signature
|
|
36
|
+
test(`Sign a taproot transaction`, async ({end, equal}) => {
|
|
37
|
+
const ecp = (await import('ecpair')).ECPairFactory(tinysecp);
|
|
38
|
+
const {kill, nodes} = await spawnLightningCluster({});
|
|
39
|
+
|
|
40
|
+
const [{generate, lnd}] = nodes;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await beginGroupSigningSession({
|
|
44
|
+
lnd,
|
|
45
|
+
is_key_spend: true,
|
|
46
|
+
key_family: 0,
|
|
47
|
+
key_index: 0,
|
|
48
|
+
public_keys: [Buffer.alloc(33, 2).toString('hex')],
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// On LND 0.14.3 and below, taproot signing is not supported
|
|
52
|
+
if (err.slice().shift() === 501) {
|
|
53
|
+
await kill({});
|
|
54
|
+
|
|
55
|
+
return end();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await generate({count});
|
|
62
|
+
|
|
63
|
+
const {address} = await createChainAddress({lnd});
|
|
64
|
+
const [utxo] = (await getUtxos({lnd})).utxos;
|
|
65
|
+
|
|
66
|
+
const funded = await asyncRetry({interval, times}, async () => {
|
|
67
|
+
try {
|
|
68
|
+
return await fundPsbt({
|
|
69
|
+
lnd,
|
|
70
|
+
inputs: [{
|
|
71
|
+
transaction_id: utxo.transaction_id,
|
|
72
|
+
transaction_vout: utxo.transaction_vout,
|
|
73
|
+
}],
|
|
74
|
+
outputs: [{address, tokens}],
|
|
75
|
+
});
|
|
76
|
+
} catch (err) {
|
|
77
|
+
// On LND 0.11.1 and below, funding a PSBT is not supported
|
|
78
|
+
if (err.slice().shift() === 501) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// A Taproot script output should be funded and spent with script
|
|
87
|
+
try {
|
|
88
|
+
await generate({count});
|
|
89
|
+
|
|
90
|
+
const scriptKey = await getPublicKey({lnd, family: 805});
|
|
91
|
+
|
|
92
|
+
const publicKey = hexAsBuffer(scriptKey.public_key);
|
|
93
|
+
|
|
94
|
+
const witnessScript = compile([publicKey.slice(1), OP_CHECKSIG]);
|
|
95
|
+
|
|
96
|
+
const branches = [{script: witnessScript.toString('hex')}];
|
|
97
|
+
|
|
98
|
+
const {hash} = hashForTree({branches});
|
|
99
|
+
|
|
100
|
+
const output = v1OutputScript({hash, internal_key: defaultInternalKey});
|
|
101
|
+
|
|
102
|
+
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
103
|
+
|
|
104
|
+
// Make a PSBT paying to the Taproot output
|
|
105
|
+
const {psbt} = createPsbt({
|
|
106
|
+
outputs: [{tokens, script: output.script}],
|
|
107
|
+
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Sign the PSBT
|
|
111
|
+
const signed = await signPsbt({
|
|
112
|
+
lnd,
|
|
113
|
+
psbt: (await fundPsbt({lnd, psbt})).psbt,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Send the tx to the chain
|
|
117
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
118
|
+
|
|
119
|
+
// Make a new tx that will spend the output back into the wallet
|
|
120
|
+
const tx = new Transaction();
|
|
121
|
+
|
|
122
|
+
// The new tx spends the Taproot output
|
|
123
|
+
tx.addInput(
|
|
124
|
+
fromHex(signed.transaction).getHash(),
|
|
125
|
+
fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Make an output to pay back into the wallet
|
|
129
|
+
const chainOutput = toOutputScript(
|
|
130
|
+
(await createChainAddress({lnd})).address,
|
|
131
|
+
networks.regtest
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Add output to the pay back transaction
|
|
135
|
+
tx.addOutput(chainOutput, smallTokens);
|
|
136
|
+
|
|
137
|
+
const {signatures} = await signTransaction({
|
|
138
|
+
lnd,
|
|
139
|
+
inputs: [{
|
|
140
|
+
key_family: 805,
|
|
141
|
+
key_index: scriptKey.index,
|
|
142
|
+
output_script: output.script,
|
|
143
|
+
output_tokens: tokens,
|
|
144
|
+
root_hash: hash,
|
|
145
|
+
sighash: Transaction.SIGHASH_DEFAULT,
|
|
146
|
+
vin: 0,
|
|
147
|
+
witness_script: witnessScript.toString('hex'),
|
|
148
|
+
}],
|
|
149
|
+
transaction: tx.toHex(),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const [signature] = signatures.map(hexAsBuffer);
|
|
153
|
+
|
|
154
|
+
const {block} = controlBlock({
|
|
155
|
+
external_key: output.external_key,
|
|
156
|
+
leaf_script: witnessScript.toString('hex'),
|
|
157
|
+
script_branches: branches,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Add the signature to the input
|
|
161
|
+
tx.ins.forEach((input, i) => {
|
|
162
|
+
return tx.setWitness(i, [
|
|
163
|
+
signature,
|
|
164
|
+
witnessScript,
|
|
165
|
+
hexAsBuffer(block),
|
|
166
|
+
]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await broadcastChainTransaction({lnd, transaction: tx.toHex()});
|
|
170
|
+
|
|
171
|
+
await asyncRetry({interval, times}, async () => {
|
|
172
|
+
await generate({});
|
|
173
|
+
|
|
174
|
+
const {utxos} = await getUtxos({lnd});
|
|
175
|
+
|
|
176
|
+
const utxo = utxos.find(n => n.transaction_id === tx.getId());
|
|
177
|
+
|
|
178
|
+
if (!utxo || !utxo.confirmation_count) {
|
|
179
|
+
throw new Error('ExpectedReceivedTaprootSpend');
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
} catch (err) {
|
|
183
|
+
equal(err, null, 'Expected no error');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// A Taproot script can be funded and spent with internal key + script hash
|
|
187
|
+
try {
|
|
188
|
+
await generate({count});
|
|
189
|
+
|
|
190
|
+
const topLevelKey = await getPublicKey({lnd, family: 805});
|
|
191
|
+
|
|
192
|
+
const unusedKey = ecp.makeRandom({network: networks.regtest});
|
|
193
|
+
|
|
194
|
+
const witnessScript = compile([unusedKey.publicKey.slice(1), OP_CHECKSIG]);
|
|
195
|
+
|
|
196
|
+
const branches = [{script: witnessScript.toString('hex')}];
|
|
197
|
+
|
|
198
|
+
const {hash} = hashForTree({branches});
|
|
199
|
+
|
|
200
|
+
const output = v1OutputScript({
|
|
201
|
+
hash,
|
|
202
|
+
internal_key: topLevelKey.public_key,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
206
|
+
|
|
207
|
+
// Make a PSBT paying to the Taproot output
|
|
208
|
+
const {psbt} = createPsbt({
|
|
209
|
+
outputs: [{tokens, script: output.script}],
|
|
210
|
+
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Sign the PSBT
|
|
214
|
+
const signed = await signPsbt({
|
|
215
|
+
lnd,
|
|
216
|
+
psbt: (await fundPsbt({lnd, psbt})).psbt,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Send the tx to the chain
|
|
220
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
221
|
+
|
|
222
|
+
// Make a new tx that will spend the output back into the wallet
|
|
223
|
+
const tx = new Transaction();
|
|
224
|
+
|
|
225
|
+
// The new tx spends the Taproot output
|
|
226
|
+
tx.addInput(
|
|
227
|
+
fromHex(signed.transaction).getHash(),
|
|
228
|
+
fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// Make an output to pay back into the wallet
|
|
232
|
+
const chainOutput = toOutputScript(
|
|
233
|
+
(await createChainAddress({lnd})).address,
|
|
234
|
+
networks.regtest
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// Add output to the pay back transaction
|
|
238
|
+
tx.addOutput(chainOutput, smallTokens);
|
|
239
|
+
|
|
240
|
+
const {signatures} = await signTransaction({
|
|
241
|
+
lnd,
|
|
242
|
+
inputs: [{
|
|
243
|
+
key_family: 805,
|
|
244
|
+
key_index: topLevelKey.index,
|
|
245
|
+
output_script: output.script,
|
|
246
|
+
output_tokens: tokens,
|
|
247
|
+
root_hash: hash,
|
|
248
|
+
sighash: Transaction.SIGHASH_DEFAULT,
|
|
249
|
+
vin: 0,
|
|
250
|
+
}],
|
|
251
|
+
transaction: tx.toHex(),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const [signature] = signatures.map(hexAsBuffer);
|
|
255
|
+
|
|
256
|
+
// Add the signature to the input
|
|
257
|
+
tx.ins.forEach((input, i) => tx.setWitness(i, [signature]));
|
|
258
|
+
|
|
259
|
+
await broadcastChainTransaction({lnd, transaction: tx.toHex()});
|
|
260
|
+
|
|
261
|
+
await asyncRetry({interval, times}, async () => {
|
|
262
|
+
await generate({});
|
|
263
|
+
|
|
264
|
+
const {utxos} = await getUtxos({lnd});
|
|
265
|
+
|
|
266
|
+
const utxo = utxos.find(n => n.transaction_id === tx.getId());
|
|
267
|
+
|
|
268
|
+
if (!utxo || !utxo.confirmation_count) {
|
|
269
|
+
throw new Error('ExpectedReceivedTaprootSpend');
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
} catch (err) {
|
|
273
|
+
equal(err, null, 'Expected no error');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// A Taproot script can be funded and spent with bip86 internal key
|
|
277
|
+
try {
|
|
278
|
+
await generate({count});
|
|
279
|
+
|
|
280
|
+
const topLevelKey = await getPublicKey({lnd, family: 805});
|
|
281
|
+
|
|
282
|
+
const output = v1OutputScript({
|
|
283
|
+
internal_key: topLevelKey.public_key,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
287
|
+
|
|
288
|
+
// Make a PSBT paying to the Taproot output
|
|
289
|
+
const {psbt} = createPsbt({
|
|
290
|
+
outputs: [{tokens, script: output.script}],
|
|
291
|
+
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Sign the PSBT
|
|
295
|
+
const signed = await signPsbt({
|
|
296
|
+
lnd,
|
|
297
|
+
psbt: (await fundPsbt({lnd, psbt})).psbt,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Send the tx to the chain
|
|
301
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
302
|
+
|
|
303
|
+
// Make a new tx that will spend the output back into the wallet
|
|
304
|
+
const tx = new Transaction();
|
|
305
|
+
|
|
306
|
+
// The new tx spends the Taproot output
|
|
307
|
+
tx.addInput(
|
|
308
|
+
fromHex(signed.transaction).getHash(),
|
|
309
|
+
fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// Make an output to pay back into the wallet
|
|
313
|
+
const chainOutput = toOutputScript(
|
|
314
|
+
(await createChainAddress({lnd})).address,
|
|
315
|
+
networks.regtest
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// Add output to the pay back transaction
|
|
319
|
+
tx.addOutput(chainOutput, smallTokens);
|
|
320
|
+
|
|
321
|
+
const {signatures} = await signTransaction({
|
|
322
|
+
lnd,
|
|
323
|
+
inputs: [{
|
|
324
|
+
key_family: 805,
|
|
325
|
+
key_index: topLevelKey.index,
|
|
326
|
+
output_script: output.script,
|
|
327
|
+
output_tokens: tokens,
|
|
328
|
+
sighash: Transaction.SIGHASH_DEFAULT,
|
|
329
|
+
vin: 0,
|
|
330
|
+
}],
|
|
331
|
+
transaction: tx.toHex(),
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const [signature] = signatures.map(hexAsBuffer);
|
|
335
|
+
|
|
336
|
+
// Add the signature to the input
|
|
337
|
+
tx.ins.forEach((input, i) => tx.setWitness(i, [signature]));
|
|
338
|
+
|
|
339
|
+
await broadcastChainTransaction({lnd, transaction: tx.toHex()});
|
|
340
|
+
|
|
341
|
+
await asyncRetry({interval, times}, async () => {
|
|
342
|
+
await generate({});
|
|
343
|
+
|
|
344
|
+
const {utxos} = await getUtxos({lnd});
|
|
345
|
+
|
|
346
|
+
const utxo = utxos.find(n => n.transaction_id === tx.getId());
|
|
347
|
+
|
|
348
|
+
if (!utxo || !utxo.confirmation_count) {
|
|
349
|
+
throw new Error('ExpectedReceivedTaprootSpend');
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
} catch (err) {
|
|
353
|
+
equal(err, null, 'Expected no error');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
await kill({});
|
|
357
|
+
|
|
358
|
+
return end();
|
|
359
|
+
});
|
|
@@ -15,7 +15,7 @@ test(`Sign transaction`, async ({end, equal}) => {
|
|
|
15
15
|
inputs: [{
|
|
16
16
|
key_family: 6,
|
|
17
17
|
key_index: 1,
|
|
18
|
-
output_script: '
|
|
18
|
+
output_script: '00147ab105a90ccd7e49d96672abcac2995bdb852baa',
|
|
19
19
|
output_tokens: 1e8,
|
|
20
20
|
sighash: Transaction.SIGHASH_ALL,
|
|
21
21
|
vin: 0,
|