Skip to content

Commit c60d1d7

Browse files
Added tests in control hash module
1 parent 9e7d2ce commit c60d1d7

File tree

1 file changed

+166
-2
lines changed

1 file changed

+166
-2
lines changed

consensus/src/units/control_hash.rs

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
use crate::{units::UnitCoord, Hasher, NodeCount, NodeMap, Round};
1+
use crate::{units::UnitCoord, Hasher, NodeCount, NodeIndex, NodeMap, Round};
22
use codec::{Decode, Encode};
33

4+
#[derive(Debug, PartialEq)]
5+
pub enum Error {
6+
NotDescendantOfPreviousUnit(NodeIndex),
7+
DescendantOfPreviousUnitButWrongRound(Round),
8+
NotEnoughParentsForRound(Round),
9+
ParentsHigherThanRound(Round),
10+
}
11+
412
/// 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.
614
#[derive(Clone, Eq, PartialEq, Hash, Debug, Decode, Encode)]
715
pub struct ControlHash<H: Hasher> {
816
pub(crate) parents: NodeMap<Round>,
917
pub(crate) combined_hash: H::Hash,
1018
}
1119

1220
impl<H: Hasher> ControlHash<H> {
21+
/// Creates new control hash from parents hashes and rounds
1322
pub(crate) fn new(parents_with_rounds_and_hashes: &NodeMap<(H::Hash, Round)>) -> Self {
1423
let mut parents_with_rounds = NodeMap::with_size(parents_with_rounds_and_hashes.size());
1524
for (parent_index, (_, parent_round)) in parents_with_rounds_and_hashes.iter() {
@@ -26,23 +35,78 @@ impl<H: Hasher> ControlHash<H> {
2635
parent_map.using_encoded(H::hash)
2736
}
2837

38+
/// Iterator over non-empty parents - returns [`UnitCoord`]s
2939
pub(crate) fn parents(&self) -> impl Iterator<Item = UnitCoord> + '_ {
3040
self.parents
3141
.iter()
3242
.map(|(node_index, &round)| UnitCoord::new(round, node_index))
3343
}
3444

45+
/// Returns number of non-empty parents
3546
pub(crate) fn n_parents(&self) -> NodeCount {
3647
NodeCount(self.parents().count())
3748
}
3849

50+
/// Returns number of all members in abft consensus
3951
pub(crate) fn n_members(&self) -> NodeCount {
4052
self.parents.size()
4153
}
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+
}
42105
}
43106

44107
#[cfg(test)]
45108
pub mod tests {
109+
use crate::units::control_hash::Error;
46110
use crate::units::UnitCoord;
47111
use crate::{units::ControlHash, NodeCount, NodeIndex};
48112
use aleph_bft_mock::Hasher64;
@@ -90,4 +154,104 @@ pub mod tests {
90154
];
91155
assert_eq!(parents, expected_parents);
92156
}
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+
}
93257
}

0 commit comments

Comments
 (0)