tendermint/proposal/
canonical_proposal.rs1use super::Type;
4use crate::{
5 block::{Height, Id as BlockId, Round},
6 chain::Id as ChainId,
7 prelude::*,
8 Time,
9};
10
11#[derive(Clone, PartialEq, Eq)]
13pub struct CanonicalProposal {
14 pub msg_type: Type,
16 pub height: Height,
18 pub round: Round,
20 pub pol_round: Option<Round>,
22 pub block_id: Option<BlockId>,
24 pub timestamp: Option<Time>,
26 pub chain_id: ChainId,
28}
29
30tendermint_pb_modules! {
31 use crate::{
32 block::{Id as BlockId, Round},
33 chain::Id as ChainId,
34 error::Error,
35 prelude::*,
36 };
37 use super::CanonicalProposal;
38 use pb::types::CanonicalProposal as RawCanonicalProposal;
39
40 impl Protobuf<RawCanonicalProposal> for CanonicalProposal {}
41
42 impl TryFrom<RawCanonicalProposal> for CanonicalProposal {
43 type Error = Error;
44
45 fn try_from(value: RawCanonicalProposal) -> Result<Self, Self::Error> {
46 if value.pol_round < -1 {
47 return Err(Error::negative_pol_round());
48 }
49 let round = Round::try_from(i32::try_from(value.round).map_err(Error::integer_overflow)?)?;
50 let pol_round = match value.pol_round {
51 -1 => None,
52 n => Some(Round::try_from(
53 i32::try_from(n).map_err(Error::integer_overflow)?,
54 )?),
55 };
56 let block_id = value.block_id.filter(|i| !i.hash.is_empty());
59 Ok(CanonicalProposal {
60 msg_type: value.r#type.try_into()?,
61 height: value.height.try_into()?,
62 round,
63 pol_round,
64 block_id: block_id.map(TryInto::try_into).transpose()?,
65 timestamp: value.timestamp.map(|t| t.try_into()).transpose()?,
66 chain_id: ChainId::try_from(value.chain_id).unwrap(),
67 })
68 }
69 }
70
71 impl From<CanonicalProposal> for RawCanonicalProposal {
72 fn from(value: CanonicalProposal) -> Self {
73 let block_id = value.block_id.filter(|i| i != &BlockId::default());
76 RawCanonicalProposal {
77 r#type: value.msg_type.into(),
78 height: value.height.into(),
79 round: i32::from(value.round) as i64,
80 pol_round: match value.pol_round {
81 None => -1,
82 Some(p) => i32::from(p) as i64,
83 },
84 block_id: block_id.map(Into::into),
85 timestamp: value.timestamp.map(Into::into),
86 chain_id: value.chain_id.as_str().to_string(),
87 }
88 }
89 }
90}
91
92impl CanonicalProposal {
93 pub fn new(proposal: super::Proposal, chain_id: ChainId) -> CanonicalProposal {
95 CanonicalProposal {
96 msg_type: proposal.msg_type,
97 height: proposal.height,
98 round: proposal.round,
99 pol_round: proposal.pol_round,
100 block_id: proposal.block_id,
101 timestamp: proposal.timestamp,
102 chain_id,
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 tendermint_pb_modules! {
110 use pb::types::{
111 CanonicalBlockId as RawCanonicalBlockId,
112 CanonicalPartSetHeader as RawCanonicalPartSetHeader,
113 CanonicalProposal as RawCanonicalProposal,
114 };
115
116 use crate::{
117 prelude::*,
118 proposal::{canonical_proposal::CanonicalProposal, Type},
119 };
120
121 #[test]
122 fn canonical_proposal_domain_checks() {
123 let proto_cp = RawCanonicalProposal {
127 r#type: 32,
128 height: 2,
129 round: 4,
130 pol_round: -1,
131 block_id: Some(RawCanonicalBlockId {
132 hash: vec![],
133 part_set_header: Some(RawCanonicalPartSetHeader {
134 total: 1,
135 hash: vec![1],
136 }),
137 }),
138 timestamp: None,
139 chain_id: "testchain".to_string(),
140 };
141 let cp = CanonicalProposal::try_from(proto_cp).unwrap();
142 assert_eq!(cp.msg_type, Type::Proposal);
143 assert!(cp.pol_round.is_none());
144 assert!(cp.block_id.is_none());
145 }
146 }
147}