ln-service 53.9.1 → 53.9.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
- ## 53.9.1
3
+ ## 53.9.4
4
4
 
5
5
  - `getPendingPayments`: Add method to get payments in flight
6
6
 
package/README.md CHANGED
@@ -3685,7 +3685,7 @@ Requires `offchain:write` permission
3685
3685
  [outgoing_channels]: [<Pay Out of Outgoing Channel Ids String>]
3686
3686
  [pathfinding_timeout]: <Time to Spend Finding a Route Milliseconds Number>
3687
3687
  [payment]: <Payment Identifier Hex String>
3688
- routes: [[{
3688
+ [routes]: [[{
3689
3689
  [base_fee_mtokens]: <Base Routing Fee In Millitokens String>
3690
3690
  [channel]: <Standard Format Channel Id String>
3691
3691
  [cltv_delta]: <CLTV Blocks Delta Number>
@@ -5433,6 +5433,8 @@ const {secret} = await once(sub, 'confirmed');
5433
5433
 
5434
5434
  Subscribe to successful outgoing payments
5435
5435
 
5436
+ Payments may be omitted if LND does not finalize the payment record
5437
+
5436
5438
  Requires `offchain:read` permission
5437
5439
 
5438
5440
  Note: Method not supported on LND 0.13.4 and below
package/package.json CHANGED
@@ -9,16 +9,16 @@
9
9
  "dependencies": {
10
10
  "bolt07": "1.8.0",
11
11
  "cors": "2.8.5",
12
- "express": "4.17.2",
12
+ "express": "4.17.3",
13
13
  "invoices": "2.0.4",
14
- "lightning": "5.8.1",
14
+ "lightning": "5.8.6",
15
15
  "macaroon": "3.0.4",
16
16
  "morgan": "1.10.0",
17
17
  "ws": "8.5.0"
18
18
  },
19
19
  "description": "Interaction helper for your Lightning Network daemon",
20
20
  "devDependencies": {
21
- "@alexbosworth/tap": "15.0.10",
21
+ "@alexbosworth/tap": "15.0.11",
22
22
  "@alexbosworth/node-fetch": "2.6.2",
23
23
  "async": "3.2.3",
24
24
  "asyncjs-util": "1.2.8",
@@ -28,12 +28,13 @@
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.4",
31
+ "ln-docker-daemons": "2.2.5",
32
+ "p2tr": "1.3.0",
32
33
  "portfinder": "1.0.28",
33
34
  "psbt": "2.0.0",
34
35
  "rimraf": "3.0.2",
35
36
  "secp256k1": "4.0.3",
36
- "tiny-secp256k1": "2.2.0",
37
+ "tiny-secp256k1": "2.2.1",
37
38
  "uuid": "8.3.2",
38
39
  "varuint-bitcoin": "1.1.2"
39
40
  },
@@ -68,5 +69,5 @@
68
69
  "integration-test-0.12.0": "DOCKER_LND_VERSION=v0.12.0-beta npm run test",
69
70
  "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/routerrpc-integration/*.js test/signerrpc-integration/*.js test/tower_clientrpc-integration/*.js test/tower_serverrpc-integration/*.js test/walletrpc-integration/*.js"
70
71
  },
71
- "version": "53.9.1"
72
+ "version": "53.9.4"
72
73
  }
@@ -1,14 +1,21 @@
1
1
  const asyncRetry = require('async/retry');
2
2
  const {address} = require('bitcoinjs-lib');
3
+ const {controlBlock} = require('p2tr');
3
4
  const {createPsbt} = require('psbt');
4
- const {crypto} = require('bitcoinjs-lib');
5
5
  const {decodePsbt} = require('psbt');
6
+ const {hashForTree} = require('p2tr');
7
+ const {leafHash} = require('p2tr');
6
8
  const {networks} = require('bitcoinjs-lib');
9
+ const {pointAdd} = require('tiny-secp256k1');
10
+ const {privateAdd} = require('tiny-secp256k1');
7
11
  const {script} = require('bitcoinjs-lib');
12
+ const {signHash} = require('p2tr');
13
+ const {signSchnorr} = require('tiny-secp256k1');
8
14
  const {spawnLightningCluster} = require('ln-docker-daemons');
9
15
  const {test} = require('@alexbosworth/tap');
10
16
  const tinysecp = require('tiny-secp256k1');
11
17
  const {Transaction} = require('bitcoinjs-lib');
18
+ const {v1OutputScript} = require('p2tr');
12
19
 
13
20
  const {broadcastChainTransaction} = require('./../../');
14
21
  const {createChainAddress} = require('./../../');
@@ -23,25 +30,17 @@ const chainAddressRowType = 'chain_address';
23
30
  const {compile} = script;
24
31
  const confirmationCount = 6;
25
32
  const count = 100;
33
+ const defaultInternalKey = '0350929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0';
26
34
  const description = 'description';
27
- const extra = Buffer.alloc(32);
35
+ const {from} = Buffer;
28
36
  const {fromBech32} = address;
29
37
  const {fromHex} = Transaction;
30
38
  const {fromOutputScript} = address;
31
39
  const hexAsBuffer = hex => Buffer.from(hex, 'hex');
32
40
  const interval = retryCount => 10 * Math.pow(2, retryCount);
33
- const isLowPublicKey = keyPair => keyPair.publicKey[0] === 2;
34
- const makeTaprootKey = (k, h) => tinysecp.xOnlyPointAddTweak(k, h).xOnlyPubkey;
35
- const nLess1 = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140';
36
- const one256BitBigEndian = '0000000000000000000000000000000000000000000000000000000000000001';
37
- const OP_1 = 81;
38
- const {privateAdd} = tinysecp;
39
- const {privateSub} = tinysecp;
41
+ const OP_CHECKSIG = 172;
40
42
  const regtestBech32AddressHrp = 'bcrt';
41
- const shortKey = keyPair => keyPair.publicKey.slice(1, 33);
42
- const {signSchnorr} = tinysecp;
43
43
  const smallTokens = 2e5;
44
- const tapHash = k => crypto.taggedHash('TapTweak', k.publicKey.slice(1, 33));
45
44
  const times = 20;
46
45
  const {toOutputScript} = address;
47
46
  const tokens = 1e6;
@@ -120,22 +119,123 @@ test(`Fund PSBT`, async ({end, equal}) => {
120
119
  equal(!!decodedInput.witness_utxo.script_pub, true, 'PSBT input address');
121
120
  equal(decodedInput.witness_utxo.tokens, 5000000000, 'PSBT has input tokens');
122
121
 
123
- // A Taproot output should be funded
122
+ // A Taproot script can be funded and spent with internal key + script hash
123
+ try {
124
+ await generate({count});
125
+
126
+ const keyPair1 = ecp.makeRandom({network: networks.regtest});
127
+ const keyPair2 = ecp.makeRandom({network: networks.regtest});
128
+ const unusedKey = ecp.makeRandom({network: networks.regtest});
129
+
130
+ const witnessScript = compile([unusedKey.publicKey.slice(1), OP_CHECKSIG]);
131
+
132
+ const branches = [{script: witnessScript.toString('hex')}];
133
+
134
+ const {hash} = hashForTree({branches});
135
+
136
+ // Create a combined key using public key material
137
+ const combinedPoint = pointAdd(keyPair1.publicKey, keyPair2.publicKey);
138
+
139
+ const output = v1OutputScript({
140
+ hash,
141
+ internal_key: Buffer.from(combinedPoint).toString('hex'),
142
+ });
143
+
144
+ const [utxo] = (await getUtxos({lnd})).utxos.reverse();
145
+
146
+ // Make a PSBT paying to the Taproot output
147
+ const {psbt} = createPsbt({
148
+ outputs: [{tokens, script: output.script}],
149
+ utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
150
+ });
151
+
152
+ // Sign the PSBT
153
+ const signed = await signPsbt({
154
+ lnd,
155
+ psbt: (await fundPsbt({lnd, psbt})).psbt,
156
+ });
157
+
158
+ // Send the tx to the chain
159
+ await broadcastChainTransaction({lnd, transaction: signed.transaction});
160
+
161
+ // Make a new tx that will spend the output back into the wallet
162
+ const tx = new Transaction();
163
+
164
+ // The new tx spends the Taproot output
165
+ tx.addInput(
166
+ fromHex(signed.transaction).getHash(),
167
+ fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
168
+ );
169
+
170
+ // Make an output to pay back into the wallet
171
+ const chainOutput = toOutputScript(
172
+ (await createChainAddress({lnd})).address,
173
+ networks.regtest
174
+ );
175
+
176
+ // Add output to the pay back transaction
177
+ tx.addOutput(chainOutput, smallTokens);
178
+
179
+ const [hashToSign] = tx.ins.map((input, i) => {
180
+ return tx.hashForWitnessV1(
181
+ i,
182
+ [hexAsBuffer(output.script)],
183
+ [tokens],
184
+ Transaction.SIGHASH_DEFAULT,
185
+ );
186
+ });
187
+
188
+ // Ready for private key combining
189
+ const combinedKey = privateAdd(keyPair1.privateKey, keyPair2.privateKey);
190
+
191
+ const signedInput = signHash({
192
+ hash,
193
+ private_key: Buffer.from(combinedKey).toString('hex'),
194
+ public_key: Buffer.from(combinedPoint).toString('hex'),
195
+ sign_hash: hashToSign.toString('hex'),
196
+ });
197
+
198
+ const signature = hexAsBuffer(signedInput.signature);
199
+
200
+ // Add the signature to the input
201
+ tx.ins.forEach((input, i) => tx.setWitness(i, [signature]));
202
+
203
+ await broadcastChainTransaction({lnd, transaction: tx.toHex()});
204
+
205
+ await asyncRetry({interval, times}, async () => {
206
+ await generate({});
207
+
208
+ const {utxos} = await getUtxos({lnd});
209
+
210
+ const utxo = utxos.find(n => n.transaction_id === tx.getId());
211
+
212
+ if (!utxo || !utxo.confirmation_count) {
213
+ throw new Error('ExpectedReceivedTaprootSpend');
214
+ }
215
+ });
216
+ } catch (err) {
217
+ equal(err, null, 'Expected no error');
218
+ }
219
+
220
+ // A Taproot script output should be funded and spent with script
124
221
  try {
125
222
  await generate({count});
126
223
 
127
224
  const keyPair = ecp.makeRandom({network: networks.regtest});
128
225
 
129
- const outputKey = makeTaprootKey(shortKey(keyPair), tapHash(keyPair));
130
- const tweakHash = tapHash(keyPair);
226
+ const witnessScript = compile([keyPair.publicKey.slice(1), OP_CHECKSIG]);
227
+
228
+ const branches = [{script: witnessScript.toString('hex')}];
229
+
230
+ const {hash} = hashForTree({branches});
131
231
 
132
- const outputScript = compile([OP_1, Buffer.from(outputKey)]);
232
+ const output = v1OutputScript({hash, internal_key: defaultInternalKey});
133
233
 
134
234
  const [utxo] = (await getUtxos({lnd})).utxos.reverse();
135
235
 
136
236
  // Make a PSBT paying to the Taproot output
137
237
  const {psbt} = createPsbt({
138
- outputs: [{tokens, script: outputScript.toString('hex')}],
238
+ outputs: [{tokens, script: output.script}],
139
239
  utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
140
240
  });
141
241
 
@@ -169,22 +269,110 @@ test(`Fund PSBT`, async ({end, equal}) => {
169
269
  const [hashToSign] = tx.ins.map((input, i) => {
170
270
  return tx.hashForWitnessV1(
171
271
  i,
172
- [outputScript],
272
+ [hexAsBuffer(output.script)],
173
273
  [tokens],
174
274
  Transaction.SIGHASH_DEFAULT,
275
+ hexAsBuffer(leafHash({script: witnessScript.toString('hex')}).hash),
175
276
  );
176
277
  });
177
278
 
178
- const isLow = isLowPublicKey(keyPair);
179
- const ONE = hexAsBuffer(one256BitBigEndian);
180
- const subtract = privateSub(hexAsBuffer(nLess1), keyPair.privateKey);
279
+ const signature = from(signSchnorr(hashToSign, keyPair.privateKey));
280
+
281
+ const {block} = controlBlock({
282
+ external_key: output.external_key,
283
+ leaf_script: witnessScript.toString('hex'),
284
+ script_branches: branches,
285
+ });
286
+
287
+ // Add the signature to the input
288
+ tx.ins.forEach((input, i) => {
289
+ return tx.setWitness(i, [
290
+ signature,
291
+ witnessScript,
292
+ hexAsBuffer(block),
293
+ ]);
294
+ });
295
+
296
+ await broadcastChainTransaction({lnd, transaction: tx.toHex()});
297
+
298
+ await asyncRetry({interval, times}, async () => {
299
+ await generate({});
300
+
301
+ const {utxos} = await getUtxos({lnd});
302
+
303
+ const utxo = utxos.find(n => n.transaction_id === tx.getId());
304
+
305
+ if (!utxo || !utxo.confirmation_count) {
306
+ throw new Error('ExpectedReceivedTaprootSpend');
307
+ }
308
+ });
309
+ } catch (err) {
310
+ equal(err, null, 'Expected no error');
311
+ }
312
+
313
+ // A Taproot output should be funded for a regular key spend
314
+ try {
315
+ await generate({count});
316
+
317
+ const keyPair = ecp.makeRandom({network: networks.regtest});
318
+
319
+ const output = v1OutputScript({
320
+ internal_key: keyPair.publicKey.toString('hex'),
321
+ });
322
+
323
+ const outputScript = hexAsBuffer(output.script);
324
+
325
+ const [utxo] = (await getUtxos({lnd})).utxos.reverse();
326
+
327
+ // Make a PSBT paying to the Taproot output
328
+ const {psbt} = createPsbt({
329
+ outputs: [{tokens, script: outputScript.toString('hex')}],
330
+ utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
331
+ });
332
+
333
+ // Sign the PSBT
334
+ const signed = await signPsbt({
335
+ lnd,
336
+ psbt: (await fundPsbt({lnd, psbt})).psbt,
337
+ });
338
+
339
+ // Send the tx to the chain
340
+ await broadcastChainTransaction({lnd, transaction: signed.transaction});
341
+
342
+ // Make a new tx that will spend the output back into the wallet
343
+ const tx = new Transaction();
344
+
345
+ // The new tx spends the Taproot output
346
+ tx.addInput(
347
+ fromHex(signed.transaction).getHash(),
348
+ fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
349
+ );
350
+
351
+ // Make an output to pay back into the wallet
352
+ const chainOutput = toOutputScript(
353
+ (await createChainAddress({lnd})).address,
354
+ networks.regtest
355
+ );
181
356
 
182
- const privateKey = isLow ? keyPair.privateKey : privateAdd(subtract, ONE);
357
+ // Add output to the pay back transaction
358
+ tx.addOutput(chainOutput, smallTokens);
183
359
 
184
- // Only low keys are allowed to save a leading byte on the public key
185
- const lowPrivateKey = privateAdd(privateKey, tweakHash);
360
+ const [hashToSign] = tx.ins.map((input, i) => {
361
+ return tx.hashForWitnessV1(
362
+ i,
363
+ [outputScript],
364
+ [tokens],
365
+ Transaction.SIGHASH_DEFAULT,
366
+ );
367
+ });
368
+
369
+ const signedInput = signHash({
370
+ private_key: keyPair.privateKey.toString('hex'),
371
+ public_key: keyPair.publicKey.toString('hex'),
372
+ sign_hash: hashToSign.toString('hex'),
373
+ });
186
374
 
187
- const signature = signSchnorr(hashToSign, lowPrivateKey, extra);
375
+ const signature = hexAsBuffer(signedInput.signature);
188
376
 
189
377
  // Add the signature to the input
190
378
  tx.ins.forEach((input, i) => tx.setWitness(i, [Buffer.from(signature)]));