Skip to main content

frost_dkg/
parameters.rs

1use super::*;
2use elliptic_curve::group::GroupEncoding;
3use elliptic_curve::subtle::ConditionallySelectable;
4use elliptic_curve_tools::SumOfProducts;
5use std::collections::HashSet;
6use std::num::NonZeroUsize;
7use vsss_rs::{IdentifierPrimeField, ParticipantIdGenerator, ParticipantIdGeneratorCollection};
8
9/// The parameters used by the DKG participants.
10/// These parameters must be the same for every participant; otherwise, the
11/// protocol will abort.
12#[derive(Debug, Clone)]
13pub struct Parameters<'a, G>
14where
15    G: GroupEncoding + Default + SumOfProducts + ConditionallySelectable,
16    G::Scalar: ScalarHash,
17{
18    pub(crate) threshold: usize,
19    pub(crate) limit: usize,
20    pub(crate) message_generator: G,
21    pub(crate) participant_number_generators:
22        Vec<ParticipantIdGenerator<'a, IdentifierPrimeField<G::Scalar>>>,
23}
24
25impl<'a, G> Parameters<'a, G>
26where
27    G: GroupEncoding + Default + SumOfProducts + ConditionallySelectable,
28    G::Scalar: ScalarHash,
29{
30    /// Create validated parameters using the group's default generator and
31    /// sequential participant identifiers.
32    pub fn new(threshold: NonZeroUsize, limit: NonZeroUsize) -> DkgResult<Self> {
33        let parameters = Self {
34            threshold: threshold.get(),
35            limit: limit.get(),
36            message_generator: G::generator(),
37            participant_number_generators: vec![ParticipantIdGenerator::Sequential {
38                start: IdentifierPrimeField::ONE,
39                increment: IdentifierPrimeField::ONE,
40                count: limit.get(),
41            }],
42        };
43        parameters.validate()?;
44        Ok(parameters)
45    }
46
47    /// Use a custom message generator.
48    pub fn with_message_generator(mut self, message_generator: G) -> DkgResult<Self> {
49        self.message_generator = message_generator;
50        self.validate()?;
51        Ok(self)
52    }
53
54    /// Use custom participant identifier generators.
55    pub fn with_participant_number_generators(
56        mut self,
57        participant_number_generators: Vec<
58            ParticipantIdGenerator<'a, IdentifierPrimeField<G::Scalar>>,
59        >,
60    ) -> DkgResult<Self> {
61        self.participant_number_generators = participant_number_generators;
62        self.validate()?;
63        Ok(self)
64    }
65
66    fn validate(&self) -> DkgResult<()> {
67        if self.threshold < 2 {
68            return Err(Error::Initialization(
69                "Threshold must be at least 2".to_string(),
70            ));
71        }
72        if self.threshold > self.limit {
73            return Err(Error::Initialization(
74                "Threshold cannot exceed the participant limit".to_string(),
75            ));
76        }
77        if self.message_generator.is_identity().into() {
78            return Err(Error::Initialization(
79                "Message generator cannot be the identity".to_string(),
80            ));
81        }
82
83        let participant_ids =
84            ParticipantIdGeneratorCollection::from(&self.participant_number_generators)
85                .iter()
86                .take(self.limit)
87                .collect::<Vec<_>>();
88        if participant_ids.len() != self.limit {
89            return Err(Error::Initialization(format!(
90                "Participant ID generators produced {} identifiers, expected {}",
91                participant_ids.len(),
92                self.limit
93            )));
94        }
95        let unique_ids = participant_ids.iter().copied().collect::<HashSet<_>>();
96        if unique_ids.len() != participant_ids.len() {
97            return Err(Error::Initialization(
98                "Participant identifiers must be unique".to_string(),
99            ));
100        }
101        Ok(())
102    }
103
104    /// The threshold parameter.
105    pub fn threshold(&self) -> usize {
106        self.threshold
107    }
108
109    /// The participant limit.
110    pub fn limit(&self) -> usize {
111        self.limit
112    }
113
114    /// Get the message generator.
115    pub fn message_generator(&self) -> G {
116        self.message_generator
117    }
118
119    /// Get the participant ID generators.
120    pub fn participant_number_generators(
121        &self,
122    ) -> &[ParticipantIdGenerator<'a, IdentifierPrimeField<G::Scalar>>] {
123        &self.participant_number_generators
124    }
125
126    /// Get the participant ID generators.
127    #[deprecated(since = "0.6.0", note = "use `participant_number_generators` instead")]
128    pub fn participant_number_generator(
129        &self,
130    ) -> &[ParticipantIdGenerator<'a, IdentifierPrimeField<G::Scalar>>] {
131        self.participant_number_generators()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use k256::{ProjectivePoint, Scalar};
139
140    #[test]
141    fn new_creates_valid_default_parameters() {
142        let parameters = Parameters::<ProjectivePoint>::new(
143            NonZeroUsize::new(2).expect("threshold is non-zero"),
144            NonZeroUsize::new(3).expect("limit is non-zero"),
145        )
146        .expect("valid parameters");
147
148        assert_eq!(parameters.threshold(), 2);
149        assert_eq!(parameters.limit(), 3);
150        assert_eq!(parameters.message_generator(), ProjectivePoint::GENERATOR);
151        assert_eq!(parameters.participant_number_generators().len(), 1);
152    }
153
154    #[test]
155    fn new_rejects_invalid_thresholds() {
156        let below_minimum = Parameters::<ProjectivePoint>::new(
157            NonZeroUsize::new(1).expect("threshold is non-zero"),
158            NonZeroUsize::new(2).expect("limit is non-zero"),
159        );
160        assert!(
161            matches!(below_minimum, Err(Error::Initialization(message)) if message.contains("at least 2"))
162        );
163
164        let above_limit = Parameters::<ProjectivePoint>::new(
165            NonZeroUsize::new(3).expect("threshold is non-zero"),
166            NonZeroUsize::new(2).expect("limit is non-zero"),
167        );
168        assert!(
169            matches!(above_limit, Err(Error::Initialization(message)) if message.contains("cannot exceed"))
170        );
171    }
172
173    #[test]
174    fn custom_message_generator_is_validated() {
175        let parameters = Parameters::<ProjectivePoint>::new(
176            NonZeroUsize::new(2).expect("threshold is non-zero"),
177            NonZeroUsize::new(3).expect("limit is non-zero"),
178        )
179        .expect("valid parameters");
180
181        let result = parameters.with_message_generator(ProjectivePoint::IDENTITY);
182
183        assert!(
184            matches!(result, Err(Error::Initialization(message)) if message.contains("identity"))
185        );
186    }
187
188    #[test]
189    fn custom_participant_identifiers_are_validated() {
190        let ids = [
191            IdentifierPrimeField(Scalar::ONE),
192            IdentifierPrimeField(Scalar::from(2u64)),
193            IdentifierPrimeField(Scalar::from(3u64)),
194        ];
195        let parameters = Parameters::<ProjectivePoint>::new(
196            NonZeroUsize::new(2).expect("threshold is non-zero"),
197            NonZeroUsize::new(3).expect("limit is non-zero"),
198        )
199        .expect("valid parameters")
200        .with_participant_number_generators(vec![ParticipantIdGenerator::list(&ids)])
201        .expect("valid participant identifiers");
202        assert_eq!(parameters.participant_number_generators().len(), 1);
203
204        let duplicate_ids = [ids[0], ids[0], ids[2]];
205        let duplicate_result = Parameters::<ProjectivePoint>::new(
206            NonZeroUsize::new(2).expect("threshold is non-zero"),
207            NonZeroUsize::new(3).expect("limit is non-zero"),
208        )
209        .expect("valid parameters")
210        .with_participant_number_generators(vec![ParticipantIdGenerator::list(&duplicate_ids)]);
211        assert!(
212            matches!(duplicate_result, Err(Error::Initialization(message)) if message.contains("unique"))
213        );
214    }
215}