ln-service 53.9.2 → 53.10.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
@@ -1,6 +1,11 @@
1
1
  # Versions
2
2
 
3
- ## 53.9.2
3
+ ## 53.10.0
4
+
5
+ - `createChainAddress`: Add support for creating P2TR addresses
6
+ - `getUtxos`: Add support for showing P2TR address types
7
+
8
+ ## 53.9.4
4
9
 
5
10
  - `getPendingPayments`: Add method to get payments in flight
6
11
 
package/README.md CHANGED
@@ -514,12 +514,19 @@ Create a new receive address.
514
514
 
515
515
  Requires `address:write` permission
516
516
 
517
+ LND 0.14.3 and below do not support p2tr addresses
518
+
517
519
  {
518
- [format]: <Receive Address Type String> // "np2wpkh" || "p2wpkh"
520
+ [format]: <Receive Address Type String> // "np2wpkh" || "p2tr" || "p2wpkh"
519
521
  [is_unused]: <Get As-Yet Unused Address Bool>
520
522
  lnd: <Authenticated LND API Object>
521
523
  }
522
524
 
525
+ @returns via cbk or Promise
526
+ {
527
+ address: <Chain Address String>
528
+ }
529
+
523
530
  Example:
524
531
 
525
532
  ```node
@@ -3530,7 +3537,7 @@ Requires `onchain:write` permission
3530
3537
 
3531
3538
  Requires LND built with `walletrpc` tag
3532
3539
 
3533
- This method is not supported in LND 0.14.2 and below
3540
+ This method is not supported in LND 0.14.3 and below
3534
3541
 
3535
3542
  {
3536
3543
  lnd: <Authenticated LND API Object>
@@ -3685,7 +3692,7 @@ Requires `offchain:write` permission
3685
3692
  [outgoing_channels]: [<Pay Out of Outgoing Channel Ids String>]
3686
3693
  [pathfinding_timeout]: <Time to Spend Finding a Route Milliseconds Number>
3687
3694
  [payment]: <Payment Identifier Hex String>
3688
- routes: [[{
3695
+ [routes]: [[{
3689
3696
  [base_fee_mtokens]: <Base Routing Fee In Millitokens String>
3690
3697
  [channel]: <Standard Format Channel Id String>
3691
3698
  [cltv_delta]: <CLTV Blocks Delta Number>
@@ -5433,6 +5440,8 @@ const {secret} = await once(sub, 'confirmed');
5433
5440
 
5434
5441
  Subscribe to successful outgoing payments
5435
5442
 
5443
+ Payments may be omitted if LND does not finalize the payment record
5444
+
5436
5445
  Requires `offchain:read` permission
5437
5446
 
5438
5447
  Note: Method not supported on LND 0.13.4 and below
package/package.json CHANGED
@@ -11,14 +11,14 @@
11
11
  "cors": "2.8.5",
12
12
  "express": "4.17.3",
13
13
  "invoices": "2.0.4",
14
- "lightning": "5.8.1",
14
+ "lightning": "5.9.0",
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,8 +28,8 @@
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",
32
- "p2tr": "1.2.0",
31
+ "ln-docker-daemons": "2.2.6",
32
+ "p2tr": "1.3.0",
33
33
  "portfinder": "1.0.28",
34
34
  "psbt": "2.0.0",
35
35
  "rimraf": "3.0.2",
@@ -69,5 +69,5 @@
69
69
  "integration-test-0.12.0": "DOCKER_LND_VERSION=v0.12.0-beta npm run test",
70
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"
71
71
  },
72
- "version": "53.9.2"
72
+ "version": "53.10.0"
73
73
  }
@@ -10,7 +10,6 @@ const {delay} = require('./../macros');
10
10
  const {generateBlocks} = require('./../macros');
11
11
  const {getHeight} = require('./../../');
12
12
  const {getChainBalance} = require('./../../');
13
- const {spawnLnd} = require('./../macros');
14
13
  const {subscribeToBlocks} = require('./../../');
15
14
  const {waitForTermination} = require('./../macros');
16
15
 
@@ -11,7 +11,6 @@ const {getChainTransactions} = require('./../../');
11
11
  const {getHeight} = require('./../../');
12
12
  const {mineTransaction} = require('./../macros');
13
13
  const {sendToChainAddress} = require('./../../');
14
- const {spawnLnd} = require('./../macros');
15
14
  const {subscribeToChainAddress} = require('./../../');
16
15
  const {waitForTermination} = require('./../macros');
17
16
 
@@ -7,6 +7,7 @@ const {createChainAddress} = require('./../../');
7
7
  const formats = ['np2wpkh', 'p2wpkh'];
8
8
  const p2shAddressVersion = 196;
9
9
  const pkHashByteLength = 20;
10
+ const prefixForV1 = 'bcrt1p';
10
11
  const regtestBech32AddressHrp = 'bcrt';
11
12
 
12
13
  // Creating addresses should result in addresses
@@ -34,6 +35,14 @@ test(`Create address results in address creation`, async ({end, equal}) => {
34
35
  equal(np2wpkh.address, unusedNp2wpkh.address, 'Nested is reused');
35
36
  equal(p2wpkh.address, unusedP2wpkh.address, 'Native is reused');
36
37
 
38
+ try {
39
+ const {address} = await createChainAddress({lnd, format: 'p2tr'});
40
+
41
+ equal(address.startsWith(prefixForV1), true, 'A taproot address is made');
42
+ } catch (err) {
43
+ // LND 0.14.3 and below do not support TR addresses
44
+ }
45
+
37
46
  await kill({});
38
47
 
39
48
  return end();
@@ -178,7 +178,7 @@ test(`Get pending channels`, async ({end, equal}) => {
178
178
  equal(forceClose.received, 0, 'No receive amount');
179
179
  equal(forceClose.recovered_tokens, undefined, 'No recovered amount');
180
180
 
181
- // LND 0.14.2 and below do not support remote balance info
181
+ // LND 0.14.3 and below do not support remote balance info
182
182
  if (!!forceClose.remote_balance) {
183
183
  equal(forceClose.remote_balance, giftTokens, 'Got gift remote balance');
184
184
  } else {
@@ -1,9 +1,16 @@
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
5
  const {decodePsbt} = require('psbt');
6
+ const {hashForTree} = require('p2tr');
7
+ const {leafHash} = require('p2tr');
5
8
  const {networks} = require('bitcoinjs-lib');
9
+ const {pointAdd} = require('tiny-secp256k1');
10
+ const {privateAdd} = require('tiny-secp256k1');
11
+ const {script} = require('bitcoinjs-lib');
6
12
  const {signHash} = require('p2tr');
13
+ const {signSchnorr} = require('tiny-secp256k1');
7
14
  const {spawnLightningCluster} = require('ln-docker-daemons');
8
15
  const {test} = require('@alexbosworth/tap');
9
16
  const tinysecp = require('tiny-secp256k1');
@@ -20,14 +27,18 @@ const {sendToChainAddress} = require('./../../');
20
27
  const {signPsbt} = require('./../../');
21
28
 
22
29
  const chainAddressRowType = 'chain_address';
30
+ const {compile} = script;
23
31
  const confirmationCount = 6;
24
32
  const count = 100;
33
+ const defaultInternalKey = '0350929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0';
25
34
  const description = 'description';
35
+ const {from} = Buffer;
26
36
  const {fromBech32} = address;
27
37
  const {fromHex} = Transaction;
28
38
  const {fromOutputScript} = address;
29
39
  const hexAsBuffer = hex => Buffer.from(hex, 'hex');
30
40
  const interval = retryCount => 10 * Math.pow(2, retryCount);
41
+ const OP_CHECKSIG = 172;
31
42
  const regtestBech32AddressHrp = 'bcrt';
32
43
  const smallTokens = 2e5;
33
44
  const times = 20;
@@ -108,7 +119,198 @@ test(`Fund PSBT`, async ({end, equal}) => {
108
119
  equal(!!decodedInput.witness_utxo.script_pub, true, 'PSBT input address');
109
120
  equal(decodedInput.witness_utxo.tokens, 5000000000, 'PSBT has input tokens');
110
121
 
111
- // 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
221
+ try {
222
+ await generate({count});
223
+
224
+ const keyPair = ecp.makeRandom({network: networks.regtest});
225
+
226
+ const witnessScript = compile([keyPair.publicKey.slice(1), OP_CHECKSIG]);
227
+
228
+ const branches = [{script: witnessScript.toString('hex')}];
229
+
230
+ const {hash} = hashForTree({branches});
231
+
232
+ const output = v1OutputScript({hash, internal_key: defaultInternalKey});
233
+
234
+ const [utxo] = (await getUtxos({lnd})).utxos.reverse();
235
+
236
+ // Make a PSBT paying to the Taproot output
237
+ const {psbt} = createPsbt({
238
+ outputs: [{tokens, script: output.script}],
239
+ utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
240
+ });
241
+
242
+ // Sign the PSBT
243
+ const signed = await signPsbt({
244
+ lnd,
245
+ psbt: (await fundPsbt({lnd, psbt})).psbt,
246
+ });
247
+
248
+ // Send the tx to the chain
249
+ await broadcastChainTransaction({lnd, transaction: signed.transaction});
250
+
251
+ // Make a new tx that will spend the output back into the wallet
252
+ const tx = new Transaction();
253
+
254
+ // The new tx spends the Taproot output
255
+ tx.addInput(
256
+ fromHex(signed.transaction).getHash(),
257
+ fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
258
+ );
259
+
260
+ // Make an output to pay back into the wallet
261
+ const chainOutput = toOutputScript(
262
+ (await createChainAddress({lnd})).address,
263
+ networks.regtest
264
+ );
265
+
266
+ // Add output to the pay back transaction
267
+ tx.addOutput(chainOutput, smallTokens);
268
+
269
+ const [hashToSign] = tx.ins.map((input, i) => {
270
+ return tx.hashForWitnessV1(
271
+ i,
272
+ [hexAsBuffer(output.script)],
273
+ [tokens],
274
+ Transaction.SIGHASH_DEFAULT,
275
+ hexAsBuffer(leafHash({script: witnessScript.toString('hex')}).hash),
276
+ );
277
+ });
278
+
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
112
314
  try {
113
315
  await generate({count});
114
316