@@ -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"
@@ -47,11 +50,19 @@ func isFinalizableWitnessInput(pInput *PInput) bool {
4750
4851 case txscript .IsPayToTaproot (pkScript ):
4952 if pInput .TaprootKeySpendSig == nil &&
50- pInput .TaprootScriptSpendSig == nil {
53+ pInput .TaprootScriptSpendSig == nil &&
54+ pInput .MuSig2PartialSigs == nil {
5155
5256 return false
5357 }
5458
59+ // For each participant, we need a corresponding
60+ // MuSig2 partial signature.
61+ if len (pInput .MuSig2PartialSigs ) > 0 {
62+ return len (pInput .MuSig2PartialSigs ) ==
63+ len (pInput .MuSig2PubNonces )
64+ }
65+
5566 // For each of the script spend signatures we need a
5667 // corresponding tap script leaf with the control block.
5768 for _ , sig := range pInput .TaprootScriptSpendSig {
@@ -133,7 +144,8 @@ func isFinalizable(p *Packet, inIndex int) bool {
133144
134145 // The input cannot be finalized without any signatures.
135146 if pInput .PartialSigs == nil && pInput .TaprootKeySpendSig == nil &&
136- pInput .TaprootScriptSpendSig == nil {
147+ pInput .TaprootScriptSpendSig == nil &&
148+ pInput .MuSig2PartialSigs == nil {
137149
138150 return false
139151 }
@@ -577,6 +589,91 @@ func finalizeTaprootInput(p *Packet, inIndex int) error {
577589
578590 serializedWitness , err = writeWitness (witnessStack ... )
579591
592+ // MuSig2 spend path.
593+ case len (pInput .MuSig2PartialSigs ) > 0 :
594+ if len (pInput .MuSig2PubNonces ) !=
595+ len (pInput .MuSig2PartialSigs ) {
596+
597+ return fmt .Errorf ("number of MuSig2 pub nonces " +
598+ "does not match number of partial signatures" )
599+ }
600+
601+ // We'll need to combine MuSig2 partial signatures into a single
602+ // one, which requires the message that was signed over.
603+ firstSig := pInput .MuSig2PartialSigs [0 ]
604+
605+ // We don't (yet) support signing over a tap leaf hash.
606+ // TODO(guggero): Add support for signing over a tap leaf hash.
607+ if len (firstSig .TapLeafHash ) > 0 {
608+ return fmt .Errorf ("combining partial MuSig2 " +
609+ "signatures for a tap leaf is not supported" )
610+ }
611+
612+ prevOutFetcher := PrevOutputFetcher (p )
613+ sigHashes := txscript .NewTxSigHashes (
614+ p .UnsignedTx , prevOutFetcher ,
615+ )
616+ sigHash , err := txscript .CalcTaprootSignatureHash (
617+ sigHashes , pInput .SighashType , p .UnsignedTx ,
618+ inIndex , prevOutFetcher ,
619+ )
620+ if err != nil {
621+ return fmt .Errorf ("error calculating signature hash: " +
622+ "%w" , err )
623+ }
624+
625+ var sigHashMsg [32 ]byte
626+ copy (sigHashMsg [:], sigHash )
627+
628+ var (
629+ pubNonces = make (
630+ [][musig2 .PubNonceSize ]byte ,
631+ len (pInput .MuSig2PubNonces ),
632+ )
633+ keys = make (
634+ []* btcec.PublicKey , len (pInput .MuSig2PubNonces ),
635+ )
636+ partialSigs = make (
637+ []* musig2.PartialSignature ,
638+ len (pInput .MuSig2PartialSigs ),
639+ )
640+ )
641+ for i , pubNonce := range pInput .MuSig2PubNonces {
642+ copy (pubNonces [i ][:], pubNonce .PubNonce [:])
643+ keys [i ] = pubNonce .PubKey
644+
645+ partialSigs [i ] = & pInput .MuSig2PartialSigs [i ].PartialSig
646+ }
647+ aggregateNonce , err := musig2 .AggregateNonces (pubNonces )
648+ if err != nil {
649+ return fmt .Errorf ("error aggregating pub nonces: %w" ,
650+ err )
651+ }
652+
653+ aggKey , _ , _ , err := musig2 .AggregateKeys (
654+ keys , true , musig2 .WithBIP86KeyTweak (),
655+ )
656+ if err != nil {
657+ return fmt .Errorf ("error aggregating keys: %w" , err )
658+ }
659+
660+ combinedNonce , err := computeSigningNonce (
661+ aggregateNonce , aggKey .FinalKey , sigHashMsg ,
662+ )
663+ if err != nil {
664+ return fmt .Errorf ("error computing signing nonce: %w" ,
665+ err )
666+ }
667+
668+ combineOpt := musig2 .WithBip86TweakedCombine (
669+ sigHashMsg , keys , true ,
670+ )
671+ schnorrSig := musig2 .CombineSigs (
672+ combinedNonce , partialSigs , combineOpt ,
673+ )
674+
675+ serializedWitness , err = writeWitness (schnorrSig .Serialize ())
676+
580677 default :
581678 return ErrInvalidPsbtFormat
582679 }
@@ -595,3 +692,57 @@ func finalizeTaprootInput(p *Packet, inIndex int) error {
595692 p .Inputs [inIndex ] = * newInput
596693 return nil
597694}
695+
696+ // computeSigningNonce calculates the final nonce used for signing. This will
697+ // be the R value used in the final signature.
698+ func computeSigningNonce (combinedNonce [musig2 .PubNonceSize ]byte ,
699+ combinedKey * btcec.PublicKey , msg [32 ]byte ) (* btcec.PublicKey , error ) {
700+
701+ // Next we'll compute the value b, that blinds our second public
702+ // nonce:
703+ // * b = h(tag=NonceBlindTag, combinedNonce || combinedKey || m).
704+ var (
705+ nonceMsgBuf bytes.Buffer
706+ nonceBlinder btcec.ModNScalar
707+ )
708+ nonceMsgBuf .Write (combinedNonce [:])
709+ nonceMsgBuf .Write (schnorr .SerializePubKey (combinedKey ))
710+ nonceMsgBuf .Write (msg [:])
711+ nonceBlindHash := chainhash .TaggedHash (
712+ musig2 .NonceBlindTag , nonceMsgBuf .Bytes (),
713+ )
714+ nonceBlinder .SetByteSlice (nonceBlindHash [:])
715+
716+ // Next, we'll parse the public nonces into R1 and R2.
717+ r1J , err := btcec .ParseJacobian (
718+ combinedNonce [:btcec .PubKeyBytesLenCompressed ],
719+ )
720+ if err != nil {
721+ return nil , err
722+ }
723+ r2J , err := btcec .ParseJacobian (
724+ combinedNonce [btcec .PubKeyBytesLenCompressed :],
725+ )
726+ if err != nil {
727+ return nil , err
728+ }
729+
730+ // With our nonce blinding value, we'll now combine both the public
731+ // nonces, using the blinding factor to tweak the second nonce:
732+ // * R = R_1 + b*R_2
733+ var nonce btcec.JacobianPoint
734+ btcec .ScalarMultNonConst (& nonceBlinder , & r2J , & r2J )
735+ btcec .AddNonConst (& r1J , & r2J , & nonce )
736+
737+ // If the combined nonce is the point at infinity, we'll use the
738+ // generator point instead.
739+ var infinityPoint btcec.JacobianPoint
740+ if nonce == infinityPoint {
741+ G := btcec .Generator ()
742+ G .AsJacobian (& nonce )
743+ }
744+
745+ nonce .ToAffine ()
746+
747+ return btcec .NewPublicKey (& nonce .X , & nonce .Y ), nil
748+ }
0 commit comments