consensus_types/
sync_info.rsuse crate::{
    common::Round, quorum_cert::QuorumCert,
    timeout_certificate::TimeoutCertificate,
};
use anyhow::{ensure, Context};
use diem_types::{
    block_info::BlockInfo, validator_verifier::ValidatorVerifier,
};
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Display, Formatter};
#[derive(Deserialize, Serialize, Clone, Eq, PartialEq)]
pub struct SyncInfo {
    highest_quorum_cert: QuorumCert,
    highest_commit_cert: Option<QuorumCert>,
    highest_timeout_cert: Option<TimeoutCertificate>,
}
impl Debug for SyncInfo {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}
impl Display for SyncInfo {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        let htc_repr = match self.highest_timeout_certificate() {
            Some(tc) => format!("{}", tc.round()),
            None => "None".to_string(),
        };
        write!(
            f,
            "SyncInfo[HQC: {}, HCC: {}, HTC: {}]",
            self.highest_certified_round(),
            self.highest_commit_round(),
            htc_repr,
        )
    }
}
impl SyncInfo {
    pub fn new(
        highest_quorum_cert: QuorumCert, highest_commit_cert: QuorumCert,
        highest_timeout_cert: Option<TimeoutCertificate>,
    ) -> Self {
        let commit_cert = if highest_quorum_cert == highest_commit_cert {
            None
        } else {
            Some(highest_commit_cert)
        };
        let highest_timeout_cert = highest_timeout_cert.filter(|tc| {
            tc.round() > highest_quorum_cert.certified_block().round()
        });
        Self {
            highest_quorum_cert,
            highest_commit_cert: commit_cert,
            highest_timeout_cert,
        }
    }
    pub fn highest_quorum_cert(&self) -> &QuorumCert {
        &self.highest_quorum_cert
    }
    pub fn highest_commit_cert(&self) -> &QuorumCert {
        self.highest_commit_cert
            .as_ref()
            .unwrap_or(&self.highest_quorum_cert)
    }
    pub fn highest_timeout_certificate(&self) -> Option<&TimeoutCertificate> {
        self.highest_timeout_cert.as_ref()
    }
    pub fn highest_certified_round(&self) -> Round {
        self.highest_quorum_cert.certified_block().round()
    }
    pub fn highest_timeout_round(&self) -> Round {
        self.highest_timeout_certificate()
            .map_or(0, |tc| tc.round())
    }
    pub fn highest_commit_round(&self) -> Round {
        self.highest_commit_cert().commit_info().round()
    }
    pub fn highest_round(&self) -> Round {
        std::cmp::max(
            self.highest_certified_round(),
            self.highest_timeout_round(),
        )
    }
    pub fn verify(&self, validator: &ValidatorVerifier) -> anyhow::Result<()> {
        let epoch = self.highest_quorum_cert.certified_block().epoch();
        ensure!(
            epoch == self.highest_commit_cert().certified_block().epoch(),
            "Multi epoch in SyncInfo - HCC and HQC"
        );
        if let Some(tc) = &self.highest_timeout_cert {
            ensure!(
                epoch == tc.epoch(),
                "Multi epoch in SyncInfo - TC and HQC"
            );
        }
        ensure!(
            self.highest_quorum_cert.certified_block().round()
                >= self.highest_commit_cert().certified_block().round(),
            "HQC has lower round than HCC"
        );
        ensure!(
            *self.highest_commit_cert().commit_info() != BlockInfo::empty(),
            "HCC has no committed block"
        );
        self.highest_quorum_cert
            .verify(validator)
            .and_then(|_| {
                self.highest_commit_cert
                    .as_ref()
                    .map_or(Ok(()), |cert| cert.verify(validator))
            })
            .and_then(|_| {
                if let Some(tc) = &self.highest_timeout_cert {
                    tc.verify(validator)?;
                }
                Ok(())
            })
            .context("Fail to verify SyncInfo")?;
        Ok(())
    }
    pub fn epoch(&self) -> u64 {
        self.highest_quorum_cert.certified_block().epoch()
    }
    pub fn has_newer_certificates(&self, other: &SyncInfo) -> bool {
        self.highest_certified_round() > other.highest_certified_round()
            || self.highest_timeout_round() > other.highest_timeout_round()
            || self.highest_commit_round() > other.highest_commit_round()
    }
}