Skip to main content

miden_protocol/block/
validator_config.rs

1use 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// VALIDATOR CONFIG
17// ================================================================================================
18
19/// The ordered set of validator public keys authorized to sign a block, and how many of them must
20/// sign.
21///
22/// The protocol does not support partial signing yet, so the quorum must be equal to the number of
23/// keys. The quorum stays a separate field because the block header commits to it. Thus a smaller
24/// quorum can be added later without a change to the shape of the commitment.
25///
26/// A block header commits to the [`ValidatorConfig`] authorized to sign the *next* block. A block's
27/// signatures are verified positionally against the validator set committed to by its parent: the
28/// signature in slot `i` is checked against the key at index `i` in this set.
29///
30/// The number of validators is not fixed by the protocol: a chain may run with a single validator
31/// and grow its validator set over time by rotating in a larger [`ValidatorConfig`] (see
32/// [`ProposedBlock::with_next_validator_config`](crate::block::ProposedBlock::with_next_validator_config)),
33/// up to [`ValidatorConfig::MAX_VALIDATORS`] keys. The set holds at least one key, kept in a
34/// canonical order (sorted by their serialized bytes) so that the commitment is independent of the
35/// order in which the keys were provided.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ValidatorConfig {
38    /// Distinct validator public keys, sorted by their serialized bytes.
39    keys: Vec<PublicKey>,
40
41    /// The number of validators that must sign a block for it to be valid.
42    quorum: u16,
43}
44
45impl ValidatorConfig {
46    // CONSTANTS
47    // --------------------------------------------------------------------------------------------
48
49    /// The maximum number of validator keys in a set.
50    pub const MAX_VALIDATORS: usize = 5;
51
52    // CONSTRUCTORS
53    // --------------------------------------------------------------------------------------------
54
55    /// Returns a new [`ValidatorConfig`] from the provided public keys and quorum.
56    ///
57    /// The keys are sorted into a canonical order by their serialized bytes.
58    ///
59    /// # Errors
60    ///
61    /// Returns an error if:
62    /// - `keys` is empty;
63    /// - `keys` contains more than [`ValidatorConfig::MAX_VALIDATORS`] keys;
64    /// - the set contains duplicate keys;
65    /// - `quorum` does not equal the number of keys.
66    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        // Sort into a canonical order so the commitment is independent of input order.
81        keys.sort_by_key(|key| key.to_bytes());
82
83        // After sorting, duplicates are adjacent.
84        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    // PUBLIC ACCESSORS
92    // --------------------------------------------------------------------------------------------
93
94    /// Returns the validator public keys in canonical order.
95    pub fn keys(&self) -> &[PublicKey] {
96        &self.keys
97    }
98
99    /// Returns the number of validator keys in the set.
100    pub fn len(&self) -> usize {
101        self.keys.len()
102    }
103
104    /// Returns `false`, as a validator set always contains at least one key.
105    pub fn is_empty(&self) -> bool {
106        false
107    }
108
109    /// Returns the number of validators that must sign a block for it to be valid.
110    pub fn quorum(&self) -> u16 {
111        self.quorum
112    }
113
114    /// Returns a commitment to the validator configuration.
115    ///
116    /// It is committed to by the [`BlockHeader`](crate::block::BlockHeader) as a single word. Since
117    /// the preimage covers every key, the commitment also implicitly binds the number of
118    /// validators.
119    pub fn to_commitment(&self) -> Word {
120        <Self as SequentialCommit>::to_commitment(self)
121    }
122
123    /// Returns the preimage of [`ValidatorConfig::to_commitment`] as a sequence of field elements.
124    ///
125    /// The element layout is:
126    ///
127    /// ```text
128    /// [[quorum, 0, 0, 0], KEY_COMMITMENT_0, KEY_COMMITMENT_1, ..., KEY_COMMITMENT_N]
129    /// ```
130    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
150// SERIALIZATION
151// ================================================================================================
152
153impl 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// TESTS
179// ================================================================================================
180
181#[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        // The canonical order makes the set and its commitment independent of input order.
254        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}