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