Skip to main content

hotmint_types/
certificate.rs

1use serde::{Deserialize, Serialize};
2
3use crate::block::BlockHash;
4use crate::crypto::AggregateSignature;
5use crate::view::ViewNumber;
6
7/// C_v(B_k): quorum certificate — 2f+1 validators signed the block hash in view v
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct QuorumCertificate {
10    pub block_hash: BlockHash,
11    pub view: ViewNumber,
12    pub aggregate_signature: AggregateSignature,
13}
14
15impl QuorumCertificate {
16    /// Rank of a QC is its view number (used for comparison in safety rules)
17    pub fn rank(&self) -> ViewNumber {
18        self.view
19    }
20}
21
22/// C_v(C_v(B_k)): double certificate — QC of QC, triggers commit
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct DoubleCertificate {
25    pub inner_qc: QuorumCertificate,
26    pub outer_qc: QuorumCertificate,
27}
28
29/// TC_v: timeout certificate — 2f+1 validators want to leave view v
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct TimeoutCertificate {
32    pub view: ViewNumber,
33    pub aggregate_signature: AggregateSignature,
34    /// Each signer's highest known QC
35    pub highest_qcs: Vec<Option<QuorumCertificate>>,
36}
37
38impl TimeoutCertificate {
39    /// The highest QC carried in the TC
40    pub fn highest_qc(&self) -> Option<&QuorumCertificate> {
41        self.highest_qcs
42            .iter()
43            .filter_map(|qc| qc.as_ref())
44            .max_by_key(|qc| qc.view)
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    fn make_qc(view: u64, hash: [u8; 32]) -> QuorumCertificate {
53        QuorumCertificate {
54            block_hash: BlockHash(hash),
55            view: ViewNumber(view),
56            aggregate_signature: AggregateSignature::new(4),
57        }
58    }
59
60    #[test]
61    fn test_qc_rank() {
62        let qc = make_qc(5, [1u8; 32]);
63        assert_eq!(qc.rank(), ViewNumber(5));
64    }
65
66    #[test]
67    fn test_tc_highest_qc_some() {
68        let qc1 = make_qc(3, [1u8; 32]);
69        let qc2 = make_qc(7, [2u8; 32]);
70        let tc = TimeoutCertificate {
71            view: ViewNumber(8),
72            aggregate_signature: AggregateSignature::new(4),
73            highest_qcs: vec![Some(qc1), None, Some(qc2), None],
74        };
75        let highest = tc.highest_qc().unwrap();
76        assert_eq!(highest.view, ViewNumber(7));
77    }
78
79    #[test]
80    fn test_tc_highest_qc_all_none() {
81        let tc = TimeoutCertificate {
82            view: ViewNumber(5),
83            aggregate_signature: AggregateSignature::new(4),
84            highest_qcs: vec![None, None, None, None],
85        };
86        assert!(tc.highest_qc().is_none());
87    }
88
89    #[test]
90    fn test_tc_highest_qc_empty() {
91        let tc = TimeoutCertificate {
92            view: ViewNumber(5),
93            aggregate_signature: AggregateSignature::new(4),
94            highest_qcs: vec![],
95        };
96        assert!(tc.highest_qc().is_none());
97    }
98}