mirror of https://github.com/poanetwork/hbbft.git
Merge pull request #218 from poanetwork/afck-rename
Rename Agreement to BinaryAgreement.
This commit is contained in:
commit
aa067d29e4
|
@ -4,9 +4,9 @@ use std::sync::Arc;
|
|||
use itertools::Itertools;
|
||||
|
||||
use super::bool_multimap::BoolMultimap;
|
||||
use super::bool_set::BoolSet;
|
||||
use super::sbv_broadcast::{self, SbvBroadcast};
|
||||
use super::{AgreementContent, Error, Message, Nonce, Result, Step};
|
||||
use agreement::bool_set::BoolSet;
|
||||
use super::{Error, Message, MessageContent, Nonce, Result, Step};
|
||||
use coin::{self, Coin, CoinMessage};
|
||||
use messaging::{DistAlgorithm, NetworkInfo, Target};
|
||||
use traits::NodeIdT;
|
||||
|
@ -39,14 +39,14 @@ impl<N> From<bool> for CoinState<N> {
|
|||
|
||||
/// Binary Agreement instance
|
||||
#[derive(Debug)]
|
||||
pub struct Agreement<N> {
|
||||
pub struct BinaryAgreement<N> {
|
||||
/// Shared network information.
|
||||
netinfo: Arc<NetworkInfo<N>>,
|
||||
/// Session ID, e.g, the Honey Badger algorithm epoch.
|
||||
session_id: u64,
|
||||
/// The ID of the proposer of the value for this agreement instance.
|
||||
/// The ID of the proposer of the value for this Binary Agreement instance.
|
||||
proposer_id: N,
|
||||
/// Agreement algorithm epoch.
|
||||
/// Binary Agreement algorithm epoch.
|
||||
epoch: u32,
|
||||
/// This epoch's Synchronized Binary Value Broadcast instance.
|
||||
sbv_broadcast: SbvBroadcast<N>,
|
||||
|
@ -65,14 +65,14 @@ pub struct Agreement<N> {
|
|||
decision: Option<bool>,
|
||||
/// A cache for messages for future epochs that cannot be handled yet.
|
||||
// TODO: Find a better solution for this; defend against spam.
|
||||
incoming_queue: BTreeMap<u32, Vec<(N, AgreementContent)>>,
|
||||
incoming_queue: BTreeMap<u32, Vec<(N, MessageContent)>>,
|
||||
/// The values we found in the first _N - f_ `Aux` messages that were in `bin_values`.
|
||||
conf_values: Option<BoolSet>,
|
||||
/// The state of this epoch's coin.
|
||||
coin_state: CoinState<N>,
|
||||
}
|
||||
|
||||
impl<N: NodeIdT> DistAlgorithm for Agreement<N> {
|
||||
impl<N: NodeIdT> DistAlgorithm for BinaryAgreement<N> {
|
||||
type NodeId = N;
|
||||
type Input = bool;
|
||||
type Output = bool;
|
||||
|
@ -109,12 +109,12 @@ impl<N: NodeIdT> DistAlgorithm for Agreement<N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<N: NodeIdT> Agreement<N> {
|
||||
impl<N: NodeIdT> BinaryAgreement<N> {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<N>>, session_id: u64, proposer_id: N) -> Result<Self> {
|
||||
if !netinfo.is_node_validator(&proposer_id) {
|
||||
return Err(Error::UnknownProposer);
|
||||
}
|
||||
Ok(Agreement {
|
||||
Ok(BinaryAgreement {
|
||||
netinfo: netinfo.clone(),
|
||||
session_id,
|
||||
proposer_id,
|
||||
|
@ -130,7 +130,7 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Sets the input value for agreement.
|
||||
/// Sets the input value for Binary Agreement.
|
||||
fn handle_input(&mut self, input: bool) -> Result<Step<N>> {
|
||||
if self.epoch != 0 || self.estimated.is_some() {
|
||||
return Err(Error::InputNotAccepted);
|
||||
|
@ -151,13 +151,13 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
fn handle_message_content(
|
||||
&mut self,
|
||||
sender_id: &N,
|
||||
content: AgreementContent,
|
||||
content: MessageContent,
|
||||
) -> Result<Step<N>> {
|
||||
match content {
|
||||
AgreementContent::SbvBroadcast(msg) => self.handle_sbv_broadcast(sender_id, msg),
|
||||
AgreementContent::Conf(v) => self.handle_conf(sender_id, v),
|
||||
AgreementContent::Term(v) => self.handle_term(sender_id, v),
|
||||
AgreementContent::Coin(msg) => self.handle_coin(sender_id, *msg),
|
||||
MessageContent::SbvBroadcast(msg) => self.handle_sbv_broadcast(sender_id, msg),
|
||||
MessageContent::Conf(v) => self.handle_conf(sender_id, v),
|
||||
MessageContent::Term(v) => self.handle_term(sender_id, v),
|
||||
MessageContent::Coin(msg) => self.handle_coin(sender_id, *msg),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
fn handle_sbvb_step(&mut self, sbvb_step: sbv_broadcast::Step<N>) -> Result<Step<N>> {
|
||||
let mut step = Step::default();
|
||||
let output = step.extend_with(sbvb_step, |msg| {
|
||||
AgreementContent::SbvBroadcast(msg).with_epoch(self.epoch)
|
||||
MessageContent::SbvBroadcast(msg).with_epoch(self.epoch)
|
||||
});
|
||||
if self.conf_values.is_some() {
|
||||
return Ok(step); // The `Conf` round has already started.
|
||||
|
@ -250,11 +250,11 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
return Ok(self.try_finish_conf_round()?);
|
||||
}
|
||||
|
||||
self.send(AgreementContent::Conf(values))
|
||||
self.send(MessageContent::Conf(values))
|
||||
}
|
||||
|
||||
/// Multicasts and handles a message. Does nothing if we are only an observer.
|
||||
fn send(&mut self, content: AgreementContent) -> Result<Step<N>> {
|
||||
fn send(&mut self, content: MessageContent) -> Result<Step<N>> {
|
||||
if !self.netinfo.is_validator() {
|
||||
return Ok(Step::default());
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
fn on_coin_step(&mut self, coin_step: coin::Step<N, Nonce>) -> Result<Step<N>> {
|
||||
let mut step = Step::default();
|
||||
let epoch = self.epoch;
|
||||
let to_msg = |c_msg| AgreementContent::Coin(Box::new(c_msg)).with_epoch(epoch);
|
||||
let to_msg = |c_msg| MessageContent::Coin(Box::new(c_msg)).with_epoch(epoch);
|
||||
let coin_output = step.extend_with(coin_step, to_msg);
|
||||
if let Some(coin) = coin_output.into_iter().next() {
|
||||
self.coin_state = coin.into();
|
||||
|
@ -287,7 +287,7 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
/// the unique conf value agrees with the coin, terminates and decides on that value.
|
||||
fn try_update_epoch(&mut self) -> Result<Step<N>> {
|
||||
if self.decision.is_some() {
|
||||
// Avoid an infinite regression without making an Agreement step.
|
||||
// Avoid an infinite regression without making a Binary Agreement step.
|
||||
return Ok(Step::default());
|
||||
}
|
||||
let coin = match self.coin_state.value() {
|
||||
|
@ -329,7 +329,7 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
if self.decision.is_some() {
|
||||
return Step::default();
|
||||
}
|
||||
// Output the agreement value.
|
||||
// Output the Binary Agreement value.
|
||||
let mut step = Step::default();
|
||||
step.output.push_back(b);
|
||||
// Latch the decided state.
|
||||
|
@ -342,7 +342,7 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
b
|
||||
);
|
||||
if self.netinfo.is_validator() {
|
||||
let msg = AgreementContent::Term(b).with_epoch(self.epoch + 1);
|
||||
let msg = MessageContent::Term(b).with_epoch(self.epoch + 1);
|
||||
step.messages.push_back(Target::All.message(msg));
|
||||
}
|
||||
step
|
||||
|
@ -383,7 +383,7 @@ impl<N: NodeIdT> Agreement<N> {
|
|||
self.epoch += 1;
|
||||
self.coin_state = self.coin_state();
|
||||
debug!(
|
||||
"{:?} Agreement instance {:?} started epoch {}, {} terminated",
|
||||
"{:?} BinaryAgreement instance {:?} started epoch {}, {} terminated",
|
||||
self.netinfo.our_id(),
|
||||
self.proposer_id,
|
||||
self.epoch,
|
|
@ -1,4 +1,4 @@
|
|||
//! # Binary Byzantine agreement protocol
|
||||
//! # Binary Agreement
|
||||
//!
|
||||
//! The Binary Agreement protocol allows each node to input one binary (`bool`) value, and will
|
||||
//! output a binary value. The output is guaranteed to have been input by at least one correct
|
||||
|
@ -63,7 +63,7 @@
|
|||
//! * After _f + 1_ nodes have sent us their coin shares, we receive the coin output and assign it
|
||||
//! to `s`.
|
||||
|
||||
mod agreement;
|
||||
mod binary_agreement;
|
||||
mod bool_multimap;
|
||||
pub mod bool_set;
|
||||
mod sbv_broadcast;
|
||||
|
@ -74,9 +74,9 @@ use self::bool_set::BoolSet;
|
|||
use coin::{self, CoinMessage};
|
||||
use messaging;
|
||||
|
||||
pub use self::agreement::Agreement;
|
||||
pub use self::binary_agreement::BinaryAgreement;
|
||||
|
||||
/// An agreement error.
|
||||
/// An Binary Agreement error.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
|
||||
pub enum Error {
|
||||
#[fail(display = "HandleCoin error: {}", _0)]
|
||||
|
@ -89,13 +89,13 @@ pub enum Error {
|
|||
InputNotAccepted,
|
||||
}
|
||||
|
||||
/// An agreement result.
|
||||
/// An Binary Agreement result.
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
pub type Step<N> = messaging::Step<Agreement<N>>;
|
||||
pub type Step<N> = messaging::Step<BinaryAgreement<N>>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub enum AgreementContent {
|
||||
pub enum MessageContent {
|
||||
/// Synchronized Binary Value Broadcast message.
|
||||
SbvBroadcast(sbv_broadcast::Message),
|
||||
/// `Conf` message.
|
||||
|
@ -106,7 +106,7 @@ pub enum AgreementContent {
|
|||
Coin(Box<CoinMessage>),
|
||||
}
|
||||
|
||||
impl AgreementContent {
|
||||
impl MessageContent {
|
||||
/// Creates an message with a given epoch number.
|
||||
pub fn with_epoch(self, epoch: u32) -> Message {
|
||||
Message {
|
||||
|
@ -118,31 +118,31 @@ impl AgreementContent {
|
|||
/// Returns `true` if this message can be ignored if its epoch has already passed.
|
||||
pub fn can_expire(&self) -> bool {
|
||||
match *self {
|
||||
AgreementContent::Term(_) => false,
|
||||
MessageContent::Term(_) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages sent during the binary Byzantine agreement stage.
|
||||
/// Messages sent during the Binary Agreement stage.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
|
||||
pub struct Message {
|
||||
pub epoch: u32,
|
||||
pub content: AgreementContent,
|
||||
pub content: MessageContent,
|
||||
}
|
||||
|
||||
// NOTE: Extending rand_derive to correctly generate random values from boxes would make this
|
||||
// implementation obsolete; however at the time of this writing, `rand::Rand` is already deprecated
|
||||
// with no replacement in sight.
|
||||
impl rand::Rand for AgreementContent {
|
||||
impl rand::Rand for MessageContent {
|
||||
fn rand<R: rand::Rng>(rng: &mut R) -> Self {
|
||||
let message_type = *rng.choose(&["sbvb", "conf", "term", "coin"]).unwrap();
|
||||
|
||||
match message_type {
|
||||
"sbvb" => AgreementContent::SbvBroadcast(rand::random()),
|
||||
"conf" => AgreementContent::Conf(rand::random()),
|
||||
"term" => AgreementContent::Term(rand::random()),
|
||||
"coin" => AgreementContent::Coin(Box::new(rand::random())),
|
||||
"sbvb" => MessageContent::SbvBroadcast(rand::random()),
|
||||
"conf" => MessageContent::Conf(rand::random()),
|
||||
"term" => MessageContent::Term(rand::random()),
|
||||
"coin" => MessageContent::Coin(Box::new(rand::random())),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -156,11 +156,11 @@ impl Nonce {
|
|||
invocation_id: &[u8],
|
||||
session_id: u64,
|
||||
proposer_id: usize,
|
||||
agreement_epoch: u32,
|
||||
binary_agreement_epoch: u32,
|
||||
) -> Self {
|
||||
Nonce(Vec::from(format!(
|
||||
"Nonce for Honey Badger {:?}@{}:{}:{}",
|
||||
invocation_id, session_id, agreement_epoch, proposer_id
|
||||
invocation_id, session_id, binary_agreement_epoch, proposer_id
|
||||
)))
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//! # Synchronized Binary Value Broadcast
|
||||
//!
|
||||
//! This performs the `BVal` and `Aux` steps for `Agreement`.
|
||||
//! This performs the `BVal` and `Aux` steps for `BinaryAgreement`.
|
||||
//!
|
||||
//! Validators input binary values, and each node outputs a set of one or two binary values.
|
||||
//! These outputs are not necessarily the same in each node, but it is guaranteed that whenever two
|
|
@ -63,9 +63,9 @@ pub enum FaultKind {
|
|||
InvalidVoteSignature,
|
||||
/// A validator committed an invalid vote in `DynamicHoneyBadger`.
|
||||
InvalidCommittedVote,
|
||||
/// `Agreement` received a duplicate `BVal` message.
|
||||
/// `BinaryAgreement` received a duplicate `BVal` message.
|
||||
DuplicateBVal,
|
||||
/// `Agreement` received a duplicate `Aux` message.
|
||||
/// `BinaryAgreement` received a duplicate `Aux` message.
|
||||
DuplicateAux,
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
//!
|
||||
//! This is used in Subset to send each node's proposal to the other nodes.
|
||||
//!
|
||||
//! [**Binary Agreement**](agreement/index.html)
|
||||
//! [**Binary Agreement**](binary_agreement/index.html)
|
||||
//!
|
||||
//! Each node inputs a binary value: `true` or `false`. As output, either all correct nodes receive
|
||||
//! `true` or all correct nodes receive `false`. The output is guaranteed to be a value that was
|
||||
|
@ -137,7 +137,7 @@ extern crate serde_derive;
|
|||
pub extern crate threshold_crypto as crypto;
|
||||
extern crate tiny_keccak;
|
||||
|
||||
pub mod agreement;
|
||||
pub mod binary_agreement;
|
||||
pub mod broadcast;
|
||||
pub mod coin;
|
||||
pub mod dynamic_honey_badger;
|
||||
|
|
148
src/subset.rs
148
src/subset.rs
|
@ -17,17 +17,17 @@
|
|||
//! the element proposed by that node.
|
||||
//! * It also instantiates Binary Agreement for each participating node, to decide whether
|
||||
//! that node's proposed element should be included in the set. Whenever an element is
|
||||
//! received via broadcast, we input "yes" (`true`) into the corresponding `Agreement` instance.
|
||||
//! * When _N - f_ `Agreement` instances have decided "yes", we input "no" (`false`) into the
|
||||
//! received via broadcast, we input "yes" (`true`) into the corresponding `BinaryAgreement` instance.
|
||||
//! * When _N - f_ `BinaryAgreement` instances have decided "yes", we input "no" (`false`) into the
|
||||
//! remaining ones, where we haven't provided input yet.
|
||||
//! * Once all `Agreement` instances have decided, `Subset` returns the set of all proposed
|
||||
//! * Once all `BinaryAgreement` instances have decided, `Subset` returns the set of all proposed
|
||||
//! values for which the decision was "yes".
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
use agreement::{self, Agreement};
|
||||
use binary_agreement::{self, BinaryAgreement};
|
||||
use broadcast::{self, Broadcast};
|
||||
use fmt::HexBytes;
|
||||
use messaging::{self, DistAlgorithm, NetworkInfo};
|
||||
|
@ -37,20 +37,20 @@ use traits::NodeIdT;
|
|||
/// A subset error.
|
||||
#[derive(Clone, PartialEq, Debug, Fail)]
|
||||
pub enum Error {
|
||||
#[fail(display = "NewAgreement error: {}", _0)]
|
||||
NewAgreement(agreement::Error),
|
||||
#[fail(display = "ProcessAgreementAgreement0 error: {}", _0)]
|
||||
ProcessAgreementAgreement0(agreement::Error),
|
||||
#[fail(display = "ProcessAgreementAgreement1 error: {}", _0)]
|
||||
ProcessAgreementAgreement1(agreement::Error),
|
||||
#[fail(display = "NewBinaryAgreement error: {}", _0)]
|
||||
NewBinaryAgreement(binary_agreement::Error),
|
||||
#[fail(display = "ProcessBinaryAgreement0 error: {}", _0)]
|
||||
ProcessBinaryAgreement0(binary_agreement::Error),
|
||||
#[fail(display = "ProcessBinaryAgreement1 error: {}", _0)]
|
||||
ProcessBinaryAgreement1(binary_agreement::Error),
|
||||
#[fail(display = "NewBroadcast error: {}", _0)]
|
||||
NewBroadcast(broadcast::Error),
|
||||
#[fail(display = "ProcessBroadcastBroadcast error: {}", _0)]
|
||||
ProcessBroadcastBroadcast(broadcast::Error),
|
||||
#[fail(display = "Multiple agreement results")]
|
||||
MultipleAgreementResults,
|
||||
#[fail(display = "No such agreement instance")]
|
||||
NoSuchAgreementInstance,
|
||||
#[fail(display = "Multiple Binary Agreement results")]
|
||||
MultipleBinaryAgreementResults,
|
||||
#[fail(display = "No such Binary Agreement instance")]
|
||||
NoSuchBinaryAgreementInstance,
|
||||
#[fail(display = "No such broadcast instance")]
|
||||
NoSuchBroadcastInstance,
|
||||
}
|
||||
|
@ -66,9 +66,9 @@ type ProposedValue = Vec<u8>;
|
|||
pub enum Message<N: Rand> {
|
||||
/// A message for the broadcast algorithm concerning the set element proposed by the given node.
|
||||
Broadcast(N, broadcast::Message),
|
||||
/// A message for the agreement algorithm concerning the set element proposed by the given
|
||||
/// A message for the Binary Agreement algorithm concerning the set element proposed by the given
|
||||
/// node.
|
||||
Agreement(N, agreement::Message),
|
||||
BinaryAgreement(N, binary_agreement::Message),
|
||||
}
|
||||
|
||||
/// Subset algorithm instance
|
||||
|
@ -77,9 +77,9 @@ pub struct Subset<N: Rand> {
|
|||
/// Shared network information.
|
||||
netinfo: Arc<NetworkInfo<N>>,
|
||||
broadcast_instances: BTreeMap<N, Broadcast<N>>,
|
||||
agreement_instances: BTreeMap<N, Agreement<N>>,
|
||||
ba_instances: BTreeMap<N, BinaryAgreement<N>>,
|
||||
broadcast_results: BTreeMap<N, ProposedValue>,
|
||||
agreement_results: BTreeMap<N, bool>,
|
||||
ba_results: BTreeMap<N, bool>,
|
||||
/// Whether the instance has decided on a value.
|
||||
decided: bool,
|
||||
}
|
||||
|
@ -109,12 +109,14 @@ impl<N: NodeIdT + Rand> DistAlgorithm for Subset<N> {
|
|||
) -> Result<Step<N>> {
|
||||
match message {
|
||||
Message::Broadcast(p_id, b_msg) => self.handle_broadcast(sender_id, &p_id, b_msg),
|
||||
Message::Agreement(p_id, a_msg) => self.handle_agreement(sender_id, &p_id, a_msg),
|
||||
Message::BinaryAgreement(p_id, a_msg) => {
|
||||
self.handle_binary_agreement(sender_id, &p_id, a_msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn terminated(&self) -> bool {
|
||||
self.agreement_instances.values().all(Agreement::terminated)
|
||||
self.ba_instances.values().all(BinaryAgreement::terminated)
|
||||
}
|
||||
|
||||
fn our_id(&self) -> &Self::NodeId {
|
||||
|
@ -133,22 +135,22 @@ impl<N: NodeIdT + Rand> Subset<N> {
|
|||
);
|
||||
}
|
||||
|
||||
// Create all agreement instances.
|
||||
let mut agreement_instances: BTreeMap<N, Agreement<N>> = BTreeMap::new();
|
||||
// Create all Binary Agreement instances.
|
||||
let mut ba_instances: BTreeMap<N, BinaryAgreement<N>> = BTreeMap::new();
|
||||
for proposer_id in netinfo.all_ids() {
|
||||
agreement_instances.insert(
|
||||
ba_instances.insert(
|
||||
proposer_id.clone(),
|
||||
Agreement::new(netinfo.clone(), session_id, proposer_id.clone())
|
||||
.map_err(Error::NewAgreement)?,
|
||||
BinaryAgreement::new(netinfo.clone(), session_id, proposer_id.clone())
|
||||
.map_err(Error::NewBinaryAgreement)?,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Subset {
|
||||
netinfo,
|
||||
broadcast_instances,
|
||||
agreement_instances,
|
||||
ba_instances,
|
||||
broadcast_results: BTreeMap::new(),
|
||||
agreement_results: BTreeMap::new(),
|
||||
ba_results: BTreeMap::new(),
|
||||
decided: false,
|
||||
})
|
||||
}
|
||||
|
@ -180,17 +182,17 @@ impl<N: NodeIdT + Rand> Subset<N> {
|
|||
self.process_broadcast(proposer_id, |bc| bc.handle_message(sender_id, bmessage))
|
||||
}
|
||||
|
||||
/// Receives an agreement message from a remote node `sender_id` concerning
|
||||
/// Receives a Binary Agreement message from a remote node `sender_id` concerning
|
||||
/// a value proposed by the node `proposer_id`.
|
||||
fn handle_agreement(
|
||||
fn handle_binary_agreement(
|
||||
&mut self,
|
||||
sender_id: &N,
|
||||
proposer_id: &N,
|
||||
amessage: agreement::Message,
|
||||
amessage: binary_agreement::Message,
|
||||
) -> Result<Step<N>> {
|
||||
// Send the message to the local instance of Agreement
|
||||
self.process_agreement(proposer_id, |agreement| {
|
||||
agreement.handle_message(sender_id, amessage)
|
||||
// Send the message to the local instance of Binary Agreement.
|
||||
self.process_binary_agreement(proposer_id, |binary_agreement| {
|
||||
binary_agreement.handle_message(sender_id, amessage)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -218,35 +220,35 @@ impl<N: NodeIdT + Rand> Subset<N> {
|
|||
}
|
||||
};
|
||||
self.broadcast_results.insert(proposer_id.clone(), value);
|
||||
let set_agreement_input = |agreement: &mut Agreement<N>| {
|
||||
if agreement.accepts_input() {
|
||||
agreement.handle_input(true)
|
||||
let set_binary_agreement_input = |ba: &mut BinaryAgreement<N>| {
|
||||
if ba.accepts_input() {
|
||||
ba.handle_input(true)
|
||||
} else {
|
||||
Ok(agreement::Step::default())
|
||||
Ok(binary_agreement::Step::default())
|
||||
}
|
||||
};
|
||||
step.extend(self.process_agreement(proposer_id, set_agreement_input)?);
|
||||
step.extend(self.process_binary_agreement(proposer_id, set_binary_agreement_input)?);
|
||||
Ok(step)
|
||||
}
|
||||
|
||||
/// Callback to be invoked on receipt of the decision value of the Agreement
|
||||
/// Callback to be invoked on receipt of the decision value of the Binary Agreement
|
||||
/// instance `id`.
|
||||
fn process_agreement<F>(&mut self, proposer_id: &N, f: F) -> Result<Step<N>>
|
||||
fn process_binary_agreement<F>(&mut self, proposer_id: &N, f: F) -> Result<Step<N>>
|
||||
where
|
||||
F: FnOnce(&mut Agreement<N>) -> result::Result<agreement::Step<N>, agreement::Error>,
|
||||
F: FnOnce(&mut BinaryAgreement<N>) -> binary_agreement::Result<binary_agreement::Step<N>>,
|
||||
{
|
||||
let mut step = Step::default();
|
||||
let value = {
|
||||
let agreement = self
|
||||
.agreement_instances
|
||||
let binary_agreement = self
|
||||
.ba_instances
|
||||
.get_mut(proposer_id)
|
||||
.ok_or(Error::NoSuchAgreementInstance)?;
|
||||
if agreement.terminated() {
|
||||
.ok_or(Error::NoSuchBinaryAgreementInstance)?;
|
||||
if binary_agreement.terminated() {
|
||||
return Ok(step);
|
||||
}
|
||||
let to_msg = |a_msg| Message::Agreement(proposer_id.clone(), a_msg);
|
||||
let to_msg = |a_msg| Message::BinaryAgreement(proposer_id.clone(), a_msg);
|
||||
let output = step.extend_with(
|
||||
f(agreement).map_err(Error::ProcessAgreementAgreement0)?,
|
||||
f(binary_agreement).map_err(Error::ProcessBinaryAgreement0)?,
|
||||
to_msg,
|
||||
);
|
||||
if let Some(output) = output.into_iter().next() {
|
||||
|
@ -255,69 +257,68 @@ impl<N: NodeIdT + Rand> Subset<N> {
|
|||
return Ok(step);
|
||||
}
|
||||
};
|
||||
if self
|
||||
.agreement_results
|
||||
.insert(proposer_id.clone(), value)
|
||||
.is_some()
|
||||
{
|
||||
return Err(Error::MultipleAgreementResults);
|
||||
if self.ba_results.insert(proposer_id.clone(), value).is_some() {
|
||||
return Err(Error::MultipleBinaryAgreementResults);
|
||||
}
|
||||
debug!(
|
||||
"{:?} Updated Agreement results: {:?}",
|
||||
"{:?} Updated Binary Agreement results: {:?}",
|
||||
self.netinfo.our_id(),
|
||||
self.agreement_results
|
||||
self.ba_results
|
||||
);
|
||||
|
||||
if value && self.count_true() == self.netinfo.num_correct() {
|
||||
// Upon delivery of value 1 from at least N − f instances of BA, provide
|
||||
// input 0 to each instance of BA that has not yet been provided input.
|
||||
for (id, agreement) in &mut self.agreement_instances {
|
||||
if agreement.accepts_input() {
|
||||
let to_msg = |a_msg| Message::Agreement(id.clone(), a_msg);
|
||||
for (id, binary_agreement) in &mut self.ba_instances {
|
||||
if binary_agreement.accepts_input() {
|
||||
let to_msg = |a_msg| Message::BinaryAgreement(id.clone(), a_msg);
|
||||
for output in step.extend_with(
|
||||
agreement
|
||||
binary_agreement
|
||||
.handle_input(false)
|
||||
.map_err(Error::ProcessAgreementAgreement1)?,
|
||||
.map_err(Error::ProcessBinaryAgreement1)?,
|
||||
to_msg,
|
||||
) {
|
||||
if self.agreement_results.insert(id.clone(), output).is_some() {
|
||||
return Err(Error::MultipleAgreementResults);
|
||||
if self.ba_results.insert(id.clone(), output).is_some() {
|
||||
return Err(Error::MultipleBinaryAgreementResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
step.output.extend(self.try_agreement_completion());
|
||||
step.output.extend(self.try_binary_agreement_completion());
|
||||
Ok(step)
|
||||
}
|
||||
|
||||
/// Returns the number of agreement instances that have decided "yes".
|
||||
/// Returns the number of Binary Agreement instances that have decided "yes".
|
||||
fn count_true(&self) -> usize {
|
||||
self.agreement_results.values().filter(|v| **v).count()
|
||||
self.ba_results.values().filter(|v| **v).count()
|
||||
}
|
||||
|
||||
fn try_agreement_completion(&mut self) -> Option<BTreeMap<N, ProposedValue>> {
|
||||
fn try_binary_agreement_completion(&mut self) -> Option<BTreeMap<N, ProposedValue>> {
|
||||
if self.decided || self.count_true() < self.netinfo.num_correct() {
|
||||
return None;
|
||||
}
|
||||
// Once all instances of BA have completed, let C ⊂ [1..N] be
|
||||
// the indexes of each BA that delivered 1. Wait for the output
|
||||
// v_j for each RBC_j such that j∈C. Finally output ∪ j∈C v_j.
|
||||
if self.agreement_results.len() < self.netinfo.num_nodes() {
|
||||
if self.ba_results.len() < self.netinfo.num_nodes() {
|
||||
return None;
|
||||
}
|
||||
debug!(
|
||||
"{:?} All Agreement instances have terminated",
|
||||
"{:?} All Binary Agreement instances have terminated",
|
||||
self.netinfo.our_id()
|
||||
);
|
||||
// All instances of Agreement that delivered `true` (or "1" in the paper).
|
||||
// All instances of BinaryAgreement that delivered `true` (or "1" in the paper).
|
||||
let delivered_1: BTreeSet<&N> = self
|
||||
.agreement_results
|
||||
.ba_results
|
||||
.iter()
|
||||
.filter(|(_, v)| **v)
|
||||
.map(|(k, _)| k)
|
||||
.collect();
|
||||
debug!("Agreement instances that delivered 1: {:?}", delivered_1);
|
||||
debug!(
|
||||
"Binary Agreement instances that delivered 1: {:?}",
|
||||
delivered_1
|
||||
);
|
||||
|
||||
// Results of Broadcast instances in `delivered_1`
|
||||
let broadcast_results: BTreeMap<N, ProposedValue> = self
|
||||
|
@ -328,7 +329,10 @@ impl<N: NodeIdT + Rand> Subset<N> {
|
|||
.collect();
|
||||
|
||||
if delivered_1.len() == broadcast_results.len() {
|
||||
debug!("{:?} Agreement instances completed:", self.netinfo.our_id());
|
||||
debug!(
|
||||
"{:?} Binary Agreement instances completed:",
|
||||
self.netinfo.our_id()
|
||||
);
|
||||
for (id, result) in &broadcast_results {
|
||||
debug!(" {:?} → {:?}", id, HexBytes(&result));
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![deny(unused_must_use)]
|
||||
//! Tests of the Binary Byzantine Agreement protocol. Only one proposer instance
|
||||
//! Tests of the Binary Agreement protocol. Only one proposer instance
|
||||
//! is tested. Each of the nodes in the simulated network run only one instance
|
||||
//! of Agreement. This way we only test correctness of the protocol and not
|
||||
//! of Binary Agreement. This way we only test correctness of the protocol and not
|
||||
//! message dispatch between multiple proposers.
|
||||
//!
|
||||
//! There are three properties that are tested:
|
||||
|
@ -33,13 +33,13 @@ use std::sync::Arc;
|
|||
|
||||
use rand::Rng;
|
||||
|
||||
use hbbft::agreement::Agreement;
|
||||
use hbbft::binary_agreement::BinaryAgreement;
|
||||
use hbbft::messaging::NetworkInfo;
|
||||
|
||||
use network::{Adversary, MessageScheduler, NodeId, SilentAdversary, TestNetwork, TestNode};
|
||||
|
||||
fn test_agreement<A: Adversary<Agreement<NodeId>>>(
|
||||
mut network: TestNetwork<A, Agreement<NodeId>>,
|
||||
fn test_binary_agreement<A: Adversary<BinaryAgreement<NodeId>>>(
|
||||
mut network: TestNetwork<A, BinaryAgreement<NodeId>>,
|
||||
input: Option<bool>,
|
||||
) {
|
||||
let ids: Vec<NodeId> = network.nodes.keys().cloned().collect();
|
||||
|
@ -64,9 +64,9 @@ fn test_agreement<A: Adversary<Agreement<NodeId>>>(
|
|||
assert!(expected.iter().eq(network.observer.outputs()));
|
||||
}
|
||||
|
||||
fn test_agreement_different_sizes<A, F>(new_adversary: F)
|
||||
fn test_binary_agreement_different_sizes<A, F>(new_adversary: F)
|
||||
where
|
||||
A: Adversary<Agreement<NodeId>>,
|
||||
A: Adversary<BinaryAgreement<NodeId>>,
|
||||
F: Fn(usize, usize) -> A,
|
||||
{
|
||||
// This returns an error in all but the first test.
|
||||
|
@ -85,12 +85,11 @@ where
|
|||
num_good_nodes, num_faulty_nodes, input
|
||||
);
|
||||
let adversary = |_| new_adversary(num_good_nodes, num_faulty_nodes);
|
||||
let new_agreement = |netinfo: Arc<NetworkInfo<NodeId>>| {
|
||||
Agreement::new(netinfo, 0, NodeId(0)).expect("agreement instance")
|
||||
let new_ba = |netinfo: Arc<NetworkInfo<NodeId>>| {
|
||||
BinaryAgreement::new(netinfo, 0, NodeId(0)).expect("Binary Agreement instance")
|
||||
};
|
||||
let network =
|
||||
TestNetwork::new(num_good_nodes, num_faulty_nodes, adversary, new_agreement);
|
||||
test_agreement(network, input);
|
||||
let network = TestNetwork::new(num_good_nodes, num_faulty_nodes, adversary, new_ba);
|
||||
test_binary_agreement(network, input);
|
||||
info!(
|
||||
"Test success: {} good nodes and {} faulty nodes, input: {:?}",
|
||||
num_good_nodes, num_faulty_nodes, input
|
||||
|
@ -100,13 +99,13 @@ where
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_agreement_random_silent() {
|
||||
fn test_binary_agreement_random_silent() {
|
||||
let new_adversary = |_: usize, _: usize| SilentAdversary::new(MessageScheduler::Random);
|
||||
test_agreement_different_sizes(new_adversary);
|
||||
test_binary_agreement_different_sizes(new_adversary);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_agreement_first_silent() {
|
||||
fn test_binary_agreement_first_silent() {
|
||||
let new_adversary = |_: usize, _: usize| SilentAdversary::new(MessageScheduler::First);
|
||||
test_agreement_different_sizes(new_adversary);
|
||||
test_binary_agreement_different_sizes(new_adversary);
|
||||
}
|
Loading…
Reference in New Issue