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 std::fmt::{Display, Formatter, Result as FmtResult};

use crate::{
    Data, Hasher, Index, MultiKeychain, NodeCount, NodeIndex, Round, SessionId, Signable, Signed,
    UncheckedSigned,
};
use codec::{Decode, Encode};
use derivative::Derivative;
use parking_lot::RwLock;

mod control_hash;
mod store;
#[cfg(test)]
mod testing;
mod validator;

pub use control_hash::{ControlHash, Error as ControlHashError};
pub(crate) use store::*;
#[cfg(test)]
pub use testing::{
    create_preunits, creator_set, full_unit_to_unchecked_signed_unit,
    minimal_reconstructed_dag_units_up_to, preunit_to_full_unit, preunit_to_signed_unit,
    preunit_to_unchecked_signed_unit, random_full_parent_reconstrusted_units_up_to,
    random_full_parent_units_up_to, random_reconstructed_unit_with_parents,
    random_unit_with_parents, DagUnit as TestingDagUnit, FullUnit as TestingFullUnit,
    SignedUnit as TestingSignedUnit, WrappedSignedUnit,
};
pub use validator::{ValidationError, Validator};

/// The coordinates of a unit, i.e. creator and round. In the absence of forks this uniquely
/// determines a unit within a session.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default, Encode, Decode)]
pub struct UnitCoord {
    round: Round,
    creator: NodeIndex,
}

impl UnitCoord {
    pub fn new(round: Round, creator: NodeIndex) -> Self {
        Self { creator, round }
    }

    pub fn creator(&self) -> NodeIndex {
        self.creator
    }

    pub fn round(&self) -> Round {
        self.round
    }
}

impl Display for UnitCoord {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "(#{} by {})", self.round, self.creator.0)
    }
}

/// The simplest type representing a unit, consisting of coordinates and a control hash
#[derive(Clone, Eq, PartialEq, Hash, Debug, Decode, Encode)]
pub struct PreUnit<H: Hasher> {
    coord: UnitCoord,
    control_hash: ControlHash<H>,
}

impl<H: Hasher> PreUnit<H> {
    pub(crate) fn new(creator: NodeIndex, round: Round, control_hash: ControlHash<H>) -> Self {
        PreUnit {
            coord: UnitCoord::new(round, creator),
            control_hash,
        }
    }

    pub(crate) fn n_members(&self) -> NodeCount {
        self.control_hash.n_members()
    }

    pub(crate) fn creator(&self) -> NodeIndex {
        self.coord.creator()
    }

    pub(crate) fn round(&self) -> Round {
        self.coord.round()
    }

    pub(crate) fn control_hash(&self) -> &ControlHash<H> {
        &self.control_hash
    }
}

#[derive(Debug, Decode, Derivative, Encode)]
#[derivative(Eq, PartialEq, Hash)]
pub struct FullUnit<H: Hasher, D: Data> {
    pre_unit: PreUnit<H>,
    data: Option<D>,
    session_id: SessionId,
    #[codec(skip)]
    #[derivative(PartialEq = "ignore", Hash = "ignore")]
    hash: RwLock<Option<H::Hash>>,
}

impl<H: Hasher, D: Data> From<FullUnit<H, D>> for Option<D> {
    fn from(value: FullUnit<H, D>) -> Self {
        value.data
    }
}

impl<H: Hasher, D: Data> Clone for FullUnit<H, D> {
    fn clone(&self) -> Self {
        let hash = self.hash.try_read().and_then(|guard| *guard);
        FullUnit {
            pre_unit: self.pre_unit.clone(),
            data: self.data.clone(),
            session_id: self.session_id,
            hash: RwLock::new(hash),
        }
    }
}

impl<H: Hasher, D: Data> FullUnit<H, D> {
    pub(crate) fn new(pre_unit: PreUnit<H>, data: Option<D>, session_id: SessionId) -> Self {
        FullUnit {
            pre_unit,
            data,
            session_id,
            hash: RwLock::new(None),
        }
    }
    pub(crate) fn as_pre_unit(&self) -> &PreUnit<H> {
        &self.pre_unit
    }
    pub(crate) fn data(&self) -> &Option<D> {
        &self.data
    }
    pub(crate) fn included_data(&self) -> Vec<D> {
        self.data.iter().cloned().collect()
    }
}

impl<H: Hasher, D: Data> Signable for FullUnit<H, D> {
    type Hash = H::Hash;
    fn hash(&self) -> H::Hash {
        Unit::hash(self)
    }
}

impl<H: Hasher, D: Data> Index for FullUnit<H, D> {
    fn index(&self) -> NodeIndex {
        self.creator()
    }
}

pub(crate) type UncheckedSignedUnit<H, D, S> = UncheckedSigned<FullUnit<H, D>, S>;

pub(crate) type SignedUnit<H, D, K> = Signed<FullUnit<H, D>, K>;

/// Abstract representation of a unit from the Dag point of view.
pub trait Unit: 'static + Send + Clone {
    type Hasher: Hasher;

    fn hash(&self) -> <Self::Hasher as Hasher>::Hash;

    fn coord(&self) -> UnitCoord;

    fn control_hash(&self) -> &ControlHash<Self::Hasher>;

    fn session_id(&self) -> SessionId;

    fn creator(&self) -> NodeIndex {
        self.coord().creator()
    }

    fn round(&self) -> Round {
        self.coord().round()
    }
}

pub trait WrappedUnit<H: Hasher>: Unit<Hasher = H> {
    type Wrapped: Unit<Hasher = H>;

    fn unpack(self) -> Self::Wrapped;
}

pub trait UnitWithParents: Unit {
    fn parents(&self) -> impl Iterator<Item = &HashFor<Self>>;
    fn direct_parents(&self) -> impl Iterator<Item = &HashFor<Self>>;
    fn parent_for(&self, index: NodeIndex) -> Option<&HashFor<Self>>;

    fn node_count(&self) -> NodeCount;
}

impl<H: Hasher, D: Data> Unit for FullUnit<H, D> {
    type Hasher = H;

    fn hash(&self) -> H::Hash {
        let hash = *self.hash.read();
        match hash {
            Some(hash) => hash,
            None => {
                let hash = self.using_encoded(H::hash);
                *self.hash.write() = Some(hash);
                hash
            }
        }
    }

    fn coord(&self) -> UnitCoord {
        self.pre_unit.coord
    }

    fn control_hash(&self) -> &ControlHash<Self::Hasher> {
        self.pre_unit.control_hash()
    }

    fn session_id(&self) -> SessionId {
        self.session_id
    }
}

impl<H: Hasher, D: Data, MK: MultiKeychain> Unit for SignedUnit<H, D, MK> {
    type Hasher = H;

    fn hash(&self) -> H::Hash {
        Unit::hash(self.as_signable())
    }

    fn coord(&self) -> UnitCoord {
        self.as_signable().coord()
    }

    fn control_hash(&self) -> &ControlHash<Self::Hasher> {
        self.as_signable().control_hash()
    }

    fn session_id(&self) -> SessionId {
        self.as_signable().session_id()
    }
}

pub type HashFor<U> = <<U as Unit>::Hasher as Hasher>::Hash;

#[cfg(test)]
pub mod tests {
    use crate::{
        units::{random_full_parent_units_up_to, FullUnit, Unit},
        Hasher, NodeCount,
    };
    use aleph_bft_mock::{Data, Hasher64};
    use codec::{Decode, Encode};

    pub type TestFullUnit = FullUnit<Hasher64, Data>;

    #[test]
    fn test_full_unit_hash_is_correct() {
        for full_unit in random_full_parent_units_up_to(3, NodeCount(4), 43)
            .into_iter()
            .flatten()
        {
            let hash = full_unit.using_encoded(Hasher64::hash);
            assert_eq!(full_unit.hash(), hash);
        }
    }

    #[test]
    fn test_full_unit_codec() {
        for full_unit in random_full_parent_units_up_to(3, NodeCount(4), 43)
            .into_iter()
            .flatten()
        {
            let encoded = full_unit.encode();
            let decoded =
                TestFullUnit::decode(&mut encoded.as_slice()).expect("should decode correctly");
            assert_eq!(decoded, full_unit);
        }
    }
}