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::ValidatorKeys;
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 = ValidatorKeys::MAX,
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/// [`ValidatorKeys`]): 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 or
53/// threshold support.
54///
55/// This is a plain, unchecked container: neither [`BlockSignatures::new`] nor deserialization
56/// verify anything about the signatures they hold. The only way to establish that a
57/// [`BlockSignatures`] value is valid is to call [`BlockSignatures::verify_against`].
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct BlockSignatures {
60    /// Positional signatures; `signatures[i]` corresponds to validator key `i`.
61    signatures: Vec<Signature>,
62}
63
64impl BlockSignatures {
65    // CONSTRUCTORS
66    // --------------------------------------------------------------------------------------------
67
68    /// Returns a new [`BlockSignatures`] from the provided signatures, ordered positionally.
69    ///
70    /// This performs no validation beyond checking that the number of signatures does not exceed
71    /// [`ValidatorKeys::MAX`]: the caller is responsible for ordering `signatures` to align
72    /// with the validator set it is meant to be checked against. Call
73    /// [`BlockSignatures::verify_against`] to establish that the signatures are valid.
74    pub fn new(signatures: Vec<Signature>) -> Result<Self, BlockSignaturesError> {
75        if signatures.len() > ValidatorKeys::MAX {
76            return Err(BlockSignaturesError::TooManySignatures { count: signatures.len() });
77        }
78        Ok(Self { signatures })
79    }
80
81    // PUBLIC ACCESSORS
82    // --------------------------------------------------------------------------------------------
83
84    /// Returns the positional signatures, where signature `i` corresponds to validator key `i`.
85    pub fn as_signatures(&self) -> &[Signature] {
86        &self.signatures
87    }
88
89    /// Returns the number of signatures.
90    pub fn len(&self) -> usize {
91        self.signatures.len()
92    }
93
94    /// Returns `true` if there are no signatures.
95    pub fn is_empty(&self) -> bool {
96        self.signatures.is_empty()
97    }
98
99    // VERIFICATION
100    // --------------------------------------------------------------------------------------------
101
102    /// Verifies the signatures positionally against `validator_keys` over `block_commitment`.
103    ///
104    /// This is the canonical verification of an ordered signature set, and the only place that
105    /// establishes a [`BlockSignatures`] value is valid: the number of signatures must match the
106    /// number of validator keys, and the signature in slot `i` must verify against the validator
107    /// key at index `i`.
108    ///
109    /// # Errors
110    ///
111    /// Returns an error if the number of signatures does not match the number of validator keys, or
112    /// if a signature does not verify against the validator key at its position.
113    pub fn verify_against(
114        &self,
115        block_commitment: Word,
116        validator_keys: &ValidatorKeys,
117    ) -> Result<(), SignatureVerificationError> {
118        if self.signatures.len() != validator_keys.len() {
119            return Err(SignatureVerificationError::SignatureCountMismatch {
120                expected: validator_keys.len(),
121                actual: self.signatures.len(),
122            });
123        }
124
125        for (position, (signature, validator_key)) in
126            self.signatures.iter().zip(validator_keys.as_keys()).enumerate()
127        {
128            if !signature.verify(block_commitment, validator_key) {
129                return Err(SignatureVerificationError::InvalidSignatureAtPosition { position });
130            }
131        }
132
133        Ok(())
134    }
135}
136
137// SERIALIZATION
138// ================================================================================================
139
140impl Serializable for BlockSignatures {
141    fn write_into<W: ByteWriter>(&self, target: &mut W) {
142        self.signatures.write_into(target);
143    }
144}
145
146impl Deserializable for BlockSignatures {
147    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
148        let signatures = Vec::<Signature>::read_from(source)?;
149        Ok(Self { signatures })
150    }
151}
152
153// TESTS
154// ================================================================================================
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use crate::testing::random_secret_key::random_secret_key;
160    use crate::testing::validator_keys::{random_validator_set, sign_all};
161
162    #[test]
163    fn verify_against_accepts_correctly_ordered_signatures() {
164        let (signers, keys) = random_validator_set(5);
165        let commitment = Word::empty();
166
167        let signatures = sign_all(&keys, &signers, commitment);
168
169        assert_eq!(signatures.len(), 5);
170        signatures.verify_against(commitment, &keys).unwrap();
171    }
172
173    #[test]
174    fn verify_against_rejects_invalid_signature() {
175        let (signers, keys) = random_validator_set(3);
176        let commitment = Word::empty();
177        let outsider = random_secret_key();
178
179        // Position 1 holds a signature that does not verify against the key committed there.
180        let mut signatures = sign_all(&keys, &signers, commitment).as_signatures().to_vec();
181        signatures[1] = outsider.sign(commitment);
182        let signatures = BlockSignatures::new(signatures).unwrap();
183
184        assert!(matches!(
185            signatures.verify_against(commitment, &keys),
186            Err(SignatureVerificationError::InvalidSignatureAtPosition { position: 1 })
187        ));
188    }
189
190    #[test]
191    fn verify_against_rejects_mismatched_keys() {
192        let (signers, keys) = random_validator_set(3);
193        let commitment = Word::empty();
194        let signatures = sign_all(&keys, &signers, commitment);
195
196        // The same, fully valid set does not verify against a different validator set of the same
197        // size.
198        let (_, other_keys) = random_validator_set(3);
199        assert!(matches!(
200            signatures.verify_against(commitment, &other_keys),
201            Err(SignatureVerificationError::InvalidSignatureAtPosition { .. })
202        ));
203    }
204
205    #[test]
206    fn verify_against_rejects_count_mismatch() {
207        let (signers, keys) = random_validator_set(3);
208        let commitment = Word::empty();
209        let signatures = sign_all(&keys, &signers, commitment);
210
211        // A validator set of a different size cannot align positionally.
212        let (_, other_keys) = random_validator_set(4);
213        assert!(matches!(
214            signatures.verify_against(commitment, &other_keys),
215            Err(SignatureVerificationError::SignatureCountMismatch { expected: 4, actual: 3 })
216        ));
217    }
218
219    #[test]
220    fn serde_round_trip() {
221        let (signers, keys) = random_validator_set(3);
222        let commitment = Word::empty();
223        let signatures = sign_all(&keys, &signers, commitment);
224
225        let bytes = signatures.to_bytes();
226        let deserialized = BlockSignatures::read_from_bytes(&bytes).unwrap();
227        assert_eq!(signatures, deserialized);
228    }
229}