@@ -8,8 +8,10 @@ import (
88 "github.com/btcsuite/btcd/btcec/v2"
99 "github.com/btcsuite/btcd/btcec/v2/schnorr"
1010 "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
11+ "github.com/btcsuite/btcd/chaincfg/chainhash"
1112 "github.com/btcsuite/btcd/txscript"
1213 "github.com/btcsuite/btcd/wire"
14+ "github.com/lightningnetwork/lnd/fn"
1315 "github.com/lightningnetwork/lnd/input"
1416 "github.com/lightningnetwork/lnd/keychain"
1517 "github.com/lightningnetwork/lnd/lnwire"
3739 ErrSessionNotFinalized = fmt .Errorf ("musig2 session not finalized" )
3840)
3941
42+ // tapscriptRootToSignOpt is a function that takes a tapscript root and returns
43+ // a musig2 sign opt that'll apply the tweak when signing+verifying.
44+ func tapscriptRootToSignOpt (root chainhash.Hash ) musig2.SignOption {
45+ return musig2 .WithTaprootSignTweak (root [:])
46+ }
47+
4048// MusigPartialSig is a wrapper around the base musig2.PartialSignature type
4149// that also includes information about the set of nonces used, and also the
4250// signer. This allows us to implement the input.Signature interface, as that
@@ -54,18 +62,24 @@ type MusigPartialSig struct {
5462
5563 // signerKeys is the set of public keys of all signers.
5664 signerKeys []* btcec.PublicKey
65+
66+ // tapscriptRoot is an optional tweak, that if specified, will be used
67+ // instead of the normal BIP 86 tweak when validating the signature.
68+ tapscriptTweak fn.Option [chainhash.Hash ]
5769}
5870
5971// NewMusigPartialSig creates a new musig partial signature.
6072func NewMusigPartialSig (sig * musig2.PartialSignature ,
6173 signerNonce , combinedNonce lnwire.Musig2Nonce ,
62- signerKeys []* btcec.PublicKey ) * MusigPartialSig {
74+ signerKeys []* btcec.PublicKey , tapscriptTweak fn.Option [chainhash.Hash ],
75+ ) * MusigPartialSig {
6376
6477 return & MusigPartialSig {
65- sig : sig ,
66- signerNonce : signerNonce ,
67- combinedNonce : combinedNonce ,
68- signerKeys : signerKeys ,
78+ sig : sig ,
79+ signerNonce : signerNonce ,
80+ combinedNonce : combinedNonce ,
81+ signerKeys : signerKeys ,
82+ tapscriptTweak : tapscriptTweak ,
6983 }
7084}
7185
@@ -135,9 +149,15 @@ func (p *MusigPartialSig) Verify(msg []byte, pub *btcec.PublicKey) bool {
135149 var m [32 ]byte
136150 copy (m [:], msg )
137151
152+ // If we have a tapscript tweak, then we'll use that as a tweak
153+ // otherwise, we'll fall back to the normal BIP 86 sign tweak.
154+ signOpts := fn .MapOption (tapscriptRootToSignOpt )(
155+ p .tapscriptTweak ,
156+ ).UnwrapOr (musig2 .WithBip86SignTweak ())
157+
138158 return p .sig .Verify (
139159 p .signerNonce , p .combinedNonce , p .signerKeys , pub , m ,
140- musig2 .WithSortedKeys (), musig2 . WithBip86SignTweak () ,
160+ musig2 .WithSortedKeys (), signOpts ,
141161 )
142162}
143163
@@ -160,6 +180,14 @@ func (n *MusigNoncePair) String() string {
160180 n .SigningNonce .PubNonce [:])
161181}
162182
183+ // TapscriptRootToTweak is a function that takes a musig2 taproot tweak and
184+ // returns the root hash of the tapscript tree.
185+ func musig2TweakToRoot (tweak input.MuSig2Tweaks ) chainhash.Hash {
186+ var root chainhash.Hash
187+ copy (root [:], tweak .TaprootTweak )
188+ return root
189+ }
190+
163191// MusigSession abstracts over the details of a logical musig session. A single
164192// session is used for each commitment transactions. The sessions use a JIT
165193// nonce style, wherein part of the session can be created using only the
@@ -197,6 +225,11 @@ type MusigSession struct {
197225 // commitType tracks if this is the session for the local or remote
198226 // commitment.
199227 commitType MusigCommitType
228+
229+ // tapscriptTweak is an optional tweak, that if specified, will be used
230+ // instead of the normal BIP 86 tweak when creating the musig2
231+ // aggregate key and session.
232+ tapscriptTweak fn.Option [input.MuSig2Tweaks ]
200233}
201234
202235// NewPartialMusigSession creates a new musig2 session given only the
@@ -205,7 +238,8 @@ type MusigSession struct {
205238func NewPartialMusigSession (verificationNonce musig2.Nonces ,
206239 localKey , remoteKey keychain.KeyDescriptor ,
207240 signer input.MuSig2Signer , inputTxOut * wire.TxOut ,
208- commitType MusigCommitType ) * MusigSession {
241+ commitType MusigCommitType ,
242+ tapscriptTweak fn.Option [input.MuSig2Tweaks ]) * MusigSession {
209243
210244 signerKeys := []* btcec.PublicKey {localKey .PubKey , remoteKey .PubKey }
211245
@@ -214,13 +248,14 @@ func NewPartialMusigSession(verificationNonce musig2.Nonces,
214248 }
215249
216250 return & MusigSession {
217- nonces : nonces ,
218- remoteKey : remoteKey ,
219- localKey : localKey ,
220- inputTxOut : inputTxOut ,
221- signerKeys : signerKeys ,
222- signer : signer ,
223- commitType : commitType ,
251+ nonces : nonces ,
252+ remoteKey : remoteKey ,
253+ localKey : localKey ,
254+ inputTxOut : inputTxOut ,
255+ signerKeys : signerKeys ,
256+ signer : signer ,
257+ commitType : commitType ,
258+ tapscriptTweak : tapscriptTweak ,
224259 }
225260}
226261
@@ -254,9 +289,9 @@ func (m *MusigSession) FinalizeSession(signingNonce musig2.Nonces) error {
254289 remoteNonce = m .nonces .SigningNonce
255290 }
256291
257- tweakDesc := input.MuSig2Tweaks {
292+ tweakDesc := m . tapscriptTweak . UnwrapOr ( input.MuSig2Tweaks {
258293 TaprootBIP0086Tweak : true ,
259- }
294+ })
260295 m .session , err = m .signer .MuSig2CreateSession (
261296 input .MuSig2Version100RC2 , m .localKey .KeyLocator , m .signerKeys ,
262297 & tweakDesc , [][musig2 .PubNonceSize ]byte {remoteNonce .PubNonce },
@@ -351,8 +386,11 @@ func (m *MusigSession) SignCommit(tx *wire.MsgTx) (*MusigPartialSig, error) {
351386 return nil , err
352387 }
353388
389+ tapscriptRoot := fn .MapOption (musig2TweakToRoot )(m .tapscriptTweak )
390+
354391 return NewMusigPartialSig (
355392 sig , m .session .PublicNonce , m .combinedNonce , m .signerKeys ,
393+ tapscriptRoot ,
356394 ), nil
357395}
358396
@@ -364,7 +402,7 @@ func (m *MusigSession) Refresh(verificationNonce *musig2.Nonces,
364402
365403 return NewPartialMusigSession (
366404 * verificationNonce , m .localKey , m .remoteKey , m .signer ,
367- m .inputTxOut , m .commitType ,
405+ m .inputTxOut , m .commitType , m . tapscriptTweak ,
368406 ), nil
369407}
370408
@@ -451,9 +489,11 @@ func (m *MusigSession) VerifyCommitSig(commitTx *wire.MsgTx,
451489 // When we verify a commitment signature, we always assume that we're
452490 // verifying a signature on our local commitment. Therefore, we'll use:
453491 // their remote nonce, and also public key.
492+ tapscriptRoot := fn .MapOption (musig2TweakToRoot )(m .tapscriptTweak )
454493 partialSig := NewMusigPartialSig (
455494 & musig2.PartialSignature {S : & sig .Sig },
456495 m .nonces .SigningNonce .PubNonce , m .combinedNonce , m .signerKeys ,
496+ tapscriptRoot ,
457497 )
458498
459499 // With the partial sig loaded with the proper context, we'll now
@@ -537,6 +577,10 @@ type MusigSessionCfg struct {
537577 // InputTxOut is the output that we're signing for. This will be the
538578 // funding input.
539579 InputTxOut * wire.TxOut
580+
581+ // TapscriptRoot is an optional tweak that can be used to modify the
582+ // musig2 public key used in the session.
583+ TapscriptTweak fn.Option [chainhash.Hash ]
540584}
541585
542586// MusigPairSession houses the two musig2 sessions needed to do funding and
@@ -561,13 +605,16 @@ func NewMusigPairSession(cfg *MusigSessionCfg) *MusigPairSession {
561605 //
562606 // Both sessions will be created using only the verification nonce for
563607 // the local+remote party.
608+ tapscriptTweak := fn .MapOption (TapscriptRootToTweak )(
609+ cfg .TapscriptTweak ,
610+ )
564611 localSession := NewPartialMusigSession (
565- cfg .LocalNonce , cfg .LocalKey , cfg .RemoteKey ,
566- cfg .Signer , cfg . InputTxOut , LocalMusigCommit ,
612+ cfg .LocalNonce , cfg .LocalKey , cfg .RemoteKey , cfg . Signer ,
613+ cfg .InputTxOut , LocalMusigCommit , tapscriptTweak ,
567614 )
568615 remoteSession := NewPartialMusigSession (
569- cfg .RemoteNonce , cfg .LocalKey , cfg .RemoteKey ,
570- cfg .Signer , cfg . InputTxOut , RemoteMusigCommit ,
616+ cfg .RemoteNonce , cfg .LocalKey , cfg .RemoteKey , cfg . Signer ,
617+ cfg .InputTxOut , RemoteMusigCommit , tapscriptTweak ,
571618 )
572619
573620 return & MusigPairSession {
0 commit comments