zebra_chain/block/
height.rs1use std::ops::{Add, Sub};
4use thiserror::Error;
5use zcash_primitives::consensus::BlockHeight;
6
7use crate::{serialization::SerializationError, BoxError};
8
9#[cfg(feature = "json-conversion")]
10pub mod json_conversion;
11
12#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
27#[cfg_attr(any(test, feature = "proptest-impl"), derive(Default))]
28pub struct Height(pub u32);
29
30#[derive(Error, Debug)]
31pub enum HeightError {
32 #[error("The resulting height would overflow Height::MAX.")]
33 Overflow,
34 #[error("The resulting height would underflow Height::MIN.")]
35 Underflow,
36}
37
38impl std::str::FromStr for Height {
39 type Err = SerializationError;
40 fn from_str(s: &str) -> Result<Self, Self::Err> {
41 match s.parse() {
42 Ok(h) if (Height(h) <= Height::MAX) => Ok(Height(h)),
43 Ok(_) => Err(SerializationError::Parse("Height exceeds maximum height")),
44 Err(_) => Err(SerializationError::Parse("Height(u32) integer parse error")),
45 }
46 }
47}
48
49impl Height {
50 pub const MIN: Height = Height(0);
58
59 pub const MAX: Height = Height(u32::MAX / 2);
68
69 pub const MAX_AS_U32: u32 = Self::MAX.0;
74
75 pub const MAX_EXPIRY_HEIGHT: Height = Height(499_999_999);
79
80 pub fn next(self) -> Result<Self, HeightError> {
86 (self + 1).ok_or(HeightError::Overflow)
87 }
88
89 pub fn previous(self) -> Result<Self, HeightError> {
95 (self - 1).ok_or(HeightError::Underflow)
96 }
97
98 pub fn is_min(self) -> bool {
100 self == Self::MIN
101 }
102
103 pub fn as_usize(self) -> usize {
105 self.0.try_into().expect("fits in usize")
106 }
107}
108
109impl From<Height> for BlockHeight {
110 fn from(height: Height) -> Self {
111 BlockHeight::from_u32(height.0)
112 }
113}
114
115impl TryFrom<BlockHeight> for Height {
116 type Error = &'static str;
117
118 fn try_from(height: BlockHeight) -> Result<Self, Self::Error> {
120 Self::try_from(u32::from(height))
121 }
122}
123
124pub type HeightDiff = i64;
129
130impl TryFrom<u32> for Height {
134 type Error = &'static str;
135
136 fn try_from(height: u32) -> Result<Self, Self::Error> {
138 assert_eq!(Height::MIN.0, 0);
142
143 if height <= Height::MAX.0 {
144 Ok(Height(height))
145 } else {
146 Err("heights must be less than or equal to Height::MAX")
147 }
148 }
149}
150
151impl From<Height> for u32 {
152 fn from(height: Height) -> Self {
153 height.0
154 }
155}
156
157impl From<Height> for u64 {
158 fn from(height: Height) -> Self {
159 height.0.into()
160 }
161}
162
163pub trait TryIntoHeight {
165 type Error;
167
168 fn try_into_height(&self) -> Result<Height, Self::Error>;
170}
171
172impl TryIntoHeight for u64 {
173 type Error = BoxError;
174
175 fn try_into_height(&self) -> Result<Height, Self::Error> {
176 u32::try_from(*self)?.try_into().map_err(Into::into)
177 }
178}
179
180impl TryIntoHeight for usize {
181 type Error = BoxError;
182
183 fn try_into_height(&self) -> Result<Height, Self::Error> {
184 u32::try_from(*self)?.try_into().map_err(Into::into)
185 }
186}
187
188impl TryIntoHeight for str {
189 type Error = BoxError;
190
191 fn try_into_height(&self) -> Result<Height, Self::Error> {
192 self.parse().map_err(Into::into)
193 }
194}
195
196impl TryIntoHeight for String {
197 type Error = BoxError;
198
199 fn try_into_height(&self) -> Result<Height, Self::Error> {
200 self.as_str().try_into_height()
201 }
202}
203
204impl TryIntoHeight for i32 {
205 type Error = BoxError;
206
207 fn try_into_height(&self) -> Result<Height, Self::Error> {
208 u32::try_from(*self)?.try_into().map_err(Into::into)
209 }
210}
211
212impl Sub<Height> for Height {
215 type Output = HeightDiff;
216
217 fn sub(self, rhs: Height) -> Self::Output {
220 let lhs = HeightDiff::from(self.0);
222 let rhs = HeightDiff::from(rhs.0);
223
224 lhs - rhs
225 }
226}
227
228impl Sub<HeightDiff> for Height {
229 type Output = Option<Self>;
230
231 fn sub(self, rhs: HeightDiff) -> Option<Self> {
234 let lhs = HeightDiff::from(self.0);
236 let res = lhs - rhs;
237
238 let res = u32::try_from(res).ok()?;
240 Height::try_from(res).ok()
241 }
242}
243
244impl Add<HeightDiff> for Height {
245 type Output = Option<Height>;
246
247 fn add(self, rhs: HeightDiff) -> Option<Height> {
250 let lhs = i64::from(self.0);
252 let res = lhs + rhs;
253
254 let res = u32::try_from(res).ok()?;
256 Height::try_from(res).ok()
257 }
258}
259
260#[test]
261fn operator_tests() {
262 let _init_guard = zebra_test::init();
263
264 assert_eq!(Some(Height(2)), Height(1) + 1);
266 assert_eq!(None, Height::MAX + 1);
267
268 let height = Height(u32::pow(2, 31) - 2);
269 assert!(height < Height::MAX);
270
271 let max_height = (height + 1).expect("this addition should produce the max height");
272 assert!(height < max_height);
273 assert!(max_height <= Height::MAX);
274 assert_eq!(Height::MAX, max_height);
275 assert_eq!(None, max_height + 1);
276
277 assert_eq!(None, Height(Height::MAX_AS_U32 + 1) + 0);
279 assert_eq!(None, Height(i32::MAX as u32) + 1);
280 assert_eq!(None, Height(u32::MAX) + 0);
281
282 assert_eq!(Some(Height(1)), Height(2) + -1);
284 assert_eq!(Some(Height(0)), Height(1) + -1);
285 assert_eq!(None, Height(0) + -1);
286 assert_eq!(Some(Height(Height::MAX_AS_U32 - 1)), Height::MAX + -1);
287
288 assert_eq!(None, Height(Height::MAX_AS_U32 + 1) + 1);
291 assert_eq!(None, Height(i32::MAX as u32) + 1);
292 assert_eq!(None, Height(u32::MAX) + 1);
293
294 assert_eq!(Some(Height::MAX), Height(i32::MAX as u32 + 1) + -1);
296 assert_eq!(None, Height(u32::MAX) + -1);
297
298 assert_eq!(Some(Height(1)), Height(2) - 1);
299 assert_eq!(Some(Height(0)), Height(1) - 1);
300 assert_eq!(None, Height(0) - 1);
301 assert_eq!(Some(Height(Height::MAX_AS_U32 - 1)), Height::MAX - 1);
302
303 assert_eq!(Some(Height(2)), Height(1) - -1);
305 assert_eq!(Some(Height::MAX), Height(Height::MAX_AS_U32 - 1) - -1);
306 assert_eq!(None, Height::MAX - -1);
307
308 assert_eq!(Some(Height::MAX), Height(i32::MAX as u32 + 1) - 1);
311 assert_eq!(None, Height(u32::MAX) - 1);
312
313 assert_eq!(None, Height(Height::MAX_AS_U32 + 1) - -1);
315 assert_eq!(None, Height(i32::MAX as u32) - -1);
316 assert_eq!(None, Height(u32::MAX) - -1);
317
318 assert_eq!(1, (Height(2) - Height(1)));
319 assert_eq!(0, (Height(1) - Height(1)));
320 assert_eq!(-1, Height(0) - Height(1));
321 assert_eq!(-5, Height(2) - Height(7));
322 assert_eq!(Height::MAX.0 as HeightDiff, (Height::MAX - Height(0)));
323 assert_eq!(1, (Height::MAX - Height(Height::MAX_AS_U32 - 1)));
324 assert_eq!(-1, Height(Height::MAX_AS_U32 - 1) - Height::MAX);
325 assert_eq!(-(Height::MAX_AS_U32 as HeightDiff), Height(0) - Height::MAX);
326}