Skip to content

Commit 3099b54

Browse files
committed
musig2: add combinedNonce getter
1 parent 824e867 commit 3099b54

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

btcec/schnorr/musig2/context.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,20 @@ func (s *Session) RegisterPubNonce(nonce [PubNonceSize]byte) (bool, error) {
548548
return haveAllNonces, nil
549549
}
550550

551+
// CombinedNonce returns the combined public nonce for the signing session.
552+
// This will be available after either:
553+
// - All individual nonces have been registered via RegisterPubNonce, or
554+
// - A combined nonce has been registered via RegisterCombinedNonce
555+
//
556+
// If the combined nonce is not yet available, this method returns an error.
557+
func (s *Session) CombinedNonce() ([PubNonceSize]byte, error) {
558+
if s.combinedNonce == nil {
559+
return [PubNonceSize]byte{}, ErrCombinedNonceUnavailable
560+
}
561+
562+
return *s.combinedNonce, nil
563+
}
564+
551565
// RegisterCombinedNonce allows a caller to directly register a combined nonce
552566
// that was generated externally. This is useful in coordinator-based
553567
// protocols where the coordinator aggregates all nonces and distributes the

btcec/schnorr/musig2/musig2_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,4 +746,84 @@ func TestSigningWithAggregatedNonce(t *testing.T) {
746746
t.Fatalf("final signature is invalid")
747747
}
748748
})
749+
750+
t.Run("get combined nonce after RegisterCombinedNonce", func(t *testing.T) {
751+
privKey, _ := btcec.NewPrivateKey()
752+
privKey2, _ := btcec.NewPrivateKey()
753+
signSet := []*btcec.PublicKey{privKey.PubKey(), privKey2.PubKey()}
754+
755+
signCtx, _ := NewContext(privKey, false, WithKnownSigners(signSet))
756+
session, _ := signCtx.NewSession()
757+
758+
// Should fail before registering combined nonce.
759+
_, err := session.CombinedNonce()
760+
if err != ErrCombinedNonceUnavailable {
761+
t.Fatalf("expected ErrCombinedNonceUnavailable, got: %v", err)
762+
}
763+
764+
// Register combined nonce.
765+
expectedNonce := [PubNonceSize]byte{1, 2, 3}
766+
err = session.RegisterCombinedNonce(expectedNonce)
767+
if err != nil {
768+
t.Fatalf("RegisterCombinedNonce failed: %v", err)
769+
}
770+
771+
// Should succeed after registering.
772+
gotNonce, err := session.CombinedNonce()
773+
if err != nil {
774+
t.Fatalf("CombinedNonce failed: %v", err)
775+
}
776+
777+
if gotNonce != expectedNonce {
778+
t.Fatalf("expected nonce %x, got %x", expectedNonce, gotNonce)
779+
}
780+
})
781+
782+
t.Run("get combined nonce after RegisterPubNonce", func(t *testing.T) {
783+
const numSigners = 3
784+
785+
signerKeys := make([]*btcec.PrivateKey, numSigners)
786+
signSet := make([]*btcec.PublicKey, numSigners)
787+
for i := 0; i < numSigners; i++ {
788+
privKey, _ := btcec.NewPrivateKey()
789+
signerKeys[i] = privKey
790+
signSet[i] = privKey.PubKey()
791+
}
792+
793+
sessions := make([]*Session, numSigners)
794+
for i, signerKey := range signerKeys {
795+
signCtx, _ := NewContext(signerKey, false, WithKnownSigners(signSet))
796+
session, _ := signCtx.NewSession()
797+
sessions[i] = session
798+
}
799+
800+
pubNonces := make([][PubNonceSize]byte, numSigners)
801+
for i, session := range sessions {
802+
pubNonces[i] = session.PublicNonce()
803+
}
804+
805+
// Should fail before all nonces are registered.
806+
_, err := sessions[0].CombinedNonce()
807+
if err != ErrCombinedNonceUnavailable {
808+
t.Fatalf("expected ErrCombinedNonceUnavailable before all nonces, got: %v", err)
809+
}
810+
811+
// Register all nonces via RegisterPubNonce.
812+
for i := 1; i < numSigners; i++ {
813+
sessions[0].RegisterPubNonce(pubNonces[i])
814+
}
815+
816+
// Should succeed after all nonces are registered.
817+
gotNonce, err := sessions[0].CombinedNonce()
818+
if err != nil {
819+
t.Fatalf("CombinedNonce failed: %v", err)
820+
}
821+
822+
// Verify it matches what AggregateNonces produces.
823+
expectedNonce, _ := AggregateNonces(pubNonces)
824+
if gotNonce != expectedNonce {
825+
t.Fatalf("combined nonce mismatch: expected %x, got %x",
826+
expectedNonce[:8], gotNonce[:8])
827+
}
828+
})
749829
}

0 commit comments

Comments
 (0)