1//! Block height.
23use std::ops::{Add, Sub};
4use thiserror::Error;
5use zcash_primitives::consensus::BlockHeight;
67use crate::{serialization::SerializationError, BoxError};
89#[cfg(feature = "json-conversion")]
10pub mod json_conversion;
1112/// The length of the chain back to the genesis block.
13///
14/// Two [`Height`]s can't be added, but they can be *subtracted* to get their difference,
15/// represented as an [`HeightDiff`]. This difference can then be added to or subtracted from a
16/// [`Height`]. Note the similarity with `chrono::DateTime` and `chrono::Duration`.
17///
18/// # Invariants
19///
20/// Users should not construct block heights greater than `Height::MAX`.
21///
22/// # Consensus
23///
24/// There are multiple formats for serializing a height, so we don't implement
25/// `ZcashSerialize` or `ZcashDeserialize` for `Height`.
26#[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);
2930#[derive(Error, Debug)]
31pub enum HeightError {
32#[error("The resulting height would overflow Height::MAX.")]
33Overflow,
34#[error("The resulting height would underflow Height::MIN.")]
35Underflow,
36}
3738impl std::str::FromStr for Height {
39type Err = SerializationError;
40fn from_str(s: &str) -> Result<Self, Self::Err> {
41match s.parse() {
42Ok(h) if (Height(h) <= Height::MAX) => Ok(Height(h)),
43Ok(_) => Err(SerializationError::Parse("Height exceeds maximum height")),
44Err(_) => Err(SerializationError::Parse("Height(u32) integer parse error")),
45 }
46 }
47}
4849impl Height {
50/// The minimum [`Height`].
51 ///
52 /// Due to the underlying type, it is impossible to construct block heights
53 /// less than [`Height::MIN`].
54 ///
55 /// Style note: Sometimes, [`Height::MIN`] is less readable than
56 /// `Height(0)`. Use whichever makes sense in context.
57pub const MIN: Height = Height(0);
5859/// The maximum [`Height`].
60 ///
61 /// Users should not construct block heights greater than [`Height::MAX`].
62 ///
63 /// The spec says *"Implementations MUST support block heights up to and
64 /// including 2^31 − 1"*.
65 ///
66 /// Note that `u32::MAX / 2 == 2^31 - 1 == i32::MAX`.
67pub const MAX: Height = Height(u32::MAX / 2);
6869/// The maximum [`Height`] as a [`u32`], for range patterns.
70 ///
71 /// `Height::MAX.0` can't be used in match range patterns, use this
72 /// alias instead.
73pub const MAX_AS_U32: u32 = Self::MAX.0;
7475/// The maximum expiration [`Height`] that is allowed in all transactions
76 /// previous to Nu5 and in non-coinbase transactions from Nu5 activation
77 /// height and above.
78pub const MAX_EXPIRY_HEIGHT: Height = Height(499_999_999);
7980/// Returns the next [`Height`].
81 ///
82 /// # Panics
83 ///
84 /// - If the current height is at its maximum.
85pub fn next(self) -> Result<Self, HeightError> {
86 (self + 1).ok_or(HeightError::Overflow)
87 }
8889/// Returns the previous [`Height`].
90 ///
91 /// # Panics
92 ///
93 /// - If the current height is at its minimum.
94pub fn previous(self) -> Result<Self, HeightError> {
95 (self - 1).ok_or(HeightError::Underflow)
96 }
9798/// Returns `true` if the [`Height`] is at its minimum.
99pub fn is_min(self) -> bool {
100self == Self::MIN
101 }
102103/// Returns the value as a `usize`.
104pub fn as_usize(self) -> usize {
105self.0.try_into().expect("fits in usize")
106 }
107}
108109impl From<Height> for BlockHeight {
110fn from(height: Height) -> Self {
111 BlockHeight::from_u32(height.0)
112 }
113}
114115impl TryFrom<BlockHeight> for Height {
116type Error = &'static str;
117118/// Checks that the `height` is within the valid [`Height`] range.
119fn try_from(height: BlockHeight) -> Result<Self, Self::Error> {
120Self::try_from(u32::from(height))
121 }
122}
123124/// A difference between two [`Height`]s, possibly negative.
125///
126/// This can represent the difference between any height values,
127/// even if they are outside the valid height range (for example, in buggy RPC code).
128pub type HeightDiff = i64;
129130// We don't implement TryFrom<u64>, because it causes type inference issues for integer constants.
131// Instead, use 1u64.try_into_height().
132133impl TryFrom<u32> for Height {
134type Error = &'static str;
135136/// Checks that the `height` is within the valid [`Height`] range.
137fn try_from(height: u32) -> Result<Self, Self::Error> {
138// Check the bounds.
139 //
140 // Clippy warns that `height >= Height::MIN.0` is always true.
141assert_eq!(Height::MIN.0, 0);
142143if height <= Height::MAX.0 {
144Ok(Height(height))
145 } else {
146Err("heights must be less than or equal to Height::MAX")
147 }
148 }
149}
150151/// Convenience trait for converting a type into a valid Zcash [`Height`].
152pub trait TryIntoHeight {
153/// The error type returned by [`Height`] conversion failures.
154type Error;
155156/// Convert `self` to a `Height`, if possible.
157fn try_into_height(&self) -> Result<Height, Self::Error>;
158}
159160impl TryIntoHeight for u64 {
161type Error = BoxError;
162163fn try_into_height(&self) -> Result<Height, Self::Error> {
164 u32::try_from(*self)?.try_into().map_err(Into::into)
165 }
166}
167168impl TryIntoHeight for usize {
169type Error = BoxError;
170171fn try_into_height(&self) -> Result<Height, Self::Error> {
172 u32::try_from(*self)?.try_into().map_err(Into::into)
173 }
174}
175176impl TryIntoHeight for str {
177type Error = BoxError;
178179fn try_into_height(&self) -> Result<Height, Self::Error> {
180self.parse().map_err(Into::into)
181 }
182}
183184impl TryIntoHeight for String {
185type Error = BoxError;
186187fn try_into_height(&self) -> Result<Height, Self::Error> {
188self.as_str().try_into_height()
189 }
190}
191192impl TryIntoHeight for i32 {
193type Error = BoxError;
194195fn try_into_height(&self) -> Result<Height, Self::Error> {
196 u32::try_from(*self)?.try_into().map_err(Into::into)
197 }
198}
199200// We don't implement Add<u32> or Sub<u32>, because they cause type inference issues for integer constants.
201202impl Sub<Height> for Height {
203type Output = HeightDiff;
204205/// Subtract two heights, returning the result, which can be negative.
206 /// Since [`HeightDiff`] is `i64` and [`Height`] is `u32`, the result is always correct.
207fn sub(self, rhs: Height) -> Self::Output {
208// All these conversions are exact, and the subtraction can't overflow or underflow.
209let lhs = HeightDiff::from(self.0);
210let rhs = HeightDiff::from(rhs.0);
211212 lhs - rhs
213 }
214}
215216impl Sub<HeightDiff> for Height {
217type Output = Option<Self>;
218219/// Subtract a height difference from a height, returning `None` if the resulting height is
220 /// outside the valid `Height` range (this also checks the result is non-negative).
221fn sub(self, rhs: HeightDiff) -> Option<Self> {
222// We need to convert the height to [`i64`] so we can subtract negative [`HeightDiff`]s.
223let lhs = HeightDiff::from(self.0);
224let res = lhs - rhs;
225226// Check the bounds.
227let res = u32::try_from(res).ok()?;
228 Height::try_from(res).ok()
229 }
230}
231232impl Add<HeightDiff> for Height {
233type Output = Option<Height>;
234235/// Add a height difference to a height, returning `None` if the resulting height is outside
236 /// the valid `Height` range (this also checks the result is non-negative).
237fn add(self, rhs: HeightDiff) -> Option<Height> {
238// We need to convert the height to [`i64`] so we can add negative [`HeightDiff`]s.
239let lhs = i64::from(self.0);
240let res = lhs + rhs;
241242// Check the bounds.
243let res = u32::try_from(res).ok()?;
244 Height::try_from(res).ok()
245 }
246}
247248#[test]
249fn operator_tests() {
250let _init_guard = zebra_test::init();
251252// Elementary checks.
253assert_eq!(Some(Height(2)), Height(1) + 1);
254assert_eq!(None, Height::MAX + 1);
255256let height = Height(u32::pow(2, 31) - 2);
257assert!(height < Height::MAX);
258259let max_height = (height + 1).expect("this addition should produce the max height");
260assert!(height < max_height);
261assert!(max_height <= Height::MAX);
262assert_eq!(Height::MAX, max_height);
263assert_eq!(None, max_height + 1);
264265// Bad heights aren't caught at compile-time or runtime, until we add or subtract
266assert_eq!(None, Height(Height::MAX_AS_U32 + 1) + 0);
267assert_eq!(None, Height(i32::MAX as u32) + 1);
268assert_eq!(None, Height(u32::MAX) + 0);
269270// Adding negative numbers
271assert_eq!(Some(Height(1)), Height(2) + -1);
272assert_eq!(Some(Height(0)), Height(1) + -1);
273assert_eq!(None, Height(0) + -1);
274assert_eq!(Some(Height(Height::MAX_AS_U32 - 1)), Height::MAX + -1);
275276// Bad heights aren't caught at compile-time or runtime, until we add or subtract,
277 // and the result is invalid
278assert_eq!(None, Height(Height::MAX_AS_U32 + 1) + 1);
279assert_eq!(None, Height(i32::MAX as u32) + 1);
280assert_eq!(None, Height(u32::MAX) + 1);
281282// Adding negative numbers
283assert_eq!(Some(Height::MAX), Height(i32::MAX as u32 + 1) + -1);
284assert_eq!(None, Height(u32::MAX) + -1);
285286assert_eq!(Some(Height(1)), Height(2) - 1);
287assert_eq!(Some(Height(0)), Height(1) - 1);
288assert_eq!(None, Height(0) - 1);
289assert_eq!(Some(Height(Height::MAX_AS_U32 - 1)), Height::MAX - 1);
290291// Subtracting negative numbers
292assert_eq!(Some(Height(2)), Height(1) - -1);
293assert_eq!(Some(Height::MAX), Height(Height::MAX_AS_U32 - 1) - -1);
294assert_eq!(None, Height::MAX - -1);
295296// Bad heights aren't caught at compile-time or runtime, until we add or subtract,
297 // and the result is invalid
298assert_eq!(Some(Height::MAX), Height(i32::MAX as u32 + 1) - 1);
299assert_eq!(None, Height(u32::MAX) - 1);
300301// Subtracting negative numbers
302assert_eq!(None, Height(Height::MAX_AS_U32 + 1) - -1);
303assert_eq!(None, Height(i32::MAX as u32) - -1);
304assert_eq!(None, Height(u32::MAX) - -1);
305306assert_eq!(1, (Height(2) - Height(1)));
307assert_eq!(0, (Height(1) - Height(1)));
308assert_eq!(-1, Height(0) - Height(1));
309assert_eq!(-5, Height(2) - Height(7));
310assert_eq!(Height::MAX.0 as HeightDiff, (Height::MAX - Height(0)));
311assert_eq!(1, (Height::MAX - Height(Height::MAX_AS_U32 - 1)));
312assert_eq!(-1, Height(Height::MAX_AS_U32 - 1) - Height::MAX);
313assert_eq!(-(Height::MAX_AS_U32 as HeightDiff), Height(0) - Height::MAX);
314}