@@ -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,25 +62,30 @@ 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
59- // NewMusigPartialSig creates a new musig partial signature.
60- func NewMusigPartialSig (sig * musig2.PartialSignature ,
61- signerNonce , combinedNonce lnwire.Musig2Nonce ,
62- signerKeys [] * btcec. PublicKey ) * MusigPartialSig {
71+ // NewMusigPartialSig creates a new MuSig2 partial signature.
72+ func NewMusigPartialSig (sig * musig2.PartialSignature , signerNonce ,
73+ combinedNonce lnwire.Musig2Nonce , signerKeys [] * btcec. PublicKey ,
74+ tapscriptTweak fn. Option [chainhash. Hash ] ) * MusigPartialSig {
6375
6476 return & MusigPartialSig {
65- sig : sig ,
66- signerNonce : signerNonce ,
67- combinedNonce : combinedNonce ,
68- signerKeys : signerKeys ,
77+ sig : sig ,
78+ signerNonce : signerNonce ,
79+ combinedNonce : combinedNonce ,
80+ signerKeys : signerKeys ,
81+ tapscriptTweak : tapscriptTweak ,
6982 }
7083}
7184
7285// FromWireSig maps a wire partial sig to this internal type that we'll use to
7386// perform signature validation.
74- func (p * MusigPartialSig ) FromWireSig (sig * lnwire. PartialSigWithNonce ,
75- ) * MusigPartialSig {
87+ func (p * MusigPartialSig ) FromWireSig (
88+ sig * lnwire. PartialSigWithNonce ) * MusigPartialSig {
7689
7790 p .sig = & musig2.PartialSignature {
7891 S : & sig .Sig ,
@@ -135,9 +148,15 @@ func (p *MusigPartialSig) Verify(msg []byte, pub *btcec.PublicKey) bool {
135148 var m [32 ]byte
136149 copy (m [:], msg )
137150
151+ // If we have a tapscript tweak, then we'll use that as a tweak
152+ // otherwise, we'll fall back to the normal BIP 86 sign tweak.
153+ signOpts := fn .MapOption (tapscriptRootToSignOpt )(
154+ p .tapscriptTweak ,
155+ ).UnwrapOr (musig2 .WithBip86SignTweak ())
156+
138157 return p .sig .Verify (
139158 p .signerNonce , p .combinedNonce , p .signerKeys , pub , m ,
140- musig2 .WithSortedKeys (), musig2 . WithBip86SignTweak () ,
159+ musig2 .WithSortedKeys (), signOpts ,
141160 )
142161}
143162
@@ -160,6 +179,14 @@ func (n *MusigNoncePair) String() string {
160179 n .SigningNonce .PubNonce [:])
161180}
162181
182+ // TapscriptRootToTweak is a function that takes a MuSig2 taproot tweak and
183+ // returns the root hash of the tapscript tree.
184+ func muSig2TweakToRoot (tweak input.MuSig2Tweaks ) chainhash.Hash {
185+ var root chainhash.Hash
186+ copy (root [:], tweak .TaprootTweak )
187+ return root
188+ }
189+
163190// MusigSession abstracts over the details of a logical musig session. A single
164191// session is used for each commitment transactions. The sessions use a JIT
165192// nonce style, wherein part of the session can be created using only the
@@ -197,15 +224,20 @@ type MusigSession struct {
197224 // commitType tracks if this is the session for the local or remote
198225 // commitment.
199226 commitType MusigCommitType
227+
228+ // tapscriptTweak is an optional tweak, that if specified, will be used
229+ // instead of the normal BIP 86 tweak when creating the MuSig2
230+ // aggregate key and session.
231+ tapscriptTweak fn.Option [input.MuSig2Tweaks ]
200232}
201233
202234// NewPartialMusigSession creates a new musig2 session given only the
203235// verification nonce (local nonce), and the other information that has already
204236// been bound to the session.
205237func NewPartialMusigSession (verificationNonce musig2.Nonces ,
206- localKey , remoteKey keychain.KeyDescriptor ,
207- signer input. MuSig2Signer , inputTxOut * wire.TxOut ,
208- commitType MusigCommitType ) * MusigSession {
238+ localKey , remoteKey keychain.KeyDescriptor , signer input. MuSig2Signer ,
239+ inputTxOut * wire.TxOut , commitType MusigCommitType ,
240+ tapscriptTweak fn. Option [input. MuSig2Tweaks ] ) * MusigSession {
209241
210242 signerKeys := []* btcec.PublicKey {localKey .PubKey , remoteKey .PubKey }
211243
@@ -214,13 +246,14 @@ func NewPartialMusigSession(verificationNonce musig2.Nonces,
214246 }
215247
216248 return & MusigSession {
217- nonces : nonces ,
218- remoteKey : remoteKey ,
219- localKey : localKey ,
220- inputTxOut : inputTxOut ,
221- signerKeys : signerKeys ,
222- signer : signer ,
223- commitType : commitType ,
249+ nonces : nonces ,
250+ remoteKey : remoteKey ,
251+ localKey : localKey ,
252+ inputTxOut : inputTxOut ,
253+ signerKeys : signerKeys ,
254+ signer : signer ,
255+ commitType : commitType ,
256+ tapscriptTweak : tapscriptTweak ,
224257 }
225258}
226259
@@ -254,9 +287,9 @@ func (m *MusigSession) FinalizeSession(signingNonce musig2.Nonces) error {
254287 remoteNonce = m .nonces .SigningNonce
255288 }
256289
257- tweakDesc := input.MuSig2Tweaks {
290+ tweakDesc := m . tapscriptTweak . UnwrapOr ( input.MuSig2Tweaks {
258291 TaprootBIP0086Tweak : true ,
259- }
292+ })
260293 m .session , err = m .signer .MuSig2CreateSession (
261294 input .MuSig2Version100RC2 , m .localKey .KeyLocator , m .signerKeys ,
262295 & tweakDesc , [][musig2 .PubNonceSize ]byte {remoteNonce .PubNonce },
@@ -351,8 +384,11 @@ func (m *MusigSession) SignCommit(tx *wire.MsgTx) (*MusigPartialSig, error) {
351384 return nil , err
352385 }
353386
387+ tapscriptRoot := fn .MapOption (muSig2TweakToRoot )(m .tapscriptTweak )
388+
354389 return NewMusigPartialSig (
355390 sig , m .session .PublicNonce , m .combinedNonce , m .signerKeys ,
391+ tapscriptRoot ,
356392 ), nil
357393}
358394
@@ -364,7 +400,7 @@ func (m *MusigSession) Refresh(verificationNonce *musig2.Nonces,
364400
365401 return NewPartialMusigSession (
366402 * verificationNonce , m .localKey , m .remoteKey , m .signer ,
367- m .inputTxOut , m .commitType ,
403+ m .inputTxOut , m .commitType , m . tapscriptTweak ,
368404 ), nil
369405}
370406
@@ -451,9 +487,11 @@ func (m *MusigSession) VerifyCommitSig(commitTx *wire.MsgTx,
451487 // When we verify a commitment signature, we always assume that we're
452488 // verifying a signature on our local commitment. Therefore, we'll use:
453489 // their remote nonce, and also public key.
490+ tapscriptRoot := fn .MapOption (muSig2TweakToRoot )(m .tapscriptTweak )
454491 partialSig := NewMusigPartialSig (
455492 & musig2.PartialSignature {S : & sig .Sig },
456493 m .nonces .SigningNonce .PubNonce , m .combinedNonce , m .signerKeys ,
494+ tapscriptRoot ,
457495 )
458496
459497 // With the partial sig loaded with the proper context, we'll now
@@ -537,6 +575,10 @@ type MusigSessionCfg struct {
537575 // InputTxOut is the output that we're signing for. This will be the
538576 // funding input.
539577 InputTxOut * wire.TxOut
578+
579+ // TapscriptRoot is an optional tweak that can be used to modify the
580+ // MuSig2 public key used in the session.
581+ TapscriptTweak fn.Option [chainhash.Hash ]
540582}
541583
542584// MusigPairSession houses the two musig2 sessions needed to do funding and
@@ -561,13 +603,14 @@ func NewMusigPairSession(cfg *MusigSessionCfg) *MusigPairSession {
561603 //
562604 // Both sessions will be created using only the verification nonce for
563605 // the local+remote party.
606+ tapscriptTweak := fn .MapOption (TapscriptRootToTweak )(cfg .TapscriptTweak )
564607 localSession := NewPartialMusigSession (
565- cfg .LocalNonce , cfg .LocalKey , cfg .RemoteKey ,
566- cfg .Signer , cfg . InputTxOut , LocalMusigCommit ,
608+ cfg .LocalNonce , cfg .LocalKey , cfg .RemoteKey , cfg . Signer ,
609+ cfg .InputTxOut , LocalMusigCommit , tapscriptTweak ,
567610 )
568611 remoteSession := NewPartialMusigSession (
569- cfg .RemoteNonce , cfg .LocalKey , cfg .RemoteKey ,
570- cfg .Signer , cfg . InputTxOut , RemoteMusigCommit ,
612+ cfg .RemoteNonce , cfg .LocalKey , cfg .RemoteKey , cfg . Signer ,
613+ cfg .InputTxOut , RemoteMusigCommit , tapscriptTweak ,
571614 )
572615
573616 return & MusigPairSession {
0 commit comments