use crate::vote_data::VoteData;
use anyhow::{ensure, Context};
use diem_crypto::{hash::CryptoHash, HashValue};
use diem_types::{
block_info::BlockInfo,
ledger_info::{
LedgerInfo, LedgerInfoWithSignatures, LedgerInfoWithSignaturesUnchecked,
},
validator_verifier::ValidatorVerifier,
};
use serde::{Deserialize, Serialize};
use std::{
collections::BTreeMap,
fmt::{Display, Formatter},
};
#[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)]
pub struct QuorumCert {
vote_data: VoteData,
signed_ledger_info: LedgerInfoWithSignatures,
}
#[derive(Deserialize)]
pub struct QuorumCertUnchecked {
vote_data: VoteData,
signed_ledger_info: LedgerInfoWithSignaturesUnchecked,
}
impl From<QuorumCertUnchecked> for QuorumCert {
fn from(q: QuorumCertUnchecked) -> Self {
Self {
vote_data: q.vote_data,
signed_ledger_info: q.signed_ledger_info.into(),
}
}
}
impl Display for QuorumCert {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(
f,
"QuorumCert: [{}, {}]",
self.vote_data, self.signed_ledger_info
)
}
}
impl QuorumCert {
pub fn new(
vote_data: VoteData, signed_ledger_info: LedgerInfoWithSignatures,
) -> Self {
QuorumCert {
vote_data,
signed_ledger_info,
}
}
pub fn vote_data(&self) -> &VoteData { &self.vote_data }
pub fn certified_block(&self) -> &BlockInfo { self.vote_data().proposed() }
pub fn parent_block(&self) -> &BlockInfo { self.vote_data().parent() }
pub fn ledger_info(&self) -> &LedgerInfoWithSignatures {
&self.signed_ledger_info
}
pub fn commit_info(&self) -> &BlockInfo {
self.ledger_info().ledger_info().commit_info()
}
pub fn ends_epoch(&self) -> bool {
self.signed_ledger_info.ledger_info().ends_epoch()
}
pub fn certificate_for_genesis_from_ledger_info(
ledger_info: &LedgerInfo, genesis_id: HashValue,
) -> QuorumCert {
let ancestor = BlockInfo::new(
ledger_info
.epoch()
.checked_add(1)
.expect("Integer overflow when creating cert for genesis from ledger info"),
0,
genesis_id,
ledger_info.transaction_accumulator_hash(),
ledger_info.version(),
ledger_info.timestamp_usecs(),
None,
ledger_info.pivot_decision().cloned(),
);
let vote_data = VoteData::new(ancestor.clone(), ancestor.clone());
let li = LedgerInfo::new(ancestor, vote_data.hash());
QuorumCert::new(
vote_data,
LedgerInfoWithSignatures::new(li, BTreeMap::new()),
)
}
pub fn verify(&self, validator: &ValidatorVerifier) -> anyhow::Result<()> {
let vote_hash = self.vote_data.hash();
ensure!(
self.ledger_info().ledger_info().consensus_data_hash() == vote_hash,
"Quorum Cert's hash mismatch LedgerInfo"
);
if self.certified_block().round() == 0 {
ensure!(
self.parent_block() == self.certified_block(),
"Genesis QC has inconsistent parent block with certified block"
);
ensure!(
self.certified_block()
== self.ledger_info().ledger_info().commit_info(),
"Genesis QC has inconsistent commit block with certified block"
);
ensure!(
self.ledger_info().signatures().is_empty(),
"Genesis QC should not carry signatures"
);
return Ok(());
}
self.ledger_info()
.verify_signatures(validator)
.context("Fail to verify QuorumCert")?;
self.vote_data.verify()?;
Ok(())
}
}