Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Subset optimization optional #250

Merged
merged 2 commits into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions src/dynamic_honey_badger/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rand::{self, Rand, Rng};
use serde::{Deserialize, Serialize};

use super::{ChangeState, DynamicHoneyBadger, JoinPlan, Result, Step, VoteCounter};
use honey_badger::HoneyBadger;
use honey_badger::{HoneyBadger, SubsetHandlingStrategy};
use messaging::NetworkInfo;
use traits::{Contribution, NodeIdT};
use util::SubRng;
Expand All @@ -21,6 +21,8 @@ pub struct DynamicHoneyBadgerBuilder<C, N> {
/// Random number generator passed on to algorithm instance for key generation. Also used to
/// instantiate `HoneyBadger`.
rng: Box<dyn rand::Rng>,
/// Strategy used to handle the output of the `Subset` algorithm.
subset_handling_strategy: SubsetHandlingStrategy,
_phantom: PhantomData<(C, N)>,
}

Expand All @@ -30,6 +32,7 @@ impl<C, N> Default for DynamicHoneyBadgerBuilder<C, N> {
DynamicHoneyBadgerBuilder {
max_future_epochs: 3,
rng: Box::new(rand::thread_rng()),
subset_handling_strategy: SubsetHandlingStrategy::Incremental,
_phantom: PhantomData,
}
}
Expand Down Expand Up @@ -58,26 +61,43 @@ where
self
}

/// Sets the strategy to use when handling `Subset` output.
pub fn subset_handling_strategy(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a short doc comment explaining subset handling. (Also in HoneyBadgerBuilder.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed I was missing documentation and intended to get to it today -- thank you for reminding me! :)

&mut self,
subset_handling_strategy: SubsetHandlingStrategy,
) -> &mut Self {
self.subset_handling_strategy = subset_handling_strategy;
self
}

/// Creates a new Dynamic Honey Badger instance with an empty buffer.
pub fn build(
&mut self,
netinfo: NetworkInfo<N>,
) -> Result<(DynamicHoneyBadger<C, N>, Step<C, N>)> {
let DynamicHoneyBadgerBuilder {
max_future_epochs,
rng,
subset_handling_strategy,
_phantom,
} = self;
let max_future_epochs = *max_future_epochs;
let arc_netinfo = Arc::new(netinfo.clone());
let (honey_badger, hb_step) = HoneyBadger::builder(arc_netinfo.clone())
.max_future_epochs(self.max_future_epochs)
.rng(self.rng.sub_rng())
.max_future_epochs(max_future_epochs)
.rng(rng.sub_rng())
.subset_handling_strategy(subset_handling_strategy.clone())
.build();
let mut dhb = DynamicHoneyBadger {
netinfo,
max_future_epochs: self.max_future_epochs,
max_future_epochs,
start_epoch: 0,
vote_counter: VoteCounter::new(arc_netinfo, 0),
key_gen_msg_buffer: Vec::new(),
honey_badger,
key_gen_state: None,
incoming_queue: Vec::new(),
rng: Box::new(self.rng.sub_rng()),
rng: Box::new(rng.sub_rng()),
};
let step = dhb.process_output(hb_step)?;
Ok((dhb, step))
Expand Down
14 changes: 14 additions & 0 deletions src/honey_badger/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rand::{self, Rand, Rng};
use serde::{Deserialize, Serialize};

use super::{HoneyBadger, Message, Step};
use honey_badger::SubsetHandlingStrategy;
use messaging::{NetworkInfo, Target};
use traits::{Contribution, NodeIdT};
use util::SubRng;
Expand All @@ -21,6 +22,8 @@ where
max_future_epochs: usize,
/// Random number generator passed on to algorithm instance for signing and encrypting.
rng: Box<dyn Rng>,
/// Strategy used to handle the output of the `Subset` algorithm.
subset_handling_strategy: SubsetHandlingStrategy,
_phantom: PhantomData<C>,
}

Expand All @@ -36,6 +39,7 @@ where
netinfo,
max_future_epochs: 3,
rng: Box::new(rand::thread_rng()),
subset_handling_strategy: SubsetHandlingStrategy::Incremental,
_phantom: PhantomData,
}
}
Expand All @@ -52,6 +56,15 @@ where
self
}

/// Sets the strategy to use when handling `Subset` output.
pub fn subset_handling_strategy(
&mut self,
subset_handling_strategy: SubsetHandlingStrategy,
) -> &mut Self {
self.subset_handling_strategy = subset_handling_strategy;
self
}

/// Creates a new Honey Badger instance in epoch 0 and makes the initial `Step` on that
/// instance.
pub fn build(&mut self) -> (HoneyBadger<C, N>, Step<C, N>) {
Expand All @@ -64,6 +77,7 @@ where
incoming_queue: BTreeMap::new(),
remote_epochs: BTreeMap::new(),
rng: Box::new(self.rng.sub_rng()),
subset_handling_strategy: self.subset_handling_strategy.clone(),
};
let step = if self.netinfo.is_validator() {
// The first message in an epoch announces the epoch transition.
Expand Down
125 changes: 104 additions & 21 deletions src/honey_badger/epoch_state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::marker::PhantomData;
use std::mem::replace;
use std::sync::Arc;

use bincode;
Expand Down Expand Up @@ -105,6 +106,77 @@ where
}
}

/// A flag used when constructing an `EpochState` to determine which behavior to use when receiving
/// proposals from a `Subset` instance.
#[derive(Debug, Clone)]
pub enum SubsetHandlingStrategy {
/// Sets the `EpochState` to return proposals as they are contributed.
Incremental,
/// Sets the `EpochState` to return all received proposals once consensus has been finalized.
AllAtEnd,
}

/// Used in an `EpochState` to encapsulate the state necessary to maintain each
/// `SubsetHandlingStrategy`.
#[derive(Debug, Clone)]
enum SubsetHandler<N> {
Incremental,
AllAtEnd(Vec<(N, Vec<u8>)>),
}

/// The result of a call to `SubsetHandler::handle(...)`.
struct SubsetHandleData<N> {
/// The number of contributions propagated from the handler.
contributions: Vec<(N, Vec<u8>)>,
/// Indicates whether the underlying `Subset` algorithm has achieved consensus and whether
/// there may be more contributions or not.
is_done: bool,
}

impl<N> SubsetHandler<N> {
fn handle(&mut self, o: SubsetOutput<N>) -> SubsetHandleData<N> {
use self::SubsetHandler::*;
use self::SubsetOutput::*;
let contributions;
let is_done;
match o {
Contribution(proposer_id, data) => {
let proposal = (proposer_id, data);
contributions = match self {
Incremental => vec![proposal],
AllAtEnd(cs) => {
cs.push(proposal);
vec![]
}
};
is_done = false;
}
Done => {
contributions = match self {
Incremental => vec![],
AllAtEnd(cs) => replace(cs, vec![]),
};
is_done = true;
}
}

SubsetHandleData {
contributions,
is_done,
}
}
}

impl<N> From<SubsetHandlingStrategy> for SubsetHandler<N> {
fn from(s: SubsetHandlingStrategy) -> Self {
use self::SubsetHandlingStrategy::*;
match s {
Incremental => SubsetHandler::Incremental,
AllAtEnd => SubsetHandler::AllAtEnd(Vec::new()),
}
}
}

/// The sub-algorithms and their intermediate results for a single epoch.
#[derive(Debug)]
pub struct EpochState<C, N: Rand> {
Expand All @@ -118,6 +190,8 @@ pub struct EpochState<C, N: Rand> {
decryption: BTreeMap<N, DecryptionState<N>>,
/// Nodes found so far in `Subset` output.
accepted_proposers: BTreeSet<N>,
/// Determines the behavior upon receiving proposals from `subset`.
subset_handler: SubsetHandler<N>,
_phantom: PhantomData<C>,
}

Expand All @@ -127,14 +201,19 @@ where
N: NodeIdT + Rand,
{
/// Creates a new `Subset` instance.
pub fn new(netinfo: Arc<NetworkInfo<N>>, epoch: u64) -> Result<Self> {
pub fn new(
netinfo: Arc<NetworkInfo<N>>,
epoch: u64,
subset_handling_strategy: SubsetHandlingStrategy,
) -> Result<Self> {
let cs = Subset::new(netinfo.clone(), epoch).map_err(ErrorKind::CreateSubset)?;
Ok(EpochState {
epoch,
netinfo,
subset: SubsetState::Ongoing(cs),
decryption: BTreeMap::default(),
accepted_proposers: Default::default(),
subset_handler: subset_handling_strategy.into(),
_phantom: PhantomData,
})
}
Expand Down Expand Up @@ -230,30 +309,34 @@ where
if has_seen_done {
error!("`SubsetOutput::Done` was not the last `SubsetOutput`");
}
match cs_output {
SubsetOutput::Contribution(k, v) => {
step.extend(self.send_decryption_share(k.clone(), &v)?);
self.accepted_proposers.insert(k);
}
SubsetOutput::Done => {
self.subset = SubsetState::Complete(self.accepted_proposers.clone());

let faulty_shares: Vec<_> = self
.decryption
.keys()
.filter(|id| !self.accepted_proposers.contains(id))
.cloned()
.collect();
for id in faulty_shares {
if let Some(DecryptionState::Ongoing(td)) = self.decryption.remove(&id) {
for id in td.sender_ids() {
let fault_kind = FaultKind::UnexpectedDecryptionShare;
step.fault_log.append(id.clone(), fault_kind);
}
let SubsetHandleData {
contributions,
is_done,
} = self.subset_handler.handle(cs_output);

for (k, v) in contributions {
step.extend(self.send_decryption_share(k.clone(), &v)?);
self.accepted_proposers.insert(k);
}
ErichDonGubler marked this conversation as resolved.
Show resolved Hide resolved

if is_done {
self.subset = SubsetState::Complete(self.accepted_proposers.clone());
let faulty_shares: Vec<_> = self
.decryption
.keys()
.filter(|id| !self.accepted_proposers.contains(id))
.cloned()
.collect();
for id in faulty_shares {
if let Some(DecryptionState::Ongoing(td)) = self.decryption.remove(&id) {
for id in td.sender_ids() {
let fault_kind = FaultKind::UnexpectedDecryptionShare;
step.fault_log.append(id.clone(), fault_kind);
}
}
has_seen_done = true
}
has_seen_done = true;
}
}
Ok(step)
Expand Down
10 changes: 9 additions & 1 deletion src/honey_badger/honey_badger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use super::{Batch, Error, ErrorKind, HoneyBadgerBuilder, Message, MessageContent
use messaging::{self, DistAlgorithm, NetworkInfo, Target};
use traits::{Contribution, NodeIdT};

pub use super::epoch_state::SubsetHandlingStrategy;

/// An instance of the Honey Badger Byzantine fault tolerant consensus algorithm.
pub struct HoneyBadger<C, N: Rand> {
/// Shared network data.
Expand All @@ -31,6 +33,8 @@ pub struct HoneyBadger<C, N: Rand> {
/// A random number generator used for secret key generation.
// Boxed to avoid overloading the algorithm's type with more generics.
pub(super) rng: Box<dyn Rng>,
/// Represents the optimization strategy to use for output of the `Subset` algorithm.
pub(super) subset_handling_strategy: SubsetHandlingStrategy,
}

impl<C, N> fmt::Debug for HoneyBadger<C, N>
Expand Down Expand Up @@ -216,7 +220,11 @@ where
fn epoch_state_mut(&mut self, epoch: u64) -> Result<&mut EpochState<C, N>> {
Ok(match self.epochs.entry(epoch) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(EpochState::new(self.netinfo.clone(), epoch)?),
Entry::Vacant(entry) => entry.insert(EpochState::new(
self.netinfo.clone(),
epoch,
self.subset_handling_strategy.clone(),
)?),
})
}
}
2 changes: 1 addition & 1 deletion src/honey_badger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ mod message;
pub use self::batch::Batch;
pub use self::builder::HoneyBadgerBuilder;
pub use self::error::{Error, ErrorKind, Result};
pub use self::honey_badger::{HoneyBadger, Step};
pub use self::honey_badger::{HoneyBadger, Step, SubsetHandlingStrategy};
pub use self::message::{Message, MessageContent};