ln-service 53.14.0 → 53.16.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,5 +1,22 @@
1
1
  # Versions
2
2
 
3
+ ## 53.16.0
4
+
5
+ - `beginGroupSigningSession`: Add method to start a MuSig2 signing session
6
+ - `endGroupSigningSession`: Add method to complete a MuSig2 signing session
7
+ - `updateGroupSigningSession`: Add method to add nonces to a MuSig2 session
8
+
9
+ ## 53.15.0
10
+
11
+ - `getRouteToDestination`, `isDestinationPayable`, `pay`,
12
+ `payViaPaymentDetails`, `payViaPaymentRequest`, `probeForRoute`,
13
+ `subscribeToPayViaDetails`, `subscribeToPayViaRequest`,
14
+ `subscribeToProbeForRoute` - add support for pathfinding `confidence`
15
+
16
+ ## 53.14.1
17
+
18
+ - `signTransaction`: Fix multi-input signing for upcoming Taproot API changes
19
+
3
20
  ## 53.14.0
4
21
 
5
22
  - `addExternalSocket`: Add method to add a socket to graph announcements
package/README.md CHANGED
@@ -105,6 +105,7 @@ for `unlocker` methods.
105
105
  - [addExternalSocket](#addexternalsocket) - Advertise a new p2p host:ip address
106
106
  - [addPeer](#addpeer) - Connect to a peer
107
107
  - [authenticatedLndGrpc](#authenticatedlndgrpc) - LND API Object
108
+ - [beginGroupSigningSession](#begingroupsigningsession) - Start MuSig2 session
108
109
  - [broadcastChainTransaction](#broadcastchaintransaction) - Push a chain tx
109
110
  - [cancelHodlInvoice](#cancelhodlinvoice) - Cancel a held or any open invoice
110
111
  - [cancelPendingChannel](#cancelpendingchannel) - Cancel a pending open channel
@@ -132,6 +133,7 @@ for `unlocker` methods.
132
133
  - [disableChannel](#disablechannel) - Disable a channel for outgoing payments
133
134
  - [disconnectWatchtower](#disconnectwatchtower) - Disconnect a watchtower
134
135
  - [enableChannel](#enablechannel) - Enable a channel for outgoing payments
136
+ - [endGroupSigningSession](#endgroupsigningsession) - Complete MuSig2 session
135
137
  - [fundPendingChannels](#fundpendingchannels) - Fund pending open channels
136
138
  - [fundPsbt](#fundpsbt) - Create an unsigned PSBT with funding inputs and
137
139
  spending outputs
@@ -256,6 +258,7 @@ for `unlocker` methods.
256
258
  transaction
257
259
  - [updateColor](#updatecolor) - Update node graph color value
258
260
  - [updateConnectedWatchtower](#updateconnectedwatchtower) - Update watchtower
261
+ - [updateGroupSigningSession](#updategroupsigningsession) - Sign with MuSig2
259
262
  - [updatePathfindingSettings](#updatepathfindingsettings) - Update pathfinding
260
263
  configuration
261
264
  - [updateRoutingFees](#updateroutingfees) - Change routing fees
@@ -277,7 +280,7 @@ for `unlocker` methods.
277
280
  - [ln-accounting](https://npmjs.com/package/ln-accounting) - accounting records
278
281
  - [ln-docker-daemons](https://github.com/alexbosworth/ln-docker-daemons) -
279
282
  run regtest integration tests
280
- - [ln-pathfinding](https://npmjs.com/package/ln-accounting) - pathfinding
283
+ - [ln-pathfinding](https://npmjs.com/package/ln-pathfinding) - pathfinding
281
284
  utilities
282
285
  - [ln-sync](https://www.npmjs.com/package/ln-sync) - metadata helper methods
283
286
  - [probing](https://npmjs.com/package/probing) - payment probing utilities
@@ -380,6 +383,46 @@ const {lnd} = lnService.authenticatedLndGrpc({
380
383
  const wallet = await lnService.getWalletInfo({lnd});
381
384
  ```
382
385
 
386
+ ### beginGroupSigningSession
387
+
388
+ Start a MuSig2 group signing session
389
+
390
+ Requires LND built with `signrpc`, `walletrpc` build tags
391
+
392
+ Requires `address:read`, `signer:generate` permissions
393
+
394
+ This method is not supported in LND 0.14.3 and below
395
+
396
+ {
397
+ lnd: <Authenticated LND API Object>
398
+ [is_key_spend]: <Key Is BIP 86 Key Spend Key Bool>
399
+ key_family: <HD Seed Key Family Number>
400
+ key_index: <Key Index Number>
401
+ public_keys: [<External Public Key Hex String>]
402
+ [root_hash]: <Taproot Script Root Hash Hex String>
403
+ }
404
+
405
+ @returns via cbk or Promise
406
+ {
407
+ external_key: <Final Script or Top Level Public Key Hex String>
408
+ id: <Session Id Hex String>
409
+ [internal_key]: <Internal Top Level Public Key Hex String>
410
+ nonce: <Session Compound Nonces Hex String>
411
+ }
412
+
413
+ Example:
414
+
415
+ ```node
416
+ const {beginGroupSigningSession} = require('ln-service');
417
+
418
+ const session = await beginGroupSigningSession({
419
+ lnd,
420
+ key_family: 0,
421
+ key_index: 0,
422
+ public_keys: [externalPublicKey],
423
+ });
424
+ ```
425
+
383
426
  ### broadcastChainTransaction
384
427
 
385
428
  Publish a raw blockchain transaction to Blockchain network peers
@@ -1118,6 +1161,36 @@ await enableChannel({
1118
1161
  });
1119
1162
  ```
1120
1163
 
1164
+ ### endGroupSigningSession
1165
+
1166
+ Complete a MuSig2 signing session
1167
+
1168
+ Requires LND built with `signrpc` build tag
1169
+
1170
+ Requires `signer:generate` permission
1171
+
1172
+ This method is not supported in LND 0.14.3 and below
1173
+
1174
+ {
1175
+ id: <Session Id Hex String>
1176
+ lnd: <Authenticated LND API Object>
1177
+ [signatures]: [<Combine External Partial Signature Hex String>]
1178
+ }
1179
+
1180
+ @returns via cbk or Promise
1181
+ {
1182
+ [signature]: <Combined Signature Hex String>
1183
+ }
1184
+
1185
+ Example:
1186
+
1187
+ ```node
1188
+ const {endGroupSigningSession} = require('ln-service');
1189
+
1190
+ // Cancel a group signing session
1191
+ await endGroupSigningSession({id, lnd});
1192
+ ```
1193
+
1121
1194
  ### fundPendingChannels
1122
1195
 
1123
1196
  Fund pending channels
@@ -2931,8 +3004,11 @@ Call this iteratively after failed route attempts to get new routes
2931
3004
 
2932
3005
  Requires `info:read` permission
2933
3006
 
3007
+ Preferred `confidence` is not supported on LND 0.14.3 and below
3008
+
2934
3009
  {
2935
3010
  [cltv_delta]: <Final CLTV Delta Number>
3011
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
2936
3012
  destination: <Final Send Destination Hex Encoded Public Key String>
2937
3013
  [features]: [{
2938
3014
  bit: <Feature Bit Number>
@@ -3331,8 +3407,11 @@ Determine if a payment destination is actually payable by probing it
3331
3407
 
3332
3408
  Requires `offchain:write` permission
3333
3409
 
3410
+ Preferred `confidence` is not supported on LND 0.14.3 and below
3411
+
3334
3412
  {
3335
3413
  [cltv_delta]: <Final CLTV Delta Number>
3414
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
3336
3415
  destination: <Pay to Node with Public Key Hex String>
3337
3416
  [incoming_peer]: <Pay Through Specific Final Hop Public Key Hex String>
3338
3417
  lnd: <Authenticated LND API Object>
@@ -3612,7 +3691,10 @@ Requires `offchain:write` permission
3612
3691
 
3613
3692
  `max_path_mtokens` is not supported in LND 0.12.0 or below
3614
3693
 
3694
+ Preferred `confidence` is not supported on LND 0.14.3 and below
3695
+
3615
3696
  {
3697
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
3616
3698
  [incoming_peer]: <Pay Through Specific Final Hop Public Key Hex String>
3617
3699
  lnd: <Authenticated LND API Object>
3618
3700
  [max_fee]: <Maximum Additional Fee Tokens To Pay Number>
@@ -3699,8 +3781,11 @@ Requires `offchain:write` permission
3699
3781
 
3700
3782
  `max_path_mtokens` is not supported in LND 0.12.0 or below
3701
3783
 
3784
+ Preferred `confidence` is not supported on LND 0.14.3 and below
3785
+
3702
3786
  {
3703
3787
  [cltv_delta]: <Final CLTV Delta Number>
3788
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
3704
3789
  destination: <Destination Public Key String>
3705
3790
  [features]: [{
3706
3791
  bit: <Feature Bit Number>
@@ -3786,7 +3871,10 @@ Requires `offchain:write` permission
3786
3871
 
3787
3872
  `max_path_mtokens` is not supported in LND 0.12.0 or below
3788
3873
 
3874
+ Preferred `confidence` is not supported on LND 0.14.3 and below
3875
+
3789
3876
  {
3877
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
3790
3878
  [incoming_peer]: <Pay Through Specific Final Hop Public Key Hex String>
3791
3879
  lnd: <Authenticated LND API Object>
3792
3880
  [max_fee]: <Maximum Fee Tokens To Pay Number>
@@ -3988,8 +4076,11 @@ If `total_mtokens` are specified, a `payment` nonce is required.
3988
4076
 
3989
4077
  Requires `offchain:write` permission
3990
4078
 
4079
+ Preferred `confidence` is not supported on LND 0.14.3 and below
4080
+
3991
4081
  {
3992
4082
  [cltv_delta]: <Final CLTV Delta Number>
4083
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
3993
4084
  destination: <Destination Public Key Hex String>
3994
4085
  [features]: [{
3995
4086
  bit: <Feature Bit Number>
@@ -5573,8 +5664,11 @@ Requires `offchain:write` permission
5573
5664
 
5574
5665
  `max_path_mtokens` is not supported in LND 0.12.0 or below
5575
5666
 
5667
+ Preferred `confidence` is not supported on LND 0.14.3 and below
5668
+
5576
5669
  {
5577
5670
  [cltv_delta]: <Final CLTV Delta Number>
5671
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
5578
5672
  destination: <Destination Public Key String>
5579
5673
  [features]: [{
5580
5674
  bit: <Feature Bit Number>
@@ -5736,7 +5830,10 @@ Requires `offchain:write` permission
5736
5830
 
5737
5831
  `max_path_mtokens` is not supported in LND 0.12.0 or below
5738
5832
 
5833
+ Preferred `confidence` is not supported on LND 0.14.3 and below
5834
+
5739
5835
  {
5836
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
5740
5837
  [incoming_peer]: <Pay Through Specific Final Hop Public Key Hex String>
5741
5838
  lnd: <Authenticated LND API Object>
5742
5839
  [max_fee]: <Maximum Fee Tokens To Pay Number>
@@ -6144,8 +6241,11 @@ Subscribe to a probe attempt
6144
6241
 
6145
6242
  Requires `offchain:write` permission
6146
6243
 
6244
+ Preferred `confidence` is not supported on LND 0.14.3 and below
6245
+
6147
6246
  {
6148
6247
  [cltv_delta]: <Final CLTV Delta Number>
6248
+ [confidence]: <Preferred Route Confidence Number Out of One Million Number>
6149
6249
  destination: <Destination Public Key Hex String>
6150
6250
  [features]: [{
6151
6251
  bit: <Feature Bit Number>
@@ -6734,6 +6834,43 @@ await updateConnectedWatchtower({
6734
6834
  });
6735
6835
  ```
6736
6836
 
6837
+ ### updateGroupSigningSession
6838
+
6839
+ Update a MuSig2 signing session with nonces and generate a partial sig
6840
+
6841
+ All remote nonces are expected to be passed
6842
+
6843
+ Requires LND built with `signrpc` build tag
6844
+
6845
+ Requires `signer:generate` permission
6846
+
6847
+ This method is not supported in LND 0.14.3 and below
6848
+
6849
+ {
6850
+ hash: <Hash to Sign Hex String>
6851
+ id: <MuSig2 Session Id Hex String>
6852
+ lnd: <Authenticated LND API Object>
6853
+ nonces: [<Nonce Hex String>]
6854
+ }
6855
+
6856
+ @returns via cbk or Promise
6857
+ {
6858
+ signature: <Partial Signature Hex String>
6859
+ }
6860
+
6861
+ Example:
6862
+
6863
+ ```node
6864
+ const {updateGroupSigningSession} = require('ln-service');
6865
+
6866
+ const {signature} = await updateGroupSigningSession({
6867
+ lnd,
6868
+ hash: v1TxDigestHash,
6869
+ id: sessionId,
6870
+ nonces: [externalNonce],
6871
+ });
6872
+ ```
6873
+
6737
6874
  ### updatePathfindingSettings
6738
6875
 
6739
6876
  Update current pathfinding settings
package/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const {addExternalSocket} = require('lightning');
2
2
  const {addPeer} = require('lightning');
3
3
  const {authenticatedLndGrpc} = require('lightning');
4
+ const {beginGroupSigningSession} = require('lightning');
4
5
  const {broadcastChainTransaction} = require('lightning');
5
6
  const {cancelHodlInvoice} = require('lightning');
6
7
  const {cancelPendingChannel} = require('lightning');
@@ -25,6 +26,7 @@ const {diffieHellmanComputeSecret} = require('lightning');
25
26
  const {disableChannel} = require('lightning');
26
27
  const {disconnectWatchtower} = require('lightning');
27
28
  const {enableChannel} = require('lightning');
29
+ const {endGroupSigningSession} = require('lightning');
28
30
  const {fundPendingChannels} = require('lightning');
29
31
  const {fundPsbt} = require('lightning');
30
32
  const {getAccessIds} = require('lightning');
@@ -135,6 +137,7 @@ const {updateAlias} = require('lightning');
135
137
  const {updateChainTransaction} = require('lightning');
136
138
  const {updateColor} = require('lightning');
137
139
  const {updateConnectedWatchtower} = require('lightning');
140
+ const {updateGroupSigningSession} = require('lightning');
138
141
  const {updatePathfindingSettings} = require('lightning');
139
142
  const {updateRoutingFees} = require('lightning');
140
143
  const {verifyAccess} = require('lightning');
@@ -147,6 +150,7 @@ module.exports = {
147
150
  addExternalSocket,
148
151
  addPeer,
149
152
  authenticatedLndGrpc,
153
+ beginGroupSigningSession,
150
154
  broadcastChainTransaction,
151
155
  cancelHodlInvoice,
152
156
  cancelPendingChannel,
@@ -171,6 +175,7 @@ module.exports = {
171
175
  disableChannel,
172
176
  disconnectWatchtower,
173
177
  enableChannel,
178
+ endGroupSigningSession,
174
179
  fundPendingChannels,
175
180
  fundPsbt,
176
181
  getAccessIds,
@@ -281,6 +286,7 @@ module.exports = {
281
286
  updateChainTransaction,
282
287
  updateColor,
283
288
  updateConnectedWatchtower,
289
+ updateGroupSigningSession,
284
290
  updatePathfindingSettings,
285
291
  updateRoutingFees,
286
292
  verifyAccess,
package/package.json CHANGED
@@ -9,12 +9,12 @@
9
9
  "dependencies": {
10
10
  "bolt07": "1.8.1",
11
11
  "cors": "2.8.5",
12
- "express": "4.17.3",
12
+ "express": "4.18.1",
13
13
  "invoices": "2.0.6",
14
- "lightning": "5.13.0",
14
+ "lightning": "5.15.0",
15
15
  "macaroon": "3.0.4",
16
16
  "morgan": "1.10.0",
17
- "ws": "8.5.0"
17
+ "ws": "8.6.0"
18
18
  },
19
19
  "description": "Interaction helper for your Lightning Network daemon",
20
20
  "devDependencies": {
@@ -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.9",
31
+ "ln-docker-daemons": "2.2.10",
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.14.0"
73
+ "version": "53.16.0"
74
74
  }
@@ -66,7 +66,7 @@ test(`Subscribe to chain transactions`, async ({end, equal, fail}) => {
66
66
  .filter(n => !!n.is_outgoing);
67
67
 
68
68
  if (!transaction) {
69
- throw new Error('ExpectedTrnasaction');
69
+ throw new Error('ExpectedTransactionInSubscribeToChainAddressResult');
70
70
  }
71
71
 
72
72
  // Wait for generation to be over
@@ -0,0 +1,440 @@
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 {leafHash} = require('p2tr');
7
+ const {networks} = require('bitcoinjs-lib');
8
+ const {script} = require('bitcoinjs-lib');
9
+ const {spawnLightningCluster} = require('ln-docker-daemons');
10
+ const {test} = require('@alexbosworth/tap');
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 {endGroupSigningSession} = require('./../../');
18
+ const {fundPsbt} = require('./../../');
19
+ const {getPublicKey} = require('./../../');
20
+ const {getUtxos} = require('./../../');
21
+ const {signPsbt} = require('./../../');
22
+ const {updateGroupSigningSession} = require('./../../');
23
+
24
+ const {compile} = script;
25
+ const count = 100;
26
+ const defaultInternalKey = '0350929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0';
27
+ const {from} = Buffer;
28
+ const {fromHex} = Transaction;
29
+ const hexAsBuffer = hex => Buffer.from(hex, 'hex');
30
+ const interval = 100;
31
+ const OP_CHECKSIG = 172;
32
+ const smallTokens = 2e5;
33
+ const times = 20;
34
+ const {toOutputScript} = address;
35
+ const tokens = 1e6;
36
+
37
+ // Starting a group signing session should result in a new MuSig2 session
38
+ test(`Begin group signing session`, async ({end, equal}) => {
39
+ const {kill, nodes} = await spawnLightningCluster({size: 2});
40
+
41
+ const [{generate, lnd}, target] = nodes;
42
+
43
+ try {
44
+ await beginGroupSigningSession({
45
+ lnd,
46
+ is_key_spend: true,
47
+ key_family: 0,
48
+ key_index: 0,
49
+ public_keys: [Buffer.alloc(33, 2).toString('hex')],
50
+ });
51
+ } catch (err) {
52
+ // On LND 0.14.3 and below, group signing is not supported
53
+ if (err.slice().shift() === 501) {
54
+ await kill({});
55
+
56
+ return end();
57
+ }
58
+
59
+ throw err;
60
+ }
61
+
62
+ // A Taproot script can be funded and spent with MuSig2
63
+ try {
64
+ await generate({count});
65
+
66
+ const controlKey = await getPublicKey({lnd, family: 0});
67
+ const targetKey = await getPublicKey({family: 0, lnd: target.lnd});
68
+
69
+ const controlGroup = await beginGroupSigningSession({
70
+ lnd,
71
+ is_key_spend: true,
72
+ key_family: 0,
73
+ key_index: controlKey.index,
74
+ public_keys: [targetKey.public_key],
75
+ });
76
+
77
+ const targetGroup = await beginGroupSigningSession({
78
+ is_key_spend: true,
79
+ key_family: 0,
80
+ key_index: targetKey.index,
81
+ lnd: target.lnd,
82
+ public_keys: [controlKey.public_key],
83
+ });
84
+
85
+ equal(controlGroup.external_key, targetGroup.external_key, 'Equal e-keys');
86
+ equal(controlGroup.internal_key, targetGroup.internal_key, 'Equal i-keys');
87
+
88
+ const script = Buffer.concat([
89
+ Buffer.from([81]),
90
+ Buffer.from([32]),
91
+ hexAsBuffer(controlGroup.external_key),
92
+ ]);
93
+
94
+ const [utxo] = (await getUtxos({lnd})).utxos.reverse();
95
+
96
+ // Make a PSBT paying to the Taproot output
97
+ const {psbt} = createPsbt({
98
+ outputs: [{tokens, script: script.toString('hex')}],
99
+ utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
100
+ });
101
+
102
+ // Sign the PSBT
103
+ const signed = await signPsbt({
104
+ lnd,
105
+ psbt: (await fundPsbt({lnd, psbt})).psbt,
106
+ });
107
+
108
+ // Send the tx to the chain
109
+ await broadcastChainTransaction({lnd, transaction: signed.transaction});
110
+
111
+ // Make a new tx that will spend the output back into the wallet
112
+ const tx = new Transaction();
113
+
114
+ // The new tx spends the Taproot output
115
+ tx.addInput(
116
+ fromHex(signed.transaction).getHash(),
117
+ fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
118
+ );
119
+
120
+ // Make an output to pay back into the wallet
121
+ const chainOutput = toOutputScript(
122
+ (await createChainAddress({lnd})).address,
123
+ networks.regtest
124
+ );
125
+
126
+ // Add output to the pay back transaction
127
+ tx.addOutput(chainOutput, smallTokens);
128
+
129
+ const [hashToSign] = tx.ins.map((input, i) => {
130
+ return tx.hashForWitnessV1(
131
+ i,
132
+ [script],
133
+ [tokens],
134
+ Transaction.SIGHASH_DEFAULT,
135
+ );
136
+ });
137
+
138
+ const controlSign = await updateGroupSigningSession({
139
+ lnd,
140
+ hash: hashToSign.toString('hex'),
141
+ id: controlGroup.id,
142
+ nonces: [targetGroup.nonce],
143
+ });
144
+
145
+ const targetSign = await updateGroupSigningSession({
146
+ hash: hashToSign.toString('hex'),
147
+ id: targetGroup.id,
148
+ lnd: target.lnd,
149
+ nonces: [controlGroup.nonce],
150
+ });
151
+
152
+ await endGroupSigningSession({lnd: target.lnd, id: targetGroup.id});
153
+
154
+ const {signature} = await endGroupSigningSession({
155
+ lnd,
156
+ id: controlGroup.id,
157
+ signatures: [targetSign.signature],
158
+ });
159
+
160
+ // Add the signature to the input
161
+ tx.ins.forEach((input, i) => tx.setWitness(i, [hexAsBuffer(signature)]));
162
+
163
+ await broadcastChainTransaction({lnd, transaction: tx.toHex()});
164
+
165
+ await asyncRetry({interval, times}, async () => {
166
+ await generate({});
167
+
168
+ const {utxos} = await getUtxos({lnd});
169
+
170
+ const utxo = utxos.find(n => n.transaction_id === tx.getId());
171
+
172
+ if (!utxo || !utxo.confirmation_count) {
173
+ throw new Error('ExpectedReceivedTaprootSpend');
174
+ }
175
+ });
176
+ } catch (err) {
177
+ equal(err, null, 'Expected no error');
178
+ }
179
+
180
+ // A Taproot script can be funded and spent with MuSig2 for a script output
181
+ try {
182
+ await generate({count});
183
+
184
+ const controlKey = await getPublicKey({lnd, family: 0});
185
+ const targetKey = await getPublicKey({family: 0, lnd: target.lnd});
186
+ const unusedKey = (await getPublicKey({lnd, family: 805})).public_key;
187
+
188
+ const xOnlyUnused = hexAsBuffer(unusedKey.slice(2));
189
+
190
+ const witnessScript = compile([xOnlyUnused, OP_CHECKSIG]);
191
+
192
+ const branches = [{script: witnessScript.toString('hex')}];
193
+
194
+ const {hash} = hashForTree({branches});
195
+
196
+ const controlGroup = await beginGroupSigningSession({
197
+ lnd,
198
+ key_family: 0,
199
+ key_index: controlKey.index,
200
+ public_keys: [targetKey.public_key],
201
+ root_hash: hash,
202
+ });
203
+
204
+ const targetGroup = await beginGroupSigningSession({
205
+ key_family: 0,
206
+ key_index: targetKey.index,
207
+ lnd: target.lnd,
208
+ public_keys: [controlKey.public_key],
209
+ root_hash: hash,
210
+ });
211
+
212
+ equal(controlGroup.external_key, targetGroup.external_key, 'Equal e-keys');
213
+ equal(controlGroup.internal_key, targetGroup.internal_key, 'Equal i-keys');
214
+
215
+ const script = Buffer.concat([
216
+ from([81]),
217
+ from([32]),
218
+ hexAsBuffer(controlGroup.external_key),
219
+ ]);
220
+
221
+ const [utxo] = (await getUtxos({lnd})).utxos.reverse();
222
+
223
+ // Make a PSBT paying to the Taproot output
224
+ const {psbt} = createPsbt({
225
+ outputs: [{tokens, script: script.toString('hex')}],
226
+ utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
227
+ });
228
+
229
+ // Sign the PSBT
230
+ const signed = await signPsbt({
231
+ lnd,
232
+ psbt: (await fundPsbt({lnd, psbt})).psbt,
233
+ });
234
+
235
+ // Send the tx to the chain
236
+ await broadcastChainTransaction({lnd, transaction: signed.transaction});
237
+
238
+ // Make a new tx that will spend the output back into the wallet
239
+ const tx = new Transaction();
240
+
241
+ // The new tx spends the Taproot output
242
+ tx.addInput(
243
+ fromHex(signed.transaction).getHash(),
244
+ fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
245
+ );
246
+
247
+ // Make an output to pay back into the wallet
248
+ const chainOutput = toOutputScript(
249
+ (await createChainAddress({lnd})).address,
250
+ networks.regtest
251
+ );
252
+
253
+ // Add output to the pay back transaction
254
+ tx.addOutput(chainOutput, smallTokens);
255
+
256
+ const [hashToSign] = tx.ins.map((input, i) => {
257
+ return tx.hashForWitnessV1(
258
+ i,
259
+ [script],
260
+ [tokens],
261
+ Transaction.SIGHASH_DEFAULT,
262
+ );
263
+ });
264
+
265
+ const controlSign = await updateGroupSigningSession({
266
+ lnd,
267
+ hash: hashToSign.toString('hex'),
268
+ id: controlGroup.id,
269
+ nonces: [targetGroup.nonce],
270
+ });
271
+
272
+ const targetSign = await updateGroupSigningSession({
273
+ hash: hashToSign.toString('hex'),
274
+ id: targetGroup.id,
275
+ lnd: target.lnd,
276
+ nonces: [controlGroup.nonce],
277
+ });
278
+
279
+ await endGroupSigningSession({lnd: target.lnd, id: targetGroup.id});
280
+
281
+ const {signature} = await endGroupSigningSession({
282
+ lnd,
283
+ id: controlGroup.id,
284
+ signatures: [targetSign.signature],
285
+ });
286
+
287
+ // Add the signature to the input
288
+ tx.ins.forEach((input, i) => tx.setWitness(i, [hexAsBuffer(signature)]));
289
+
290
+ await broadcastChainTransaction({lnd, transaction: tx.toHex()});
291
+
292
+ await asyncRetry({interval, times}, async () => {
293
+ await generate({});
294
+
295
+ const {utxos} = await getUtxos({lnd});
296
+
297
+ const utxo = utxos.find(n => n.transaction_id === tx.getId());
298
+
299
+ if (!utxo || !utxo.confirmation_count) {
300
+ throw new Error('ExpectedReceivedTaprootSpend');
301
+ }
302
+ });
303
+ } catch (err) {
304
+ equal(err, null, 'Expected no error');
305
+ }
306
+
307
+ // A Taproot script can be funded and spent on the script path
308
+ try {
309
+ await generate({count});
310
+
311
+ const controlKey = await getPublicKey({lnd, family: 0});
312
+ const targetKey = await getPublicKey({family: 0, lnd: target.lnd});
313
+
314
+ const controlGroup = await beginGroupSigningSession({
315
+ lnd,
316
+ key_family: 0,
317
+ key_index: controlKey.index,
318
+ public_keys: [targetKey.public_key],
319
+ });
320
+
321
+ const targetGroup = await beginGroupSigningSession({
322
+ key_family: 0,
323
+ key_index: targetKey.index,
324
+ lnd: target.lnd,
325
+ public_keys: [controlKey.public_key],
326
+ });
327
+
328
+ const scriptKey = hexAsBuffer(controlGroup.external_key);
329
+
330
+ const witnessScript = compile([scriptKey, OP_CHECKSIG]);
331
+
332
+ const branches = [{script: witnessScript.toString('hex')}];
333
+
334
+ const {hash} = hashForTree({branches});
335
+
336
+ const output = v1OutputScript({hash, internal_key: defaultInternalKey});
337
+
338
+ const [utxo] = (await getUtxos({lnd})).utxos.reverse();
339
+
340
+ // Make a PSBT paying to the Taproot output
341
+ const {psbt} = createPsbt({
342
+ outputs: [{tokens, script: output.script}],
343
+ utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
344
+ });
345
+
346
+ // Sign the PSBT
347
+ const signed = await signPsbt({
348
+ lnd,
349
+ psbt: (await fundPsbt({lnd, psbt})).psbt,
350
+ });
351
+
352
+ // Send the tx to the chain
353
+ await broadcastChainTransaction({lnd, transaction: signed.transaction});
354
+
355
+ // Make a new tx that will spend the output back into the wallet
356
+ const tx = new Transaction();
357
+
358
+ // The new tx spends the Taproot output
359
+ tx.addInput(
360
+ fromHex(signed.transaction).getHash(),
361
+ fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
362
+ );
363
+
364
+ // Make an output to pay back into the wallet
365
+ const chainOutput = toOutputScript(
366
+ (await createChainAddress({lnd})).address,
367
+ networks.regtest
368
+ );
369
+
370
+ // Add output to the pay back transaction
371
+ tx.addOutput(chainOutput, smallTokens);
372
+
373
+ const [hashToSign] = tx.ins.map((input, i) => {
374
+ return tx.hashForWitnessV1(
375
+ i,
376
+ [hexAsBuffer(output.script)],
377
+ [tokens],
378
+ Transaction.SIGHASH_DEFAULT,
379
+ hexAsBuffer(leafHash({script: witnessScript.toString('hex')}).hash),
380
+ );
381
+ });
382
+
383
+ const controlSign = await updateGroupSigningSession({
384
+ lnd,
385
+ hash: hashToSign.toString('hex'),
386
+ id: controlGroup.id,
387
+ nonces: [targetGroup.nonce],
388
+ });
389
+
390
+ const targetSign = await updateGroupSigningSession({
391
+ hash: hashToSign.toString('hex'),
392
+ id: targetGroup.id,
393
+ lnd: target.lnd,
394
+ nonces: [controlGroup.nonce],
395
+ });
396
+
397
+ await endGroupSigningSession({lnd: target.lnd, id: targetGroup.id});
398
+
399
+ const {signature} = await endGroupSigningSession({
400
+ lnd,
401
+ id: controlGroup.id,
402
+ signatures: [targetSign.signature],
403
+ });
404
+
405
+ const {block} = controlBlock({
406
+ external_key: output.external_key,
407
+ leaf_script: witnessScript.toString('hex'),
408
+ script_branches: branches,
409
+ });
410
+
411
+ // Add the signature to the input
412
+ tx.ins.forEach((input, i) => {
413
+ return tx.setWitness(i, [
414
+ hexAsBuffer(signature),
415
+ witnessScript,
416
+ hexAsBuffer(block),
417
+ ]);
418
+ });
419
+
420
+ await broadcastChainTransaction({lnd, transaction: tx.toHex()});
421
+
422
+ await asyncRetry({interval, times}, async () => {
423
+ await generate({});
424
+
425
+ const {utxos} = await getUtxos({lnd});
426
+
427
+ const utxo = utxos.find(n => n.transaction_id === tx.getId());
428
+
429
+ if (!utxo || !utxo.confirmation_count) {
430
+ throw new Error('ExpectedReceivedTaprootSpend');
431
+ }
432
+ });
433
+ } catch (err) {
434
+ equal(err, null, 'Expected no error');
435
+ }
436
+
437
+ await kill({});
438
+
439
+ return end();
440
+ });