use crate::{
common::Author, quorum_cert::QuorumCert, timeout_2chain::TwoChainTimeout, vote_data::VoteData,
};
use anyhow::{ensure, Context};
use aptos_crypto::{bls12381, hash::CryptoHash};
use aptos_types::{
ledger_info::LedgerInfo, validator_signer::ValidatorSigner,
validator_verifier::ValidatorVerifier,
};
use serde::{Deserialize, Serialize};
use short_hex_str::AsShortHexStr;
use std::fmt::{Debug, Display, Formatter};
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct Vote {
vote_data: VoteData,
author: Author,
ledger_info: LedgerInfo,
signature: bls12381::Signature,
two_chain_timeout: Option<(TwoChainTimeout, bls12381::Signature)>,
}
impl Debug for Vote {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl Display for Vote {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(
f,
"Vote: [vote data: {}, author: {}, is_timeout: {}, {}]",
self.vote_data,
self.author.short_str(),
self.is_timeout(),
self.ledger_info
)
}
}
impl Vote {
pub fn new(
vote_data: VoteData,
author: Author,
mut ledger_info_placeholder: LedgerInfo,
validator_signer: &ValidatorSigner,
) -> Self {
ledger_info_placeholder.set_consensus_data_hash(vote_data.hash());
let signature = validator_signer.sign(&ledger_info_placeholder);
Self::new_with_signature(vote_data, author, ledger_info_placeholder, signature)
}
pub fn new_with_signature(
vote_data: VoteData,
author: Author,
ledger_info: LedgerInfo,
signature: bls12381::Signature,
) -> Self {
Self {
vote_data,
author,
ledger_info,
signature,
two_chain_timeout: None,
}
}
pub fn add_2chain_timeout(&mut self, timeout: TwoChainTimeout, signature: bls12381::Signature) {
self.two_chain_timeout = Some((timeout, signature));
}
pub fn vote_data(&self) -> &VoteData {
&self.vote_data
}
pub fn author(&self) -> Author {
self.author
}
pub fn ledger_info(&self) -> &LedgerInfo {
&self.ledger_info
}
pub fn signature(&self) -> &bls12381::Signature {
&self.signature
}
pub fn generate_2chain_timeout(&self, qc: QuorumCert) -> TwoChainTimeout {
TwoChainTimeout::new(
self.vote_data.proposed().epoch(),
self.vote_data.proposed().round(),
qc,
)
}
pub fn epoch(&self) -> u64 {
self.vote_data.proposed().epoch()
}
pub fn two_chain_timeout(&self) -> Option<&(TwoChainTimeout, bls12381::Signature)> {
self.two_chain_timeout.as_ref()
}
pub fn is_timeout(&self) -> bool {
self.two_chain_timeout.is_some()
}
pub fn verify(&self, validator: &ValidatorVerifier) -> anyhow::Result<()> {
ensure!(
self.ledger_info.consensus_data_hash() == self.vote_data.hash(),
"Vote's hash mismatch with LedgerInfo"
);
validator
.verify(self.author(), &self.ledger_info, &self.signature)
.context("Failed to verify Vote")?;
if let Some((timeout, signature)) = &self.two_chain_timeout {
ensure!(
(timeout.epoch(), timeout.round())
== (self.epoch(), self.vote_data.proposed().round()),
"2-chain timeout has different (epoch, round) than Vote"
);
timeout.verify(validator)?;
validator
.verify(self.author(), &timeout.signing_format(), signature)
.context("Failed to verify 2-chain timeout signature")?;
}
self.vote_data().verify()?;
Ok(())
}
}