casper_types/block/
block_signatures.rs

1mod block_signatures_v1;
2mod block_signatures_v2;
3
4pub use block_signatures_v1::BlockSignaturesV1;
5pub use block_signatures_v2::BlockSignaturesV2;
6
7use alloc::{collections::BTreeMap, vec::Vec};
8use core::{
9    fmt::{self, Display, Formatter},
10    hash::Hash,
11};
12use itertools::Either;
13#[cfg(feature = "std")]
14use std::error::Error as StdError;
15
16#[cfg(feature = "datasize")]
17use datasize::DataSize;
18#[cfg(any(feature = "testing", test))]
19use rand::Rng;
20#[cfg(any(feature = "std", test))]
21use serde::{Deserialize, Serialize};
22
23#[cfg(any(feature = "testing", test))]
24use crate::testing::TestRng;
25use crate::{
26    bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
27    crypto, BlockHash, ChainNameDigest, EraId, FinalitySignature, PublicKey, Signature,
28};
29
30const TAG_LENGTH: usize = U8_SERIALIZED_LENGTH;
31
32/// Tag for block signatures v1.
33pub const BLOCK_SIGNATURES_V1_TAG: u8 = 0;
34/// Tag for block signatures v2.
35pub const BLOCK_SIGNATURES_V2_TAG: u8 = 1;
36
37/// A collection of signatures for a single block, along with the associated block's hash and era
38/// ID.
39#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
40#[cfg_attr(any(feature = "std", test), derive(Serialize, Deserialize))]
41#[cfg_attr(feature = "datasize", derive(DataSize))]
42pub enum BlockSignatures {
43    /// Version 1 of the block signatures.
44    V1(BlockSignaturesV1),
45    /// Version 2 of the block signatures.
46    V2(BlockSignaturesV2),
47}
48
49impl BlockSignatures {
50    /// Returns the block hash of the associated block.
51    pub fn block_hash(&self) -> &BlockHash {
52        match self {
53            BlockSignatures::V1(block_signatures) => block_signatures.block_hash(),
54            BlockSignatures::V2(block_signatures) => block_signatures.block_hash(),
55        }
56    }
57
58    /// Returns the era id of the associated block.
59    pub fn era_id(&self) -> EraId {
60        match self {
61            BlockSignatures::V1(block_signatures) => block_signatures.era_id(),
62            BlockSignatures::V2(block_signatures) => block_signatures.era_id(),
63        }
64    }
65
66    /// Returns the finality signature associated with the given public key, if available.
67    pub fn finality_signature(&self, public_key: &PublicKey) -> Option<FinalitySignature> {
68        match self {
69            BlockSignatures::V1(block_signatures) => block_signatures
70                .finality_signature(public_key)
71                .map(FinalitySignature::V1),
72            BlockSignatures::V2(block_signatures) => block_signatures
73                .finality_signature(public_key)
74                .map(FinalitySignature::V2),
75        }
76    }
77
78    /// Returns `true` if there is a signature associated with the given public key.
79    pub fn has_finality_signature(&self, public_key: &PublicKey) -> bool {
80        match self {
81            BlockSignatures::V1(block_signatures) => {
82                block_signatures.has_finality_signature(public_key)
83            }
84            BlockSignatures::V2(block_signatures) => {
85                block_signatures.has_finality_signature(public_key)
86            }
87        }
88    }
89
90    /// Returns an iterator over all the signatures.
91    pub fn finality_signatures(&self) -> impl Iterator<Item = FinalitySignature> + '_ {
92        match self {
93            BlockSignatures::V1(block_signatures) => Either::Left(
94                block_signatures
95                    .finality_signatures()
96                    .map(FinalitySignature::V1),
97            ),
98            BlockSignatures::V2(block_signatures) => Either::Right(
99                block_signatures
100                    .finality_signatures()
101                    .map(FinalitySignature::V2),
102            ),
103        }
104    }
105
106    /// Returns an `BTreeMap` of public keys to signatures.
107    pub fn proofs(&self) -> &BTreeMap<PublicKey, Signature> {
108        match self {
109            BlockSignatures::V1(block_signatures) => &block_signatures.proofs,
110            BlockSignatures::V2(block_signatures) => &block_signatures.proofs,
111        }
112    }
113
114    /// Returns an iterator over all the validator public keys.
115    pub fn signers(&self) -> impl Iterator<Item = &'_ PublicKey> + '_ {
116        match self {
117            BlockSignatures::V1(block_signatures) => Either::Left(block_signatures.signers()),
118            BlockSignatures::V2(block_signatures) => Either::Right(block_signatures.signers()),
119        }
120    }
121
122    /// Returns the number of signatures in the collection.
123    pub fn len(&self) -> usize {
124        match self {
125            BlockSignatures::V1(block_signatures) => block_signatures.len(),
126            BlockSignatures::V2(block_signatures) => block_signatures.len(),
127        }
128    }
129
130    /// Returns `true` if there are no signatures in the collection.
131    pub fn is_empty(&self) -> bool {
132        match self {
133            BlockSignatures::V1(block_signatures) => block_signatures.is_empty(),
134            BlockSignatures::V2(block_signatures) => block_signatures.is_empty(),
135        }
136    }
137
138    /// Merges the collection of signatures in `other` into `self`.
139    ///
140    /// Returns an error if the block hashes, block heights, era IDs, or chain name hashes do not
141    /// match.
142    pub fn merge(&mut self, mut other: Self) -> Result<(), BlockSignaturesMergeError> {
143        if self.block_hash() != other.block_hash() {
144            return Err(BlockSignaturesMergeError::BlockHashMismatch {
145                self_hash: *self.block_hash(),
146                other_hash: *other.block_hash(),
147            });
148        }
149
150        if self.era_id() != other.era_id() {
151            return Err(BlockSignaturesMergeError::EraIdMismatch {
152                self_era_id: self.era_id(),
153                other_era_id: other.era_id(),
154            });
155        }
156
157        match (self, &mut other) {
158            (BlockSignatures::V1(self_), BlockSignatures::V1(other)) => {
159                self_.proofs.append(&mut other.proofs);
160            }
161            (BlockSignatures::V2(self_), BlockSignatures::V2(other)) => {
162                if self_.block_height != other.block_height {
163                    return Err(BlockSignaturesMergeError::BlockHeightMismatch {
164                        self_height: self_.block_height,
165                        other_height: other.block_height,
166                    });
167                }
168
169                if self_.chain_name_hash != other.chain_name_hash {
170                    return Err(BlockSignaturesMergeError::ChainNameHashMismatch {
171                        self_chain_name_hash: self_.chain_name_hash,
172                        other_chain_name_hash: other.chain_name_hash,
173                    });
174                }
175
176                self_.proofs.append(&mut other.proofs);
177            }
178            _ => return Err(BlockSignaturesMergeError::VersionMismatch),
179        }
180
181        Ok(())
182    }
183
184    /// Returns `Ok` if and only if all the signatures are cryptographically valid.
185    pub fn is_verified(&self) -> Result<(), crypto::Error> {
186        match self {
187            BlockSignatures::V1(block_signatures) => block_signatures.is_verified(),
188            BlockSignatures::V2(block_signatures) => block_signatures.is_verified(),
189        }
190    }
191
192    /// Converts self into a `BTreeMap` of public keys to signatures.
193    pub fn into_proofs(self) -> BTreeMap<PublicKey, Signature> {
194        match self {
195            BlockSignatures::V1(block_signatures) => block_signatures.proofs,
196            BlockSignatures::V2(block_signatures) => block_signatures.proofs,
197        }
198    }
199
200    /// Inserts a new signature.
201    pub fn insert_signature(&mut self, public_key: PublicKey, signature: Signature) {
202        match self {
203            BlockSignatures::V1(block_signatures) => {
204                block_signatures.insert_signature(public_key, signature)
205            }
206            BlockSignatures::V2(block_signatures) => {
207                block_signatures.insert_signature(public_key, signature)
208            }
209        }
210    }
211
212    /// Removes a signature corresponding to the specified key.
213    pub fn remove_signature(&mut self, public_key: &PublicKey) -> Option<Signature> {
214        match self {
215            BlockSignatures::V1(block_signatures) => block_signatures.proofs.remove(public_key),
216            BlockSignatures::V2(block_signatures) => block_signatures.proofs.remove(public_key),
217        }
218    }
219
220    /// Sets the era ID to its max value, rendering it and hence `self` invalid (assuming the
221    /// relevant era ID for this `BlockHeaderWithSignatures` wasn't already the max value).
222    #[cfg(any(feature = "testing", test))]
223    pub fn invalidate_era(&mut self) {
224        match self {
225            BlockSignatures::V1(block_signatures) => block_signatures.era_id = EraId::new(u64::MAX),
226            BlockSignatures::V2(block_signatures) => block_signatures.era_id = EraId::new(u64::MAX),
227        }
228    }
229
230    /// Replaces the signature field of the last `proofs` entry with the `System` variant
231    /// of [`Signature`], rendering that entry invalid.
232    #[cfg(any(feature = "testing", test))]
233    pub fn invalidate_last_signature(&mut self) {
234        let proofs = match self {
235            BlockSignatures::V1(block_signatures) => &mut block_signatures.proofs,
236            BlockSignatures::V2(block_signatures) => &mut block_signatures.proofs,
237        };
238        let last_proof = proofs
239            .last_entry()
240            .expect("should have at least one signature");
241        *last_proof.into_mut() = Signature::System;
242    }
243
244    /// Returns a random `BlockSignatures`.
245    #[cfg(any(feature = "testing", test))]
246    pub fn random(rng: &mut TestRng) -> Self {
247        if rng.gen() {
248            BlockSignatures::V1(BlockSignaturesV1::random(rng))
249        } else {
250            BlockSignatures::V2(BlockSignaturesV2::random(rng))
251        }
252    }
253}
254
255impl Display for BlockSignatures {
256    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
257        match self {
258            BlockSignatures::V1(block_signatures) => write!(formatter, "{}", block_signatures),
259            BlockSignatures::V2(block_signatures) => write!(formatter, "{}", block_signatures),
260        }
261    }
262}
263
264impl From<BlockSignaturesV1> for BlockSignatures {
265    fn from(block_signatures: BlockSignaturesV1) -> Self {
266        BlockSignatures::V1(block_signatures)
267    }
268}
269
270impl From<BlockSignaturesV2> for BlockSignatures {
271    fn from(block_signatures: BlockSignaturesV2) -> Self {
272        BlockSignatures::V2(block_signatures)
273    }
274}
275
276impl ToBytes for BlockSignatures {
277    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
278        let mut buf = bytesrepr::allocate_buffer(self)?;
279        self.write_bytes(&mut buf)?;
280        Ok(buf)
281    }
282
283    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
284        match self {
285            BlockSignatures::V1(block_signatures) => {
286                writer.push(BLOCK_SIGNATURES_V1_TAG);
287                block_signatures.write_bytes(writer)?;
288            }
289            BlockSignatures::V2(block_signatures) => {
290                writer.push(BLOCK_SIGNATURES_V2_TAG);
291                block_signatures.write_bytes(writer)?;
292            }
293        }
294        Ok(())
295    }
296
297    fn serialized_length(&self) -> usize {
298        TAG_LENGTH
299            + match self {
300                BlockSignatures::V1(block_signatures) => block_signatures.serialized_length(),
301                BlockSignatures::V2(block_signatures) => block_signatures.serialized_length(),
302            }
303    }
304}
305
306impl FromBytes for BlockSignatures {
307    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
308        let (tag, remainder) = u8::from_bytes(bytes)?;
309        match tag {
310            BLOCK_SIGNATURES_V1_TAG => {
311                let (block_signatures, remainder) = BlockSignaturesV1::from_bytes(remainder)?;
312                Ok((BlockSignatures::V1(block_signatures), remainder))
313            }
314            BLOCK_SIGNATURES_V2_TAG => {
315                let (block_signatures, remainder) = BlockSignaturesV2::from_bytes(remainder)?;
316                Ok((BlockSignatures::V2(block_signatures), remainder))
317            }
318            _ => Err(bytesrepr::Error::Formatting),
319        }
320    }
321}
322
323/// An error returned during an attempt to merge two incompatible [`BlockSignaturesV1`].
324#[derive(Copy, Clone, Eq, PartialEq, Debug)]
325#[non_exhaustive]
326pub enum BlockSignaturesMergeError {
327    /// A mismatch between block hashes.
328    BlockHashMismatch {
329        /// The `self` hash.
330        self_hash: BlockHash,
331        /// The `other` hash.
332        other_hash: BlockHash,
333    },
334    /// A mismatch between block heights.
335    BlockHeightMismatch {
336        /// The `self` height.
337        self_height: u64,
338        /// The `other` height.
339        other_height: u64,
340    },
341    /// A mismatch between era IDs.
342    EraIdMismatch {
343        /// The `self` era ID.
344        self_era_id: EraId,
345        /// The `other` era ID.
346        other_era_id: EraId,
347    },
348    /// A mismatch between chain name hashes.
349    ChainNameHashMismatch {
350        /// The `self` chain name hash.
351        self_chain_name_hash: ChainNameDigest,
352        /// The `other` chain name hash.
353        other_chain_name_hash: ChainNameDigest,
354    },
355    /// A mismatch between the versions of the block signatures.
356    VersionMismatch,
357}
358
359impl Display for BlockSignaturesMergeError {
360    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
361        match self {
362            BlockSignaturesMergeError::BlockHashMismatch {
363                self_hash,
364                other_hash,
365            } => {
366                write!(
367                    formatter,
368                    "mismatch between block hashes while merging block signatures - self: {}, \
369                    other: {}",
370                    self_hash, other_hash
371                )
372            }
373            BlockSignaturesMergeError::BlockHeightMismatch {
374                self_height,
375                other_height,
376            } => {
377                write!(
378                    formatter,
379                    "mismatch between block heights while merging block signatures - self: {}, \
380                    other: {}",
381                    self_height, other_height
382                )
383            }
384            BlockSignaturesMergeError::EraIdMismatch {
385                self_era_id,
386                other_era_id,
387            } => {
388                write!(
389                    formatter,
390                    "mismatch between era ids while merging block signatures - self: {}, other: \
391                    {}",
392                    self_era_id, other_era_id
393                )
394            }
395            BlockSignaturesMergeError::ChainNameHashMismatch {
396                self_chain_name_hash,
397                other_chain_name_hash,
398            } => {
399                write!(
400                    formatter,
401                    "mismatch between chain name hashes while merging block signatures - self: {}, \
402                    other: {}",
403                    self_chain_name_hash, other_chain_name_hash
404                )
405            }
406            BlockSignaturesMergeError::VersionMismatch => {
407                write!(
408                    formatter,
409                    "mismatch between versions of block signatures while merging"
410                )
411            }
412        }
413    }
414}
415
416#[cfg(feature = "std")]
417impl StdError for BlockSignaturesMergeError {}
418
419#[cfg(test)]
420mod tests {
421    use super::*;
422
423    #[test]
424    fn bytesrepr_roundtrip() {
425        let rng = &mut TestRng::new();
426        let hash = BlockSignatures::random(rng);
427        bytesrepr::test_serialization_roundtrip(&hash);
428    }
429}