mirror of https://github.com/poanetwork/hbbft.git
Merge pull request #250 from ErichDonGubler/subset-optimization-option
Make Subset optimization optional
This commit is contained in:
commit
79e5ef19fd
|
@ -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;
|
||||
|
@ -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)>,
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -58,26 +61,43 @@ 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 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))
|
||||
|
|
|
@ -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;
|
||||
|
@ -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>,
|
||||
}
|
||||
|
||||
|
@ -36,6 +39,7 @@ where
|
|||
netinfo,
|
||||
max_future_epochs: 3,
|
||||
rng: Box::new(rand::thread_rng()),
|
||||
subset_handling_strategy: SubsetHandlingStrategy::Incremental,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -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>) {
|
||||
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
@ -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> {
|
||||
|
@ -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>,
|
||||
}
|
||||
|
||||
|
@ -127,7 +201,11 @@ 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,
|
||||
|
@ -135,6 +213,7 @@ where
|
|||
subset: SubsetState::Ongoing(cs),
|
||||
decryption: BTreeMap::default(),
|
||||
accepted_proposers: Default::default(),
|
||||
subset_handler: subset_handling_strategy.into(),
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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(),
|
||||
)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
Loading…
Reference in New Issue