@@ -9,18 +9,24 @@ import (
9
9
10
10
"github.com/btcsuite/btcd/btcec/v2"
11
11
"github.com/btcsuite/btcd/btcec/v2/schnorr"
12
+ "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
12
13
"github.com/btcsuite/btcd/btcutil/psbt"
13
14
"github.com/btcsuite/btcd/txscript"
14
15
"github.com/btcsuite/btcd/wire"
15
16
"github.com/btcsuite/btcwallet/wallet"
16
17
"github.com/btcsuite/btcwallet/wtxmgr"
17
18
"github.com/lightninglabs/lndclient"
18
19
"github.com/lightninglabs/loop/assets"
20
+ "github.com/lightninglabs/loop/assets/htlc"
19
21
"github.com/lightninglabs/loop/utils"
20
22
"github.com/lightninglabs/taproot-assets/address"
21
23
"github.com/lightninglabs/taproot-assets/proof"
24
+ "github.com/lightninglabs/taproot-assets/tappsbt"
22
25
"github.com/lightninglabs/taproot-assets/taprpc"
26
+ "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"
23
27
"github.com/lightningnetwork/lnd/input"
28
+ "github.com/lightningnetwork/lnd/keychain"
29
+ "github.com/lightningnetwork/lnd/lntypes"
24
30
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
25
31
)
26
32
@@ -333,3 +339,110 @@ func getSigHash(tx *wire.MsgTx, idx int,
333
339
334
340
return sigHash , nil
335
341
}
342
+
343
+ // GetHTLC creates a new zero-fee HTLC packet to be able to partially sign it
344
+ // and send it to the server for further processing.
345
+ //
346
+ // TODO(bhandras): add support for spending multiple deposits into the HTLC.
347
+ func (s * Sweeper ) GetHTLC (ctx context.Context , deposit * Kit ,
348
+ depositProof * proof.Proof , amount uint64 , hash lntypes.Hash ,
349
+ csvExpiry uint32 ) (* htlc.SwapKit , * psbt.Packet , []* tappsbt.VPacket ,
350
+ []* tappsbt.VPacket , * assetwalletrpc.CommitVirtualPsbtsResponse , error ) {
351
+
352
+ // Genearate the HTLC address that will be used to sweep the deposit to
353
+ // in case the client is uncooperative.
354
+ rpcHtlcAddr , swapKit , err := deposit .NewHtlcAddr (
355
+ ctx , s .tapdClient , amount , hash , csvExpiry ,
356
+ )
357
+ if err != nil {
358
+ return nil , nil , nil , nil , nil , fmt .Errorf ("unable to create " +
359
+ "htlc addr: %v" , err )
360
+ }
361
+
362
+ htlcAddr , err := address .DecodeAddress (
363
+ rpcHtlcAddr .Encoded , & s .addressParams ,
364
+ )
365
+ if err != nil {
366
+ return nil , nil , nil , nil , nil , err
367
+ }
368
+
369
+ // Now we can create the sweep vpacket that'd sweep the deposited
370
+ // assets to the HTLC output.
371
+ depositSpendVpkt , err := assets .CreateOpTrueSweepVpkt (
372
+ ctx , []* proof.Proof {depositProof }, htlcAddr , & s .addressParams ,
373
+ )
374
+ if err != nil {
375
+ return nil , nil , nil , nil , nil , fmt .Errorf ("unable to create " +
376
+ "deposit spend vpacket: %v" , err )
377
+ }
378
+
379
+ // By committing the virtual transaction to the BTC template we
380
+ // created, our lnd node will fund the BTC level transaction with an
381
+ // input to pay for the fees. We'll further add a change output to the
382
+ // transaction that will be generated using the above key descriptor.
383
+ feeRate := chainfee .SatPerVByte (0 )
384
+
385
+ // Use an empty lock ID, as we don't need to lock any UTXOs for this
386
+ // operation.
387
+ lockID := wtxmgr.LockID {}
388
+
389
+ htlcBtcPkt , activeAssets , passiveAssets , commitResp , err :=
390
+ s .tapdClient .PrepareAndCommitVirtualPsbts (
391
+ ctx , depositSpendVpkt , feeRate , nil ,
392
+ s .addressParams .Params , nil , & lockID ,
393
+ time .Duration (0 ),
394
+ )
395
+ if err != nil {
396
+ return nil , nil , nil , nil , nil , fmt .Errorf ("deposit spend " +
397
+ "HTLC prepare and commit failed: %v" , err )
398
+ }
399
+
400
+ htlcBtcPkt .UnsignedTx .Version = 3
401
+
402
+ return swapKit , htlcBtcPkt , activeAssets , passiveAssets , commitResp , nil
403
+ }
404
+
405
+ // PartialSignMuSig2 is used to partially sign a message hash with the deposit's
406
+ // keys.
407
+ func (s * Sweeper ) PartialSignMuSig2 (ctx context.Context , d * Kit ,
408
+ anchorRootHash []byte , cosignerNonce [musig2 .PubNonceSize ]byte ,
409
+ message [32 ]byte ) ([musig2 .PubNonceSize ]byte , []byte , error ) {
410
+
411
+ signers := [][]byte {
412
+ d .FunderInternalKey .SerializeCompressed (),
413
+ d .CoSignerInternalKey .SerializeCompressed (),
414
+ }
415
+
416
+ session , err := s .signer .MuSig2CreateSession (
417
+ ctx , input .MuSig2Version100RC2 ,
418
+ & keychain.KeyLocator {
419
+ Family : d .KeyLocator .Family ,
420
+ Index : d .KeyLocator .Index ,
421
+ }, signers , lndclient .MuSig2TaprootTweakOpt (
422
+ anchorRootHash , false ,
423
+ ),
424
+ )
425
+ if err != nil {
426
+ return [musig2 .PubNonceSize ]byte {}, nil , err
427
+ }
428
+
429
+ _ , err = s .signer .MuSig2RegisterNonces (
430
+ ctx , session .SessionID ,
431
+ [][musig2 .PubNonceSize ]byte {cosignerNonce },
432
+ )
433
+ if err != nil {
434
+ return [musig2 .PubNonceSize ]byte {}, nil , err
435
+ }
436
+
437
+ clientPartialSig , err := s .signer .MuSig2Sign (
438
+ ctx , session .SessionID , message , true ,
439
+ )
440
+ if err != nil {
441
+ return [musig2 .PubNonceSize ]byte {}, nil , err
442
+ }
443
+
444
+ fmt .Printf ("!!! client partial sig: %x\n " , clientPartialSig )
445
+ fmt .Printf ("!!! client nonce: %x\n " , session .PublicNonce )
446
+
447
+ return session .PublicNonce , clientPartialSig , nil
448
+ }
0 commit comments