Skip to main content

miden_protocol/block/
block_signatures.rs

1use alloc::vec::Vec;
2
3use miden_crypto::dsa::ecdsa_k256_keccak::Signature;
4
5use crate::Word;
6use crate::block::ValidatorConfig;
7use crate::utils::serde::{
8    ByteReader,
9    ByteWriter,
10    Deserializable,
11    DeserializationError,
12    Serializable,
13};
14
15// SIGNATURE VERIFICATION ERROR
16// ================================================================================================
17
18/// Error returned when verifying [`BlockSignatures`] against a validator set (see
19/// [`BlockSignatures::verify_against`]).
20#[derive(Debug, thiserror::Error)]
21#[non_exhaustive]
22pub enum SignatureVerificationError {
23    #[error("block has {actual} signatures but the validator set has {expected} keys")]
24    SignatureCountMismatch { expected: usize, actual: usize },
25    #[error(
26        "block signature at position {position} does not verify against the validator key at that position"
27    )]
28    InvalidSignatureAtPosition { position: usize },
29}
30
31// BLOCK SIGNATURES ERROR
32// ================================================================================================
33
34/// Error returned when constructing an invalid [`BlockSignatures`] set.
35#[derive(Debug, thiserror::Error)]
36#[non_exhaustive]
37pub enum BlockSignaturesError {
38    #[error(
39        "block signature set contains {count} signatures but must contain at most {max}",
40        max = ValidatorConfig::MAX_VALIDATORS,
41    )]
42    TooManySignatures { count: usize },
43}
44
45// BLOCK SIGNATURES
46// ================================================================================================
47
48/// The set of validator signatures over a block header ordered by validator key.
49///
50/// The signatures are expected to be ordered with respect to a validator set (see
51/// [`ValidatorConfig`]): the signature in slot `i` is produced by, and verified against, the
52/// validator key at index `i`. Every validator in the set must sign; there is no partial-signing
53/// support.
54///
55/// TODO(validator_quorum): [`ValidatorConfig`] requires the quorum to be equal to the validator
56/// count, which is why every validator must sign. A smaller quorum needs signatures that identify
57/// the validator they belong to, so that a partial set can still be matched against the validator
58/// keys.
59///
60/// This is a plain, unchecked container: neither [`BlockSignatures::new`] nor deserialization
61/// verify anything about the signatures they hold. The only way to establish that a
62/// [`BlockSignatures`] value is valid is to call [`BlockSignatures::verify_against`].
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct BlockSignatures {
65    /// Positional signatures; `signatures[i]` corresponds to validator key `i`.
66    signatures: Vec<Signature>,
67}
68
69impl BlockSignatures {
70    // CONSTRUCTORS
71    // --------------------------------------------------------------------------------------------
72
73    /// Returns a new [`BlockSignatures`] from the provided signatures, ordered positionally.
74    ///
75    /// This performs no validation beyond checking that the number of signatures does not exceed
76    /// [`ValidatorConfig::MAX_VALIDATORS`]: the caller is responsible for ordering `signatures` to align
77    /// with the validator set it is meant to be checked against. Call
78    /// [`BlockSignatures::verify_against`] to establish that the signatures are valid.
79    pub fn new(signatures: Vec<Signature>) -> Result<Self, BlockSignaturesError> {
80        if signatures.len() > ValidatorConfig::MAX_VALIDATORS {
81            return Err(BlockSignaturesError::TooManySignatures { count: signatures.len() });
82        }
83        Ok(Self { signatures })
84    }
85
86    // PUBLIC ACCESSORS
87    // --------------------------------------------------------------------------------------------
88
89    /// Returns the positional signatures, where signature `i` corresponds to validator key `i`.
90    pub fn as_signatures(&self) -> &[Signature] {
91        &self.signatures
92    }
93
94    /// Returns the number of signatures.
95    pub fn len(&self) -> usize {
96        self.signatures.len()
97    }
98
99    /// Returns `true` if there are no signatures.
100    pub fn is_empty(&self) -> bool {
101        self.signatures.is_empty()
102    }
103
104    // VERIFICATION
105    // --------------------------------------------------------------------------------------------
106
107    /// Verifies the signatures positionally against `validator_config` over `block_commitment`.
108    ///
109    /// This is the canonical verification of an ordered signature set, and the only place that
110    /// establishes a [`BlockSignatures`] value is valid: the number of signatures must match the
111    /// number of validator keys, and the signature in slot `i` must verify against the validator
112    /// key at index `i`.
113    ///
114    /// # Errors
115    ///
116    /// Returns an error if the number of signatures does not match the number of validator keys, or
117    /// if a signature does not verify against the validator key at its position.
118    pub fn verify_against(
119        &self,
120        block_commitment: Word,
121        validator_config: &ValidatorConfig,
122    ) -> Result<(), SignatureVerificationError> {
123        if self.signatures.len() != validator_config.len() {
124            return Err(SignatureVerificationError::SignatureCountMismatch {
125                expected: validator_config.len(),
126                actual: self.signatures.len(),
127            });
128        }
129
130        for (position, (signature, validator_key)) in
131            self.signatures.iter().zip(validator_config.keys()).enumerate()
132        {
133            if !signature.verify(block_commitment, validator_key) {
134                return Err(SignatureVerificationError::InvalidSignatureAtPosition { position });
135            }
136        }
137
138        Ok(())
139    }
140}
141
142// SERIALIZATION
143// ================================================================================================
144
145impl Serializable for BlockSignatures {
146    fn write_into<W: ByteWriter>(&self, target: &mut W) {
147        self.signatures.write_into(target);
148    }
149}
150
151impl Deserializable for BlockSignatures {
152    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
153        let signatures = Vec::<Signature>::read_from(source)?;
154        Ok(Self { signatures })
155    }
156}
157
158// TESTS
159// ================================================================================================
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164    use crate::testing::random_secret_key::random_secret_key;
165
166    #[test]
167    fn verify_against_accepts_correctly_ordered_signatures() {
168        let (signers, keys) = ValidatorConfig::random_with_signers(5);
169        let commitment = Word::empty();
170
171        let signatures = keys.sign_all(&signers, commitment);
172
173        assert_eq!(signatures.len(), 5);
174        signatures.verify_against(commitment, &keys).unwrap();
175    }
176
177    #[test]
178    fn verify_against_rejects_invalid_signature() {
179        let (signers, keys) = ValidatorConfig::random_with_signers(3);
180        let commitment = Word::empty();
181        let outsider = random_secret_key();
182
183        // Position 1 holds a signature that does not verify against the key committed there.
184        let mut signatures = keys.sign_all(&signers, commitment).as_signatures().to_vec();
185        signatures[1] = outsider.sign(commitment);
186        let signatures = BlockSignatures::new(signatures).unwrap();
187
188        assert!(matches!(
189            signatures.verify_against(commitment, &keys),
190            Err(SignatureVerificationError::InvalidSignatureAtPosition { position: 1 })
191        ));
192    }
193
194    #[test]
195    fn verify_against_rejects_mismatched_keys() {
196        let (signers, keys) = ValidatorConfig::random_with_signers(3);
197        let commitment = Word::empty();
198        let signatures = keys.sign_all(&signers, commitment);
199
200        // The same, fully valid set does not verify against a different validator set of the same
201        // size.
202        let (_, other_keys) = ValidatorConfig::random_with_signers(3);
203        assert!(matches!(
204            signatures.verify_against(commitment, &other_keys),
205            Err(SignatureVerificationError::InvalidSignatureAtPosition { .. })
206        ));
207    }
208
209    #[test]
210    fn verify_against_rejects_count_mismatch() {
211        let (signers, keys) = ValidatorConfig::random_with_signers(3);
212        let commitment = Word::empty();
213        let signatures = keys.sign_all(&signers, commitment);
214
215        // A validator set of a different size cannot align positionally.
216        let (_, other_keys) = ValidatorConfig::random_with_signers(4);
217        assert!(matches!(
218            signatures.verify_against(commitment, &other_keys),
219            Err(SignatureVerificationError::SignatureCountMismatch { expected: 4, actual: 3 })
220        ));
221    }
222
223    #[test]
224    fn serde_round_trip() {
225        let (signers, keys) = ValidatorConfig::random_with_signers(3);
226        let commitment = Word::empty();
227        let signatures = keys.sign_all(&signers, commitment);
228
229        let bytes = signatures.to_bytes();
230        let deserialized = BlockSignatures::read_from_bytes(&bytes).unwrap();
231        assert_eq!(signatures, deserialized);
232    }
233}