tendermint/consensus/
state.rs1pub use core::{cmp::Ordering, fmt};
4
5use serde::{Deserialize, Serialize};
6
7pub use crate::block;
8use crate::prelude::*;
9
10pub const NIL_PLACEHOLDER: &str = "<nil>";
13
14#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
18pub struct State {
19 pub height: block::Height,
21
22 pub round: block::Round,
24
25 pub step: i8,
27
28 #[serde(with = "tendermint_proto::serializers::optional")]
30 pub block_id: Option<block::Id>,
31}
32
33impl State {
34 pub fn block_id_prefix(&self) -> String {
36 self.block_id
37 .as_ref()
38 .map(block::Id::prefix)
39 .unwrap_or_else(|| NIL_PLACEHOLDER.to_owned())
40 }
41}
42
43impl fmt::Display for State {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{}/{}/{}", self.height, self.round, self.step)
46 }
47}
48
49impl Ord for State {
50 fn cmp(&self, other: &State) -> Ordering {
51 match self.height.cmp(&other.height) {
52 Ordering::Greater => Ordering::Greater,
53 Ordering::Less => Ordering::Less,
54 Ordering::Equal => match self.round.cmp(&other.round) {
55 Ordering::Greater => Ordering::Greater,
56 Ordering::Less => Ordering::Less,
57 Ordering::Equal => self.step.cmp(&other.step),
58 },
59 }
60 }
61}
62
63impl PartialOrd for State {
64 fn partial_cmp(&self, other: &State) -> Option<Ordering> {
65 Some(self.cmp(other))
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use core::str::FromStr;
72
73 use super::State;
74 use crate::{block, Hash};
75
76 #[test]
77 fn state_ord_test() {
78 let new = State {
79 height: block::Height::from(9001_u32),
80 round: block::Round::default(),
81 step: 0,
82 block_id: None,
83 };
84
85 let old = State {
86 height: block::Height::from(1001_u32),
87 round: block::Round::from(1_u16),
88 step: 0,
89 block_id: None,
90 };
91
92 let older = State {
93 height: block::Height::from(1001_u32),
94 round: block::Round::default(),
95 step: 0,
96 block_id: None,
97 };
98
99 let oldest = State {
100 height: block::Height::default(),
101 round: block::Round::default(),
102 step: 0,
103 block_id: None,
104 };
105
106 assert!(old < new);
107 assert!(older < old);
108 assert!(oldest < older);
109 assert!(oldest < new);
110 }
111
112 #[test]
113 fn state_deser_update_null_test() {
114 let state_json_string = r#"{
116 "height": "5",
117 "round": "1",
118 "step": 6,
119 "block_id": null
120 }"#;
121 let state: State = State {
122 height: block::Height::from(5_u32),
123 round: block::Round::from(1_u16),
124 step: 6,
125 block_id: None,
126 };
127 let state_from_json: State = serde_json::from_str(state_json_string).unwrap();
128 assert_eq!(state_from_json, state);
129 }
130
131 #[test]
132 fn state_deser_update_total_test() {
133 let state_json_string = r#"{
136 "height": "5",
137 "round": "1",
138 "step": 6,
139 "block_id": {
140 "hash": "1234567890123456789012345678901234567890123456789012345678901234",
141 "parts": {
142 "total": "1",
143 "hash": "1234567890123456789012345678901234567890123456789012345678901234"
144 }
145 }
146 }"#;
147 let state: State = State {
148 height: block::Height::from(5_u32),
149 round: block::Round::from(1_u16),
150 step: 6,
151 block_id: Some(block::Id {
152 hash: Hash::from_str(
153 "1234567890123456789012345678901234567890123456789012345678901234",
154 )
155 .unwrap(),
156 part_set_header: block::parts::Header::new(
157 1,
158 Hash::from_str(
159 "1234567890123456789012345678901234567890123456789012345678901234",
160 )
161 .unwrap(),
162 )
163 .unwrap(),
164 }),
165 };
166 let state_from_json: State = serde_json::from_str(state_json_string).unwrap();
167 assert_eq!(state_from_json, state);
168 }
169}