Skip to main content

frost_dkg/
participant.rs

1mod round1;
2mod round2;
3mod round3;
4
5use super::*;
6use elliptic_curve::group::GroupEncoding;
7use elliptic_curve::subtle::ConditionallySelectable;
8use elliptic_curve::{Field, Group};
9use elliptic_curve_tools::{SumOfProducts, group, prime_field, prime_field_vec};
10use rand_core::CryptoRng;
11use serde::{Deserialize, Serialize};
12use std::collections::HashSet;
13use std::fmt::{self, Debug, Formatter};
14use std::marker::PhantomData;
15use vsss_rs::{
16    DefaultShare, IdentifierPrimeField, ShareElement, ShareVerifierGroup, ValueGroup,
17    ValuePrimeField, subtle::ConstantTimeEq,
18};
19
20/// A participant that contributes a secret.
21pub type SecretParticipant<G> = Participant<SecretParticipantImpl<G>, G>;
22
23/// A participant that refreshes an existing sharing.
24pub type RefreshParticipant<G> = Participant<RefreshParticipantImpl<G>, G>;
25
26/// The inner share representation.
27pub type SecretShare<F> = DefaultShare<IdentifierPrimeField<F>, IdentifierPrimeField<F>>;
28
29/// The inner Feldman share verifiers.
30pub type FeldmanShareVerifier<G> = ShareVerifierGroup<G>;
31
32/// A validated set of participant identifiers used to reconstruct a secret.
33#[derive(Copy, Clone, Debug)]
34pub struct ReconstructionSet<'a, F: ScalarHash> {
35    identifiers: &'a [IdentifierPrimeField<F>],
36}
37
38impl<'a, F: ScalarHash> ReconstructionSet<'a, F> {
39    /// Validate participant identifiers for secret reconstruction.
40    pub fn new(identifiers: &'a [IdentifierPrimeField<F>]) -> DkgResult<Self> {
41        if identifiers.len() < 2 {
42            return Err(Error::Initialization(
43                "A reconstruction set requires at least 2 participant identifiers".to_string(),
44            ));
45        }
46        if identifiers.iter().any(|id| bool::from(id.is_zero())) {
47            return Err(Error::Initialization(
48                "Reconstruction participant identifiers cannot be zero".to_string(),
49            ));
50        }
51        let unique_identifiers = identifiers.iter().copied().collect::<HashSet<_>>();
52        if unique_identifiers.len() != identifiers.len() {
53            return Err(Error::Initialization(
54                "Reconstruction participant identifiers must be unique".to_string(),
55            ));
56        }
57        Ok(Self { identifiers })
58    }
59
60    /// The validated participant identifiers.
61    pub fn identifiers(&self) -> &[IdentifierPrimeField<F>] {
62        self.identifiers
63    }
64
65    fn contains(&self, identifier: &IdentifierPrimeField<F>) -> bool {
66        self.identifiers.contains(identifier)
67    }
68}
69
70/// The participant implementation.
71pub trait ParticipantImpl<G>
72where
73    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
74    G::Scalar: ScalarHash,
75{
76    /// Get the participant type.
77    fn get_type(&self) -> ParticipantType;
78    /// Get the participant's secret.
79    fn random_value(rng: impl CryptoRng) -> G::Scalar;
80    /// Check the Feldman verifier at position 0.
81    ///
82    /// During new key generation or an update, this value is not the identity;
83    /// during a refresh, it must be the identity.
84    fn check_feldman_verifier(verifier: G) -> bool;
85}
86
87/// A DKG participant finite-state machine.
88///
89/// This type contains secret key material. Its serialized representation is
90/// plaintext and must only be stored or transported using authenticated
91/// encryption or an equivalently protected mechanism. Deserializing
92/// unauthenticated participant state can violate protocol invariants.
93#[derive(Serialize, Deserialize)]
94pub struct Participant<I, G>
95where
96    I: ParticipantImpl<G> + Default,
97    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
98    G::Scalar: ScalarHash,
99{
100    pub(crate) ordinal: usize,
101    #[serde(bound(
102        serialize = "IdentifierPrimeField<G::Scalar>: Serialize",
103        deserialize = "IdentifierPrimeField<G::Scalar>: Deserialize<'de>"
104    ))]
105    pub(crate) id: IdentifierPrimeField<G::Scalar>,
106    pub(crate) threshold: usize,
107    pub(crate) limit: usize,
108    pub(crate) round: Round,
109    pub(crate) completed: bool,
110    #[serde(bound(
111        serialize = "SecretShare<G::Scalar>: Serialize",
112        deserialize = "SecretShare<G::Scalar>: Deserialize<'de>"
113    ))]
114    pub(crate) secret_shares: Vec<SecretShare<G::Scalar>>,
115    #[serde(bound(
116        serialize = "ValueGroup<G>: Serialize",
117        deserialize = "ValueGroup<G>: Deserialize<'de>"
118    ))]
119    pub(crate) feldman_verifiers: Vec<ValueGroup<G>>,
120    #[serde(with = "prime_field")]
121    pub(crate) original_secret: G::Scalar,
122    #[serde(with = "group")]
123    pub(crate) verifying_share: G,
124    #[serde(bound(
125        serialize = "SecretShare<G::Scalar>: Serialize",
126        deserialize = "SecretShare<G::Scalar>: Deserialize<'de>"
127    ))]
128    pub(crate) secret_share: SecretShare<G::Scalar>,
129    #[serde(with = "group")]
130    pub(crate) message_generator: G,
131    #[serde(bound(
132        serialize = "ValueGroup<G>: Serialize",
133        deserialize = "ValueGroup<G>: Deserialize<'de>"
134    ))]
135    pub(crate) public_key: ValueGroup<G>,
136    #[serde(with = "prime_field_vec")]
137    pub(crate) powers_of_i: Vec<G::Scalar>,
138    #[serde(bound(
139        serialize = "Round1Data<G>: Serialize",
140        deserialize = "Round1Data<G>: Deserialize<'de>"
141    ))]
142    pub(crate) received_round1_data: Vec<Option<Round1Data<G>>>,
143    #[serde(bound(
144        serialize = "Round2Data<G::Scalar>: Serialize",
145        deserialize = "Round2Data<G::Scalar>: Deserialize<'de>"
146    ))]
147    pub(crate) received_round2_data: Vec<Option<Round2Data<G::Scalar>>>,
148    #[serde(bound(
149        serialize = "IdentifierPrimeField<G::Scalar>: Serialize",
150        deserialize = "IdentifierPrimeField<G::Scalar>: Deserialize<'de>"
151    ))]
152    pub(crate) all_participant_ids: Vec<IdentifierPrimeField<G::Scalar>>,
153    #[serde(bound(
154        serialize = "IdentifierPrimeField<G::Scalar>: Serialize",
155        deserialize = "IdentifierPrimeField<G::Scalar>: Deserialize<'de>"
156    ))]
157    pub(crate) valid_participant_ids: Vec<Option<IdentifierPrimeField<G::Scalar>>>,
158    pub(crate) participant_impl: I,
159}
160
161impl<I, G> Debug for Participant<I, G>
162where
163    I: ParticipantImpl<G> + Default,
164    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
165    G::Scalar: ScalarHash,
166{
167    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
168        f.debug_struct("Participant")
169            .field("ordinal", &self.ordinal)
170            .field("id", &self.id)
171            .field("threshold", &self.threshold)
172            .field("limit", &self.limit)
173            .field("round", &self.round)
174            .field("completed", &self.completed)
175            .field("feldman_verifiers", &self.feldman_verifiers)
176            .field("public_key", &self.public_key)
177            .field("powers_of_i", &self.powers_of_i)
178            .finish()
179    }
180}
181
182impl<G> Participant<SecretParticipantImpl<G>, G>
183where
184    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
185    G::Scalar: ScalarHash,
186{
187    /// Create a participant that generates a new key share.
188    pub fn new_secret(
189        id: IdentifierPrimeField<G::Scalar>,
190        parameters: &Parameters<G>,
191    ) -> DkgResult<Self> {
192        let rng = rand::rng();
193        let secret = SecretParticipantImpl::<G>::random_value(rng);
194        Self::initialize(id, parameters, IdentifierPrimeField(secret), None)
195    }
196
197    /// Create a participant with an existing secret share.
198    ///
199    /// This updates the polynomial instead of only refreshing the shares.
200    pub fn with_secret(
201        new_identifier: IdentifierPrimeField<G::Scalar>,
202        old_share: &SecretShare<G::Scalar>,
203        parameters: &Parameters<G>,
204        reconstruction_set: &ReconstructionSet<'_, G::Scalar>,
205    ) -> DkgResult<Self> {
206        if !reconstruction_set.contains(&old_share.identifier) {
207            return Err(Error::Initialization(
208                "The old share is not included in the reconstruction set".to_string(),
209            ));
210        }
211        let secret = *old_share.value * *Self::lagrange(old_share, reconstruction_set);
212        Self::initialize(
213            new_identifier,
214            parameters,
215            IdentifierPrimeField(secret),
216            None,
217        )
218    }
219}
220
221impl<G> Participant<RefreshParticipantImpl<G>, G>
222where
223    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
224    G::Scalar: ScalarHash,
225{
226    /// Create a participant that refreshes an existing key share, if supplied.
227    ///
228    /// If no share is supplied, other participants must possess valid shares of
229    /// the original secret.
230    pub fn new_refresh(
231        id: IdentifierPrimeField<G::Scalar>,
232        existing_share: Option<&SecretShare<G::Scalar>>,
233        parameters: &Parameters<G>,
234    ) -> DkgResult<Self> {
235        if existing_share.is_some_and(|share| share.identifier != id) {
236            return Err(Error::Initialization(
237                "The existing share identifier does not match the refresh participant".to_string(),
238            ));
239        }
240        let secret = existing_share
241            .map(|share| share.value.0)
242            .unwrap_or_else(|| G::Scalar::random(&mut rand::rng()));
243        Self::initialize(
244            id,
245            parameters,
246            IdentifierPrimeField(secret),
247            Some(parameters.message_generator * secret),
248        )
249    }
250}
251
252impl<I, G> Participant<I, G>
253where
254    I: ParticipantImpl<G> + Default,
255    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
256    G::Scalar: ScalarHash,
257{
258    fn initialize(
259        id: IdentifierPrimeField<G::Scalar>,
260        parameters: &Parameters<G>,
261        secret: ValuePrimeField<G::Scalar>,
262        verifying_share: Option<G>,
263    ) -> DkgResult<Self> {
264        let rng = rand::rng();
265
266        let mut powers_of_i = vec![G::Scalar::ONE; parameters.threshold];
267        powers_of_i[1] = *id;
268        for i in 2..parameters.threshold {
269            powers_of_i[i] = powers_of_i[i - 1] * *id;
270        }
271
272        let participant_type = I::default().get_type();
273        let secret_to_split = match participant_type {
274            ParticipantType::Secret => secret,
275            ParticipantType::Refresh => IdentifierPrimeField(G::Scalar::ZERO),
276        };
277
278        let (shares, verifiers) = vsss_rs::feldman::split_secret_with_participant_generators::<
279            SecretShare<G::Scalar>,
280            ShareVerifierGroup<G>,
281        >(
282            parameters.threshold,
283            parameters.limit,
284            &secret_to_split,
285            Some(ValueGroup(parameters.message_generator)),
286            rng,
287            &parameters.participant_number_generators,
288        )?;
289        let verifiers = verifiers.iter().skip(1).copied().collect::<Vec<_>>();
290
291        let verifying_share = match participant_type {
292            ParticipantType::Secret => verifiers[0].0,
293            ParticipantType::Refresh => verifying_share.ok_or(Error::Initialization(
294                "Verifying share is required for refresh".to_string(),
295            ))?,
296        };
297
298        if verifiers.iter().skip(1).any(|c| c.is_identity().into())
299            || !I::check_feldman_verifier(*verifiers[0])
300        {
301            return Err(Error::Initialization(
302                "Invalid Feldman verifier".to_string(),
303            ));
304        }
305
306        let ordinal = shares
307            .iter()
308            .position(|s| s.identifier == id)
309            .ok_or_else(|| {
310                Error::Initialization(format!(
311                    "Invalid participant ID '{id}'; it is not in the generated set of shares"
312                ))
313            })?;
314
315        let all_participant_ids = shares.iter().map(|share| share.identifier).collect();
316        Ok(Self {
317            ordinal,
318            id,
319            threshold: parameters.threshold,
320            limit: parameters.limit,
321            completed: false,
322            round: Round::One,
323            original_secret: secret.0,
324            verifying_share,
325            secret_shares: shares,
326            feldman_verifiers: verifiers,
327            secret_share: SecretShare::<G::Scalar>::default(),
328            message_generator: parameters.message_generator,
329            public_key: ValueGroup::<G>::identity(),
330            powers_of_i,
331            received_round1_data: std::iter::repeat_with(|| None)
332                .take(parameters.limit)
333                .collect(),
334            received_round2_data: std::iter::repeat_with(|| None)
335                .take(parameters.limit)
336                .collect(),
337            all_participant_ids,
338            valid_participant_ids: vec![None; parameters.limit],
339            participant_impl: Default::default(),
340        })
341    }
342
343    /// The ordinal index of this participant.
344    pub fn ordinal(&self) -> usize {
345        self.ordinal
346    }
347
348    /// The identifier associated with this participant.
349    pub fn id(&self) -> IdentifierPrimeField<G::Scalar> {
350        self.id
351    }
352
353    /// Return whether this participant has completed the protocol.
354    pub fn completed(&self) -> bool {
355        self.completed
356    }
357
358    /// Return the current round.
359    pub fn round(&self) -> Round {
360        self.round
361    }
362
363    /// Return the configured threshold.
364    pub fn threshold(&self) -> usize {
365        self.threshold
366    }
367
368    /// Return the configured participant limit.
369    pub fn limit(&self) -> usize {
370        self.limit
371    }
372
373    /// Computed secret share.
374    /// This value is unavailable until at least two rounds have run, so [`None`]
375    /// is returned until completion.
376    pub fn secret_share(&self) -> Option<SecretShare<G::Scalar>> {
377        if self.completed {
378            Some(self.secret_share)
379        } else {
380            None
381        }
382    }
383
384    /// Computed public key.
385    ///
386    /// This value is unavailable until every round has run, so [`None`] is
387    /// returned until completion.
388    pub fn public_key(&self) -> Option<G> {
389        if self.completed {
390            Some(*self.public_key)
391        } else {
392            None
393        }
394    }
395
396    /// Return all participants that started the protocol.
397    pub fn all_participant_ids(&self) -> &[IdentifierPrimeField<G::Scalar>] {
398        &self.all_participant_ids
399    }
400
401    /// Return the valid participant IDs.
402    pub fn valid_participant_ids(&self) -> &[Option<IdentifierPrimeField<G::Scalar>>] {
403        &self.valid_participant_ids
404    }
405
406    /// Return the Feldman verifiers.
407    pub fn feldman_verifiers(&self) -> &[ShareVerifierGroup<G>] {
408        &self.feldman_verifiers
409    }
410
411    /// Get the round 1 data received so far.
412    pub fn received_round1_data(&self) -> &[Option<Round1Data<G>>] {
413        &self.received_round1_data
414    }
415
416    /// Get the round 2 data received so far.
417    pub fn received_round2_data(&self) -> &[Option<Round2Data<G::Scalar>>] {
418        &self.received_round2_data
419    }
420
421    /// The verifying share used by this participant.
422    pub fn verifying_share(&self) -> G {
423        self.verifying_share
424    }
425
426    /// The final transcript hash over received protocol messages.
427    pub fn final_transcript_hash(&self) -> [u8; 32] {
428        get_final_transcript_hash(&self.received_round1_data, &self.received_round2_data)
429    }
430
431    /// Consume a completed participant and return its final DKG output.
432    ///
433    /// Consuming the participant avoids cloning its secret share.
434    pub fn into_output(self) -> DkgResult<DkgOutput<G>> {
435        if !self.completed {
436            return Err(Error::Round(
437                "Protocol is not complete; no output is available".to_string(),
438            ));
439        }
440
441        let transcript_hash =
442            get_final_transcript_hash(&self.received_round1_data, &self.received_round2_data);
443        Ok(DkgOutput {
444            secret_share: self.secret_share,
445            public_key: self.public_key.0,
446            feldman_verifiers: self.feldman_verifiers,
447            participant_ids: self.valid_participant_ids,
448            transcript_hash,
449        })
450    }
451
452    /// The ordinal index of this participant.
453    #[deprecated(since = "0.6.0", note = "use `ordinal` instead")]
454    pub fn get_ordinal(&self) -> usize {
455        self.ordinal()
456    }
457
458    /// The identifier associated with this participant.
459    #[deprecated(since = "0.6.0", note = "use `id` instead")]
460    pub fn get_id(&self) -> IdentifierPrimeField<G::Scalar> {
461        self.id()
462    }
463
464    /// Return the current round.
465    #[deprecated(since = "0.6.0", note = "use `round` instead")]
466    pub fn get_round(&self) -> Round {
467        self.round()
468    }
469
470    /// Return the configured threshold.
471    #[deprecated(since = "0.6.0", note = "use `threshold` instead")]
472    pub fn get_threshold(&self) -> usize {
473        self.threshold()
474    }
475
476    /// Return the configured participant limit.
477    #[deprecated(since = "0.6.0", note = "use `limit` instead")]
478    pub fn get_limit(&self) -> usize {
479        self.limit()
480    }
481
482    /// Computed secret share, if the protocol is complete.
483    #[deprecated(since = "0.6.0", note = "use `secret_share` instead")]
484    pub fn get_secret_share(&self) -> Option<SecretShare<G::Scalar>> {
485        self.secret_share()
486    }
487
488    /// Computed public key, if the protocol is complete.
489    #[deprecated(since = "0.6.0", note = "use `public_key` instead")]
490    pub fn get_public_key(&self) -> Option<G> {
491        self.public_key()
492    }
493
494    /// Return all participants that started the protocol.
495    #[deprecated(since = "0.6.0", note = "use `all_participant_ids` instead")]
496    pub fn get_all_participant_ids(&self) -> &[IdentifierPrimeField<G::Scalar>] {
497        self.all_participant_ids()
498    }
499
500    /// Return the valid participant IDs.
501    #[deprecated(since = "0.6.0", note = "use `valid_participant_ids` instead")]
502    pub fn get_valid_participant_ids(&self) -> &[Option<IdentifierPrimeField<G::Scalar>>] {
503        self.valid_participant_ids()
504    }
505
506    /// Return the Feldman verifiers.
507    #[deprecated(since = "0.6.0", note = "use `feldman_verifiers` instead")]
508    pub fn get_feldman_verifiers(&self) -> Vec<ShareVerifierGroup<G>> {
509        self.feldman_verifiers().to_vec()
510    }
511
512    /// Get the round 1 data received so far.
513    #[deprecated(since = "0.6.0", note = "use `received_round1_data` instead")]
514    pub fn get_received_round1_data(&self) -> &[Option<Round1Data<G>>] {
515        self.received_round1_data()
516    }
517
518    /// Get the round 2 data received so far.
519    #[deprecated(since = "0.6.0", note = "use `received_round2_data` instead")]
520    pub fn get_received_round2_data(&self) -> &[Option<Round2Data<G::Scalar>>] {
521        self.received_round2_data()
522    }
523
524    /// Receive data from another participant.
525    pub fn receive(&mut self, data: &[u8]) -> DkgResult<()> {
526        let (&round, payload) = data
527            .split_first()
528            .ok_or_else(|| Error::InvalidMessage("message is empty".to_string()))?;
529        let round = Round::try_from(round).map_err(Error::InvalidMessage)?;
530        match round {
531            Round::One => {
532                let round1_payload = postcard::from_bytes::<Round1Data<G>>(payload)?;
533                self.receive_round1data(round1_payload)
534            }
535            Round::Two => {
536                let round2_payload = postcard::from_bytes::<Round2Data<G::Scalar>>(payload)?;
537                self.receive_round2data(round2_payload)
538            }
539            _ => Err(Error::Round("Protocol is complete".to_string())),
540        }
541    }
542
543    /// Run the next step in the protocol.
544    pub fn run(&mut self) -> DkgResult<RoundOutputGenerator<G>> {
545        match self.round {
546            Round::One => self.round1(),
547            Round::Two => self.round2(),
548            Round::Three => self.round3(),
549            Round::Four => Err(Error::Round("Protocol is complete".to_string())),
550        }
551    }
552
553    /// Advance the protocol by one round.
554    ///
555    /// Protocol messages are serialized automatically and returned with only
556    /// the transport routing information callers need.
557    pub fn advance(&mut self) -> DkgResult<AdvanceResult<G::Scalar>> {
558        match self.run()? {
559            RoundOutputGenerator::Round3 => Ok(AdvanceResult::Complete),
560            output => Ok(AdvanceResult::Messages(output.into_messages()?)),
561        }
562    }
563
564    pub(crate) fn check_sending_participant_id(
565        &self,
566        round: Round,
567        sender_ordinal: usize,
568        sender_id: IdentifierPrimeField<G::Scalar>,
569    ) -> DkgResult<()> {
570        let id = self
571            .all_participant_ids
572            .get(sender_ordinal)
573            .ok_or_else(|| {
574                Error::Round(format!(
575                    "Round {round}: Unknown sender ordinal, {sender_ordinal}"
576                ))
577            })?;
578        if *id != sender_id {
579            return Err(Error::Round(format!(
580                "Round {round}: Sender id mismatch, expected '{id}', got '{sender_id}'"
581            )));
582        }
583        if sender_id.is_zero().into() {
584            return Err(Error::Round(format!("Round {round}: Sender id is zero")));
585        }
586        if self.id.ct_eq(&sender_id).into() {
587            return Err(Error::Round(format!(
588                "Round {round}: Sender id is equal to our id",
589            )));
590        }
591        Ok(())
592    }
593
594    pub(crate) fn lagrange(
595        share: &SecretShare<G::Scalar>,
596        reconstruction_set: &ReconstructionSet<'_, G::Scalar>,
597    ) -> ValuePrimeField<G::Scalar> {
598        let mut num = G::Scalar::ONE;
599        let mut den = G::Scalar::ONE;
600        for &x_j in reconstruction_set.identifiers() {
601            if x_j == share.identifier {
602                continue;
603            }
604            num *= *x_j;
605            den *= *x_j - *share.identifier;
606        }
607
608        // The validated reconstruction set contains unique identifiers,
609        // including the share identifier, so this denominator is nonzero.
610        let den_inverse = den.invert().unwrap_or(G::Scalar::ZERO);
611        IdentifierPrimeField(num * den_inverse)
612    }
613}
614
615/// The secret participant implementation.
616#[derive(Default, Clone, Debug, Serialize, Deserialize)]
617pub struct SecretParticipantImpl<G>(PhantomData<G>);
618
619impl<G> ParticipantImpl<G> for SecretParticipantImpl<G>
620where
621    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
622    G::Scalar: ScalarHash,
623{
624    fn get_type(&self) -> ParticipantType {
625        ParticipantType::Secret
626    }
627
628    fn random_value(mut rng: impl CryptoRng) -> <G as Group>::Scalar {
629        G::Scalar::random(&mut rng)
630    }
631
632    fn check_feldman_verifier(verifier: G) -> bool {
633        verifier.is_identity().unwrap_u8() == 0u8
634    }
635}
636
637/// The refresh participant implementation.
638#[derive(Default, Clone, Debug, Serialize, Deserialize)]
639pub struct RefreshParticipantImpl<G>(PhantomData<G>);
640
641impl<G> ParticipantImpl<G> for RefreshParticipantImpl<G>
642where
643    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
644    G::Scalar: ScalarHash,
645{
646    fn get_type(&self) -> ParticipantType {
647        ParticipantType::Refresh
648    }
649
650    fn random_value(_rng: impl CryptoRng) -> <G as Group>::Scalar {
651        G::Scalar::ZERO
652    }
653
654    fn check_feldman_verifier(verifier: G) -> bool {
655        verifier.is_identity().into()
656    }
657}
658
659/// A trait that enables dynamic dispatch over participants.
660pub trait AnyParticipant<G>: Send + Sync + Debug
661where
662    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
663    G::Scalar: ScalarHash,
664{
665    /// The ordinal index of this participant.
666    fn ordinal(&self) -> usize;
667    /// The identifier associated with this participant.
668    fn id(&self) -> IdentifierPrimeField<G::Scalar>;
669    /// The threshold.
670    fn threshold(&self) -> usize;
671    /// The participant limit.
672    fn limit(&self) -> usize;
673    /// The current round.
674    fn round(&self) -> Round;
675    /// The secret share, if the protocol is complete.
676    fn secret_share(&self) -> Option<SecretShare<G::Scalar>>;
677    /// The public key, if the protocol is complete.
678    fn public_key(&self) -> Option<G>;
679    /// The valid participant IDs from the last round.
680    fn valid_participant_ids(&self) -> &[Option<IdentifierPrimeField<G::Scalar>>];
681    /// All participant IDs that started the protocol.
682    fn all_participant_ids(&self) -> &[IdentifierPrimeField<G::Scalar>];
683    /// The Feldman verifiers.
684    fn feldman_verifiers(&self) -> &[ShareVerifierGroup<G>];
685    /// The round 1 data received so far.
686    fn received_round1_data(&self) -> &[Option<Round1Data<G>>];
687    /// The round 2 data received so far.
688    fn received_round2_data(&self) -> &[Option<Round2Data<G::Scalar>>];
689    /// The verifying share.
690    fn verifying_share(&self) -> G;
691    /// The final transcript hash.
692    fn final_transcript_hash(&self) -> [u8; 32];
693    /// Return whether the participant has completed the protocol.
694    fn completed(&self) -> bool;
695    /// Receive data from another participant.
696    fn receive(&mut self, data: &[u8]) -> DkgResult<()>;
697    /// Run the next round after receiving data from other participants.
698    fn run(&mut self) -> DkgResult<RoundOutputGenerator<G>>;
699    /// Advance the protocol and produce opaque, transport-aware messages.
700    fn advance(&mut self) -> DkgResult<AdvanceResult<G::Scalar>>;
701    /// Consume a completed participant and return its final DKG output.
702    fn into_output(self: Box<Self>) -> DkgResult<DkgOutput<G>>;
703
704    /// Get the ordinal index of this participant.
705    #[deprecated(since = "0.6.0", note = "use `ordinal` instead")]
706    fn get_ordinal(&self) -> usize {
707        self.ordinal()
708    }
709
710    /// Get the identifier associated with this participant.
711    #[deprecated(since = "0.6.0", note = "use `id` instead")]
712    fn get_id(&self) -> IdentifierPrimeField<G::Scalar> {
713        self.id()
714    }
715
716    /// Get the threshold.
717    #[deprecated(since = "0.6.0", note = "use `threshold` instead")]
718    fn get_threshold(&self) -> usize {
719        self.threshold()
720    }
721
722    /// Get the participant limit.
723    #[deprecated(since = "0.6.0", note = "use `limit` instead")]
724    fn get_limit(&self) -> usize {
725        self.limit()
726    }
727
728    /// Get the current round.
729    #[deprecated(since = "0.6.0", note = "use `round` instead")]
730    fn get_round(&self) -> Round {
731        self.round()
732    }
733
734    /// Get the secret share if completed.
735    #[deprecated(since = "0.6.0", note = "use `secret_share` instead")]
736    fn get_secret_share(&self) -> Option<SecretShare<G::Scalar>> {
737        self.secret_share()
738    }
739
740    /// Get the public key if completed.
741    #[deprecated(since = "0.6.0", note = "use `public_key` instead")]
742    fn get_public_key(&self) -> Option<G> {
743        self.public_key()
744    }
745
746    /// Get the valid participant IDs from the last round.
747    #[deprecated(since = "0.6.0", note = "use `valid_participant_ids` instead")]
748    fn get_valid_participant_ids(&self) -> &[Option<IdentifierPrimeField<G::Scalar>>] {
749        self.valid_participant_ids()
750    }
751
752    /// Get all participant IDs that started the protocol.
753    #[deprecated(since = "0.6.0", note = "use `all_participant_ids` instead")]
754    fn get_all_participant_ids(&self) -> &[IdentifierPrimeField<G::Scalar>] {
755        self.all_participant_ids()
756    }
757
758    /// Return the Feldman verifiers.
759    #[deprecated(since = "0.6.0", note = "use `feldman_verifiers` instead")]
760    fn get_feldman_verifiers(&self) -> Vec<ShareVerifierGroup<G>> {
761        self.feldman_verifiers().to_vec()
762    }
763
764    /// Get the round 1 data received so far.
765    #[deprecated(since = "0.6.0", note = "use `received_round1_data` instead")]
766    fn get_received_round1_data(&self) -> &[Option<Round1Data<G>>] {
767        self.received_round1_data()
768    }
769
770    /// Get the round 2 data received so far.
771    #[deprecated(since = "0.6.0", note = "use `received_round2_data` instead")]
772    fn get_received_round2_data(&self) -> &[Option<Round2Data<G::Scalar>>] {
773        self.received_round2_data()
774    }
775
776    /// Get the verifying share.
777    #[deprecated(since = "0.6.0", note = "use `verifying_share` instead")]
778    fn get_verifying_share(&self) -> G {
779        self.verifying_share()
780    }
781
782    /// Get the final transcript hash.
783    #[deprecated(since = "0.6.0", note = "use `final_transcript_hash` instead")]
784    fn get_final_transcript_hash(&self) -> [u8; 32] {
785        self.final_transcript_hash()
786    }
787}
788
789impl<I, G> AnyParticipant<G> for Participant<I, G>
790where
791    I: ParticipantImpl<G> + Default + Send + Sync,
792    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
793    G::Scalar: ScalarHash,
794{
795    fn ordinal(&self) -> usize {
796        self.ordinal
797    }
798
799    fn id(&self) -> IdentifierPrimeField<G::Scalar> {
800        self.id
801    }
802
803    fn threshold(&self) -> usize {
804        self.threshold
805    }
806
807    fn limit(&self) -> usize {
808        self.limit
809    }
810
811    fn round(&self) -> Round {
812        self.round
813    }
814
815    fn secret_share(&self) -> Option<SecretShare<G::Scalar>> {
816        self.secret_share()
817    }
818
819    fn public_key(&self) -> Option<G> {
820        self.public_key()
821    }
822
823    fn valid_participant_ids(&self) -> &[Option<IdentifierPrimeField<G::Scalar>>] {
824        &self.valid_participant_ids
825    }
826
827    fn all_participant_ids(&self) -> &[IdentifierPrimeField<G::Scalar>] {
828        &self.all_participant_ids
829    }
830
831    fn feldman_verifiers(&self) -> &[ShareVerifierGroup<G>] {
832        &self.feldman_verifiers
833    }
834
835    fn received_round1_data(&self) -> &[Option<Round1Data<G>>] {
836        &self.received_round1_data
837    }
838
839    fn received_round2_data(&self) -> &[Option<Round2Data<G::Scalar>>] {
840        &self.received_round2_data
841    }
842
843    fn verifying_share(&self) -> G {
844        self.verifying_share
845    }
846
847    fn final_transcript_hash(&self) -> [u8; 32] {
848        get_final_transcript_hash(&self.received_round1_data, &self.received_round2_data)
849    }
850
851    fn completed(&self) -> bool {
852        self.completed()
853    }
854
855    fn receive(&mut self, data: &[u8]) -> DkgResult<()> {
856        self.receive(data)
857    }
858
859    fn run(&mut self) -> DkgResult<RoundOutputGenerator<G>> {
860        self.run()
861    }
862
863    fn advance(&mut self) -> DkgResult<AdvanceResult<G::Scalar>> {
864        self.advance()
865    }
866
867    fn into_output(self: Box<Self>) -> DkgResult<DkgOutput<G>> {
868        (*self).into_output()
869    }
870}
871
872fn get_final_transcript_hash<G>(
873    received_round1_data: &[Option<Round1Data<G>>],
874    received_round2_data: &[Option<Round2Data<G::Scalar>>],
875) -> [u8; 32]
876where
877    G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
878    G::Scalar: ScalarHash,
879{
880    let mut transcript = merlin::Transcript::new(b"Frost DKG - Final Transcript");
881    for round1data in received_round1_data.iter().flatten() {
882        round1data.add_to_transcript(&mut transcript);
883    }
884    for round2data in received_round2_data.iter().flatten() {
885        round2data.add_to_transcript(&mut transcript);
886    }
887    let mut transcript_hash = [0u8; 32];
888    transcript.challenge_bytes(b"final result", &mut transcript_hash);
889    transcript_hash
890}
891
892#[cfg(test)]
893mod tests {
894    use super::*;
895    use k256::{ProjectivePoint, Scalar};
896    use std::num::NonZeroUsize;
897    use vsss_rs::Share;
898
899    #[test]
900    fn receive_rejects_empty_message() {
901        let parameters = Parameters::new(
902            NonZeroUsize::new(2).expect("threshold is non-zero"),
903            NonZeroUsize::new(2).expect("limit is non-zero"),
904        )
905        .expect("valid parameters");
906        let mut participant = SecretParticipant::<ProjectivePoint>::new_secret(
907            IdentifierPrimeField::ONE,
908            &parameters,
909        )
910        .expect("create participant");
911
912        let result = participant.receive(&[]);
913
914        assert!(
915            matches!(result, Err(Error::InvalidMessage(message)) if message == "message is empty")
916        );
917    }
918
919    #[test]
920    fn debug_redacts_secret_state() {
921        let parameters = Parameters::new(
922            NonZeroUsize::new(2).expect("threshold is non-zero"),
923            NonZeroUsize::new(2).expect("limit is non-zero"),
924        )
925        .expect("valid parameters");
926        let participant = SecretParticipant::<ProjectivePoint>::new_secret(
927            IdentifierPrimeField::ONE,
928            &parameters,
929        )
930        .expect("create participant");
931
932        assert_eq!(participant.feldman_verifiers().len(), 2);
933        let debug = format!("{participant:?}");
934
935        assert!(!debug.contains("original_secret"));
936        assert!(!debug.contains("secret_share"));
937        assert!(!debug.contains("secret_shares"));
938        assert!(!debug.contains("received_round2_data"));
939    }
940
941    #[test]
942    fn output_is_unavailable_before_completion() {
943        let parameters = Parameters::new(
944            NonZeroUsize::new(2).expect("threshold is non-zero"),
945            NonZeroUsize::new(2).expect("limit is non-zero"),
946        )
947        .expect("valid parameters");
948        let participant = SecretParticipant::<ProjectivePoint>::new_secret(
949            IdentifierPrimeField::ONE,
950            &parameters,
951        )
952        .expect("create participant");
953
954        let result = participant.into_output();
955
956        assert!(matches!(result, Err(Error::Round(message)) if message.contains("not complete")));
957    }
958
959    #[test]
960    fn reconstruction_set_rejects_duplicate_identifiers() {
961        let identifier = IdentifierPrimeField(Scalar::ONE);
962        let identifiers = [identifier, identifier];
963
964        let result = ReconstructionSet::new(&identifiers);
965
966        assert!(matches!(result, Err(Error::Initialization(_))));
967    }
968
969    #[test]
970    fn refresh_rejects_a_share_with_a_different_identifier() {
971        let parameters = Parameters::new(
972            NonZeroUsize::new(2).expect("threshold is non-zero"),
973            NonZeroUsize::new(2).expect("limit is non-zero"),
974        )
975        .expect("valid parameters");
976        let share = SecretShare::with_identifier_and_value(
977            IdentifierPrimeField(Scalar::ONE),
978            IdentifierPrimeField(Scalar::ONE),
979        );
980
981        let result = RefreshParticipant::<ProjectivePoint>::new_refresh(
982            IdentifierPrimeField(Scalar::from(2u64)),
983            Some(&share),
984            &parameters,
985        );
986
987        assert!(
988            matches!(result, Err(Error::Initialization(message)) if message.contains("does not match"))
989        );
990    }
991
992    #[test]
993    fn resharing_rejects_a_share_missing_from_the_reconstruction_set() {
994        let parameters = Parameters::new(
995            NonZeroUsize::new(2).expect("threshold is non-zero"),
996            NonZeroUsize::new(3).expect("limit is non-zero"),
997        )
998        .expect("valid parameters");
999        let share = SecretShare::with_identifier_and_value(
1000            IdentifierPrimeField(Scalar::ONE),
1001            IdentifierPrimeField(Scalar::ONE),
1002        );
1003        let identifiers = [
1004            IdentifierPrimeField(Scalar::from(2u64)),
1005            IdentifierPrimeField(Scalar::from(3u64)),
1006        ];
1007        let reconstruction_set =
1008            ReconstructionSet::new(&identifiers).expect("valid reconstruction set");
1009
1010        let result = SecretParticipant::<ProjectivePoint>::with_secret(
1011            IdentifierPrimeField::ONE,
1012            &share,
1013            &parameters,
1014            &reconstruction_set,
1015        );
1016
1017        assert!(
1018            matches!(result, Err(Error::Initialization(message)) if message.contains("not included"))
1019        );
1020    }
1021}