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#[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 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 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 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 pub fn threshold(&self) -> usize {
106 self.threshold
107 }
108
109 pub fn limit(&self) -> usize {
111 self.limit
112 }
113
114 pub fn message_generator(&self) -> G {
116 self.message_generator
117 }
118
119 pub fn participant_number_generators(
121 &self,
122 ) -> &[ParticipantIdGenerator<'a, IdentifierPrimeField<G::Scalar>>] {
123 &self.participant_number_generators
124 }
125
126 #[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}