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 PubNonce
s 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
impl FirstRound
sourcepub fn new(
key_agg_ctx: KeyAggContext,
nonce_seed: impl Into<NonceSeed>,
signer_index: usize,
spices: SecNonceSpices<'_>
) -> Result<FirstRound, SignerIndexError>
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.
sourcepub fn our_public_nonce(&self) -> PubNonce
pub fn our_public_nonce(&self) -> PubNonce
Returns the public nonce which should be shared with other signers.
sourcepub fn holdouts(&self) -> &[usize]
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
sourcepub fn receive_nonce(
&mut self,
signer_index: usize,
pubnonce: PubNonce
) -> Result<(), RoundContributionError>
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.
sourcepub fn is_complete(&self) -> bool
pub fn is_complete(&self) -> bool
Returns true once all public nonces have been received from every signer.
sourcepub fn finalize<M>(
self,
seckey: impl Into<Scalar>,
message: M
) -> Result<SecondRound<M>, RoundFinalizeError>
pub fn finalize<M>( self, seckey: impl Into<Scalar>, message: M ) -> Result<SecondRound<M>, RoundFinalizeError>
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.
sourcepub fn finalize_adaptor<M>(
self,
seckey: impl Into<Scalar>,
adaptor_point: impl Into<MaybePoint>,
message: M
) -> Result<SecondRound<M>, RoundFinalizeError>
pub fn finalize_adaptor<M>( self, seckey: impl Into<Scalar>, adaptor_point: impl Into<MaybePoint>, message: M ) -> Result<SecondRound<M>, RoundFinalizeError>
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.
sourcepub fn sign_for_aggregator<T>(
self,
seckey: impl Into<Scalar>,
message: impl AsRef<[u8]>,
aggregated_nonce: &AggNonce
) -> Result<T, SigningError>where
T: From<PartialSignature>,
pub fn sign_for_aggregator<T>(
self,
seckey: impl Into<Scalar>,
message: impl AsRef<[u8]>,
aggregated_nonce: &AggNonce
) -> Result<T, SigningError>where
T: From<PartialSignature>,
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 offinalize
to consume theFirstRound
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.
sourcepub 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>where
T: From<PartialSignature>,
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>where
T: From<PartialSignature>,
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 offinalize_adaptor
to consume theFirstRound
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