Skip to content

Commit

Permalink
refactored an error! by adding linearizable epochs and wrote a commen…
Browse files Browse the repository at this point in the history
…t on eras
  • Loading branch information
vkomenda committed Nov 1, 2018
1 parent 11c185b commit 59af49b
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 62 deletions.
32 changes: 26 additions & 6 deletions src/dynamic_honey_badger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
//!
//! Like Honey Badger, this protocol allows a network of _N_ nodes with at most _f_ faulty ones,
//! where _3 f < N_, to input "contributions" - any kind of data -, and to agree on a sequence of
//! _batches_ of contributions. The protocol proceeds in _epochs_, starting at number 0, and outputs
//! one batch in each epoch. It never terminates: It handles a continuous stream of incoming
//! _batches_ of contributions. The protocol proceeds in linear _epochs_, starting at number 0, and
//! outputs one batch in each epoch. It never terminates: It handles a continuous stream of incoming
//! contributions and keeps producing new batches from them. All correct nodes will output the same
//! batch for each epoch. Each validator proposes one contribution per epoch, and every batch will
//! contain the contributions of at least _N - f_ validators.
//! Epochs are divided into intervals called _eras_ starting at 0. Each following era begins
//! immediately after a batch that
//!
//! - proposes a change in the set of validators or
//!
//! - finalizes that proposed change.
//!
//! Unlike Honey Badger, this algorithm allows dynamically adding and removing validators.
//! As a signal to initiate converting observers to validators or vice versa, it defines a special
Expand All @@ -17,10 +24,10 @@
//! create new cryptographic key shares for the new group of validators.
//!
//! The state of that process after each epoch is communicated via the `change` field in `Batch`.
//! When this contains an `InProgress(..)` value, key generation begins. The joining validator (in
//! the case of an `Add` change) must be an observer starting in the following epoch or earlier.
//! When `change` is `Complete(..)`, the following epochs will be produced by the new set of
//! validators.
//! When this contains an `InProgress(..)` value, key generation begins and the following epoch
//! starts the next era. The joining validator (in the case of an `Add` change) must be an observer
//! starting in the following epoch or earlier. When `change` is `Complete(..)`, the following
//! epoch starts the next era with the new set of validators.
//!
//! New observers can only join the network after an epoch where `change` was not `None`. These
//! epochs' batches contain a `JoinPlan`, which can be sent as an invitation to the new node: The
Expand Down Expand Up @@ -131,6 +138,13 @@ impl<N: Rand> Message<N> {
#[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.
Expand Down Expand Up @@ -158,6 +172,7 @@ impl Default for Epoch {

impl<N: Rand> Epoched for Message<N> {
type Epoch = Epoch;
type LinEpoch = (u64, u64);

fn epoch(&self) -> Epoch {
match *self {
Expand All @@ -166,6 +181,11 @@ impl<N: Rand> Epoched for Message<N> {
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
Expand Down
36 changes: 19 additions & 17 deletions src/dynamic_honey_badger/sender_queueable.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use log::error;
use rand::Rand;
use serde::{de::DeserializeOwned, Serialize};

Expand All @@ -23,13 +22,13 @@ where
}
}

fn next_epoch(&self) -> Epoch {
fn next_epoch(&self) -> (u64, u64) {
let epoch = self.epoch;
let era = self.era;
if self.change == ChangeState::None {
Epoch(era, Some(epoch - era + 1))
(era, epoch - era + 1)
} else {
Epoch(epoch + 1, Some(0))
(epoch + 1, 0)
}
}
}
Expand All @@ -38,25 +37,28 @@ impl<N> SenderQueueableMessage for Message<N>
where
N: Rand,
{
fn is_accepted(&self, Epoch(them_era, them_hb_epoch): Epoch, max_future_epochs: u64) -> bool {
let Epoch(era, hb_epoch) = self.epoch();
fn is_accepted(&self, (them_era, them): (u64, u64), max_future_epochs: u64) -> bool {
let Epoch(era, us) = self.epoch();
if era != them_era {
return false;
}
match (hb_epoch, them_hb_epoch) {
(Some(us), Some(them)) => them <= us && us <= them + max_future_epochs,
(None, Some(_)) => true,
(_, None) => {
// TODO: return a Fault.
error!("Peer's Honey Badger epoch undefined");
false
}
if let Some(us) = us {
them <= us && us <= them + max_future_epochs
} else {
true
}
}

fn is_obsolete(&self, Epoch(them_era, them_hb_epoch): Epoch) -> bool {
let Epoch(era, hb_epoch) = self.epoch();
era < them_era || (era == them_era && hb_epoch.is_some() && hb_epoch < them_hb_epoch)
fn is_obsolete(&self, (them_era, them): (u64, u64)) -> bool {
let Epoch(era, us) = self.epoch();
if era < them_era {
return true;
}
if let Some(us) = us {
era == them_era && us < them
} else {
false
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/honey_badger/honey_badger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,15 @@ pub struct HoneyBadger<C, N: Rand> {

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> = ::Step<HoneyBadger<C, N>>;
Expand Down
5 changes: 5 additions & 0 deletions src/honey_badger/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ pub struct Message<N: Rand> {

impl<N: Rand> Epoched for Message<N> {
type Epoch = u64;
type LinEpoch = u64;

fn epoch(&self) -> u64 {
self.epoch
}

fn linearizable_epoch(&self) -> Option<u64> {
Some(self.epoch)
}
}
14 changes: 12 additions & 2 deletions src/sender_queue/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ use Epoched;

#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Message<M: Epoched> {
EpochStarted(<M as Epoched>::Epoch),
EpochStarted(<M as Epoched>::LinEpoch),
Algo(M),
}

impl<M> Rand for Message<M>
where
M: Epoched + Rand,
<M as Epoched>::Epoch: Rand,
<M as Epoched>::LinEpoch: Rand,
{
fn rand<R: Rng>(rng: &mut R) -> Self {
let message_type = *rng.choose(&["epoch", "algo"]).unwrap();
Expand All @@ -28,15 +29,24 @@ 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) => *epoch,
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> {
Expand Down
Loading

0 comments on commit 59af49b

Please sign in to comment.