-
Notifications
You must be signed in to change notification settings - Fork 113
/
Copy pathheight.rs
216 lines (180 loc) · 7.62 KB
/
height.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! Block height.
use crate::serialization::{SerializationError, ZcashDeserialize};
use byteorder::{LittleEndian, ReadBytesExt};
use std::{
convert::TryFrom,
io,
ops::{Add, Sub},
};
/// The length of the chain back to the genesis block.
///
/// Block heights can't be added, but they can be *subtracted*,
/// to get a difference of block heights, represented as an `i32`,
/// and height differences can be added to block heights to get new heights.
///
/// # Invariants
///
/// Users should not construct block heights greater than `Height::MAX`.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Height(pub u32);
impl std::str::FromStr for Height {
type Err = SerializationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.parse() {
Ok(h) if (Height(h) <= Height::MAX) => Ok(Height(h)),
Ok(_) => Err(SerializationError::Parse("Height exceeds maximum height")),
Err(_) => Err(SerializationError::Parse("Height(u32) integer parse error")),
}
}
}
impl Height {
/// The minimum [`Height`].
///
/// Due to the underlying type, it is impossible to construct block heights
/// less than [`Height::MIN`].
///
/// Style note: Sometimes, [`Height::MIN`] is less readable than
/// `Height(0)`. Use whichever makes sense in context.
pub const MIN: Height = Height(0);
/// The maximum [`Height`].
///
/// Users should not construct block heights greater than [`Height::MAX`].
///
/// The spec says *"Implementations MUST support block heights up to and
/// including 2^31 − 1"*.
///
/// Note that `u32::MAX / 2 == 2^31 - 1 == i32::MAX`.
pub const MAX: Height = Height(u32::MAX / 2);
/// The maximum [`Height`] as a [`u32`], for range patterns.
///
/// `Height::MAX.0` can't be used in match range patterns, use this
/// alias instead.
pub const MAX_AS_U32: u32 = Self::MAX.0;
/// The maximum expiration [`Height`] that is allowed in all transactions
/// previous to Nu5 and in non-coinbase transactions from Nu5 activation
/// height and above.
pub const MAX_EXPIRY_HEIGHT: Height = Height(499_999_999);
}
impl Add<Height> for Height {
type Output = Option<Height>;
fn add(self, rhs: Height) -> Option<Height> {
// We know that both values are positive integers. Therefore, the result is
// positive, and we can skip the conversions. The checked_add is required,
// because the result may overflow.
let height = self.0.checked_add(rhs.0)?;
let height = Height(height);
if height <= Height::MAX && height >= Height::MIN {
Some(height)
} else {
None
}
}
}
impl Sub<Height> for Height {
type Output = i32;
/// Panics if the inputs or result are outside the valid i32 range.
fn sub(self, rhs: Height) -> i32 {
// We construct heights from integers without any checks,
// so the inputs or result could be out of range.
let lhs = i32::try_from(self.0)
.expect("out of range input `self`: inputs should be valid Heights");
let rhs =
i32::try_from(rhs.0).expect("out of range input `rhs`: inputs should be valid Heights");
lhs.checked_sub(rhs)
.expect("out of range result: valid input heights should yield a valid result")
}
}
// We don't implement Add<u32> or Sub<u32>, because they cause type inference issues for integer constants.
impl Add<i32> for Height {
type Output = Option<Height>;
fn add(self, rhs: i32) -> Option<Height> {
// Because we construct heights from integers without any checks,
// the input values could be outside the valid range for i32.
let lhs = i32::try_from(self.0).ok()?;
let result = lhs.checked_add(rhs)?;
let result = u32::try_from(result).ok()?;
match result {
h if (Height(h) <= Height::MAX && Height(h) >= Height::MIN) => Some(Height(h)),
_ => None,
}
}
}
impl Sub<i32> for Height {
type Output = Option<Height>;
fn sub(self, rhs: i32) -> Option<Height> {
// These checks are required, see above for details.
let lhs = i32::try_from(self.0).ok()?;
let result = lhs.checked_sub(rhs)?;
let result = u32::try_from(result).ok()?;
match result {
h if (Height(h) <= Height::MAX && Height(h) >= Height::MIN) => Some(Height(h)),
_ => None,
}
}
}
impl ZcashDeserialize for Height {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let height = reader.read_u32::<LittleEndian>()?;
if height > Self::MAX.0 {
return Err(SerializationError::Parse("Height exceeds maximum height"));
}
Ok(Self(height))
}
}
#[test]
fn operator_tests() {
let _init_guard = zebra_test::init();
// Elementary checks.
assert_eq!(Some(Height(2)), Height(1) + Height(1));
assert_eq!(None, Height::MAX + Height(1));
let height = Height(u32::pow(2, 31) - 2);
assert!(height < Height::MAX);
let max_height = (height + Height(1)).expect("this addition should produce the max height");
assert!(height < max_height);
assert!(max_height <= Height::MAX);
assert_eq!(Height::MAX, max_height);
assert_eq!(None, max_height + Height(1));
// Bad heights aren't caught at compile-time or runtime, until we add or subtract
assert_eq!(None, Height(Height::MAX_AS_U32 + 1) + Height(0));
assert_eq!(None, Height(i32::MAX as u32) + Height(1));
assert_eq!(None, Height(u32::MAX) + Height(0));
assert_eq!(Some(Height(2)), Height(1) + 1);
assert_eq!(None, Height::MAX + 1);
// Adding negative numbers
assert_eq!(Some(Height(1)), Height(2) + -1);
assert_eq!(Some(Height(0)), Height(1) + -1);
assert_eq!(None, Height(0) + -1);
assert_eq!(Some(Height(Height::MAX_AS_U32 - 1)), Height::MAX + -1);
// Bad heights aren't caught at compile-time or runtime, until we add or subtract
// `+ 0` would also cause an error here, but it triggers a spurious clippy lint
assert_eq!(None, Height(Height::MAX_AS_U32 + 1) + 1);
assert_eq!(None, Height(i32::MAX as u32) + 1);
assert_eq!(None, Height(u32::MAX) + 1);
// Adding negative numbers
assert_eq!(None, Height(i32::MAX as u32 + 1) + -1);
assert_eq!(None, Height(u32::MAX) + -1);
assert_eq!(Some(Height(1)), Height(2) - 1);
assert_eq!(Some(Height(0)), Height(1) - 1);
assert_eq!(None, Height(0) - 1);
assert_eq!(Some(Height(Height::MAX_AS_U32 - 1)), Height::MAX - 1);
// Subtracting negative numbers
assert_eq!(Some(Height(2)), Height(1) - -1);
assert_eq!(Some(Height::MAX), Height(Height::MAX_AS_U32 - 1) - -1);
assert_eq!(None, Height::MAX - -1);
// Bad heights aren't caught at compile-time or runtime, until we add or subtract
assert_eq!(None, Height(i32::MAX as u32 + 1) - 1);
assert_eq!(None, Height(u32::MAX) - 1);
// Subtracting negative numbers
assert_eq!(None, Height(Height::MAX_AS_U32 + 1) - -1);
assert_eq!(None, Height(i32::MAX as u32) - -1);
assert_eq!(None, Height(u32::MAX) - -1);
// Sub<Height> panics on out of range errors
assert_eq!(1, Height(2) - Height(1));
assert_eq!(0, Height(1) - Height(1));
assert_eq!(-1, Height(0) - Height(1));
assert_eq!(-5, Height(2) - Height(7));
assert_eq!(Height::MAX_AS_U32 as i32, Height::MAX - Height(0));
assert_eq!(1, Height::MAX - Height(Height::MAX_AS_U32 - 1));
assert_eq!(-1, Height(Height::MAX_AS_U32 - 1) - Height::MAX);
assert_eq!(-(Height::MAX_AS_U32 as i32), Height(0) - Height::MAX);
}