tendermint/vote/
canonical_vote.rsuse serde::{Deserialize, Serialize};
use tendermint_proto::v0_37::types::CanonicalVote as RawCanonicalVote;
use crate::{block, chain::Id as ChainId, prelude::*, Time};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(try_from = "RawCanonicalVote", into = "RawCanonicalVote")]
pub struct CanonicalVote {
pub vote_type: super::Type,
pub height: block::Height,
pub round: block::Round,
pub block_id: Option<block::Id>,
pub timestamp: Option<Time>,
pub chain_id: ChainId,
}
tendermint_pb_modules! {
use super::CanonicalVote;
use crate::Error;
use crate::{block, chain::Id as ChainId, prelude::*};
use pb::types::CanonicalVote as RawCanonicalVote;
impl Protobuf<RawCanonicalVote> for CanonicalVote {}
impl TryFrom<RawCanonicalVote> for CanonicalVote {
type Error = Error;
fn try_from(value: RawCanonicalVote) -> Result<Self, Self::Error> {
if value.timestamp.is_none() {
return Err(Error::missing_timestamp());
}
let _val: i32 = value.round.try_into().map_err(Error::integer_overflow)?;
let block_id = value.block_id.filter(|i| !i.hash.is_empty());
Ok(CanonicalVote {
vote_type: value.r#type.try_into()?,
height: value.height.try_into()?,
round: (value.round as i32).try_into()?,
block_id: block_id.map(|b| b.try_into()).transpose()?,
timestamp: value.timestamp.map(|t| t.try_into()).transpose()?,
chain_id: ChainId::try_from(value.chain_id)?,
})
}
}
impl From<CanonicalVote> for RawCanonicalVote {
fn from(value: CanonicalVote) -> Self {
let block_id = value.block_id.filter(|i| i != &block::Id::default());
RawCanonicalVote {
r#type: value.vote_type.into(),
height: value.height.into(),
round: value.round.value().into(),
block_id: block_id.map(Into::into),
timestamp: value.timestamp.map(Into::into),
chain_id: value.chain_id.to_string(),
}
}
}
}
impl CanonicalVote {
pub fn new(vote: super::Vote, chain_id: ChainId) -> CanonicalVote {
CanonicalVote {
vote_type: vote.vote_type,
height: vote.height,
round: vote.round,
block_id: vote.block_id,
timestamp: vote.timestamp,
chain_id,
}
}
}
#[cfg(test)]
mod tests {
tendermint_pb_modules! {
use tendermint_proto::google::protobuf::Timestamp;
use pb::types::{
CanonicalBlockId as RawCanonicalBlockId,
CanonicalPartSetHeader as RawCanonicalPartSetHeader,
CanonicalVote as RawCanonicalVote,
};
use crate::{
prelude::*,
vote::{canonical_vote::CanonicalVote, Type},
};
#[test]
fn canonical_vote_domain_checks() {
let proto_cp = RawCanonicalVote {
r#type: 1,
height: 2,
round: 4,
block_id: Some(RawCanonicalBlockId {
hash: vec![],
part_set_header: Some(RawCanonicalPartSetHeader {
total: 1,
hash: vec![1],
}),
}),
timestamp: Some(Timestamp {
seconds: 0,
nanos: 0,
}),
chain_id: "testchain".to_string(),
};
let cp = CanonicalVote::try_from(proto_cp).unwrap();
assert_eq!(cp.vote_type, Type::Prevote);
assert!(cp.block_id.is_none());
assert!(cp.timestamp.is_some());
let mut proto_cp: RawCanonicalVote = cp.into();
proto_cp.timestamp = None;
assert!(CanonicalVote::try_from(proto_cp).is_err());
}
}
}