Skip to content

Commit 496ff02

Browse files
authored
A0-4559: Create units with ancient parents (#528)
* Create units with ancient parents * Moar tests * Forgot to fmt
1 parent 72dc180 commit 496ff02

File tree

8 files changed

+466
-145
lines changed

8 files changed

+466
-145
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ More details are available [in the book][reference-link-implementation-details].
6060
- Import AlephBFT in your crate
6161
```toml
6262
[dependencies]
63-
aleph-bft = "^0.41"
63+
aleph-bft = "^0.42"
6464
```
6565
- The main entry point is the `run_session` function, which returns a Future that runs the
6666
consensus algorithm.

consensus/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aleph-bft"
3-
version = "0.41.1"
3+
version = "0.42.0"
44
edition = "2021"
55
authors = ["Cardinal Cryptography"]
66
categories = ["algorithms", "data-structures", "cryptography", "database"]

consensus/src/creation/collector.rs

Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
use crate::{units::Unit, Hasher, NodeCount, NodeIndex, NodeMap, Round};
2+
use anyhow::Result;
3+
use thiserror::Error;
4+
5+
#[derive(Eq, Error, Debug, PartialEq)]
6+
pub enum ConstraintError {
7+
#[error("Not enough parents.")]
8+
NotEnoughParents,
9+
#[error("Missing own parent.")]
10+
MissingOwnParent,
11+
}
12+
13+
#[derive(Clone)]
14+
pub struct UnitsCollector<H: Hasher> {
15+
candidates: NodeMap<(H::Hash, Round)>,
16+
for_round: Round,
17+
direct_parents: NodeCount,
18+
}
19+
20+
impl<H: Hasher> UnitsCollector<H> {
21+
pub fn new_initial(n_members: NodeCount) -> Self {
22+
UnitsCollector {
23+
candidates: NodeMap::with_size(n_members),
24+
for_round: 1,
25+
direct_parents: NodeCount(0),
26+
}
27+
}
28+
29+
pub fn from_previous(previous: &UnitsCollector<H>) -> Self {
30+
UnitsCollector {
31+
candidates: previous.candidates.clone(),
32+
for_round: previous.for_round + 1,
33+
direct_parents: NodeCount(0),
34+
}
35+
}
36+
37+
pub fn add_unit<U: Unit<Hasher = H>>(&mut self, unit: &U) {
38+
let node_id = unit.creator();
39+
let hash = unit.hash();
40+
let round = unit.round();
41+
42+
if round >= self.for_round {
43+
return;
44+
}
45+
46+
let to_insert = match self.candidates.get(node_id) {
47+
None => Some((hash, round)),
48+
Some((_, r)) if *r < round => Some((hash, round)),
49+
_ => None,
50+
};
51+
52+
if let Some(data) = to_insert {
53+
self.candidates.insert(node_id, data);
54+
if round == self.for_round - 1 {
55+
self.direct_parents += NodeCount(1);
56+
}
57+
}
58+
}
59+
60+
pub fn prospective_parents(
61+
&self,
62+
node_id: NodeIndex,
63+
) -> Result<&NodeMap<(H::Hash, Round)>, ConstraintError> {
64+
if self.direct_parents < self.candidates.size().consensus_threshold() {
65+
return Err(ConstraintError::NotEnoughParents);
66+
}
67+
match self.candidates.get(node_id) {
68+
Some((_, r)) if *r == self.for_round - 1 => Ok(&self.candidates),
69+
_ => Err(ConstraintError::MissingOwnParent),
70+
}
71+
}
72+
}
73+
74+
#[cfg(test)]
75+
mod tests {
76+
use crate::{
77+
creation::collector::{ConstraintError, UnitsCollector},
78+
units::{random_full_parent_units_up_to, Unit},
79+
NodeCount, NodeIndex,
80+
};
81+
use aleph_bft_mock::Hasher64;
82+
83+
#[test]
84+
fn initial_fails_without_parents() {
85+
let n_members = NodeCount(4);
86+
let units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
87+
88+
let err = units_collector
89+
.prospective_parents(NodeIndex(0))
90+
.expect_err("should fail without parents");
91+
assert_eq!(err, ConstraintError::NotEnoughParents);
92+
}
93+
94+
#[test]
95+
fn initial_fails_with_too_few_parents() {
96+
let n_members = NodeCount(4);
97+
let mut units_collector = UnitsCollector::new_initial(n_members);
98+
let units = random_full_parent_units_up_to(0, n_members, 43);
99+
units_collector.add_unit(&units[0][0]);
100+
101+
let err = units_collector
102+
.prospective_parents(NodeIndex(0))
103+
.expect_err("should fail without parents");
104+
assert_eq!(err, ConstraintError::NotEnoughParents);
105+
}
106+
107+
#[test]
108+
fn initial_fails_without_own_parent() {
109+
let n_members = NodeCount(4);
110+
let mut units_collector = UnitsCollector::new_initial(n_members);
111+
let units = random_full_parent_units_up_to(0, n_members, 43);
112+
for unit in units[0].iter().skip(1) {
113+
units_collector.add_unit(unit);
114+
}
115+
116+
let err = units_collector
117+
.prospective_parents(NodeIndex(0))
118+
.expect_err("should fail without parents");
119+
assert_eq!(err, ConstraintError::MissingOwnParent);
120+
}
121+
122+
#[test]
123+
fn initial_successfully_computes_minimal_parents() {
124+
let n_members = NodeCount(4);
125+
let mut units_collector = UnitsCollector::new_initial(n_members);
126+
let units = random_full_parent_units_up_to(0, n_members, 43);
127+
for unit in units[0].iter().take(3) {
128+
units_collector.add_unit(unit);
129+
}
130+
131+
let parents = units_collector
132+
.prospective_parents(NodeIndex(0))
133+
.expect("we should be able to retrieve parents");
134+
assert_eq!(parents.item_count(), 3);
135+
136+
let new_units: Vec<_> = units[0]
137+
.iter()
138+
.take(3)
139+
.map(|unit| (unit.hash(), unit.round()))
140+
.collect();
141+
let selected_parents: Vec<_> = parents.values().cloned().collect();
142+
assert_eq!(new_units, selected_parents);
143+
}
144+
145+
#[test]
146+
fn initial_successfully_computes_full_parents() {
147+
let n_members = NodeCount(4);
148+
let mut units_collector = UnitsCollector::new_initial(n_members);
149+
let units = random_full_parent_units_up_to(0, n_members, 43);
150+
for unit in &units[0] {
151+
units_collector.add_unit(unit);
152+
}
153+
154+
let parents = units_collector
155+
.prospective_parents(NodeIndex(0))
156+
.expect("we should be able to retrieve parents");
157+
assert_eq!(parents.item_count(), 4);
158+
159+
let new_units: Vec<_> = units[0]
160+
.iter()
161+
.map(|unit| (unit.hash(), unit.round()))
162+
.collect();
163+
let selected_parents: Vec<_> = parents.values().cloned().collect();
164+
assert_eq!(new_units, selected_parents);
165+
}
166+
167+
#[test]
168+
fn initial_ignores_future_rounds() {
169+
let n_members = NodeCount(4);
170+
let mut units_collector = UnitsCollector::new_initial(n_members);
171+
let units = random_full_parent_units_up_to(2, n_members, 43);
172+
for round_units in &units {
173+
for unit in round_units {
174+
units_collector.add_unit(unit);
175+
}
176+
}
177+
178+
let parents = units_collector
179+
.prospective_parents(NodeIndex(0))
180+
.expect("we should be able to retrieve parents");
181+
assert_eq!(parents.item_count(), 4);
182+
183+
let new_units: Vec<_> = units[0]
184+
.iter()
185+
.map(|unit| (unit.hash(), unit.round()))
186+
.collect();
187+
let selected_parents: Vec<_> = parents.values().cloned().collect();
188+
assert_eq!(new_units, selected_parents);
189+
}
190+
191+
#[test]
192+
fn following_fails_without_parents() {
193+
let n_members = NodeCount(4);
194+
let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
195+
let units_collector = UnitsCollector::from_previous(&initial_units_collector);
196+
197+
let err = units_collector
198+
.prospective_parents(NodeIndex(0))
199+
.expect_err("should fail without parents");
200+
assert_eq!(err, ConstraintError::NotEnoughParents);
201+
}
202+
203+
#[test]
204+
fn following_fails_with_too_few_parents() {
205+
let n_members = NodeCount(4);
206+
let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
207+
let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
208+
let units = random_full_parent_units_up_to(1, n_members, 43);
209+
units_collector.add_unit(&units[1][0]);
210+
211+
let err = units_collector
212+
.prospective_parents(NodeIndex(0))
213+
.expect_err("should fail without parents");
214+
assert_eq!(err, ConstraintError::NotEnoughParents);
215+
}
216+
217+
#[test]
218+
fn following_fails_with_too_old_parents() {
219+
let n_members = NodeCount(4);
220+
let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
221+
let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
222+
let units = random_full_parent_units_up_to(0, n_members, 43);
223+
for unit in &units[0] {
224+
units_collector.add_unit(unit);
225+
}
226+
227+
let err = units_collector
228+
.prospective_parents(NodeIndex(0))
229+
.expect_err("should fail without parents");
230+
assert_eq!(err, ConstraintError::NotEnoughParents);
231+
}
232+
233+
#[test]
234+
fn following_fails_without_own_parent() {
235+
let n_members = NodeCount(4);
236+
let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
237+
let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
238+
let units = random_full_parent_units_up_to(1, n_members, 43);
239+
for unit in units[1].iter().skip(1) {
240+
units_collector.add_unit(unit);
241+
}
242+
243+
let err = units_collector
244+
.prospective_parents(NodeIndex(0))
245+
.expect_err("should fail without parents");
246+
assert_eq!(err, ConstraintError::MissingOwnParent);
247+
}
248+
249+
#[test]
250+
fn following_fails_with_too_old_own_parent() {
251+
let n_members = NodeCount(4);
252+
let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
253+
let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
254+
let units = random_full_parent_units_up_to(1, n_members, 43);
255+
for unit in units[1].iter().skip(1) {
256+
units_collector.add_unit(unit);
257+
}
258+
units_collector.add_unit(&units[0][0]);
259+
260+
let err = units_collector
261+
.prospective_parents(NodeIndex(0))
262+
.expect_err("should fail without parents");
263+
assert_eq!(err, ConstraintError::MissingOwnParent);
264+
}
265+
266+
#[test]
267+
fn following_successfully_computes_minimal_parents() {
268+
let n_members = NodeCount(4);
269+
let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
270+
let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
271+
let units = random_full_parent_units_up_to(1, n_members, 43);
272+
for unit in units[1].iter().take(3) {
273+
units_collector.add_unit(unit);
274+
}
275+
276+
let parents = units_collector
277+
.prospective_parents(NodeIndex(0))
278+
.expect("we should be able to retrieve parents");
279+
assert_eq!(parents.item_count(), 3);
280+
281+
let new_units: Vec<_> = units[1]
282+
.iter()
283+
.take(3)
284+
.map(|unit| (unit.hash(), unit.round()))
285+
.collect();
286+
let selected_parents: Vec<_> = parents.values().cloned().collect();
287+
assert_eq!(new_units, selected_parents);
288+
}
289+
290+
#[test]
291+
fn following_successfully_computes_minimal_parents_with_ancient() {
292+
let n_members = NodeCount(4);
293+
let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
294+
let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
295+
let units = random_full_parent_units_up_to(1, n_members, 43);
296+
for unit in units[1].iter().take(3) {
297+
units_collector.add_unit(unit);
298+
}
299+
units_collector.add_unit(&units[0][3]);
300+
301+
let parents = units_collector
302+
.prospective_parents(NodeIndex(0))
303+
.expect("we should be able to retrieve parents");
304+
assert_eq!(parents.item_count(), 4);
305+
306+
let mut new_units: Vec<_> = units[1]
307+
.iter()
308+
.take(3)
309+
.map(|unit| (unit.hash(), unit.round()))
310+
.collect();
311+
new_units.push((units[0][3].hash(), units[0][3].round()));
312+
let selected_parents: Vec<_> = parents.values().cloned().collect();
313+
assert_eq!(new_units, selected_parents);
314+
}
315+
316+
#[test]
317+
fn following_successfully_computes_full_parents() {
318+
let n_members = NodeCount(4);
319+
let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
320+
let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
321+
let units = random_full_parent_units_up_to(1, n_members, 43);
322+
for unit in &units[1] {
323+
units_collector.add_unit(unit);
324+
}
325+
326+
let parents = units_collector
327+
.prospective_parents(NodeIndex(0))
328+
.expect("we should be able to retrieve parents");
329+
assert_eq!(parents.item_count(), 4);
330+
331+
let new_units: Vec<_> = units[1]
332+
.iter()
333+
.map(|unit| (unit.hash(), unit.round()))
334+
.collect();
335+
let selected_parents: Vec<_> = parents.values().cloned().collect();
336+
assert_eq!(new_units, selected_parents);
337+
}
338+
339+
#[test]
340+
fn following_inherits_units() {
341+
let n_members = NodeCount(4);
342+
let mut initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
343+
let units = random_full_parent_units_up_to(1, n_members, 43);
344+
for unit in &units[0] {
345+
initial_units_collector.add_unit(unit);
346+
}
347+
let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
348+
for unit in units[1].iter().take(3) {
349+
units_collector.add_unit(unit);
350+
}
351+
352+
let parents = units_collector
353+
.prospective_parents(NodeIndex(0))
354+
.expect("we should be able to retrieve parents");
355+
assert_eq!(parents.item_count(), 4);
356+
357+
let mut new_units: Vec<_> = units[1]
358+
.iter()
359+
.take(3)
360+
.map(|unit| (unit.hash(), unit.round()))
361+
.collect();
362+
new_units.push((units[0][3].hash(), units[0][3].round()));
363+
let selected_parents: Vec<_> = parents.values().cloned().collect();
364+
assert_eq!(new_units, selected_parents);
365+
}
366+
}

0 commit comments

Comments
 (0)