consensus_types/
quorum_cert.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::vote_data::VoteData;
9use anyhow::{ensure, Context};
10use diem_crypto::{hash::CryptoHash, HashValue};
11use diem_types::{
12    block_info::BlockInfo,
13    ledger_info::{
14        LedgerInfo, LedgerInfoWithSignatures, LedgerInfoWithSignaturesUnchecked,
15    },
16    validator_verifier::ValidatorVerifier,
17};
18use serde::{Deserialize, Serialize};
19use std::{
20    collections::BTreeMap,
21    fmt::{Display, Formatter},
22};
23
24#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
25pub struct QuorumCert {
26    /// The vote information certified by the quorum.
27    vote_data: VoteData,
28    /// The signed LedgerInfo of a committed block that carries the data about
29    /// the certified block.
30    signed_ledger_info: LedgerInfoWithSignatures,
31}
32
33#[derive(Deserialize)]
34pub struct QuorumCertUnchecked {
35    vote_data: VoteData,
36    signed_ledger_info: LedgerInfoWithSignaturesUnchecked,
37}
38
39impl From<QuorumCertUnchecked> for QuorumCert {
40    fn from(q: QuorumCertUnchecked) -> Self {
41        Self {
42            vote_data: q.vote_data,
43            signed_ledger_info: q.signed_ledger_info.into(),
44        }
45    }
46}
47
48impl Display for QuorumCert {
49    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
50        write!(
51            f,
52            "QuorumCert: [{}, {}]",
53            self.vote_data, self.signed_ledger_info
54        )
55    }
56}
57
58impl QuorumCert {
59    pub fn new(
60        vote_data: VoteData, signed_ledger_info: LedgerInfoWithSignatures,
61    ) -> Self {
62        QuorumCert {
63            vote_data,
64            signed_ledger_info,
65        }
66    }
67
68    pub fn vote_data(&self) -> &VoteData { &self.vote_data }
69
70    pub fn certified_block(&self) -> &BlockInfo { self.vote_data().proposed() }
71
72    pub fn parent_block(&self) -> &BlockInfo { self.vote_data().parent() }
73
74    pub fn ledger_info(&self) -> &LedgerInfoWithSignatures {
75        &self.signed_ledger_info
76    }
77
78    pub fn commit_info(&self) -> &BlockInfo {
79        self.ledger_info().ledger_info().commit_info()
80    }
81
82    /// If the QC commits reconfiguration and starts a new epoch
83    pub fn ends_epoch(&self) -> bool {
84        self.signed_ledger_info.ledger_info().ends_epoch()
85    }
86
87    /// QuorumCert for the genesis block deterministically generated from
88    /// end-epoch LedgerInfo:
89    /// - the ID of the block is determined by the generated genesis block.
90    /// - the accumulator root hash of the LedgerInfo is set to the last
91    ///   executed state of previous epoch.
92    /// - the map of signatures is empty because genesis block is implicitly
93    ///   agreed.
94    pub fn certificate_for_genesis_from_ledger_info(
95        ledger_info: &LedgerInfo, genesis_id: HashValue,
96    ) -> QuorumCert {
97        let ancestor = BlockInfo::new(
98            ledger_info
99                .epoch()
100                .checked_add(1)
101                .expect("Integer overflow when creating cert for genesis from ledger info"),
102            0,
103            genesis_id,
104            ledger_info.transaction_accumulator_hash(),
105            ledger_info.version(),
106            ledger_info.timestamp_usecs(),
107            None,
108            ledger_info.pivot_decision().cloned(),
109        );
110        let vote_data = VoteData::new(ancestor.clone(), ancestor.clone());
111        let li = LedgerInfo::new(ancestor, vote_data.hash());
112
113        QuorumCert::new(
114            vote_data,
115            LedgerInfoWithSignatures::new(li, BTreeMap::new()),
116        )
117    }
118
119    pub fn verify(&self, validator: &ValidatorVerifier) -> anyhow::Result<()> {
120        let vote_hash = self.vote_data.hash();
121        ensure!(
122            self.ledger_info().ledger_info().consensus_data_hash() == vote_hash,
123            "Quorum Cert's hash mismatch LedgerInfo"
124        );
125        // Genesis's QC is implicitly agreed upon, it doesn't have real
126        // signatures. If someone sends us a QC on a fake genesis, it'll
127        // fail to insert into BlockStore because of the round
128        // constraint.
129        if self.certified_block().round() == 0 {
130            ensure!(
131                self.parent_block() == self.certified_block(),
132                "Genesis QC has inconsistent parent block with certified block"
133            );
134            ensure!(
135                self.certified_block()
136                    == self.ledger_info().ledger_info().commit_info(),
137                "Genesis QC has inconsistent commit block with certified block"
138            );
139            ensure!(
140                self.ledger_info().signatures().is_empty(),
141                "Genesis QC should not carry signatures"
142            );
143            return Ok(());
144        }
145        self.ledger_info()
146            .verify_signatures(validator)
147            .context("Fail to verify QuorumCert")?;
148        self.vote_data.verify()?;
149        Ok(())
150    }
151}