Struct musig2::FirstRound

source ·
pub struct FirstRound { /* private fields */ }
Expand description

A state machine which manages the first round of a MuSig2 signing session.

Its task is to collect PubNonces one by one until all signers have provided one, at which point a partial signature can be created on a message using an internally cached SecNonce.

By preventing cloning or copying, and by consuming itself after creating a partial signature, FirstRound’s API is written to encourage that a SecNonce should never be reused. Take care not to shoot yourself in the foot by attempting to work around this restriction.

Implementations§

source§

impl FirstRound

source

pub fn new( key_agg_ctx: KeyAggContext, nonce_seed: impl Into<NonceSeed>, signer_index: usize, spices: SecNonceSpices<'_> ) -> Result<FirstRound, SignerIndexError>

Start the first round of a MuSig2 signing session.

Generates the nonce using the given random seed value, which can be any type that converts to NonceSeed. Usually this would either be a [u8; 32] or any type that implements rand::RngCore and rand::CryptoRng, such as rand::rngs::OsRng. If a static byte array is used as the seed, it should be generated using a cryptographically secure RNG and discarded after the FirstRound is created. Prefer using a rand::CryptoRng if possible, so that there is no possibility of reusing the same nonce seed in a new signing session.

Returns an error if the given signer index is out of range.

source

pub fn our_public_nonce(&self) -> PubNonce

Returns the public nonce which should be shared with other signers.

source

pub fn holdouts(&self) -> &[usize]

Returns a slice of all signer indexes who we have yet to receive a PubNonce from. Note that since our nonce is generated and cached internally, this slice will never contain the signer index provided to FirstRound::new

source

pub fn receive_nonce( &mut self, signer_index: usize, pubnonce: PubNonce ) -> Result<(), RoundContributionError>

Adds a PubNonce to the internal state, registering it to a specific signer at a given index. Returns an error if the signer index is out of range, or if we already have a different nonce on-file for that signer.

source

pub fn is_complete(&self) -> bool

Returns true once all public nonces have been received from every signer.

source

pub fn finalize<M>( self, seckey: impl Into<Scalar>, message: M ) -> Result<SecondRound<M>, RoundFinalizeError>
where M: AsRef<[u8]>,

Finishes the first round once all nonces are received, combining nonces into an aggregated nonce, and creating a partial signature using seckey on a given message, both of which are stored in the returned SecondRound.

See SecondRound::aggregated_nonce to access the aggregated nonce, and SecondRound::our_signature to access the partial signature.

This method intentionally consumes the FirstRound, to avoid accidentally reusing a secret-nonce.

This method should only be invoked once is_complete returns true, otherwise it will fail. Can also return an error if partial signing fails, probably because the wrong secret key was given.

For all partial signatures to be valid, everyone must naturally be signing the same message.

This method is effectively the same as invoking finalize_adaptor, but passing MaybePoint::Infinity as the adaptor point.

source

pub fn finalize_adaptor<M>( self, seckey: impl Into<Scalar>, adaptor_point: impl Into<MaybePoint>, message: M ) -> Result<SecondRound<M>, RoundFinalizeError>
where M: AsRef<[u8]>,

Finishes the first round once all nonces are received, combining nonces into an aggregated nonce, and creating a partial adaptor signature using seckey on a given message, both of which are stored in the returned SecondRound.

The adaptor_point is used to verifiably encrypt the partial signature, so that the final aggregated signature will need to be adapted with the discrete log of adaptor_point before the signature can be considered valid. All signers must agree on and use the same adaptor point for the final signature to be valid.

See SecondRound::aggregated_nonce to access the aggregated nonce, and SecondRound::our_signature to access the partial signature.

This method intentionally consumes the FirstRound, to avoid accidentally reusing a secret-nonce.

This method should only be invoked once is_complete returns true, otherwise it will fail. Can also return an error if partial signing fails, probably because the wrong secret key was given.

For all partial signatures to be valid, everyone must naturally be signing the same message.

source

pub fn sign_for_aggregator<T>( self, seckey: impl Into<Scalar>, message: impl AsRef<[u8]>, aggregated_nonce: &AggNonce ) -> Result<T, SigningError>

As an alternative to collecting nonces and partial signatures one-by-one from everyone in the group, signers can opt instead to nominate an aggregator node whose duty is to collect nonces and signatures from all other signers, and then broadcast the aggregated signature once they receive all partial signatures. Doing this dramatically decreases the number of network round-trips required for large groups of signers, and doesn’t require any trust in the aggregator node beyond the possibility that they may refuse to reveal the final signature.

To use this API with a single aggregator node:

  • Instantiate the FirstRound.
  • Send the output of FirstRound::our_public_nonce to the aggregator.
  • The aggregator node should reply with an AggNonce.
  • Once you receive the aggregated nonce, use FirstRound::sign_for_aggregator instead of finalize to consume the FirstRound and return a partial signature.
  • Send this partial signature to the aggregator.
  • The aggregator (if they are honest) will reply with the aggregated Schnorr signature, which can be verified with verify_single

See the top-level crate documentation for an example.

Invoking this method is essentially the same as invoking sign_for_aggregator_adaptor, but passing MaybePoint::Infinity as the adaptor point.

source

pub fn sign_for_aggregator_adaptor<T>( self, seckey: impl Into<Scalar>, adaptor_point: impl Into<MaybePoint>, message: impl AsRef<[u8]>, aggregated_nonce: &AggNonce ) -> Result<T, SigningError>

As an alternative to collecting nonces and partial signatures one-by-one from everyone in the group, signers can opt instead to nominate an aggregator node whose duty is to collect nonces and signatures from all other signers, and then broadcast the aggregated signature once they receive all partial signatures. Doing this dramatically decreases the number of network round-trips required for large groups of signers, and doesn’t require any trust in the aggregator node beyond the possibility that they may refuse to reveal the final signature.

To use this API with a single aggregator node:

  • The group must agree on an adaptor_point which will be used to encrypt signatures.
  • Instantiate the FirstRound.
  • Send the output of FirstRound::our_public_nonce to the aggregator.
  • The aggregator node should reply with an AggNonce.
  • Once you receive the aggregated nonce, use FirstRound::sign_for_aggregator_adaptor instead of finalize_adaptor to consume the FirstRound and return a partial signature.
  • Send this partial signature to the aggregator.
  • The aggregator (if they are honest) will reply with the aggregated Schnorr signature, which can be verified with adaptor::verify_single

See the top-level crate documentation for an example.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

source§

fn vzip(self) -> V