tendermint/vote/
canonical_vote.rs1use serde::{Deserialize, Serialize};
2use tendermint_proto::v0_37::types::CanonicalVote as RawCanonicalVote;
3
4use crate::{block, chain::Id as ChainId, prelude::*, Time};
5
6#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
8#[serde(try_from = "RawCanonicalVote", into = "RawCanonicalVote")]
9pub struct CanonicalVote {
10 pub vote_type: super::Type,
12
13 pub height: block::Height,
15
16 pub round: block::Round,
18
19 pub block_id: Option<block::Id>,
22
23 pub timestamp: Option<Time>,
25
26 pub chain_id: ChainId,
28}
29
30tendermint_pb_modules! {
31 use super::CanonicalVote;
32 use crate::Error;
33 use crate::{block, chain::Id as ChainId, prelude::*};
34 use pb::types::CanonicalVote as RawCanonicalVote;
35
36 impl Protobuf<RawCanonicalVote> for CanonicalVote {}
37
38 impl TryFrom<RawCanonicalVote> for CanonicalVote {
39 type Error = Error;
40
41 fn try_from(value: RawCanonicalVote) -> Result<Self, Self::Error> {
42 if value.timestamp.is_none() {
43 return Err(Error::missing_timestamp());
44 }
45 let _val: i32 = value.round.try_into().map_err(Error::integer_overflow)?;
46
47 let block_id = value.block_id.filter(|i| !i.hash.is_empty());
50 Ok(CanonicalVote {
51 vote_type: value.r#type.try_into()?,
52 height: value.height.try_into()?,
53 round: (value.round as i32).try_into()?,
54 block_id: block_id.map(|b| b.try_into()).transpose()?,
55 timestamp: value.timestamp.map(|t| t.try_into()).transpose()?,
56 chain_id: ChainId::try_from(value.chain_id)?,
57 })
58 }
59 }
60
61 impl From<CanonicalVote> for RawCanonicalVote {
62 fn from(value: CanonicalVote) -> Self {
63 let block_id = value.block_id.filter(|i| i != &block::Id::default());
66 RawCanonicalVote {
67 r#type: value.vote_type.into(),
68 height: value.height.into(),
69 round: value.round.value().into(),
70 block_id: block_id.map(Into::into),
71 timestamp: value.timestamp.map(Into::into),
72 chain_id: value.chain_id.to_string(),
73 }
74 }
75 }
76}
77
78impl CanonicalVote {
79 pub fn new(vote: super::Vote, chain_id: ChainId) -> CanonicalVote {
81 CanonicalVote {
82 vote_type: vote.vote_type,
83 height: vote.height,
84 round: vote.round,
85 block_id: vote.block_id,
86 timestamp: vote.timestamp,
87 chain_id,
88 }
89 }
90}
91
92#[cfg(test)]
93mod tests {
94
95 tendermint_pb_modules! {
96 use tendermint_proto::google::protobuf::Timestamp;
97 use pb::types::{
98 CanonicalBlockId as RawCanonicalBlockId,
99 CanonicalPartSetHeader as RawCanonicalPartSetHeader,
100 CanonicalVote as RawCanonicalVote,
101 };
102 use crate::{
103 prelude::*,
104 vote::{canonical_vote::CanonicalVote, Type},
105 };
106
107 #[test]
108 fn canonical_vote_domain_checks() {
109 let proto_cp = RawCanonicalVote {
113 r#type: 1,
114 height: 2,
115 round: 4,
116 block_id: Some(RawCanonicalBlockId {
117 hash: vec![],
118 part_set_header: Some(RawCanonicalPartSetHeader {
119 total: 1,
120 hash: vec![1],
121 }),
122 }),
123 timestamp: Some(Timestamp {
124 seconds: 0,
125 nanos: 0,
126 }),
127 chain_id: "testchain".to_string(),
128 };
129 let cp = CanonicalVote::try_from(proto_cp).unwrap();
130 assert_eq!(cp.vote_type, Type::Prevote);
131 assert!(cp.block_id.is_none());
132 assert!(cp.timestamp.is_some());
133
134 let mut proto_cp: RawCanonicalVote = cp.into();
137 proto_cp.timestamp = None;
138 assert!(CanonicalVote::try_from(proto_cp).is_err());
139 }
140 }
141}