miden_protocol/block/
validator_config.rs1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use crate::crypto::SequentialCommit;
5use crate::crypto::dsa::ecdsa_k256_keccak::PublicKey;
6use crate::errors::ValidatorConfigError;
7use crate::utils::serde::{
8 ByteReader,
9 ByteWriter,
10 Deserializable,
11 DeserializationError,
12 Serializable,
13};
14use crate::{Felt, WORD_SIZE, Word, ZERO};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ValidatorConfig {
38 keys: Vec<PublicKey>,
40
41 quorum: u16,
43}
44
45impl ValidatorConfig {
46 pub const MAX_VALIDATORS: usize = 5;
51
52 pub fn new(mut keys: Vec<PublicKey>, quorum: u16) -> Result<Self, ValidatorConfigError> {
67 if keys.is_empty() {
68 return Err(ValidatorConfigError::EmptySet);
69 }
70 if keys.len() > Self::MAX_VALIDATORS {
71 return Err(ValidatorConfigError::TooManyKeys { count: keys.len() });
72 }
73 if usize::from(quorum) != keys.len() {
74 return Err(ValidatorConfigError::QuorumMustEqualValidatorCount {
75 quorum,
76 count: keys.len(),
77 });
78 }
79
80 keys.sort_by_key(|key| key.to_bytes());
82
83 if keys.windows(2).any(|pair| pair[0] == pair[1]) {
85 return Err(ValidatorConfigError::DuplicateKey);
86 }
87
88 Ok(Self { keys, quorum })
89 }
90
91 pub fn keys(&self) -> &[PublicKey] {
96 &self.keys
97 }
98
99 pub fn len(&self) -> usize {
101 self.keys.len()
102 }
103
104 pub fn is_empty(&self) -> bool {
106 false
107 }
108
109 pub fn quorum(&self) -> u16 {
111 self.quorum
112 }
113
114 pub fn to_commitment(&self) -> Word {
120 <Self as SequentialCommit>::to_commitment(self)
121 }
122
123 pub fn to_elements(&self) -> Vec<Felt> {
131 <Self as SequentialCommit>::to_elements(self)
132 }
133}
134
135impl SequentialCommit for ValidatorConfig {
136 type Commitment = Word;
137
138 fn to_elements(&self) -> Vec<Felt> {
139 let mut elements: Vec<Felt> = Vec::with_capacity((self.keys.len() + 1) * WORD_SIZE);
140 elements.extend([Felt::from(self.quorum), ZERO, ZERO, ZERO]);
141
142 for key in &self.keys {
143 elements.extend_from_slice(key.to_commitment().as_elements());
144 }
145
146 elements
147 }
148}
149
150impl Serializable for ValidatorConfig {
154 fn write_into<W: ByteWriter>(&self, target: &mut W) {
155 let Self { keys, quorum } = self;
156
157 let num_keys =
158 u8::try_from(keys.len()).expect("constructor should validate num keys fits in u8");
159
160 quorum.write_into(target);
161 num_keys.write_into(target);
162 target.write_many(keys);
163 }
164}
165
166impl Deserializable for ValidatorConfig {
167 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
168 let quorum = u16::read_from(source)?;
169 let num_keys: u8 = source.read()?;
170 let keys = source
171 .read_many_iter(num_keys as usize)?
172 .collect::<Result<Vec<PublicKey>, _>>()?;
173
174 Self::new(keys, quorum).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
175 }
176}
177
178#[cfg(test)]
182mod tests {
183 use assert_matches::assert_matches;
184
185 use super::*;
186 use crate::testing::random_secret_key::random_secret_key;
187
188 fn random_keys(count: usize) -> Vec<PublicKey> {
189 (0..count).map(|_| random_secret_key().public_key()).collect()
190 }
191
192 #[test]
193 fn new_rejects_empty_set() {
194 let result = ValidatorConfig::new(Vec::new(), 1);
195 assert_matches!(result, Err(ValidatorConfigError::EmptySet));
196 }
197
198 #[test]
199 fn new_accepts_single_validator() -> anyhow::Result<()> {
200 let config = ValidatorConfig::new(random_keys(1), 1)?;
201 assert_eq!(config.len(), 1);
202 assert_eq!(config.quorum(), 1);
203 Ok(())
204 }
205
206 #[test]
207 fn new_accepts_max_validators() -> anyhow::Result<()> {
208 let max_validators = ValidatorConfig::MAX_VALIDATORS;
209 let config = ValidatorConfig::new(random_keys(max_validators), max_validators as u16)?;
210 assert_eq!(config.len(), max_validators);
211 Ok(())
212 }
213
214 #[test]
215 fn new_rejects_too_many_keys() {
216 let result = ValidatorConfig::new(random_keys(ValidatorConfig::MAX_VALIDATORS + 1), 1);
217 assert_matches!(
218 result,
219 Err(ValidatorConfigError::TooManyKeys { count }) if count == ValidatorConfig::MAX_VALIDATORS + 1
220 );
221 }
222
223 #[test]
224 fn new_rejects_duplicate_keys() {
225 let mut keys = random_keys(3);
226 keys[1] = keys[0].clone();
227 let result = ValidatorConfig::new(keys, 3);
228 assert_matches!(result, Err(ValidatorConfigError::DuplicateKey));
229 }
230
231 #[rstest::rstest]
232 #[case::zero_quorum(0)]
233 #[case::quorum_below_validator_count(2)]
234 #[case::quorum_above_validator_count(4)]
235 fn new_rejects_quorum_other_than_validator_count(#[case] quorum: u16) {
236 let result = ValidatorConfig::new(random_keys(3), quorum);
237 assert_matches!(
238 result,
239 Err(ValidatorConfigError::QuorumMustEqualValidatorCount { quorum: actual, count: 3 })
240 if actual == quorum
241 );
242 }
243
244 #[test]
245 fn new_sorts_into_canonical_order() -> anyhow::Result<()> {
246 let keys = random_keys(5);
247 let forward = ValidatorConfig::new(keys.clone(), 5)?;
248
249 let mut reversed = keys;
250 reversed.reverse();
251 let backward = ValidatorConfig::new(reversed, 5)?;
252
253 assert_eq!(forward.keys(), backward.keys());
255 assert_eq!(forward.to_commitment(), backward.to_commitment());
256 Ok(())
257 }
258
259 #[test]
260 fn commitment_binds_the_quorum() -> anyhow::Result<()> {
261 let config = ValidatorConfig::new(random_keys(3), 3)?;
262
263 assert_eq!(config.to_elements()[0], Felt::from(config.quorum()));
264 Ok(())
265 }
266
267 #[test]
268 fn serde_round_trip() -> anyhow::Result<()> {
269 let config = ValidatorConfig::new(random_keys(4), 4)?;
270 let deserialized = ValidatorConfig::read_from_bytes(&config.to_bytes())?;
271 assert_eq!(config, deserialized);
272
273 Ok(())
274 }
275}