diff --git a/Cargo.lock b/Cargo.lock index 15ca368..b0fc70b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,7 +28,7 @@ dependencies = [ [[package]] name = "aleph-bft" -version = "0.36.5" +version = "0.37.0" dependencies = [ "aleph-bft-mock", "aleph-bft-rmc", @@ -51,7 +51,7 @@ dependencies = [ [[package]] name = "aleph-bft-crypto" -version = "0.9.0" +version = "0.9.1" dependencies = [ "async-trait", "bit-vec", @@ -102,7 +102,7 @@ dependencies = [ [[package]] name = "aleph-bft-mock" -version = "0.14.0" +version = "0.15.0" dependencies = [ "aleph-bft-types", "async-trait", @@ -132,7 +132,7 @@ dependencies = [ [[package]] name = "aleph-bft-types" -version = "0.13.0" +version = "0.14.0" dependencies = [ "aleph-bft-crypto", "async-trait", diff --git a/README.md b/README.md index 91330c6..ffe98a5 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ More details are available [in the book][reference-link-implementation-details]. - Import AlephBFT in your crate ```toml [dependencies] - aleph-bft = "^0.36" + aleph-bft = "^0.37" ``` - The main entry point is the `run_session` function, which returns a Future that runs the consensus algorithm. diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index 24e3ea7..28376dc 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aleph-bft" -version = "0.36.5" +version = "0.37.0" edition = "2021" authors = ["Cardinal Cryptography"] categories = ["algorithms", "data-structures", "cryptography", "database"] @@ -14,7 +14,7 @@ description = "AlephBFT is an asynchronous and Byzantine fault tolerant consensu [dependencies] aleph-bft-rmc = { path = "../rmc", version = "0.13" } -aleph-bft-types = { path = "../types", version = "0.13" } +aleph-bft-types = { path = "../types", version = "0.14" } anyhow = "1.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.0", default-features = false, features = ["derive"] } diff --git a/consensus/src/creation/mod.rs b/consensus/src/creation/mod.rs index e8400e0..9515820 100644 --- a/consensus/src/creation/mod.rs +++ b/consensus/src/creation/mod.rs @@ -1,7 +1,7 @@ use crate::{ config::Config, units::{PreUnit, SignedUnit, Unit}, - Data, DataProvider, MultiKeychain, Receiver, Round, Sender, Terminator, + DataProvider, MultiKeychain, Receiver, Round, Sender, Terminator, }; use futures::{ channel::{ @@ -32,9 +32,9 @@ impl From> for CreatorError { } } -pub struct IO> { +pub struct IO { pub incoming_parents: Receiver, - pub outgoing_units: Sender>, + pub outgoing_units: Sender>, pub data_provider: DP, } @@ -106,9 +106,9 @@ async fn keep_processing_units_until( /// /// We refer to the documentation https://cardinal-cryptography.github.io/AlephBFT/internals.html /// Section 5.1 for a discussion of this component. -pub async fn run>( +pub async fn run( conf: Config, - mut io: IO, + mut io: IO, keychain: MK, mut starting_round: oneshot::Receiver>, mut terminator: Terminator, @@ -123,14 +123,9 @@ pub async fn run>( terminator.terminate_sync().await; } -async fn read_starting_round_and_run_creator< - U: Unit, - D: Data, - MK: MultiKeychain, - DP: DataProvider, ->( +async fn read_starting_round_and_run_creator( conf: Config, - io: &mut IO, + io: &mut IO, keychain: MK, starting_round: &mut oneshot::Receiver>, ) { @@ -159,9 +154,9 @@ async fn read_starting_round_and_run_creator< } } -async fn run_creator>( +async fn run_creator( conf: Config, - io: &mut IO, + io: &mut IO, keychain: MK, starting_round: Round, ) -> anyhow::Result<(), CreatorError> { diff --git a/consensus/src/dag/reconstruction/mod.rs b/consensus/src/dag/reconstruction/mod.rs index 09bb1ea..5a44b64 100644 --- a/consensus/src/dag/reconstruction/mod.rs +++ b/consensus/src/dag/reconstruction/mod.rs @@ -1,13 +1,14 @@ use std::collections::HashMap; use crate::{ - units::{ControlHash, HashFor, Unit, UnitCoord, UnitWithParents, WrappedUnit}, + units::{ControlHash, FullUnit, HashFor, Unit, UnitCoord, UnitWithParents, WrappedUnit}, Hasher, NodeMap, SessionId, }; mod dag; mod parents; +use aleph_bft_types::{Data, MultiKeychain, OrderedUnit, Signed}; use dag::Dag; use parents::Reconstruction as ParentReconstruction; @@ -76,6 +77,34 @@ impl UnitWithParents for ReconstructedUnit { } } +impl From, K>>> + for Option +{ + fn from(value: ReconstructedUnit, K>>) -> Self { + value.unpack().into_signable().into() + } +} + +impl From, K>>> + for OrderedUnit +{ + fn from(unit: ReconstructedUnit, K>>) -> Self { + let parents = unit.parents().values().cloned().collect(); + let unit = unit.unpack(); + let creator = unit.creator(); + let round = unit.round(); + let hash = unit.hash(); + let data = unit.into_signable().data().clone(); + OrderedUnit { + parents, + creator, + round, + hash, + data, + } + } +} + /// What we need to request to reconstruct units. #[derive(Debug, PartialEq, Eq)] pub enum Request { diff --git a/consensus/src/extension/mod.rs b/consensus/src/extension/mod.rs index 708a0df..2a2f372 100644 --- a/consensus/src/extension/mod.rs +++ b/consensus/src/extension/mod.rs @@ -1,13 +1,10 @@ -use crate::{ - dag::DagUnit, - units::{Unit, WrappedUnit}, - Data, FinalizationHandler, Hasher, MultiKeychain, -}; +use crate::{dag::DagUnit, MultiKeychain}; mod election; mod extender; mod units; +use aleph_bft_types::UnitFinalizationHandler; use extender::Extender; /// A struct responsible for executing the Consensus protocol on a local copy of the Dag. @@ -19,13 +16,13 @@ use extender::Extender; /// /// We refer to the documentation https://cardinal-cryptography.github.io/AlephBFT/internals.html /// Section 5.4 for a discussion of this component. -pub struct Ordering> { - extender: Extender>, - finalization_handler: FH, +pub struct Ordering { + extender: Extender>, + finalization_handler: UFH, } -impl> Ordering { - pub fn new(finalization_handler: FH) -> Self { +impl Ordering { + pub fn new(finalization_handler: UFH) -> Self { let extender = Extender::new(); Ordering { extender, @@ -33,20 +30,10 @@ impl> Ordering } } - fn handle_batch(&mut self, batch: Vec>) { - for unit in batch { - let unit = unit.unpack(); - self.finalization_handler.unit_finalized( - unit.creator(), - unit.round(), - unit.as_signable().data().clone(), - ) - } - } - - pub fn add_unit(&mut self, unit: DagUnit) { + pub fn add_unit(&mut self, unit: DagUnit) { for batch in self.extender.add_unit(unit) { - self.handle_batch(batch); + self.finalization_handler + .batch_finalized(batch.into_iter().map(|unit| unit.into()).collect()); } } } diff --git a/consensus/src/lib.rs b/consensus/src/lib.rs index a33a23b..a8cb66b 100644 --- a/consensus/src/lib.rs +++ b/consensus/src/lib.rs @@ -19,8 +19,8 @@ mod task_queue; mod testing; pub use aleph_bft_types::{ - Data, DataProvider, FinalizationHandler, Hasher, IncompleteMultisignatureError, Index, Indexed, - Keychain, MultiKeychain, Multisigned, Network, NodeCount, NodeIndex, NodeMap, NodeSubset, + Data, DataProvider, Hasher, IncompleteMultisignatureError, Index, Indexed, Keychain, + MultiKeychain, Multisigned, Network, NodeCount, NodeIndex, NodeMap, NodeSubset, PartialMultisignature, PartiallyMultisigned, Recipient, Round, SessionId, Signable, Signature, SignatureError, SignatureSet, Signed, SpawnHandle, TaskHandle, UncheckedSigned, }; diff --git a/consensus/src/member.rs b/consensus/src/member.rs index 73fe67f..706c88e 100644 --- a/consensus/src/member.rs +++ b/consensus/src/member.rs @@ -8,10 +8,10 @@ use crate::{ }, task_queue::TaskQueue, units::{UncheckedSignedUnit, Unit, UnitCoord}, - Config, Data, DataProvider, FinalizationHandler, Hasher, MultiKeychain, Network, NodeIndex, - Receiver, Recipient, Round, Sender, Signature, SpawnHandle, Terminator, UncheckedSigned, + Config, Data, DataProvider, Hasher, MultiKeychain, Network, NodeIndex, Receiver, Recipient, + Round, Sender, Signature, SpawnHandle, Terminator, UncheckedSigned, }; -use aleph_bft_types::NodeMap; +use aleph_bft_types::{FinalizationHandler, NodeMap, OrderedUnit, UnitFinalizationHandler}; use codec::{Decode, Encode}; use futures::{channel::mpsc, pin_mut, AsyncRead, AsyncWrite, FutureExt, StreamExt}; use futures_timer::Delay; @@ -106,36 +106,81 @@ enum TaskDetails { }, } +/// This adapter allows to map an implementation of [`FinalizationHandler`] onto implementation of [`UnitFinalizationHandler`]. +pub struct FinalizationHandlerAdapter { + finalization_handler: FH, + _phantom: PhantomData<(D, H)>, +} + +impl From for FinalizationHandlerAdapter { + fn from(value: FH) -> Self { + Self { + finalization_handler: value, + _phantom: PhantomData, + } + } +} + +impl> UnitFinalizationHandler + for FinalizationHandlerAdapter +{ + type Data = D; + type Hasher = H; + + fn batch_finalized(&mut self, batch: Vec>) { + for unit in batch { + if let Some(data) = unit.data { + self.finalization_handler.data_finalized(data) + } + } + } +} + #[derive(Clone)] -pub struct LocalIO< - D: Data, - DP: DataProvider, - FH: FinalizationHandler, - US: AsyncWrite, - UL: AsyncRead, -> { +pub struct LocalIO { data_provider: DP, - finalization_handler: FH, + finalization_handler: UFH, unit_saver: US, unit_loader: UL, - _phantom: PhantomData, } -impl, FH: FinalizationHandler, US: AsyncWrite, UL: AsyncRead> - LocalIO +impl< + H: Hasher, + DP: DataProvider, + FH: FinalizationHandler, + US: AsyncWrite, + UL: AsyncRead, + > LocalIO, US, UL> { pub fn new( data_provider: DP, finalization_handler: FH, unit_saver: US, unit_loader: UL, - ) -> LocalIO { - LocalIO { + ) -> Self { + Self { + data_provider, + finalization_handler: finalization_handler.into(), + unit_saver, + unit_loader, + } + } +} + +impl + LocalIO +{ + pub fn new_with_unit_finalization_handler( + data_provider: DP, + finalization_handler: UFH, + unit_saver: US, + unit_loader: UL, + ) -> Self { + Self { data_provider, finalization_handler, unit_saver, unit_loader, - _phantom: PhantomData, } } } @@ -573,19 +618,25 @@ where /// For a detailed description of the consensus implemented by `run_session` see /// [docs for devs](https://cardinal-cryptography.github.io/AlephBFT/index.html) /// or the [original paper](https://arxiv.org/abs/1908.05156). +/// +/// Please note that in order to fulfill the constraint [`UnitFinalizationHandler`] it is enough to provide implementation of [`FinalizationHandler`]. We provide +/// implementation of [`UnitFinalizationHandler`] for anything that satisfies +/// the trait [`FinalizationHandler`] (by means of [`FinalizationHandlerAdapter`]). Implementing +/// [`UnitFinalizationHandler`] directly is considered less stable since it exposes intrisics which might be +/// subject to change. Implement [`FinalizationHandler`] instead, unless you absolutely know +/// what you are doing. pub async fn run_session< - H: Hasher, - D: Data, - DP: DataProvider, - FH: FinalizationHandler, + DP: DataProvider, + UFH: UnitFinalizationHandler, US: AsyncWrite + Send + Sync + 'static, UL: AsyncRead + Send + Sync + 'static, - N: Network> + 'static, + N: Network>, SH: SpawnHandle, MK: MultiKeychain, >( config: Config, - local_io: LocalIO, + local_io: LocalIO, network: N, keychain: MK, spawn_handle: SH, diff --git a/consensus/src/runway/mod.rs b/consensus/src/runway/mod.rs index 7dcf024..ba07bb1 100644 --- a/consensus/src/runway/mod.rs +++ b/consensus/src/runway/mod.rs @@ -9,11 +9,10 @@ use crate::{ SignedUnit, UncheckedSignedUnit, Unit, UnitCoord, UnitStore, UnitStoreStatus, UnitWithParents, Validator, WrappedUnit, }, - Config, Data, DataProvider, FinalizationHandler, Hasher, Index, Keychain, MultiKeychain, - NodeIndex, Receiver, Round, Sender, Signature, Signed, SpawnHandle, Terminator, - UncheckedSigned, + Config, Data, DataProvider, Hasher, Index, Keychain, MultiKeychain, NodeIndex, Receiver, Round, + Sender, Signature, Signed, SpawnHandle, Terminator, UncheckedSigned, }; -use aleph_bft_types::Recipient; +use aleph_bft_types::{Recipient, UnitFinalizationHandler}; use futures::{ channel::{mpsc, oneshot}, future::pending, @@ -100,29 +99,27 @@ type CollectionResponse = UncheckedSigned< ::Signature, >; -struct Runway +struct Runway where - H: Hasher, - D: Data, - FH: FinalizationHandler, + FH: UnitFinalizationHandler, MK: MultiKeychain, { missing_coords: HashSet, - missing_parents: HashSet, - store: UnitStore>, + missing_parents: HashSet<::Hash>, + store: UnitStore>, keychain: MK, - dag: Dag, - ordering: Ordering, - alerts_for_alerter: Sender>, - notifications_from_alerter: Receiver>, - unit_messages_from_network: Receiver>, - unit_messages_for_network: Sender>, - responses_for_collection: Sender>, - resolved_requests: Sender>, - parents_for_creator: Sender>, - backup_units_for_saver: Sender>, - backup_units_from_saver: Receiver>, - new_units_from_creation: Receiver>, + dag: Dag, + ordering: Ordering, + alerts_for_alerter: Sender>, + notifications_from_alerter: Receiver>, + unit_messages_from_network: Receiver>, + unit_messages_for_network: Sender>, + responses_for_collection: Sender>, + resolved_requests: Sender>, + parents_for_creator: Sender>, + backup_units_for_saver: Sender>, + backup_units_from_saver: Receiver>, + new_units_from_creation: Receiver>, exiting: bool, } @@ -206,28 +203,36 @@ impl<'a, H: Hasher> Display for RunwayStatus<'a, H> { } } -struct RunwayConfig, MK: MultiKeychain> { - finalization_handler: FH, - backup_units_for_saver: Sender>, - backup_units_from_saver: Receiver>, - alerts_for_alerter: Sender>, - notifications_from_alerter: Receiver>, - unit_messages_from_network: Receiver>, - unit_messages_for_network: Sender>, - responses_for_collection: Sender>, - parents_for_creator: Sender>, - resolved_requests: Sender>, - new_units_from_creation: Receiver>, +struct RunwayConfig { + finalization_handler: UFH, + backup_units_for_saver: Sender>, + backup_units_from_saver: Receiver>, + alerts_for_alerter: Sender>, + notifications_from_alerter: + Receiver>, + unit_messages_from_network: + Receiver>, + unit_messages_for_network: Sender>, + responses_for_collection: Sender>, + parents_for_creator: Sender>, + resolved_requests: Sender>, + new_units_from_creation: Receiver>, } -impl Runway +type BackupUnits = Vec< + UncheckedSignedUnit< + ::Hasher, + ::Data, + ::Signature, + >, +>; + +impl Runway where - H: Hasher, - D: Data, - FH: FinalizationHandler, + UFH: UnitFinalizationHandler, MK: MultiKeychain, { - fn new(config: RunwayConfig, keychain: MK, validator: Validator) -> Self { + fn new(config: RunwayConfig, keychain: MK, validator: Validator) -> Self { let n_members = keychain.node_count(); let RunwayConfig { finalization_handler, @@ -271,7 +276,7 @@ where self.keychain.index() } - fn handle_dag_result(&mut self, result: DagResult) { + fn handle_dag_result(&mut self, result: DagResult) { let DagResult { units, requests, @@ -291,12 +296,18 @@ where } } - fn on_unit_received(&mut self, unit: UncheckedSignedUnit) { + fn on_unit_received( + &mut self, + unit: UncheckedSignedUnit, + ) { let result = self.dag.add_unit(unit, &self.store); self.handle_dag_result(result); } - fn on_unit_message(&mut self, message: RunwayNotificationIn) { + fn on_unit_message( + &mut self, + message: RunwayNotificationIn, + ) { match message { RunwayNotificationIn::NewUnit(u) => { trace!(target: "AlephBFT-runway", "{:?} New unit received {:?}.", self.index(), &u); @@ -360,7 +371,7 @@ where } } - fn on_request_parents(&mut self, node_id: NodeIndex, u_hash: H::Hash) { + fn on_request_parents(&mut self, node_id: NodeIndex, u_hash: ::Hash) { debug!(target: "AlephBFT-runway", "{:?} Received parents request for hash {:?} from {:?}.", self.index(), u_hash, node_id); match self.store.unit(&u_hash) { @@ -412,8 +423,8 @@ where fn on_parents_response( &mut self, - u_hash: H::Hash, - parents: Vec>, + u_hash: ::Hash, + parents: Vec>, ) { if self.store.unit(&u_hash).is_some() { trace!(target: "AlephBFT-runway", "{:?} We got parents response but already imported the unit.", self.index()); @@ -423,20 +434,23 @@ where self.handle_dag_result(result); } - fn on_forking_notification(&mut self, notification: ForkingNotification) { + fn on_forking_notification( + &mut self, + notification: ForkingNotification, + ) { let result = self .dag .process_forking_notification(notification, &self.store); self.handle_dag_result(result); } - fn resolve_missing_parents(&mut self, u_hash: &H::Hash) { + fn resolve_missing_parents(&mut self, u_hash: &::Hash) { if self.missing_parents.remove(u_hash) { self.send_resolved_request_notification(Request::Parents(*u_hash)); } } - fn on_reconstruction_request(&mut self, request: ReconstructionRequest) { + fn on_reconstruction_request(&mut self, request: ReconstructionRequest) { use ReconstructionRequest::*; match request { Coord(coord) => { @@ -448,7 +462,7 @@ where } } - fn on_unit_reconstructed(&mut self, unit: DagUnit) { + fn on_unit_reconstructed(&mut self, unit: DagUnit) { let unit_hash = unit.hash(); trace!(target: "AlephBFT-runway", "Unit {:?} {} reconstructed.", unit_hash, unit.coord()); if self.backup_units_for_saver.unbounded_send(unit).is_err() { @@ -456,7 +470,7 @@ where } } - fn on_unit_backup_saved(&mut self, unit: DagUnit) { + fn on_unit_backup_saved(&mut self, unit: DagUnit) { let unit_hash = unit.hash(); self.store.insert(unit.clone()); self.dag.finished_processing(&unit_hash); @@ -494,7 +508,7 @@ where } } - fn on_wrong_control_hash(&mut self, u_hash: H::Hash) { + fn on_wrong_control_hash(&mut self, u_hash: ::Hash) { trace!(target: "AlephBFT-runway", "{:?} Dealing with wrong control hash notification {:?}.", self.index(), u_hash); if self.missing_parents.insert(u_hash) { self.send_message_for_network(RunwayNotificationOut::Request(Request::Parents(u_hash))); @@ -503,7 +517,7 @@ where fn send_message_for_network( &mut self, - notification: RunwayNotificationOut, + notification: RunwayNotificationOut, ) { if self .unit_messages_for_network @@ -515,14 +529,14 @@ where } } - fn send_resolved_request_notification(&mut self, notification: Request) { + fn send_resolved_request_notification(&mut self, notification: Request) { if self.resolved_requests.unbounded_send(notification).is_err() { warn!(target: "AlephBFT-runway", "{:?} resolved_requests channel should be open", self.index()); self.exiting = true; } } - fn status(&self) -> RunwayStatus<'_, H> { + fn status(&self) -> RunwayStatus<'_, UFH::Hasher> { RunwayStatus { missing_coords: &self.missing_coords, missing_parents: &self.missing_parents, @@ -537,7 +551,7 @@ where async fn run( mut self, - data_from_backup: oneshot::Receiver>>, + data_from_backup: oneshot::Receiver>, mut terminator: Terminator, ) { let index = self.index(); @@ -664,34 +678,30 @@ fn trivial_start( } pub struct RunwayIO< - H: Hasher, - D: Data, MK: MultiKeychain, W: AsyncWrite + Send + Sync + 'static, R: AsyncRead + Send + Sync + 'static, - DP: DataProvider, - FH: FinalizationHandler, + DP: DataProvider, + UFH: UnitFinalizationHandler, > { pub data_provider: DP, - pub finalization_handler: FH, + pub finalization_handler: UFH, pub backup_write: W, pub backup_read: R, - _phantom: PhantomData<(H, D, MK::Signature)>, + _phantom: PhantomData, } impl< - H: Hasher, - D: Data, MK: MultiKeychain, W: AsyncWrite + Send + Sync + 'static, R: AsyncRead + Send + Sync + 'static, - DP: DataProvider, - FH: FinalizationHandler, - > RunwayIO + DP: DataProvider, + UFH: UnitFinalizationHandler, + > RunwayIO { pub fn new( data_provider: DP, - finalization_handler: FH, + finalization_handler: UFH, backup_write: W, backup_read: R, ) -> Self { @@ -705,20 +715,18 @@ impl< } } -pub(crate) async fn run( +pub(crate) async fn run( config: Config, - runway_io: RunwayIO, + runway_io: RunwayIO, keychain: MK, spawn_handle: SH, - network_io: NetworkIO, + network_io: NetworkIO, mut terminator: Terminator, ) where - H: Hasher, - D: Data, US: AsyncWrite + Send + Sync + 'static, UL: AsyncRead + Send + Sync + 'static, - DP: DataProvider, - FH: FinalizationHandler, + DP: DataProvider, + UFH: UnitFinalizationHandler, MK: MultiKeychain, SH: SpawnHandle, { diff --git a/consensus/src/testing/dag.rs b/consensus/src/testing/dag.rs index dc9921b..6e43d07 100644 --- a/consensus/src/testing/dag.rs +++ b/consensus/src/testing/dag.rs @@ -9,9 +9,10 @@ use crate::{ ControlHash, FullUnit, PreUnit, SignedUnit as GenericSignedUnit, Unit, UnitStore, UnitWithParents as _, Validator, }, - FinalizationHandler, NodeCount, NodeIndex, NodeMap, NodeSubset, Round, Signed, + NodeCount, NodeIndex, NodeMap, NodeSubset, Round, Signed, }; use aleph_bft_mock::{Data, Hash64, Hasher64, Keychain}; +use aleph_bft_types::{OrderedUnit, UnitFinalizationHandler}; use log::debug; use parking_lot::Mutex; use rand::{distributions::Open01, prelude::*}; @@ -217,9 +218,13 @@ impl RecordingHandler { } } -impl FinalizationHandler for RecordingHandler { - fn data_finalized(&mut self, data: Data) { - self.finalized.lock().push(data); +impl UnitFinalizationHandler for RecordingHandler { + type Data = Data; + type Hasher = Hasher64; + + fn batch_finalized(&mut self, batch: Vec>) { + let mut batch_of_data = batch.into_iter().filter_map(|unit| unit.data).collect(); + self.finalized.lock().append(&mut batch_of_data) } } diff --git a/consensus/src/units/mod.rs b/consensus/src/units/mod.rs index bfc0fbb..aad76d9 100644 --- a/consensus/src/units/mod.rs +++ b/consensus/src/units/mod.rs @@ -131,6 +131,12 @@ pub struct FullUnit { hash: RwLock>, } +impl From> for Option { + fn from(value: FullUnit) -> Self { + value.data + } +} + impl Clone for FullUnit { fn clone(&self) -> Self { let hash = self.hash.try_read().and_then(|guard| *guard); diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index d45a820..2c46b8a 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aleph-bft-crypto" -version = "0.9.0" +version = "0.9.1" edition = "2021" authors = ["Cardinal Cryptography"] documentation = "https://docs.rs/?" diff --git a/crypto/src/signature.rs b/crypto/src/signature.rs index 1066e18..619d50a 100644 --- a/crypto/src/signature.rs +++ b/crypto/src/signature.rs @@ -175,9 +175,6 @@ impl From, S>> for Uncheck } /// A correctly signed object of type `T`. -/// -/// The correctness is guaranteed by storing a (phantom) reference to the `Keychain` that verified -/// the signature. #[derive(Eq, PartialEq, Hash, Debug, Decode, Encode)] pub struct Signed { unchecked: UncheckedSigned, diff --git a/docs/src/aleph_bft_api.md b/docs/src/aleph_bft_api.md index 7cb600c..3e42ca7 100644 --- a/docs/src/aleph_bft_api.md +++ b/docs/src/aleph_bft_api.md @@ -7,8 +7,10 @@ The DataProvider trait is an abstraction for a component that provides data items. `DataProvider` is parametrized with a `Data` generic type representing the type of items we would like to order. Below we give examples of what these might be. ```rust -pub trait DataProvider { - async fn get_data(&mut self) -> Option; +pub trait DataProvider { + type Output: Data; + + async fn get_data(&mut self) -> Option; } ``` @@ -18,7 +20,7 @@ The FinalizationHandler trait is an abstraction for a component that should hand ```rust pub trait FinalizationHandler { - fn data_finalized(&mut self, data: Data, creator: NodeIndex); + fn data_finalized(&mut self, data: Data); } ``` diff --git a/examples/blockchain/src/data.rs b/examples/blockchain/src/data.rs index afbba9b..1f2d810 100644 --- a/examples/blockchain/src/data.rs +++ b/examples/blockchain/src/data.rs @@ -123,7 +123,9 @@ pub struct DataProvider { } #[async_trait] -impl aleph_bft::DataProvider for DataProvider { +impl aleph_bft::DataProvider for DataProvider { + type Output = Data; + async fn get_data(&mut self) -> Option { Some(*self.current_block.lock()) } diff --git a/examples/ordering/src/dataio.rs b/examples/ordering/src/dataio.rs index efd5e3f..c136ee5 100644 --- a/examples/ordering/src/dataio.rs +++ b/examples/ordering/src/dataio.rs @@ -31,7 +31,9 @@ impl DataProvider { } #[async_trait] -impl DataProviderT for DataProvider { +impl DataProviderT for DataProvider { + type Output = Data; + async fn get_data(&mut self) -> Option { if self.n_data == 0 { if self.stalled { @@ -56,8 +58,8 @@ pub struct FinalizationHandler { } impl FinalizationHandlerT for FinalizationHandler { - fn data_finalized(&mut self, d: Data) { - if let Err(e) = self.tx.unbounded_send(d) { + fn data_finalized(&mut self, data: Data) { + if let Err(e) = self.tx.unbounded_send(data) { error!(target: "finalization-handler", "Error when sending data from FinalizationHandler {:?}.", e); } } diff --git a/mock/Cargo.toml b/mock/Cargo.toml index 5a8ccff..d24f081 100644 --- a/mock/Cargo.toml +++ b/mock/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aleph-bft-mock" -version = "0.14.0" +version = "0.15.0" edition = "2021" authors = ["Cardinal Cryptography"] documentation = "https://docs.rs/?" @@ -11,7 +11,7 @@ readme = "./README.md" description = "Mock implementations of traits required by the aleph-bft package. Do NOT use outside of testing!" [dependencies] -aleph-bft-types = { path = "../types", version = "0.13" } +aleph-bft-types = { path = "../types", version = "0.14" } async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.0", default-features = false, features = ["derive"] } futures = "0.3" diff --git a/mock/src/dataio.rs b/mock/src/dataio.rs index c7ef064..b06f035 100644 --- a/mock/src/dataio.rs +++ b/mock/src/dataio.rs @@ -39,7 +39,9 @@ impl DataProvider { } #[async_trait] -impl DataProviderT for DataProvider { +impl DataProviderT for DataProvider { + type Output = Data; + async fn get_data(&mut self) -> Option { self.counter += 1; if let Some(n_data) = self.n_data { @@ -61,7 +63,9 @@ impl StalledDataProvider { } #[async_trait] -impl DataProviderT for StalledDataProvider { +impl DataProviderT for StalledDataProvider { + type Output = Data; + async fn get_data(&mut self) -> Option { pending().await } @@ -73,8 +77,8 @@ pub struct FinalizationHandler { } impl FinalizationHandlerT for FinalizationHandler { - fn data_finalized(&mut self, d: Data) { - if let Err(e) = self.tx.unbounded_send(d) { + fn data_finalized(&mut self, data: Data) { + if let Err(e) = self.tx.unbounded_send(data) { error!(target: "finalization-handler", "Error when sending data from FinalizationHandler {:?}.", e); } } diff --git a/mock/src/network.rs b/mock/src/network.rs index 55448b1..22bd5f3 100644 --- a/mock/src/network.rs +++ b/mock/src/network.rs @@ -51,7 +51,7 @@ impl Network { } #[async_trait::async_trait] -impl NetworkT for Network { +impl NetworkT for Network { fn send(&self, data: D, recipient: Recipient) { use Recipient::*; match recipient { diff --git a/rmc/Cargo.toml b/rmc/Cargo.toml index 6cb607a..d82be4d 100644 --- a/rmc/Cargo.toml +++ b/rmc/Cargo.toml @@ -14,7 +14,7 @@ description = "Reliable MultiCast - a primitive for Reliable Broadcast protocol. [dependencies] aleph-bft-crypto = { path = "../crypto", version = "0.9" } -aleph-bft-types = { path = "../types", version = "0.13" } +aleph-bft-types = { path = "../types", version = "0.14" } async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.0", default-features = false, features = ["derive"] } futures = "0.3" diff --git a/types/Cargo.toml b/types/Cargo.toml index 2763638..42e897b 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aleph-bft-types" -version = "0.13.0" +version = "0.14.0" edition = "2021" authors = ["Cardinal Cryptography"] documentation = "https://docs.rs/?" diff --git a/types/src/dataio.rs b/types/src/dataio.rs index edc7c36..85a900d 100644 --- a/types/src/dataio.rs +++ b/types/src/dataio.rs @@ -1,36 +1,56 @@ use async_trait::async_trait; -use crate::NodeIndex; -use crate::Round; +use crate::{Data, Hasher, NodeIndex, Round}; /// The source of data items that consensus should order. /// -/// AlephBFT internally calls [`DataProvider::get_data`] whenever a new unit is created and data needs to be placed inside. +/// AlephBFT internally calls [`DataProvider::get_data`] whenever a new unit is created and data +/// needs to be placed inside. /// -/// We refer to the documentation https://cardinal-cryptography.github.io/AlephBFT/aleph_bft_api.html for a discussion -/// and examples of how this trait can be implemented. +/// We refer to the documentation +/// https://cardinal-cryptography.github.io/AlephBFT/aleph_bft_api.html for a discussion and +/// examples of how this trait can be implemented. #[async_trait] -pub trait DataProvider: Sync + Send + 'static { - /// Outputs a new data item to be ordered - async fn get_data(&mut self) -> Option; +pub trait DataProvider: Sync + Send + 'static { + /// Type of data returned by this provider. + type Output: Data; + /// Outputs a new data item to be ordered. + async fn get_data(&mut self) -> Option; } /// The source of finalization of the units that consensus produces. /// -/// The [`FinalizationHandler::data_finalized`] method is called whenever a piece of data input to the algorithm -/// using [`DataProvider::get_data`] has been finalized, in order of finalization. -pub trait FinalizationHandler: Sync + Send + 'static { +/// The [`FinalizationHandler::data_finalized`] method is called whenever a piece of data input +/// to the algorithm using [`DataProvider::get_data`] has been finalized, in order of finalization. +pub trait FinalizationHandler: Sync + Send + 'static { /// Data, provided by [DataProvider::get_data], has been finalized. /// The calls to this function follow the order of finalization. - fn data_finalized(&mut self, data: Data); - /// A unit has been finalized. You can overwrite the default implementation for advanced finalization handling - /// in which case the method [`FinalizationHandler::data_finalized`] will not be called anymore if a unit is finalized. - /// Please note that this interface is less stable as it exposes intrinsics which migh be subject to change. - /// Do not implement this method and only implement [`FinalizationHandler::data_finalized`] unless you - /// absolutely know what you are doing. - fn unit_finalized(&mut self, _creator: NodeIndex, _round: Round, data: Option) { - if let Some(d) = data { - self.data_finalized(d); - } - } + fn data_finalized(&mut self, data: D); +} + +/// Represents state of the main internal data structure of AlephBFT (i.e. direct acyclic graph) used for +/// achieving consensus. +/// +/// Instances of this type are returned indirectly by [`member::run_session`] method using the +/// [`UnitFinalizationHandler`] trait. This way it allows to reconstruct the DAG's structure used by AlephBFT, +/// which can be then used for example for the purpose of node's performance evaluation. +pub struct OrderedUnit { + pub data: Option, + pub parents: Vec, + pub hash: H::Hash, + pub creator: NodeIndex, + pub round: Round, +} + +/// The source of finalization of the units that consensus produces. +/// +/// The [`UnitFinalizationHandler::batch_finalized`] method is called whenever a batch of units +/// has been finalized, in order of finalization. +pub trait UnitFinalizationHandler: Sync + Send + 'static { + type Data: Data; + type Hasher: Hasher; + + /// A batch of units, that contains data provided by [DataProvider::get_data], has been finalized. + /// The calls to this function follow the order of finalization. + fn batch_finalized(&mut self, batch: Vec>); } diff --git a/types/src/lib.rs b/types/src/lib.rs index 4c24740..43dda84 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -9,7 +9,7 @@ pub use aleph_bft_crypto::{ NodeIndex, NodeMap, NodeSubset, PartialMultisignature, PartiallyMultisigned, Signable, Signature, SignatureError, SignatureSet, Signed, UncheckedSigned, }; -pub use dataio::{DataProvider, FinalizationHandler}; +pub use dataio::{DataProvider, FinalizationHandler, OrderedUnit, UnitFinalizationHandler}; pub use network::{Network, Recipient}; pub use tasks::{SpawnHandle, TaskHandle}; diff --git a/types/src/network.rs b/types/src/network.rs index 9b76e8c..0cc3f3b 100644 --- a/types/src/network.rs +++ b/types/src/network.rs @@ -31,7 +31,7 @@ pub enum Recipient { /// We refer to the documentation https://cardinal-cryptography.github.io/AlephBFT/aleph_bft_api.html /// Section 3.1.2 for a discussion of the required guarantees of this trait's implementation. #[async_trait::async_trait] -pub trait Network: Send { +pub trait Network: Send + 'static { /// Send a message to a single node or everyone, depending on the value of the recipient /// argument. ///