@@ -14,6 +14,9 @@ package psbt
1414import (
1515 "bytes"
1616 "fmt"
17+ "github.com/btcsuite/btcd/btcec/v2"
18+ "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
19+ "github.com/btcsuite/btcd/chaincfg/chainhash"
1720
1821 "github.com/btcsuite/btcd/btcec/v2/schnorr"
1922 "github.com/btcsuite/btcd/txscript"
@@ -577,6 +580,91 @@ func finalizeTaprootInput(p *Packet, inIndex int) error {
577580
578581 serializedWitness , err = writeWitness (witnessStack ... )
579582
583+ // MuSig2 spend path.
584+ case len (pInput .MuSig2PartialSigs ) > 0 :
585+ if len (pInput .MuSig2PubNonces ) !=
586+ len (pInput .MuSig2PartialSigs ) {
587+
588+ return fmt .Errorf ("number of MuSig2 pub nonces " +
589+ "does not match number of partial signatures" )
590+ }
591+
592+ // We'll need to combine MuSig2 partial signatures into a single
593+ // one, which requires the message that was signed over.
594+ firstSig := pInput .MuSig2PartialSigs [0 ]
595+
596+ // We don't (yet) support signing over a tap leaf hash.
597+ // TODO(guggero): Add support for signing over a tap leaf hash.
598+ if len (firstSig .TapLeafHash ) > 0 {
599+ return fmt .Errorf ("combining partial MuSig2 " +
600+ "signatures for a tap leaf is not supported" )
601+ }
602+
603+ prevOutFetcher := PrevOutputFetcher (p )
604+ sigHashes := txscript .NewTxSigHashes (
605+ p .UnsignedTx , prevOutFetcher ,
606+ )
607+ sigHash , err := txscript .CalcTaprootSignatureHash (
608+ sigHashes , pInput .SighashType , p .UnsignedTx ,
609+ inIndex , prevOutFetcher ,
610+ )
611+ if err != nil {
612+ return fmt .Errorf ("error calculating signature hash: " +
613+ "%w" , err )
614+ }
615+
616+ var sigHashMsg [32 ]byte
617+ copy (sigHashMsg [:], sigHash )
618+
619+ var (
620+ pubNonces = make (
621+ [][musig2 .PubNonceSize ]byte ,
622+ len (pInput .MuSig2PubNonces ),
623+ )
624+ keys = make (
625+ []* btcec.PublicKey , len (pInput .MuSig2PubNonces ),
626+ )
627+ partialSigs = make (
628+ []* musig2.PartialSignature ,
629+ len (pInput .MuSig2PartialSigs ),
630+ )
631+ )
632+ for i , pubNonce := range pInput .MuSig2PubNonces {
633+ copy (pubNonces [i ][:], pubNonce .PubNonce [:])
634+ keys [i ] = pubNonce .PubKey
635+
636+ partialSigs [i ] = & pInput .MuSig2PartialSigs [i ].PartialSig
637+ }
638+ aggregateNonce , err := musig2 .AggregateNonces (pubNonces )
639+ if err != nil {
640+ return fmt .Errorf ("error aggregating pub nonces: %w" ,
641+ err )
642+ }
643+
644+ aggKey , _ , _ , err := musig2 .AggregateKeys (
645+ keys , true , musig2 .WithBIP86KeyTweak (),
646+ )
647+ if err != nil {
648+ return fmt .Errorf ("error aggregating keys: %w" , err )
649+ }
650+
651+ combinedNonce , err := computeSigningNonce (
652+ aggregateNonce , aggKey .FinalKey , sigHashMsg ,
653+ )
654+ if err != nil {
655+ return fmt .Errorf ("error computing signing nonce: %w" ,
656+ err )
657+ }
658+
659+ combineOpt := musig2 .WithBip86TweakedCombine (
660+ sigHashMsg , keys , true ,
661+ )
662+ schnorrSig := musig2 .CombineSigs (
663+ combinedNonce , partialSigs , combineOpt ,
664+ )
665+
666+ serializedWitness , err = writeWitness (schnorrSig .Serialize ())
667+
580668 default :
581669 return ErrInvalidPsbtFormat
582670 }
@@ -595,3 +683,57 @@ func finalizeTaprootInput(p *Packet, inIndex int) error {
595683 p .Inputs [inIndex ] = * newInput
596684 return nil
597685}
686+
687+ // computeSigningNonce calculates the final nonce used for signing. This will
688+ // be the R value used in the final signature.
689+ func computeSigningNonce (combinedNonce [musig2 .PubNonceSize ]byte ,
690+ combinedKey * btcec.PublicKey , msg [32 ]byte ) (* btcec.PublicKey , error ) {
691+
692+ // Next we'll compute the value b, that blinds our second public
693+ // nonce:
694+ // * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
695+ var (
696+ nonceMsgBuf bytes.Buffer
697+ nonceBlinder btcec.ModNScalar
698+ )
699+ nonceMsgBuf .Write (combinedNonce [:])
700+ nonceMsgBuf .Write (schnorr .SerializePubKey (combinedKey ))
701+ nonceMsgBuf .Write (msg [:])
702+ nonceBlindHash := chainhash .TaggedHash (
703+ musig2 .NonceBlindTag , nonceMsgBuf .Bytes (),
704+ )
705+ nonceBlinder .SetByteSlice (nonceBlindHash [:])
706+
707+ // Next, we'll parse the public nonces into R1 and R2.
708+ r1J , err := btcec .ParseJacobian (
709+ combinedNonce [:btcec .PubKeyBytesLenCompressed ],
710+ )
711+ if err != nil {
712+ return nil , err
713+ }
714+ r2J , err := btcec .ParseJacobian (
715+ combinedNonce [btcec .PubKeyBytesLenCompressed :],
716+ )
717+ if err != nil {
718+ return nil , err
719+ }
720+
721+ // With our nonce blinding value, we'll now combine both the public
722+ // nonces, using the blinding factor to tweak the second nonce:
723+ // * R = R_1 + b*R_2
724+ var nonce btcec.JacobianPoint
725+ btcec .ScalarMultNonConst (& nonceBlinder , & r2J , & r2J )
726+ btcec .AddNonConst (& r1J , & r2J , & nonce )
727+
728+ // If the combined nonce is the point at infinity, we'll use the
729+ // generator point instead.
730+ var infinityPoint btcec.JacobianPoint
731+ if nonce == infinityPoint {
732+ G := btcec .Generator ()
733+ G .AsJacobian (& nonce )
734+ }
735+
736+ nonce .ToAffine ()
737+
738+ return btcec .NewPublicKey (& nonce .X , & nonce .Y ), nil
739+ }
0 commit comments