1
- use crate :: { units:: UnitCoord , Hasher , NodeCount , NodeMap , Round } ;
1
+ use crate :: { units:: UnitCoord , Hasher , NodeCount , NodeIndex , NodeMap , Round } ;
2
2
use codec:: { Decode , Encode } ;
3
3
4
+ #[ derive( Debug , PartialEq ) ]
5
+ pub enum Error {
6
+ NotDescendantOfPreviousUnit ( NodeIndex ) ,
7
+ DescendantOfPreviousUnitButWrongRound ( Round ) ,
8
+ NotEnoughParentsForRound ( Round ) ,
9
+ ParentsHigherThanRound ( Round ) ,
10
+ }
11
+
4
12
/// Combined hashes of the parents of a unit together with the set of indices of creators of the
5
- /// parents
13
+ /// parents. By parent here we mean a parent hash and its round.
6
14
#[ derive( Clone , Eq , PartialEq , Hash , Debug , Decode , Encode ) ]
7
15
pub struct ControlHash < H : Hasher > {
8
16
pub ( crate ) parents : NodeMap < Round > ,
9
17
pub ( crate ) combined_hash : H :: Hash ,
10
18
}
11
19
12
20
impl < H : Hasher > ControlHash < H > {
21
+ /// Creates new control hash from parents hashes and rounds
13
22
pub ( crate ) fn new ( parents_with_rounds_and_hashes : & NodeMap < ( H :: Hash , Round ) > ) -> Self {
14
23
let mut parents_with_rounds = NodeMap :: with_size ( parents_with_rounds_and_hashes. size ( ) ) ;
15
24
for ( parent_index, ( _, parent_round) ) in parents_with_rounds_and_hashes. iter ( ) {
@@ -26,23 +35,78 @@ impl<H: Hasher> ControlHash<H> {
26
35
parent_map. using_encoded ( H :: hash)
27
36
}
28
37
38
+ /// Iterator over non-empty parents - returns [`UnitCoord`]s
29
39
pub ( crate ) fn parents ( & self ) -> impl Iterator < Item = UnitCoord > + ' _ {
30
40
self . parents
31
41
. iter ( )
32
42
. map ( |( node_index, & round) | UnitCoord :: new ( round, node_index) )
33
43
}
34
44
45
+ /// Returns number of non-empty parents
35
46
pub ( crate ) fn n_parents ( & self ) -> NodeCount {
36
47
NodeCount ( self . parents ( ) . count ( ) )
37
48
}
38
49
50
+ /// Returns number of all members in abft consensus
39
51
pub ( crate ) fn n_members ( & self ) -> NodeCount {
40
52
self . parents . size ( )
41
53
}
54
+
55
+ /// Validate
56
+ pub fn validate ( & self , unit_coord : UnitCoord ) -> Result < ( ) , Error > {
57
+ assert ! ( unit_coord. round > 0 , "Round must be greater than 0" ) ;
58
+
59
+ self . unit_creator_is_descendant_of_previous_unit ( unit_coord) ?;
60
+ self . previous_round_have_enough_parents ( unit_coord) ?;
61
+ self . check_if_parents_greater_than_previous_round ( unit_coord) ?;
62
+
63
+ Ok ( ( ) )
64
+ }
65
+
66
+ fn check_if_parents_greater_than_previous_round (
67
+ & self ,
68
+ unit_coord : UnitCoord ,
69
+ ) -> Result < ( ) , Error > {
70
+ let parents_greater_than_previous_round = self
71
+ . parents ( )
72
+ . filter ( |& parent| parent. round > unit_coord. round - 1 )
73
+ . count ( ) ;
74
+ if parents_greater_than_previous_round > 0 {
75
+ return Err ( Error :: ParentsHigherThanRound ( unit_coord. round - 1 ) ) ;
76
+ }
77
+ Ok ( ( ) )
78
+ }
79
+
80
+ fn previous_round_have_enough_parents ( & self , unit_coord : UnitCoord ) -> Result < ( ) , Error > {
81
+ let previous_round_parents = self
82
+ . parents ( )
83
+ . filter ( |& parent| parent. round == unit_coord. round - 1 )
84
+ . count ( ) ;
85
+ if previous_round_parents < self . n_members ( ) . consensus_threshold ( ) . 0 {
86
+ return Err ( Error :: NotEnoughParentsForRound ( unit_coord. round - 1 ) ) ;
87
+ }
88
+ Ok ( ( ) )
89
+ }
90
+
91
+ fn unit_creator_is_descendant_of_previous_unit (
92
+ & self ,
93
+ unit_coord : UnitCoord ,
94
+ ) -> Result < ( ) , Error > {
95
+ match self . parents . get ( unit_coord. creator ) {
96
+ None => return Err ( Error :: NotDescendantOfPreviousUnit ( unit_coord. creator ) ) ,
97
+ Some ( & parent_round) => {
98
+ if unit_coord. round - 1 != parent_round {
99
+ return Err ( Error :: DescendantOfPreviousUnitButWrongRound ( parent_round) ) ;
100
+ }
101
+ }
102
+ }
103
+ Ok ( ( ) )
104
+ }
42
105
}
43
106
44
107
#[ cfg( test) ]
45
108
pub mod tests {
109
+ use crate :: units:: control_hash:: Error ;
46
110
use crate :: units:: UnitCoord ;
47
111
use crate :: { units:: ControlHash , NodeCount , NodeIndex } ;
48
112
use aleph_bft_mock:: Hasher64 ;
@@ -90,4 +154,104 @@ pub mod tests {
90
154
] ;
91
155
assert_eq ! ( parents, expected_parents) ;
92
156
}
157
+
158
+ #[ test]
159
+ fn given_control_hash_when_validate_with_correct_unit_coord_then_validate_is_ok ( ) {
160
+ let parent_map = vec ! [
161
+ Some ( ( [ 0 ; 8 ] , 2 ) ) ,
162
+ None ,
163
+ Some ( ( [ 2 ; 8 ] , 2 ) ) ,
164
+ Some ( ( [ 3 ; 8 ] , 2 ) ) ,
165
+ Some ( ( [ 4 ; 8 ] , 2 ) ) ,
166
+ Some ( ( [ 5 ; 8 ] , 2 ) ) ,
167
+ Some ( ( [ 5 ; 8 ] , 1 ) ) ,
168
+ ]
169
+ . into ( ) ;
170
+ let ch = ControlHash :: < Hasher64 > :: new ( & parent_map) ;
171
+ assert ! ( ch. validate( UnitCoord :: new( 3 , NodeIndex ( 2 ) ) ) . is_ok( ) ) ;
172
+ }
173
+
174
+ #[ test]
175
+ #[ should_panic( expected = "Round must be greater than 0" ) ]
176
+ fn given_control_hash_when_validate_called_for_initial_unit_then_validate_panics ( ) {
177
+ let parent_map = vec ! [
178
+ Some ( ( [ 0 ; 8 ] , 2 ) ) ,
179
+ None ,
180
+ Some ( ( [ 2 ; 8 ] , 2 ) ) ,
181
+ Some ( ( [ 3 ; 8 ] , 2 ) ) ,
182
+ ]
183
+ . into ( ) ;
184
+ let ch = ControlHash :: < Hasher64 > :: new ( & parent_map) ;
185
+ let _ = ch. validate ( UnitCoord :: new ( 0 , NodeIndex ( 1 ) ) ) ;
186
+ }
187
+ #[ test]
188
+ fn given_control_hash_when_creator_parent_does_not_exist_then_err_is_returned_from_validate ( ) {
189
+ let parent_map = vec ! [
190
+ Some ( ( [ 0 ; 8 ] , 2 ) ) ,
191
+ None ,
192
+ Some ( ( [ 2 ; 8 ] , 2 ) ) ,
193
+ Some ( ( [ 3 ; 8 ] , 2 ) ) ,
194
+ ]
195
+ . into ( ) ;
196
+ let ch = ControlHash :: < Hasher64 > :: new ( & parent_map) ;
197
+ assert_eq ! (
198
+ ch. validate( UnitCoord :: new( 3 , NodeIndex ( 1 ) ) )
199
+ . expect_err( "validate() should return error, returned Ok(()) instead" ) ,
200
+ Error :: NotDescendantOfPreviousUnit ( NodeIndex ( 1 ) )
201
+ ) ;
202
+ }
203
+
204
+ #[ test]
205
+ fn given_control_hash_when_creator_parent_exists_but_has_wrong_round_then_err_is_returned_from_validate (
206
+ ) {
207
+ let parent_map = vec ! [
208
+ Some ( ( [ 0 ; 8 ] , 2 ) ) ,
209
+ Some ( ( [ 1 ; 8 ] , 1 ) ) ,
210
+ Some ( ( [ 2 ; 8 ] , 2 ) ) ,
211
+ Some ( ( [ 3 ; 8 ] , 2 ) ) ,
212
+ ]
213
+ . into ( ) ;
214
+ let ch = ControlHash :: < Hasher64 > :: new ( & parent_map) ;
215
+ assert_eq ! (
216
+ ch. validate( UnitCoord :: new( 3 , NodeIndex ( 1 ) ) )
217
+ . expect_err( "validate() should return error, returned Ok(()) instead" ) ,
218
+ Error :: DescendantOfPreviousUnitButWrongRound ( 1 )
219
+ ) ;
220
+ }
221
+
222
+ #[ test]
223
+ fn given_control_hash_when_there_are_not_enough_previous_round_parents_then_err_is_returned_from_validate (
224
+ ) {
225
+ let parent_map = vec ! [
226
+ None ,
227
+ Some ( ( [ 1 ; 8 ] , 2 ) ) ,
228
+ Some ( ( [ 2 ; 8 ] , 2 ) ) ,
229
+ Some ( ( [ 3 ; 8 ] , 1 ) ) ,
230
+ ]
231
+ . into ( ) ;
232
+ let ch = ControlHash :: < Hasher64 > :: new ( & parent_map) ;
233
+ assert_eq ! (
234
+ ch. validate( UnitCoord :: new( 3 , NodeIndex ( 1 ) ) )
235
+ . expect_err( "validate() should return error, returned Ok(()) instead" ) ,
236
+ Error :: NotEnoughParentsForRound ( 2 )
237
+ ) ;
238
+ }
239
+
240
+ #[ test]
241
+ fn given_control_hash_when_there_are_parents_from_greater_rounds_then_err_is_returned_from_validate (
242
+ ) {
243
+ let parent_map = vec ! [
244
+ Some ( ( [ 0 ; 8 ] , 2 ) ) ,
245
+ Some ( ( [ 1 ; 8 ] , 2 ) ) ,
246
+ Some ( ( [ 2 ; 8 ] , 2 ) ) ,
247
+ Some ( ( [ 3 ; 8 ] , 3 ) ) ,
248
+ ]
249
+ . into ( ) ;
250
+ let ch = ControlHash :: < Hasher64 > :: new ( & parent_map) ;
251
+ assert_eq ! (
252
+ ch. validate( UnitCoord :: new( 3 , NodeIndex ( 1 ) ) )
253
+ . expect_err( "validate() should return error, returned Ok(()) instead" ) ,
254
+ Error :: ParentsHigherThanRound ( 2 )
255
+ ) ;
256
+ }
93
257
}
0 commit comments