aleph-bft 0.45.4

AlephBFT is an asynchronous and Byzantine fault tolerant consensus protocol aimed at ordering arbitrary messages (transactions). It has been designed to continuously operate even in the harshest conditions: with no bounds on message-delivery delays and in the presence of malicious actors. This makes it an excellent fit for blockchain-related applications.
Documentation
use crate::{units::Unit, Hasher, NodeCount, NodeIndex, NodeMap, Round};
use anyhow::Result;
use thiserror::Error;

#[derive(Eq, Error, Debug, PartialEq)]
pub enum ConstraintError {
    #[error("Not enough parents.")]
    NotEnoughParents,
    #[error("Missing own parent.")]
    MissingOwnParent,
}

#[derive(Clone)]
pub struct UnitsCollector<H: Hasher> {
    candidates: NodeMap<(H::Hash, Round)>,
    for_round: Round,
    direct_parents: NodeCount,
}

impl<H: Hasher> UnitsCollector<H> {
    pub fn new_initial(n_members: NodeCount) -> Self {
        UnitsCollector {
            candidates: NodeMap::with_size(n_members),
            for_round: 1,
            direct_parents: NodeCount(0),
        }
    }

    pub fn from_previous(previous: &UnitsCollector<H>) -> Self {
        UnitsCollector {
            candidates: previous.candidates.clone(),
            for_round: previous.for_round + 1,
            direct_parents: NodeCount(0),
        }
    }

    pub fn add_unit<U: Unit<Hasher = H>>(&mut self, unit: &U) {
        let node_id = unit.creator();
        let hash = unit.hash();
        let round = unit.round();

        if round >= self.for_round {
            return;
        }

        let to_insert = match self.candidates.get(node_id) {
            None => Some((hash, round)),
            Some((_, r)) if *r < round => Some((hash, round)),
            _ => None,
        };

        if let Some(data) = to_insert {
            self.candidates.insert(node_id, data);
            if round == self.for_round - 1 {
                self.direct_parents += NodeCount(1);
            }
        }
    }

    pub fn prospective_parents(
        &self,
        node_id: NodeIndex,
    ) -> Result<&NodeMap<(H::Hash, Round)>, ConstraintError> {
        if self.direct_parents < self.candidates.size().consensus_threshold() {
            return Err(ConstraintError::NotEnoughParents);
        }
        match self.candidates.get(node_id) {
            Some((_, r)) if *r == self.for_round - 1 => Ok(&self.candidates),
            _ => Err(ConstraintError::MissingOwnParent),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        creation::collector::{ConstraintError, UnitsCollector},
        units::{random_full_parent_units_up_to, Unit},
        NodeCount, NodeIndex,
    };
    use aleph_bft_mock::Hasher64;

    #[test]
    fn initial_fails_without_parents() {
        let n_members = NodeCount(4);
        let units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);

        let err = units_collector
            .prospective_parents(NodeIndex(0))
            .expect_err("should fail without parents");
        assert_eq!(err, ConstraintError::NotEnoughParents);
    }

    #[test]
    fn initial_fails_with_too_few_parents() {
        let n_members = NodeCount(4);
        let mut units_collector = UnitsCollector::new_initial(n_members);
        let units = random_full_parent_units_up_to(0, n_members, 43);
        units_collector.add_unit(&units[0][0]);

        let err = units_collector
            .prospective_parents(NodeIndex(0))
            .expect_err("should fail without parents");
        assert_eq!(err, ConstraintError::NotEnoughParents);
    }

    #[test]
    fn initial_fails_without_own_parent() {
        let n_members = NodeCount(4);
        let mut units_collector = UnitsCollector::new_initial(n_members);
        let units = random_full_parent_units_up_to(0, n_members, 43);
        for unit in units[0].iter().skip(1) {
            units_collector.add_unit(unit);
        }

        let err = units_collector
            .prospective_parents(NodeIndex(0))
            .expect_err("should fail without parents");
        assert_eq!(err, ConstraintError::MissingOwnParent);
    }

    #[test]
    fn initial_successfully_computes_minimal_parents() {
        let n_members = NodeCount(4);
        let mut units_collector = UnitsCollector::new_initial(n_members);
        let units = random_full_parent_units_up_to(0, n_members, 43);
        for unit in units[0].iter().take(3) {
            units_collector.add_unit(unit);
        }

        let parents = units_collector
            .prospective_parents(NodeIndex(0))
            .expect("we should be able to retrieve parents");
        assert_eq!(parents.item_count(), 3);

        let new_units: Vec<_> = units[0]
            .iter()
            .take(3)
            .map(|unit| (unit.hash(), unit.round()))
            .collect();
        let selected_parents: Vec<_> = parents.values().cloned().collect();
        assert_eq!(new_units, selected_parents);
    }

    #[test]
    fn initial_successfully_computes_full_parents() {
        let n_members = NodeCount(4);
        let mut units_collector = UnitsCollector::new_initial(n_members);
        let units = random_full_parent_units_up_to(0, n_members, 43);
        for unit in &units[0] {
            units_collector.add_unit(unit);
        }

        let parents = units_collector
            .prospective_parents(NodeIndex(0))
            .expect("we should be able to retrieve parents");
        assert_eq!(parents.item_count(), 4);

        let new_units: Vec<_> = units[0]
            .iter()
            .map(|unit| (unit.hash(), unit.round()))
            .collect();
        let selected_parents: Vec<_> = parents.values().cloned().collect();
        assert_eq!(new_units, selected_parents);
    }

    #[test]
    fn initial_ignores_future_rounds() {
        let n_members = NodeCount(4);
        let mut units_collector = UnitsCollector::new_initial(n_members);
        let units = random_full_parent_units_up_to(2, n_members, 43);
        for round_units in &units {
            for unit in round_units {
                units_collector.add_unit(unit);
            }
        }

        let parents = units_collector
            .prospective_parents(NodeIndex(0))
            .expect("we should be able to retrieve parents");
        assert_eq!(parents.item_count(), 4);

        let new_units: Vec<_> = units[0]
            .iter()
            .map(|unit| (unit.hash(), unit.round()))
            .collect();
        let selected_parents: Vec<_> = parents.values().cloned().collect();
        assert_eq!(new_units, selected_parents);
    }

    #[test]
    fn following_fails_without_parents() {
        let n_members = NodeCount(4);
        let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let units_collector = UnitsCollector::from_previous(&initial_units_collector);

        let err = units_collector
            .prospective_parents(NodeIndex(0))
            .expect_err("should fail without parents");
        assert_eq!(err, ConstraintError::NotEnoughParents);
    }

    #[test]
    fn following_fails_with_too_few_parents() {
        let n_members = NodeCount(4);
        let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
        let units = random_full_parent_units_up_to(1, n_members, 43);
        units_collector.add_unit(&units[1][0]);

        let err = units_collector
            .prospective_parents(NodeIndex(0))
            .expect_err("should fail without parents");
        assert_eq!(err, ConstraintError::NotEnoughParents);
    }

    #[test]
    fn following_fails_with_too_old_parents() {
        let n_members = NodeCount(4);
        let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
        let units = random_full_parent_units_up_to(0, n_members, 43);
        for unit in &units[0] {
            units_collector.add_unit(unit);
        }

        let err = units_collector
            .prospective_parents(NodeIndex(0))
            .expect_err("should fail without parents");
        assert_eq!(err, ConstraintError::NotEnoughParents);
    }

    #[test]
    fn following_fails_without_own_parent() {
        let n_members = NodeCount(4);
        let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
        let units = random_full_parent_units_up_to(1, n_members, 43);
        for unit in units[1].iter().skip(1) {
            units_collector.add_unit(unit);
        }

        let err = units_collector
            .prospective_parents(NodeIndex(0))
            .expect_err("should fail without parents");
        assert_eq!(err, ConstraintError::MissingOwnParent);
    }

    #[test]
    fn following_fails_with_too_old_own_parent() {
        let n_members = NodeCount(4);
        let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
        let units = random_full_parent_units_up_to(1, n_members, 43);
        for unit in units[1].iter().skip(1) {
            units_collector.add_unit(unit);
        }
        units_collector.add_unit(&units[0][0]);

        let err = units_collector
            .prospective_parents(NodeIndex(0))
            .expect_err("should fail without parents");
        assert_eq!(err, ConstraintError::MissingOwnParent);
    }

    #[test]
    fn following_successfully_computes_minimal_parents() {
        let n_members = NodeCount(4);
        let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
        let units = random_full_parent_units_up_to(1, n_members, 43);
        for unit in units[1].iter().take(3) {
            units_collector.add_unit(unit);
        }

        let parents = units_collector
            .prospective_parents(NodeIndex(0))
            .expect("we should be able to retrieve parents");
        assert_eq!(parents.item_count(), 3);

        let new_units: Vec<_> = units[1]
            .iter()
            .take(3)
            .map(|unit| (unit.hash(), unit.round()))
            .collect();
        let selected_parents: Vec<_> = parents.values().cloned().collect();
        assert_eq!(new_units, selected_parents);
    }

    #[test]
    fn following_successfully_computes_minimal_parents_with_ancient() {
        let n_members = NodeCount(4);
        let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
        let units = random_full_parent_units_up_to(1, n_members, 43);
        for unit in units[1].iter().take(3) {
            units_collector.add_unit(unit);
        }
        units_collector.add_unit(&units[0][3]);

        let parents = units_collector
            .prospective_parents(NodeIndex(0))
            .expect("we should be able to retrieve parents");
        assert_eq!(parents.item_count(), 4);

        let mut new_units: Vec<_> = units[1]
            .iter()
            .take(3)
            .map(|unit| (unit.hash(), unit.round()))
            .collect();
        new_units.push((units[0][3].hash(), units[0][3].round()));
        let selected_parents: Vec<_> = parents.values().cloned().collect();
        assert_eq!(new_units, selected_parents);
    }

    #[test]
    fn following_successfully_computes_full_parents() {
        let n_members = NodeCount(4);
        let initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
        let units = random_full_parent_units_up_to(1, n_members, 43);
        for unit in &units[1] {
            units_collector.add_unit(unit);
        }

        let parents = units_collector
            .prospective_parents(NodeIndex(0))
            .expect("we should be able to retrieve parents");
        assert_eq!(parents.item_count(), 4);

        let new_units: Vec<_> = units[1]
            .iter()
            .map(|unit| (unit.hash(), unit.round()))
            .collect();
        let selected_parents: Vec<_> = parents.values().cloned().collect();
        assert_eq!(new_units, selected_parents);
    }

    #[test]
    fn following_inherits_units() {
        let n_members = NodeCount(4);
        let mut initial_units_collector = UnitsCollector::<Hasher64>::new_initial(n_members);
        let units = random_full_parent_units_up_to(1, n_members, 43);
        for unit in &units[0] {
            initial_units_collector.add_unit(unit);
        }
        let mut units_collector = UnitsCollector::from_previous(&initial_units_collector);
        for unit in units[1].iter().take(3) {
            units_collector.add_unit(unit);
        }

        let parents = units_collector
            .prospective_parents(NodeIndex(0))
            .expect("we should be able to retrieve parents");
        assert_eq!(parents.item_count(), 4);

        let mut new_units: Vec<_> = units[1]
            .iter()
            .take(3)
            .map(|unit| (unit.hash(), unit.round()))
            .collect();
        new_units.push((units[0][3].hash(), units[0][3].round()));
        let selected_parents: Vec<_> = parents.values().cloned().collect();
        assert_eq!(new_units, selected_parents);
    }
}