Simplify the sender queue.

Remove the distinction between linearized and regular epochs.
Avoid iterating through the whole outgoing queue on epoch change.
This commit is contained in:
Andreas Fackler 2018-11-07 17:21:24 +01:00 committed by Andreas Fackler
parent 5ea0b92484
commit d0b96f2dc8
12 changed files with 169 additions and 321 deletions

View File

@ -21,7 +21,7 @@ use honey_badger::{self, HoneyBadger, Message as HbMessage};
use sync_key_gen::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen}; use sync_key_gen::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen};
use threshold_decrypt::EncryptionSchedule; use threshold_decrypt::EncryptionSchedule;
use util::{self, SubRng}; use util::{self, SubRng};
use {Contribution, DistAlgorithm, NetworkInfo, NodeIdT, Target}; use {Contribution, DistAlgorithm, Epoched, NetworkInfo, NodeIdT, Target};
/// A Honey Badger instance that can handle adding and removing nodes. /// A Honey Badger instance that can handle adding and removing nodes.
#[derive(Derivative)] #[derive(Derivative)]
@ -493,3 +493,15 @@ where
write!(f, "{:?} DHB(era: {})", self.our_id(), self.era) write!(f, "{:?} DHB(era: {})", self.our_id(), self.era)
} }
} }
impl<C, N> Epoched for DynamicHoneyBadger<C, N>
where
C: Contribution + Serialize + DeserializeOwned,
N: NodeIdT + Serialize + DeserializeOwned + Rand,
{
type Epoch = (u64, u64);
fn epoch(&self) -> (u64, u64) {
(self.era, self.honey_badger.epoch())
}
}

View File

@ -72,7 +72,6 @@ mod dynamic_honey_badger;
mod error; mod error;
mod votes; mod votes;
use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crypto::{PublicKey, PublicKeySet, Signature}; use crypto::{PublicKey, PublicKeySet, Signature};
@ -83,7 +82,7 @@ use self::votes::{SignedVote, VoteCounter};
use super::threshold_decrypt::EncryptionSchedule; use super::threshold_decrypt::EncryptionSchedule;
use honey_badger::Message as HbMessage; use honey_badger::Message as HbMessage;
use sync_key_gen::{Ack, Part, SyncKeyGen}; use sync_key_gen::{Ack, Part, SyncKeyGen};
use {Epoched, NodeIdT}; use NodeIdT;
pub use self::batch::Batch; pub use self::batch::Batch;
pub use self::builder::DynamicHoneyBadgerBuilder; pub use self::builder::DynamicHoneyBadgerBuilder;
@ -134,62 +133,6 @@ impl<N: Rand> Message<N> {
} }
} }
/// Dynamic Honey Badger epoch. It consists of an era and an epoch of Honey Badger that started in
/// that era. For messages originating from `DynamicHoneyBadger` as opposed to `HoneyBadger`, that
/// HoneyBadger epoch is `None`.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, Serialize, Deserialize)]
pub struct Epoch(pub(super) u64, pub(super) Option<u64>);
/// The injection of linearizable epochs into `DynamicHoneyBadger` epochs.
impl From<(u64, u64)> for Epoch {
fn from((era, hb_epoch): (u64, u64)) -> Epoch {
Epoch(era, Some(hb_epoch))
}
}
impl PartialOrd for Epoch {
/// Partial ordering on epochs. For any `era` and `hb_epoch`, two epochs `Epoch(era, None)` and `Epoch(era,
/// Some(hb_epoch))` are incomparable.
fn partial_cmp(&self, other: &Epoch) -> Option<Ordering> {
let (&Epoch(a, b), &Epoch(c, d)) = (self, other);
if a < c {
Some(Ordering::Less)
} else if a > c {
Some(Ordering::Greater)
} else if b.is_none() && d.is_none() {
Some(Ordering::Equal)
} else if let (Some(b), Some(d)) = (b, d) {
Some(Ord::cmp(&b, &d))
} else {
None
}
}
}
impl Default for Epoch {
fn default() -> Epoch {
Epoch(0, Some(0))
}
}
impl<N: Rand> Epoched for Message<N> {
type Epoch = Epoch;
type LinEpoch = (u64, u64);
fn epoch(&self) -> Epoch {
match *self {
Message::HoneyBadger(era, ref msg) => Epoch(era, Some(msg.epoch())),
Message::KeyGen(era, _, _) => Epoch(era, None),
Message::SignedVote(ref signed_vote) => Epoch(signed_vote.era(), None),
}
}
fn linearizable_epoch(&self) -> Option<(u64, u64)> {
let Epoch(era, hb_epoch) = self.epoch();
hb_epoch.map(|hb_epoch| (era, hb_epoch))
}
}
/// The information a new node requires to join the network as an observer. It contains the state /// The information a new node requires to join the network as an observer. It contains the state
/// of voting and key generation after a specific epoch, so that the new node will be in sync if it /// of voting and key generation after a specific epoch, so that the new node will be in sync if it
/// joins in the next one. /// joins in the next one.

View File

@ -8,7 +8,7 @@ use serde::{de::DeserializeOwned, Serialize};
use super::epoch_state::EpochState; use super::epoch_state::EpochState;
use super::{Batch, Error, ErrorKind, HoneyBadgerBuilder, Message, Result}; use super::{Batch, Error, ErrorKind, HoneyBadgerBuilder, Message, Result};
use {util, Contribution, DistAlgorithm, Epoched, Fault, FaultKind, NetworkInfo, NodeIdT}; use {util, Contribution, DistAlgorithm, Fault, FaultKind, NetworkInfo, NodeIdT};
pub use super::epoch_state::SubsetHandlingStrategy; pub use super::epoch_state::SubsetHandlingStrategy;
use threshold_decrypt::EncryptionSchedule; use threshold_decrypt::EncryptionSchedule;
@ -40,19 +40,6 @@ pub struct HoneyBadger<C, N: Rand> {
pub(super) encryption_schedule: EncryptionSchedule, pub(super) encryption_schedule: EncryptionSchedule,
} }
impl<C, N: Rand> Epoched for HoneyBadger<C, N> {
type Epoch = u64;
type LinEpoch = u64;
fn epoch(&self) -> Self::Epoch {
self.epoch
}
fn linearizable_epoch(&self) -> Option<Self::LinEpoch> {
Some(self.epoch)
}
}
pub type Step<C, N> = ::DaStep<HoneyBadger<C, N>>; pub type Step<C, N> = ::DaStep<HoneyBadger<C, N>>;
impl<C, N> DistAlgorithm for HoneyBadger<C, N> impl<C, N> DistAlgorithm for HoneyBadger<C, N>
@ -127,16 +114,16 @@ where
return Err(ErrorKind::UnknownSender.into()); return Err(ErrorKind::UnknownSender.into());
} }
let Message { epoch, content } = message; let Message { epoch, content } = message;
if self.epoch <= epoch && epoch <= self.epoch + self.max_future_epochs { if epoch > self.epoch + self.max_future_epochs {
Ok(Fault::new(sender_id.clone(), FaultKind::UnexpectedHbMessageEpoch).into())
} else if epoch < self.epoch {
// The message is late; discard it.
Ok(Step::default())
} else {
let step = self let step = self
.epoch_state_mut(epoch)? .epoch_state_mut(epoch)?
.handle_message_content(sender_id, content)?; .handle_message_content(sender_id, content)?;
Ok(step.join(self.try_output_batches()?)) Ok(step.join(self.try_output_batches()?))
} else if epoch > self.epoch + self.max_future_epochs {
Ok(Fault::new(sender_id.clone(), FaultKind::UnexpectedHbMessageEpoch).into())
} else {
// The message is late; discard it.
Ok(Step::default())
} }
} }
@ -145,10 +132,17 @@ where
!self.netinfo.is_validator() || self.has_input !self.netinfo.is_validator() || self.has_input
} }
/// Returns the current encryption schedule that determines in which epochs contributions are
/// encrypted.
pub fn get_encryption_schedule(&self) -> EncryptionSchedule { pub fn get_encryption_schedule(&self) -> EncryptionSchedule {
self.encryption_schedule self.encryption_schedule
} }
/// Returns the current epoch.
pub fn epoch(&self) -> u64 {
self.epoch
}
/// Returns the number of validators from which we have already received a proposal for the /// Returns the number of validators from which we have already received a proposal for the
/// current epoch. /// current epoch.
pub(crate) fn received_proposals(&self) -> usize { pub(crate) fn received_proposals(&self) -> usize {

View File

@ -5,8 +5,6 @@ use serde_derive::{Deserialize, Serialize};
use subset; use subset;
use threshold_decrypt; use threshold_decrypt;
use Epoched;
/// The content of a `HoneyBadger` message. It should be further annotated with an epoch. /// The content of a `HoneyBadger` message. It should be further annotated with an epoch.
#[derive(Clone, Debug, Deserialize, Rand, Serialize)] #[derive(Clone, Debug, Deserialize, Rand, Serialize)]
pub enum MessageContent<N: Rand> { pub enum MessageContent<N: Rand> {
@ -35,15 +33,9 @@ pub struct Message<N: Rand> {
pub(super) content: MessageContent<N>, pub(super) content: MessageContent<N>,
} }
impl<N: Rand> Epoched for Message<N> { impl<N: Rand> Message<N> {
type Epoch = u64; /// Returns this message's Honey Badger epoch.
type LinEpoch = u64; pub fn epoch(&self) -> u64 {
fn epoch(&self) -> u64 {
self.epoch self.epoch
} }
fn linearizable_epoch(&self) -> Option<u64> {
Some(self.epoch)
}
} }

View File

@ -36,7 +36,7 @@ use dynamic_honey_badger::{self, Batch as DhbBatch, DynamicHoneyBadger, Message}
use transaction_queue::TransactionQueue; use transaction_queue::TransactionQueue;
use {util, Contribution, DistAlgorithm, NodeIdT}; use {util, Contribution, DistAlgorithm, NodeIdT};
pub use dynamic_honey_badger::{Change, ChangeState, Epoch, Input, NodeChange}; pub use dynamic_honey_badger::{Change, ChangeState, Input, NodeChange};
/// Queueing honey badger error variants. /// Queueing honey badger error variants.
#[derive(Debug, Fail)] #[derive(Debug, Fail)]

View File

@ -7,13 +7,12 @@ use rand::Rand;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use super::{ use super::{
SenderQueue, SenderQueueableDistAlgorithm, SenderQueueableEpoch, SenderQueueableMessage, SenderQueue, SenderQueueableDistAlgorithm, SenderQueueableMessage, SenderQueueableOutput,
SenderQueueableOutput,
}; };
use {Contribution, DaStep, Epoched, NodeIdT}; use {Contribution, DaStep, Epoched, NodeIdT};
use dynamic_honey_badger::{ use dynamic_honey_badger::{
Batch, Change, ChangeState, DynamicHoneyBadger, Epoch, Error as DhbError, Message, NodeChange, Batch, Change, ChangeState, DynamicHoneyBadger, Error as DhbError, Message, NodeChange,
}; };
impl<C, N> SenderQueueableOutput<N, Message<N>> for Batch<C, N> impl<C, N> SenderQueueableOutput<N, Message<N>> for Batch<C, N>
@ -31,14 +30,16 @@ where
None None
} }
} }
}
fn next_epoch(&self) -> (u64, u64) { impl<N: Rand> Epoched for Message<N> {
let epoch = self.epoch(); type Epoch = (u64, u64);
let era = self.era();
if *self.change() == ChangeState::None { fn epoch(&self) -> (u64, u64) {
(era, epoch - era + 1) match *self {
} else { Message::HoneyBadger(era, ref msg) => (era, msg.epoch()),
(epoch + 1, 0) Message::KeyGen(era, _, _) => (era, 0),
Message::SignedVote(ref signed_vote) => (signed_vote.era(), 0),
} }
} }
} }
@ -47,37 +48,23 @@ impl<N> SenderQueueableMessage for Message<N>
where where
N: Rand, N: Rand,
{ {
fn is_accepted(&self, (them_era, them): (u64, u64), max_future_epochs: u64) -> bool { fn is_premature(&self, (them_era, them): (u64, u64), max_future_epochs: u64) -> bool {
let Epoch(era, us) = self.epoch(); match *self {
if era != them_era { Message::HoneyBadger(era, ref msg) => {
return false; era > them_era || (era == them_era && msg.epoch() > them + max_future_epochs)
} }
if let Some(us) = us { Message::KeyGen(era, _, _) => era > them_era,
them <= us && us <= them + max_future_epochs Message::SignedVote(ref signed_vote) => signed_vote.era() > them_era,
} else {
true
} }
} }
fn is_obsolete(&self, (them_era, them): (u64, u64)) -> bool { fn is_obsolete(&self, (them_era, them): (u64, u64)) -> bool {
let Epoch(era, us) = self.epoch(); match *self {
if era < them_era { Message::HoneyBadger(era, ref msg) => {
return true; era < them_era || (era == them_era && msg.epoch() < them)
} }
if let Some(us) = us { Message::KeyGen(era, _, _) => era < them_era,
era == them_era && us < them Message::SignedVote(ref signed_vote) => signed_vote.era() < them_era,
} else {
false
}
}
}
impl SenderQueueableEpoch for Epoch {
fn spanning_epochs(&self) -> Vec<Self> {
if let Epoch(era, Some(_)) = *self {
vec![Epoch(era, None)]
} else {
vec![]
} }
} }
} }

View File

@ -1,10 +1,7 @@
use rand::Rand; use rand::Rand;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use super::{ use super::{SenderQueueableDistAlgorithm, SenderQueueableMessage, SenderQueueableOutput};
SenderQueueableDistAlgorithm, SenderQueueableEpoch, SenderQueueableMessage,
SenderQueueableOutput,
};
use honey_badger::{Batch, HoneyBadger, Message}; use honey_badger::{Batch, HoneyBadger, Message};
use {Contribution, Epoched, NodeIdT}; use {Contribution, Epoched, NodeIdT};
@ -16,9 +13,13 @@ where
fn added_node(&self) -> Option<N> { fn added_node(&self) -> Option<N> {
None None
} }
}
fn next_epoch(&self) -> u64 { impl<N: Rand> Epoched for Message<N> {
self.epoch + 1 type Epoch = u64;
fn epoch(&self) -> u64 {
self.epoch()
} }
} }
@ -26,9 +27,8 @@ impl<N> SenderQueueableMessage for Message<N>
where where
N: Rand, N: Rand,
{ {
fn is_accepted(&self, them: u64, max_future_epochs: u64) -> bool { fn is_premature(&self, them: u64, max_future_epochs: u64) -> bool {
let our_epoch = self.epoch(); self.epoch() > them + max_future_epochs
them <= our_epoch && our_epoch <= them + max_future_epochs
} }
fn is_obsolete(&self, them: u64) -> bool { fn is_obsolete(&self, them: u64) -> bool {
@ -36,9 +36,15 @@ where
} }
} }
impl SenderQueueableEpoch for u64 { impl<C, N> Epoched for HoneyBadger<C, N>
fn spanning_epochs(&self) -> Vec<Self> { where
vec![] C: Contribution + Serialize + DeserializeOwned,
N: NodeIdT + Rand,
{
type Epoch = u64;
fn epoch(&self) -> u64 {
self.epoch()
} }
} }

View File

@ -5,7 +5,7 @@ use Epoched;
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Message<M: Epoched> { pub enum Message<M: Epoched> {
EpochStarted(<M as Epoched>::LinEpoch), EpochStarted(<M as Epoched>::Epoch),
Algo(M), Algo(M),
} }
@ -13,7 +13,6 @@ impl<M> Rand for Message<M>
where where
M: Epoched + Rand, M: Epoched + Rand,
<M as Epoched>::Epoch: Rand, <M as Epoched>::Epoch: Rand,
<M as Epoched>::LinEpoch: Rand,
{ {
fn rand<R: Rng>(rng: &mut R) -> Self { fn rand<R: Rng>(rng: &mut R) -> Self {
let message_type = *rng.choose(&["epoch", "algo"]).unwrap(); let message_type = *rng.choose(&["epoch", "algo"]).unwrap();
@ -26,29 +25,6 @@ where
} }
} }
impl<M> Epoched for Message<M>
where
M: Epoched,
<M as Epoched>::Epoch: From<<M as Epoched>::LinEpoch>,
{
type Epoch = <M as Epoched>::Epoch;
type LinEpoch = <M as Epoched>::LinEpoch;
fn epoch(&self) -> Self::Epoch {
match self {
Message::EpochStarted(epoch) => <M as Epoched>::Epoch::from(*epoch),
Message::Algo(message) => message.epoch(),
}
}
fn linearizable_epoch(&self) -> Option<Self::LinEpoch> {
match self {
Message::EpochStarted(epoch) => Some(*epoch),
Message::Algo(message) => message.linearizable_epoch(),
}
}
}
impl<M: Epoched> From<M> for Message<M> { impl<M: Epoched> From<M> for Message<M> {
fn from(message: M) -> Self { fn from(message: M) -> Self {
Message::Algo(message) Message::Algo(message)

View File

@ -14,19 +14,21 @@ pub mod queueing_honey_badger;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Debug; use std::fmt::Debug;
use rand::Rand;
use serde::{de::DeserializeOwned, Serialize};
use {DaStep, DistAlgorithm, Epoched, NodeIdT, Target}; use {DaStep, DistAlgorithm, Epoched, NodeIdT, Target};
pub use self::message::Message; pub use self::message::Message;
pub trait SenderQueueableMessage: Epoched { pub trait SenderQueueableMessage: Epoched {
/// Whether the message is accepted in epoch `them`. /// Whether the message needs to be deferred.
fn is_accepted(&self, them: <Self as Epoched>::LinEpoch, max_future_epochs: u64) -> bool; fn is_premature(&self, them: <Self as Epoched>::Epoch, max_future_epochs: u64) -> bool;
/// Whether the epoch of the message is behind `them`. /// Whether the epoch of the message is behind `them`.
fn is_obsolete(&self, them: <Self as Epoched>::LinEpoch) -> bool; fn is_obsolete(&self, them: <Self as Epoched>::Epoch) -> bool;
/// Whether the message is neither obsolete nor premature.
fn is_accepted(&self, them: <Self as Epoched>::Epoch, max_future_epochs: u64) -> bool {
!self.is_premature(them, max_future_epochs) && !self.is_obsolete(them)
}
} }
pub trait SenderQueueableOutput<N, M> pub trait SenderQueueableOutput<N, M>
@ -37,29 +39,9 @@ where
/// Returns an optional new node added with the batch. This node should be added to the set of /// Returns an optional new node added with the batch. This node should be added to the set of
/// all nodes. /// all nodes.
fn added_node(&self) -> Option<N>; fn added_node(&self) -> Option<N>;
/// Computes the next epoch after the `DynamicHoneyBadger` epoch of the batch.
fn next_epoch(&self) -> <M as Epoched>::LinEpoch;
} }
pub trait SenderQueueableEpoch pub trait SenderQueueableDistAlgorithm: Epoched
where
Self: Sized,
{
/// A _spanning epoch_ of an epoch `e` is an epoch `e0` such that
///
/// - `e` and `e0` are incomparable by the partial ordering on epochs and
///
/// - the duration of `e0` is at least that of `e`.
///
/// Returned is a list of spanning epochs for the given epoch.
///
/// For example, any `DynamicHoneyBadger` epoch `Epoch((x, Some(y)))` has a unique spanning
/// epoch `Epoch((x, None))`. In turn, no epoch `Epoch((x, None))` has a spanning epoch.
fn spanning_epochs(&self) -> Vec<Self>;
}
pub trait SenderQueueableDistAlgorithm
where where
Self: DistAlgorithm, Self: DistAlgorithm,
{ {
@ -69,11 +51,8 @@ where
} }
pub type OutgoingQueue<D> = BTreeMap< pub type OutgoingQueue<D> = BTreeMap<
( <D as DistAlgorithm>::NodeId,
<D as DistAlgorithm>::NodeId, BTreeMap<<<D as DistAlgorithm>::Message as Epoched>::Epoch, Vec<<D as DistAlgorithm>::Message>>,
<<D as DistAlgorithm>::Message as Epoched>::Epoch,
),
Vec<<D as DistAlgorithm>::Message>,
>; >;
/// An instance of `DistAlgorithm` wrapped with a queue of outgoing messages, that is, a sender /// An instance of `DistAlgorithm` wrapped with a queue of outgoing messages, that is, a sender
@ -86,32 +65,27 @@ pub type OutgoingQueue<D> = BTreeMap<
pub struct SenderQueue<D> pub struct SenderQueue<D>
where where
D: SenderQueueableDistAlgorithm, D: SenderQueueableDistAlgorithm,
D::Message: Clone + SenderQueueableMessage + Serialize + DeserializeOwned, D::Message: Epoched,
D::NodeId: NodeIdT + Rand,
D::Output: SenderQueueableOutput<D::NodeId, D::Message>,
{ {
/// The managed `DistAlgorithm` instance. /// The managed `DistAlgorithm` instance.
algo: D, algo: D,
/// Our node ID. /// Our node ID.
our_id: D::NodeId, our_id: D::NodeId,
/// Current linearizable epoch of the managed `DistAlgorithm`.
lin_epoch: <D::Message as Epoched>::LinEpoch,
/// Messages that couldn't be handled yet by remote nodes. /// Messages that couldn't be handled yet by remote nodes.
outgoing_queue: OutgoingQueue<D>, outgoing_queue: OutgoingQueue<D>,
/// The set of all remote nodes on the network including validator as well as non-validator /// The set of all remote nodes on the network including validator as well as non-validator
/// (observer) nodes together with their epochs as of the last communication. /// (observer) nodes together with their epochs as of the last communication.
peer_epochs: BTreeMap<D::NodeId, <D::Message as Epoched>::LinEpoch>, peer_epochs: BTreeMap<D::NodeId, <D::Message as Epoched>::Epoch>,
} }
pub type Step<D> = ::DaStep<SenderQueue<D>>; pub type Step<D> = ::DaStep<SenderQueue<D>>;
impl<D> DistAlgorithm for SenderQueue<D> impl<D> DistAlgorithm for SenderQueue<D>
where where
D: SenderQueueableDistAlgorithm + Debug + Send + Sync, D: SenderQueueableDistAlgorithm + Debug,
D::Message: Clone + SenderQueueableMessage + Serialize + DeserializeOwned, D::Message: Clone + SenderQueueableMessage + Epoched<Epoch = <D as Epoched>::Epoch>,
D::NodeId: NodeIdT + Rand, D::NodeId: NodeIdT,
D::Output: SenderQueueableOutput<D::NodeId, D::Message>, D::Output: SenderQueueableOutput<D::NodeId, D::Message>,
<D::Message as Epoched>::Epoch: SenderQueueableEpoch + From<<D::Message as Epoched>::LinEpoch>,
{ {
type NodeId = D::NodeId; type NodeId = D::NodeId;
type Input = D::Input; type Input = D::Input;
@ -142,11 +116,10 @@ where
impl<D> SenderQueue<D> impl<D> SenderQueue<D>
where where
D: SenderQueueableDistAlgorithm + Debug + Send + Sync, D: SenderQueueableDistAlgorithm + Debug,
D::Message: Clone + SenderQueueableMessage + Serialize + DeserializeOwned, D::Message: Clone + SenderQueueableMessage + Epoched<Epoch = <D as Epoched>::Epoch>,
D::NodeId: NodeIdT + Rand, D::NodeId: NodeIdT,
D::Output: SenderQueueableOutput<D::NodeId, D::Message>, D::Output: SenderQueueableOutput<D::NodeId, D::Message>,
<D::Message as Epoched>::Epoch: SenderQueueableEpoch + From<<D::Message as Epoched>::LinEpoch>,
{ {
/// Returns a new `SenderQueueBuilder` configured to manage a given `DynamicHoneyBadger` /// Returns a new `SenderQueueBuilder` configured to manage a given `DynamicHoneyBadger`
/// instance. /// instance.
@ -167,7 +140,7 @@ where
message: Message<D::Message>, message: Message<D::Message>,
) -> Result<DaStep<Self>, D::Error> { ) -> Result<DaStep<Self>, D::Error> {
match message { match message {
Message::EpochStarted(lin_epoch) => Ok(self.handle_epoch_started(sender_id, lin_epoch)), Message::EpochStarted(epoch) => Ok(self.handle_epoch_started(sender_id, epoch)),
Message::Algo(msg) => self.handle_message_content(sender_id, msg), Message::Algo(msg) => self.handle_message_content(sender_id, msg),
} }
} }
@ -179,7 +152,7 @@ where
F: FnOnce(&mut D) -> Result<DaStep<D>, D::Error>, F: FnOnce(&mut D) -> Result<DaStep<D>, D::Error>,
{ {
let mut step = f(&mut self.algo)?; let mut step = f(&mut self.algo)?;
let mut sender_queue_step = self.update_lin_epoch(&step); let mut sender_queue_step = self.update_epoch(&step);
self.defer_messages(&mut step); self.defer_messages(&mut step);
sender_queue_step.extend(step.map(|output| output, Message::from)); sender_queue_step.extend(step.map(|output| output, Message::from));
Ok(sender_queue_step) Ok(sender_queue_step)
@ -189,34 +162,16 @@ where
fn handle_epoch_started( fn handle_epoch_started(
&mut self, &mut self,
sender_id: &D::NodeId, sender_id: &D::NodeId,
lin_epoch: <D::Message as Epoched>::LinEpoch, epoch: <D::Message as Epoched>::Epoch,
) -> DaStep<Self> { ) -> DaStep<Self> {
self.peer_epochs self.peer_epochs
.entry(sender_id.clone()) .entry(sender_id.clone())
.and_modify(|e| { .and_modify(|e| {
if *e < lin_epoch { if *e < epoch {
*e = lin_epoch; *e = epoch;
} }
}).or_insert(lin_epoch); }).or_insert(epoch);
self.remove_earlier_messages(sender_id, <D::Message as Epoched>::Epoch::from(lin_epoch)); self.process_new_epoch(sender_id, epoch)
self.process_new_epoch(sender_id, <D::Message as Epoched>::Epoch::from(lin_epoch))
}
/// Removes all messages queued for the remote node from epochs upto `epoch`.
fn remove_earlier_messages(
&mut self,
sender_id: &D::NodeId,
epoch: <D::Message as Epoched>::Epoch,
) {
let earlier_keys: Vec<_> = self
.outgoing_queue
.keys()
.cloned()
.filter(|(id, this_epoch)| id == sender_id && *this_epoch < epoch)
.collect();
for key in earlier_keys {
self.outgoing_queue.remove(&key);
}
} }
/// Processes an announcement of a new epoch update received from a remote node. /// Processes an announcement of a new epoch update received from a remote node.
@ -225,24 +180,22 @@ where
sender_id: &D::NodeId, sender_id: &D::NodeId,
epoch: <D::Message as Epoched>::Epoch, epoch: <D::Message as Epoched>::Epoch,
) -> DaStep<Self> { ) -> DaStep<Self> {
// Send any HB messages for the HB epoch. let queue = match self.outgoing_queue.get_mut(sender_id) {
let mut ready_messages = self None => return DaStep::<Self>::default(),
.outgoing_queue Some(queue) => queue,
.remove(&(sender_id.clone(), epoch)) };
.unwrap_or_default(); let earlier_keys: Vec<_> = queue
for u in epoch.spanning_epochs() { .keys()
// Send any DHB messages for the DHB era. .cloned()
ready_messages.extend( .take_while(|this_epoch| *this_epoch <= epoch)
self.outgoing_queue .collect();
.remove(&(sender_id.clone(), u)) earlier_keys
.unwrap_or_default(), .into_iter()
); .filter_map(|key| queue.remove(&key))
} .flatten()
Step::<D>::from( .filter(|msg| !msg.is_obsolete(epoch))
ready_messages .map(|msg| Target::Node(sender_id.clone()).message(Message::Algo(msg)))
.into_iter() .into()
.map(|msg| Target::Node(sender_id.clone()).message(Message::Algo(msg))),
)
} }
/// Handles a Honey Badger algorithm message in a given epoch. /// Handles a Honey Badger algorithm message in a given epoch.
@ -255,28 +208,20 @@ where
} }
/// Updates the current Honey Badger epoch. /// Updates the current Honey Badger epoch.
fn update_lin_epoch(&mut self, step: &DaStep<D>) -> DaStep<Self> { fn update_epoch(&mut self, step: &DaStep<D>) -> DaStep<Self> {
// Look up `DynamicHoneyBadger` epoch updates and collect any added peers. if step.output.is_empty() {
let new_epoch = step.output.iter().fold(self.lin_epoch, |lin_epoch, batch| { return Step::<D>::default();
let max_epoch = lin_epoch.max(batch.next_epoch());
if let Some(node) = batch.added_node() {
if &node != self.our_id() {
self.peer_epochs
.entry(node)
.or_insert_with(<D::Message as Epoched>::LinEpoch::default);
}
}
max_epoch
});
if new_epoch != self.lin_epoch {
self.lin_epoch = new_epoch;
// Announce the new epoch.
Target::All
.message(Message::EpochStarted(self.lin_epoch))
.into()
} else {
Step::<D>::default()
} }
// Look up `DynamicHoneyBadger` epoch updates and collect any added peers.
for node in step.output.iter().filter_map(|batch| batch.added_node()) {
if &node != self.our_id() {
self.peer_epochs.entry(node).or_default();
}
}
// Announce the new epoch.
Target::All
.message(Message::EpochStarted(self.algo.epoch()))
.into()
} }
/// Removes any messages to nodes at earlier epochs from the given `Step`. This may involve /// Removes any messages to nodes at earlier epochs from the given `Step`. This may involve
@ -287,10 +232,11 @@ where
let max_future_epochs = self.algo.max_future_epochs(); let max_future_epochs = self.algo.max_future_epochs();
// Append the deferred messages onto the queues. // Append the deferred messages onto the queues.
for (id, message) in step.defer_messages(&self.peer_epochs, max_future_epochs) { for (id, message) in step.defer_messages(&self.peer_epochs, max_future_epochs) {
let epoch = message.epoch();
self.outgoing_queue self.outgoing_queue
.entry((id, epoch)) .entry(id)
.or_insert_with(Vec::new) .or_default()
.entry(message.epoch())
.or_default()
.push(message); .push(message);
} }
} }
@ -309,18 +255,16 @@ where
D::Message: Epoched, D::Message: Epoched,
{ {
algo: D, algo: D,
lin_epoch: <D::Message as Epoched>::LinEpoch,
outgoing_queue: OutgoingQueue<D>, outgoing_queue: OutgoingQueue<D>,
peer_epochs: BTreeMap<D::NodeId, <D::Message as Epoched>::LinEpoch>, peer_epochs: BTreeMap<D::NodeId, <D::Message as Epoched>::Epoch>,
} }
impl<D> SenderQueueBuilder<D> impl<D> SenderQueueBuilder<D>
where where
D: SenderQueueableDistAlgorithm + Debug + Send + Sync, D: SenderQueueableDistAlgorithm + Debug,
D::Message: Clone + SenderQueueableMessage + Serialize + DeserializeOwned, D::Message: Clone + SenderQueueableMessage + Epoched<Epoch = <D as Epoched>::Epoch>,
D::NodeId: NodeIdT + Rand, D::NodeId: NodeIdT,
D::Output: SenderQueueableOutput<D::NodeId, D::Message>, D::Output: SenderQueueableOutput<D::NodeId, D::Message>,
<D::Message as Epoched>::Epoch: SenderQueueableEpoch + From<<D::Message as Epoched>::LinEpoch>,
{ {
pub fn new<I>(algo: D, peer_ids: I) -> Self pub fn new<I>(algo: D, peer_ids: I) -> Self
where where
@ -328,19 +272,13 @@ where
{ {
SenderQueueBuilder { SenderQueueBuilder {
algo, algo,
lin_epoch: <D::Message as Epoched>::LinEpoch::default(),
outgoing_queue: BTreeMap::default(), outgoing_queue: BTreeMap::default(),
peer_epochs: peer_ids peer_epochs: peer_ids
.map(|id| (id, <D::Message as Epoched>::LinEpoch::default())) .map(|id| (id, <D::Message as Epoched>::Epoch::default()))
.collect(), .collect(),
} }
} }
pub fn lin_epoch(mut self, lin_epoch: <D::Message as Epoched>::LinEpoch) -> Self {
self.lin_epoch = lin_epoch;
self
}
pub fn outgoing_queue(mut self, outgoing_queue: OutgoingQueue<D>) -> Self { pub fn outgoing_queue(mut self, outgoing_queue: OutgoingQueue<D>) -> Self {
self.outgoing_queue = outgoing_queue; self.outgoing_queue = outgoing_queue;
self self
@ -348,22 +286,21 @@ where
pub fn peer_epochs( pub fn peer_epochs(
mut self, mut self,
peer_epochs: BTreeMap<D::NodeId, <D::Message as Epoched>::LinEpoch>, peer_epochs: BTreeMap<D::NodeId, <D::Message as Epoched>::Epoch>,
) -> Self { ) -> Self {
self.peer_epochs = peer_epochs; self.peer_epochs = peer_epochs;
self self
} }
pub fn build(self, our_id: D::NodeId) -> (SenderQueue<D>, DaStep<SenderQueue<D>>) { pub fn build(self, our_id: D::NodeId) -> (SenderQueue<D>, DaStep<SenderQueue<D>>) {
let lin_epoch = <D::Message as Epoched>::LinEpoch::default(); let epoch = self.algo.epoch();
let sq = SenderQueue { let sq = SenderQueue {
algo: self.algo, algo: self.algo,
our_id, our_id,
lin_epoch: self.lin_epoch,
outgoing_queue: self.outgoing_queue, outgoing_queue: self.outgoing_queue,
peer_epochs: self.peer_epochs, peer_epochs: self.peer_epochs,
}; };
let step = Target::All.message(Message::EpochStarted(lin_epoch)).into(); let step = Target::All.message(Message::EpochStarted(epoch)).into();
(sq, step) (sq, step)
} }
} }

View File

@ -9,7 +9,20 @@ use serde::{de::DeserializeOwned, Serialize};
use super::{SenderQueue, SenderQueueableDistAlgorithm}; use super::{SenderQueue, SenderQueueableDistAlgorithm};
use queueing_honey_badger::{Change, Error as QhbError, QueueingHoneyBadger}; use queueing_honey_badger::{Change, Error as QhbError, QueueingHoneyBadger};
use transaction_queue::TransactionQueue; use transaction_queue::TransactionQueue;
use {Contribution, DaStep, NodeIdT}; use {Contribution, DaStep, Epoched, NodeIdT};
impl<T, N, Q> Epoched for QueueingHoneyBadger<T, N, Q>
where
T: Contribution + Serialize + DeserializeOwned + Clone,
N: NodeIdT + Serialize + DeserializeOwned + Rand,
Q: TransactionQueue<T>,
{
type Epoch = (u64, u64);
fn epoch(&self) -> (u64, u64) {
self.dyn_hb().epoch()
}
}
impl<T, N, Q> SenderQueueableDistAlgorithm for QueueingHoneyBadger<T, N, Q> impl<T, N, Q> SenderQueueableDistAlgorithm for QueueingHoneyBadger<T, N, Q>
where where

View File

@ -6,7 +6,6 @@ use std::hash::Hash;
use std::iter::once; use std::iter::once;
use failure::Fail; use failure::Fail;
use rand::Rand;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use fault_log::{Fault, FaultLog}; use fault_log::{Fault, FaultLog};
@ -180,30 +179,19 @@ where
/// notion of _epoch_. This interface summarizes the properties that are essential for the message /// notion of _epoch_. This interface summarizes the properties that are essential for the message
/// sender queue. /// sender queue.
pub trait Epoched { pub trait Epoched {
/// Type of epoch. It is not required to be totally ordered. /// Type of epoch.
type Epoch: EpochT; type Epoch: EpochT;
/// A subtype of `Epoch` which contains sets of "linearizable epochs" such that each of those
/// sets is totally ordered and each has a least element.
type LinEpoch: EpochT;
/// Returns the object's epoch number. /// Returns the object's epoch number.
fn epoch(&self) -> Self::Epoch; fn epoch(&self) -> Self::Epoch;
/// Returns the object's linearizable epoch number if the object's epoch can be linearized.
fn linearizable_epoch(&self) -> Option<Self::LinEpoch>;
} }
impl<M: Epoched, N> Epoched for TargetedMessage<M, N> { impl<M: Epoched, N> Epoched for TargetedMessage<M, N> {
type Epoch = <M as Epoched>::Epoch; type Epoch = <M as Epoched>::Epoch;
type LinEpoch = <M as Epoched>::LinEpoch;
fn epoch(&self) -> Self::Epoch { fn epoch(&self) -> Self::Epoch {
self.message.epoch() self.message.epoch()
} }
fn linearizable_epoch(&self) -> Option<Self::LinEpoch> {
self.message.linearizable_epoch()
}
} }
/// An alias for the type of `Step` returned by `D`'s methods. /// An alias for the type of `Step` returned by `D`'s methods.
@ -212,15 +200,15 @@ pub type DaStep<D> =
impl<'i, M, O, N> Step<M, O, N> impl<'i, M, O, N> Step<M, O, N>
where where
N: NodeIdT + Rand, N: NodeIdT,
M: 'i + Clone + SenderQueueableMessage + Serialize + DeserializeOwned, M: 'i + Clone + SenderQueueableMessage,
{ {
/// Removes and returns any messages that are not yet accepted by remote nodes according to the /// Removes and returns any messages that are not yet accepted by remote nodes according to the
/// mapping `remote_epochs`. This way the returned messages are postponed until later, and the /// mapping `remote_epochs`. This way the returned messages are postponed until later, and the
/// remaining messages can be sent to remote nodes without delay. /// remaining messages can be sent to remote nodes without delay.
pub fn defer_messages( pub fn defer_messages(
&mut self, &mut self,
peer_epochs: &BTreeMap<N, <M as Epoched>::LinEpoch>, peer_epochs: &BTreeMap<N, <M as Epoched>::Epoch>,
max_future_epochs: u64, max_future_epochs: u64,
) -> Vec<(N, M)> { ) -> Vec<(N, M)> {
let mut deferred_msgs: Vec<(N, M)> = Vec::new(); let mut deferred_msgs: Vec<(N, M)> = Vec::new();
@ -229,10 +217,10 @@ where
match msg.target.clone() { match msg.target.clone() {
Target::Node(id) => { Target::Node(id) => {
if let Some(&them) = peer_epochs.get(&id) { if let Some(&them) = peer_epochs.get(&id) {
if msg.message.is_accepted(them, max_future_epochs) { if msg.message.is_premature(them, max_future_epochs) {
passed_msgs.push(msg);
} else if !msg.message.is_obsolete(them) {
deferred_msgs.push((id, msg.message)); deferred_msgs.push((id, msg.message));
} else if !msg.message.is_obsolete(them) {
passed_msgs.push(msg);
} }
} }
} }
@ -246,11 +234,11 @@ where
// The `Target::All` message is split into two sets of point messages: those // The `Target::All` message is split into two sets of point messages: those
// which can be sent without delay and those which should be postponed. // which can be sent without delay and those which should be postponed.
for (id, &them) in peer_epochs { for (id, &them) in peer_epochs {
if msg.message.is_accepted(them, max_future_epochs) { if msg.message.is_premature(them, max_future_epochs) {
deferred_msgs.push((id.clone(), msg.message.clone()));
} else if !msg.message.is_obsolete(them) {
passed_msgs passed_msgs
.push(Target::Node(id.clone()).message(msg.message.clone())); .push(Target::Node(id.clone()).message(msg.message.clone()));
} else if !msg.message.is_obsolete(them) {
deferred_msgs.push((id.clone(), msg.message.clone()));
} }
} }
} }

View File

@ -24,7 +24,7 @@ use rand::Rng;
use hbbft::honey_badger::{Batch, HoneyBadger, MessageContent}; use hbbft::honey_badger::{Batch, HoneyBadger, MessageContent};
use hbbft::sender_queue::{self, SenderQueue, Step}; use hbbft::sender_queue::{self, SenderQueue, Step};
use hbbft::transaction_queue::TransactionQueue; use hbbft::transaction_queue::TransactionQueue;
use hbbft::{threshold_decrypt, DistAlgorithm, Epoched, NetworkInfo, Target, TargetedMessage}; use hbbft::{threshold_decrypt, DistAlgorithm, NetworkInfo, Target, TargetedMessage};
use network::{ use network::{
Adversary, MessageScheduler, MessageWithSender, NodeId, RandomAdversary, SilentAdversary, Adversary, MessageScheduler, MessageWithSender, NodeId, RandomAdversary, SilentAdversary,
@ -74,10 +74,10 @@ impl Adversary<UsizeHoneyBadger> for FaultyShareAdversary {
if sender_id < self.num_good { if sender_id < self.num_good {
if let TargetedMessage { if let TargetedMessage {
target: Target::All, target: Target::All,
message, message: sender_queue::Message::Algo(hb_msg),
} = msg } = msg
{ {
let epoch = message.epoch(); let epoch = hb_msg.epoch();
// Set the trigger to simulate decryption share messages. // Set the trigger to simulate decryption share messages.
self.share_triggers.entry(epoch).or_insert(true); self.share_triggers.entry(epoch).or_insert(true);
} }