1
1
//! Defines the core `Height` type used throughout the library
2
2
3
- use core:: cmp:: Ordering ;
4
- use core:: num:: ParseIntError ;
3
+ use core:: num:: { NonZeroU64 , ParseIntError } ;
5
4
use core:: str:: FromStr ;
6
5
7
6
use displaydoc:: Display ;
@@ -28,31 +27,29 @@ use crate::prelude::*;
28
27
) ]
29
28
#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
30
29
#[ cfg_attr( feature = "schema" , derive( schemars:: JsonSchema ) ) ]
31
- #[ derive( Copy , Clone , PartialEq , Eq , Hash ) ]
30
+ #[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
32
31
pub struct Height {
33
32
/// Previously known as "epoch"
34
33
revision_number : u64 ,
35
34
36
35
/// The height of a block
37
- revision_height : u64 ,
36
+ revision_height : NonZeroU64 ,
38
37
}
39
38
40
39
impl Height {
41
40
pub fn new ( revision_number : u64 , revision_height : u64 ) -> Result < Self , ClientError > {
42
- if revision_height == 0 {
43
- return Err ( ClientError :: InvalidHeight ) ;
44
- }
45
-
46
- Ok ( Self {
47
- revision_number,
48
- revision_height,
49
- } )
41
+ NonZeroU64 :: new ( revision_height)
42
+ . map ( |revision_height| Self {
43
+ revision_number,
44
+ revision_height,
45
+ } )
46
+ . ok_or ( ClientError :: InvalidHeight )
50
47
}
51
48
52
49
pub fn min ( revision_number : u64 ) -> Self {
53
50
Self {
54
51
revision_number,
55
- revision_height : 1 ,
52
+ revision_height : NonZeroU64 :: MIN ,
56
53
}
57
54
}
58
55
@@ -61,13 +58,17 @@ impl Height {
61
58
}
62
59
63
60
pub fn revision_height ( & self ) -> u64 {
64
- self . revision_height
61
+ self . revision_height . get ( )
65
62
}
66
63
67
64
pub fn add ( & self , delta : u64 ) -> Height {
65
+ let revision_height = self
66
+ . revision_height
67
+ . checked_add ( delta)
68
+ . expect ( "height should never overflow u64" ) ;
68
69
Height {
69
70
revision_number : self . revision_number ,
70
- revision_height : self . revision_height + delta ,
71
+ revision_height,
71
72
}
72
73
}
73
74
@@ -76,13 +77,15 @@ impl Height {
76
77
}
77
78
78
79
pub fn sub ( & self , delta : u64 ) -> Result < Height , ClientError > {
79
- if self . revision_height <= delta {
80
- return Err ( ClientError :: InvalidHeightResult ) ;
81
- }
82
-
80
+ let revision_height = self
81
+ . revision_height
82
+ . get ( )
83
+ . checked_sub ( delta)
84
+ . and_then ( NonZeroU64 :: new)
85
+ . ok_or ( ClientError :: InvalidHeightResult ) ?;
83
86
Ok ( Height {
84
87
revision_number : self . revision_number ,
85
- revision_height : self . revision_height - delta ,
88
+ revision_height : revision_height,
86
89
} )
87
90
}
88
91
@@ -91,28 +94,6 @@ impl Height {
91
94
}
92
95
}
93
96
94
- impl PartialOrd for Height {
95
- fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
96
- Some ( self . cmp ( other) )
97
- }
98
- }
99
-
100
- impl Ord for Height {
101
- fn cmp ( & self , other : & Self ) -> Ordering {
102
- if self . revision_number < other. revision_number {
103
- Ordering :: Less
104
- } else if self . revision_number > other. revision_number {
105
- Ordering :: Greater
106
- } else if self . revision_height < other. revision_height {
107
- Ordering :: Less
108
- } else if self . revision_height > other. revision_height {
109
- Ordering :: Greater
110
- } else {
111
- Ordering :: Equal
112
- }
113
- }
114
- }
115
-
116
97
impl Protobuf < RawHeight > for Height { }
117
98
118
99
impl TryFrom < RawHeight > for Height {
@@ -127,7 +108,7 @@ impl From<Height> for RawHeight {
127
108
fn from ( ics_height : Height ) -> Self {
128
109
RawHeight {
129
110
revision_number : ics_height. revision_number ,
130
- revision_height : ics_height. revision_height ,
111
+ revision_height : ics_height. revision_height . get ( ) ,
131
112
}
132
113
}
133
114
}
@@ -136,7 +117,7 @@ impl core::fmt::Debug for Height {
136
117
fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> Result < ( ) , core:: fmt:: Error > {
137
118
f. debug_struct ( "Height" )
138
119
. field ( "revision" , & self . revision_number )
139
- . field ( "height" , & self . revision_height )
120
+ . field ( "height" , & self . revision_height . get ( ) )
140
121
. finish ( )
141
122
}
142
123
}
@@ -222,20 +203,9 @@ impl FromStr for Height {
222
203
223
204
#[ test]
224
205
fn test_valid_height ( ) {
225
- assert_eq ! (
226
- "1-1" . parse:: <Height >( ) ,
227
- Ok ( Height {
228
- revision_number: 1 ,
229
- revision_height: 1
230
- } )
231
- ) ;
232
- assert_eq ! (
233
- "1-10" . parse:: <Height >( ) ,
234
- Ok ( Height {
235
- revision_number: 1 ,
236
- revision_height: 10
237
- } )
238
- ) ;
206
+ assert_eq ! ( "1-1" . parse:: <Height >( ) , Ok ( Height :: new( 1 , 1 ) . unwrap( ) ) ) ;
207
+ assert_eq ! ( "1-10" . parse:: <Height >( ) , Ok ( Height :: new( 1 , 10 ) . unwrap( ) ) ) ;
208
+ assert_eq ! ( "10-1" . parse:: <Height >( ) , Ok ( Height :: new( 10 , 1 ) . unwrap( ) ) ) ;
239
209
}
240
210
241
211
#[ test]
@@ -261,3 +231,36 @@ fn test_invalid_height() {
261
231
} )
262
232
) ;
263
233
}
234
+
235
+ #[ cfg( feature = "borsh" ) ]
236
+ #[ test]
237
+ fn test_borsh ( ) {
238
+ use borsh:: BorshDeserialize ;
239
+
240
+ let height = Height :: new ( 42 , 24 ) . unwrap ( ) ;
241
+ let encoded = borsh:: to_vec ( & height) . unwrap ( ) ;
242
+ assert_eq ! (
243
+ & [ 42 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 24 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
244
+ encoded. as_slice( )
245
+ ) ;
246
+ let decoded = Height :: try_from_slice ( & encoded) . unwrap ( ) ;
247
+ assert_eq ! ( height, decoded) ;
248
+
249
+ // Test 0 revision height doesn’t deserialize.
250
+ let encoded = [ 42 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ;
251
+ Height :: try_from_slice ( & encoded) . unwrap_err ( ) ;
252
+ }
253
+
254
+ #[ cfg( feature = "serde" ) ]
255
+ #[ test]
256
+ fn test_serde ( ) {
257
+ let height = Height :: new ( 42 , 24 ) . unwrap ( ) ;
258
+ let encoded = serde_json:: to_string ( & height) . unwrap ( ) ;
259
+ assert_eq ! ( r#"{"revision_number":42,"revision_height":24}"# , encoded) ;
260
+ let decoded = serde_json:: from_str :: < Height > ( & encoded) . unwrap ( ) ;
261
+ assert_eq ! ( height, decoded) ;
262
+
263
+ // Test 0 revision height doesn’t deserialize.
264
+ let encoded = r#"{"revision_number":42,"revision_height":0}"# ;
265
+ serde_json:: from_str :: < Height > ( & encoded) . unwrap_err ( ) ;
266
+ }
0 commit comments