consensus_types/
sync_info.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::{
9    common::Round, quorum_cert::QuorumCert,
10    timeout_certificate::TimeoutCertificate,
11};
12use anyhow::{ensure, Context};
13use diem_types::{
14    block_info::BlockInfo, validator_verifier::ValidatorVerifier,
15};
16use serde::{Deserialize, Serialize};
17use std::fmt::{Debug, Display, Formatter};
18
19#[derive(Deserialize, Serialize, Clone, Eq, PartialEq)]
20/// This struct describes basic synchronization metadata.
21pub struct SyncInfo {
22    /// Highest quorum certificate known to the peer.
23    highest_quorum_cert: QuorumCert,
24    /// Highest ledger info known to the peer.
25    highest_commit_cert: Option<QuorumCert>,
26    /// Optional highest timeout certificate if available.
27    highest_timeout_cert: Option<TimeoutCertificate>,
28}
29
30// this is required by structured log
31impl Debug for SyncInfo {
32    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
33        write!(f, "{}", self)
34    }
35}
36
37impl Display for SyncInfo {
38    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
39        let htc_repr = match self.highest_timeout_certificate() {
40            Some(tc) => format!("{}", tc.round()),
41            None => "None".to_string(),
42        };
43        write!(
44            f,
45            "SyncInfo[HQC: {}, HCC: {}, HTC: {}]",
46            self.highest_certified_round(),
47            self.highest_commit_round(),
48            htc_repr,
49        )
50    }
51}
52
53impl SyncInfo {
54    pub fn new(
55        highest_quorum_cert: QuorumCert, highest_commit_cert: QuorumCert,
56        highest_timeout_cert: Option<TimeoutCertificate>,
57    ) -> Self {
58        let commit_cert = if highest_quorum_cert == highest_commit_cert {
59            None
60        } else {
61            Some(highest_commit_cert)
62        };
63        // No need to include HTC if it's lower than HQC
64        let highest_timeout_cert = highest_timeout_cert.filter(|tc| {
65            tc.round() > highest_quorum_cert.certified_block().round()
66        });
67        Self {
68            highest_quorum_cert,
69            highest_commit_cert: commit_cert,
70            highest_timeout_cert,
71        }
72    }
73
74    /// Highest quorum certificate
75    pub fn highest_quorum_cert(&self) -> &QuorumCert {
76        &self.highest_quorum_cert
77    }
78
79    /// Highest ledger info
80    pub fn highest_commit_cert(&self) -> &QuorumCert {
81        self.highest_commit_cert
82            .as_ref()
83            .unwrap_or(&self.highest_quorum_cert)
84    }
85
86    /// Highest timeout certificate if available
87    pub fn highest_timeout_certificate(&self) -> Option<&TimeoutCertificate> {
88        self.highest_timeout_cert.as_ref()
89    }
90
91    pub fn highest_certified_round(&self) -> Round {
92        self.highest_quorum_cert.certified_block().round()
93    }
94
95    pub fn highest_timeout_round(&self) -> Round {
96        self.highest_timeout_certificate()
97            .map_or(0, |tc| tc.round())
98    }
99
100    pub fn highest_commit_round(&self) -> Round {
101        self.highest_commit_cert().commit_info().round()
102    }
103
104    /// The highest round the SyncInfo carries.
105    pub fn highest_round(&self) -> Round {
106        std::cmp::max(
107            self.highest_certified_round(),
108            self.highest_timeout_round(),
109        )
110    }
111
112    pub fn verify(&self, validator: &ValidatorVerifier) -> anyhow::Result<()> {
113        let epoch = self.highest_quorum_cert.certified_block().epoch();
114        ensure!(
115            epoch == self.highest_commit_cert().certified_block().epoch(),
116            "Multi epoch in SyncInfo - HCC and HQC"
117        );
118        if let Some(tc) = &self.highest_timeout_cert {
119            ensure!(
120                epoch == tc.epoch(),
121                "Multi epoch in SyncInfo - TC and HQC"
122            );
123        }
124
125        ensure!(
126            self.highest_quorum_cert.certified_block().round()
127                >= self.highest_commit_cert().certified_block().round(),
128            "HQC has lower round than HCC"
129        );
130        ensure!(
131            *self.highest_commit_cert().commit_info() != BlockInfo::empty(),
132            "HCC has no committed block"
133        );
134        self.highest_quorum_cert
135            .verify(validator)
136            .and_then(|_| {
137                self.highest_commit_cert
138                    .as_ref()
139                    .map_or(Ok(()), |cert| cert.verify(validator))
140            })
141            .and_then(|_| {
142                if let Some(tc) = &self.highest_timeout_cert {
143                    tc.verify(validator)?;
144                }
145                Ok(())
146            })
147            .context("Fail to verify SyncInfo")?;
148        Ok(())
149    }
150
151    pub fn epoch(&self) -> u64 {
152        self.highest_quorum_cert.certified_block().epoch()
153    }
154
155    pub fn has_newer_certificates(&self, other: &SyncInfo) -> bool {
156        self.highest_certified_round() > other.highest_certified_round()
157            || self.highest_timeout_round() > other.highest_timeout_round()
158            || self.highest_commit_round() > other.highest_commit_round()
159    }
160}