5959 // ErrNotEnoughSigners is returned if a caller attempts to obtain an
6060 // early nonce when it wasn't specified
6161 ErrNoEarlyNonce = fmt .Errorf ("no early nonce available" )
62+
63+ // ErrCombinedNonceAfterPubNonces is returned if RegisterCombinedNonce
64+ // is called after public nonces have already been registered.
65+ ErrCombinedNonceAfterPubNonces = fmt .Errorf ("can't register combined " +
66+ "nonce after public nonces" )
6267)
6368
6469// Context is a managed signing context for musig2. It takes care of things
@@ -525,7 +530,7 @@ func (s *Session) RegisterPubNonce(nonce [PubNonceSize]byte) (bool, error) {
525530 // If we already have all the nonces, then this method was called too
526531 // many times.
527532 haveAllNonces := len (s .pubNonces ) == s .ctx .opts .numSigners
528- if haveAllNonces {
533+ if haveAllNonces || s . combinedNonce != nil {
529534 return false , ErrAlredyHaveAllNonces
530535 }
531536
@@ -548,6 +553,57 @@ func (s *Session) RegisterPubNonce(nonce [PubNonceSize]byte) (bool, error) {
548553 return haveAllNonces , nil
549554}
550555
556+ // CombinedNonce returns the combined public nonce for the signing session.
557+ // This will be available after either:
558+ // - All individual nonces have been registered via RegisterPubNonce, or
559+ // - A combined nonce has been registered via RegisterCombinedNonce
560+ //
561+ // If the combined nonce is not yet available, this method returns an error.
562+ func (s * Session ) CombinedNonce () ([PubNonceSize ]byte , error ) {
563+ if s .combinedNonce == nil {
564+ return [PubNonceSize ]byte {}, ErrCombinedNonceUnavailable
565+ }
566+
567+ return * s .combinedNonce , nil
568+ }
569+
570+ // RegisterCombinedNonce allows a caller to directly register a combined nonce
571+ // that was generated externally. This is useful in coordinator-based
572+ // protocols where the coordinator aggregates all nonces and distributes the
573+ // combined nonce to participants, rather than each participant aggregating
574+ // nonces themselves.
575+ func (s * Session ) RegisterCombinedNonce (
576+ combinedNonce [PubNonceSize ]byte ) error {
577+
578+ // If we already have a combined nonce, then this method was called too
579+ // many times.
580+ if s .combinedNonce != nil {
581+ return ErrAlredyHaveAllNonces
582+ }
583+
584+ // We also don't allow this method to be called if we already registered
585+ // some public nonces.
586+ if len (s .pubNonces ) > 1 {
587+ return ErrCombinedNonceAfterPubNonces
588+ }
589+
590+ // We'll now try to parse the combined nonce into it's two points to
591+ // ensure it's valid.
592+ _ , err := btcec .ParsePubKey (combinedNonce [:33 ])
593+ if err != nil {
594+ return fmt .Errorf ("invalid combined nonce: %w" , err )
595+ }
596+ _ , err = btcec .ParsePubKey (combinedNonce [33 :])
597+ if err != nil {
598+ return fmt .Errorf ("invalid combined nonce: %w" , err )
599+ }
600+
601+ // Otherwise, we'll just set the combined nonce directly.
602+ s .combinedNonce = & combinedNonce
603+
604+ return nil
605+ }
606+
551607// Sign generates a partial signature for the target message, using the target
552608// context. If this method is called more than once per context, then an error
553609// is returned, as that means a nonce was re-used.
0 commit comments