2018-03-14 17:03:21 -07:00
|
|
|
|
//! Binary Byzantine agreement protocol from a common coin protocol.
|
2018-05-01 10:12:05 -07:00
|
|
|
|
|
2018-05-23 10:38:33 -07:00
|
|
|
|
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
2018-05-15 10:18:05 -07:00
|
|
|
|
use std::fmt::Debug;
|
2018-05-06 14:39:01 -07:00
|
|
|
|
use std::hash::Hash;
|
2018-05-23 15:00:19 -07:00
|
|
|
|
use std::iter::FromIterator;
|
2018-05-23 10:38:33 -07:00
|
|
|
|
use std::mem::replace;
|
2018-05-06 14:39:01 -07:00
|
|
|
|
|
2018-05-23 15:00:19 -07:00
|
|
|
|
use itertools::Itertools;
|
|
|
|
|
|
2018-05-16 14:50:23 -07:00
|
|
|
|
use messaging::{DistAlgorithm, Target, TargetedMessage};
|
2018-05-08 09:25:57 -07:00
|
|
|
|
|
2018-05-20 04:51:33 -07:00
|
|
|
|
error_chain!{
|
|
|
|
|
types {
|
|
|
|
|
Error, ErrorKind, ResultExt, AgreementResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errors {
|
|
|
|
|
InputNotAccepted
|
|
|
|
|
Terminated
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 15:00:19 -07:00
|
|
|
|
/// A lattice-valued description of the state of `bin_values`, essentially the same as the set of
|
|
|
|
|
/// subsets of `bool`.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
#[cfg_attr(feature = "serialization-serde", derive(Serialize))]
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
|
pub enum BinValues {
|
|
|
|
|
None,
|
|
|
|
|
False,
|
|
|
|
|
True,
|
|
|
|
|
Both,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BinValues {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
BinValues::None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
|
replace(self, BinValues::None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn single(b: bool) -> Self {
|
|
|
|
|
if b {
|
|
|
|
|
BinValues::True
|
|
|
|
|
} else {
|
|
|
|
|
BinValues::False
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Inserts a boolean value into the `BinValues` and returns true iff the `BinValues` has
|
|
|
|
|
/// changed as a result.
|
|
|
|
|
pub fn insert(&mut self, b: bool) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
BinValues::None => {
|
|
|
|
|
replace(self, BinValues::single(b));
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
BinValues::False if b => {
|
|
|
|
|
replace(self, BinValues::Both);
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
BinValues::True if !b => {
|
|
|
|
|
replace(self, BinValues::Both);
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 15:00:19 -07:00
|
|
|
|
pub fn union(&mut self, other: BinValues) {
|
|
|
|
|
match self {
|
|
|
|
|
BinValues::None => {
|
|
|
|
|
replace(self, other);
|
|
|
|
|
}
|
|
|
|
|
BinValues::False if other == BinValues::True => {
|
|
|
|
|
replace(self, BinValues::Both);
|
|
|
|
|
}
|
|
|
|
|
BinValues::True if other == BinValues::False => {
|
|
|
|
|
replace(self, BinValues::Both);
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 10:38:33 -07:00
|
|
|
|
pub fn contains(&self, b: bool) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
BinValues::None => false,
|
|
|
|
|
BinValues::Both => true,
|
|
|
|
|
BinValues::False if !b => true,
|
|
|
|
|
BinValues::True if b => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_subset(&self, other: &BinValues) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
BinValues::None => true,
|
|
|
|
|
BinValues::False if *other == BinValues::False || *other == BinValues::Both => true,
|
|
|
|
|
BinValues::True if *other == BinValues::True || *other == BinValues::Both => true,
|
|
|
|
|
BinValues::Both if *other == BinValues::Both => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn definite(&self) -> Option<bool> {
|
|
|
|
|
match self {
|
|
|
|
|
BinValues::False => Some(false),
|
|
|
|
|
BinValues::True => Some(true),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 15:00:19 -07:00
|
|
|
|
impl FromIterator<BinValues> for BinValues {
|
|
|
|
|
fn from_iter<I: IntoIterator<Item = BinValues>>(iter: I) -> Self {
|
|
|
|
|
let mut v = BinValues::new();
|
|
|
|
|
|
|
|
|
|
for i in iter {
|
|
|
|
|
v.union(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-08 09:25:57 -07:00
|
|
|
|
/// Messages sent during the binary Byzantine agreement stage.
|
2018-05-14 09:30:07 -07:00
|
|
|
|
#[cfg_attr(feature = "serialization-serde", derive(Serialize))]
|
2018-05-08 09:25:57 -07:00
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
|
pub enum AgreementMessage {
|
|
|
|
|
/// BVAL message with an epoch.
|
2018-05-15 07:15:41 -07:00
|
|
|
|
BVal(u32, bool),
|
2018-05-08 09:25:57 -07:00
|
|
|
|
/// AUX message with an epoch.
|
2018-05-15 07:15:41 -07:00
|
|
|
|
Aux(u32, bool),
|
2018-05-23 10:38:33 -07:00
|
|
|
|
/// CONF message with an epoch.
|
|
|
|
|
Conf(u32, BinValues),
|
2018-05-08 09:25:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 08:38:45 -07:00
|
|
|
|
impl AgreementMessage {
|
|
|
|
|
fn epoch(&self) -> u32 {
|
|
|
|
|
match *self {
|
|
|
|
|
AgreementMessage::BVal(epoch, _) => epoch,
|
|
|
|
|
AgreementMessage::Aux(epoch, _) => epoch,
|
2018-05-23 10:38:33 -07:00
|
|
|
|
AgreementMessage::Conf(epoch, _) => epoch,
|
2018-05-17 08:38:45 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-15 10:18:05 -07:00
|
|
|
|
/// Binary Agreement instance
|
2018-05-06 14:39:01 -07:00
|
|
|
|
pub struct Agreement<NodeUid> {
|
2018-05-19 08:10:30 -07:00
|
|
|
|
/// This node's ID.
|
2018-05-06 14:39:01 -07:00
|
|
|
|
uid: NodeUid,
|
|
|
|
|
num_nodes: usize,
|
|
|
|
|
num_faulty_nodes: usize,
|
|
|
|
|
epoch: u32,
|
|
|
|
|
/// Bin values. Reset on every epoch update.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
bin_values: BinValues,
|
2018-05-06 14:39:01 -07:00
|
|
|
|
/// Values received in BVAL messages. Reset on every epoch update.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
received_bval: BTreeMap<NodeUid, BTreeSet<bool>>,
|
2018-05-06 14:39:01 -07:00
|
|
|
|
/// Sent BVAL values. Reset on every epoch update.
|
|
|
|
|
sent_bval: BTreeSet<bool>,
|
|
|
|
|
/// Values received in AUX messages. Reset on every epoch update.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
received_aux: BTreeMap<NodeUid, bool>,
|
|
|
|
|
/// Received CONF messages. Reset on every epoch update.
|
|
|
|
|
received_conf: BTreeMap<NodeUid, BinValues>,
|
2018-05-10 04:09:22 -07:00
|
|
|
|
/// The estimate of the decision value in the current epoch.
|
|
|
|
|
estimated: Option<bool>,
|
|
|
|
|
/// The value output by the agreement instance. It is set once to `Some(b)`
|
|
|
|
|
/// and then never changed. That is, no instance of Binary Agreement can
|
|
|
|
|
/// decide on two different values of output.
|
|
|
|
|
output: Option<bool>,
|
2018-05-17 02:43:56 -07:00
|
|
|
|
/// A permanent, latching copy of the output value. This copy is required because `output` can
|
|
|
|
|
/// be consumed using `DistAlgorithm::next_output` immediately after the instance finishing to
|
|
|
|
|
/// handle a message, in which case it would otherwise be unknown whether the output value was
|
|
|
|
|
/// ever there at all. While the output value will still be required in a later epoch to decide
|
|
|
|
|
/// the termination state.
|
|
|
|
|
decision: Option<bool>,
|
2018-05-17 08:38:45 -07:00
|
|
|
|
/// A cache for messages for future epochs that cannot be handled yet.
|
|
|
|
|
// TODO: Find a better solution for this; defend against spam.
|
|
|
|
|
incoming_queue: Vec<(NodeUid, AgreementMessage)>,
|
2018-05-09 07:27:31 -07:00
|
|
|
|
/// Termination flag. The Agreement instance doesn't terminate immediately
|
|
|
|
|
/// upon deciding on the agreed value. This is done in order to help other
|
|
|
|
|
/// nodes decide despite asynchrony of communication. Once the instance
|
|
|
|
|
/// determines that all the remote nodes have reached agreement, it sets the
|
|
|
|
|
/// `terminated` flag and accepts no more incoming messages.
|
2018-05-06 14:39:01 -07:00
|
|
|
|
terminated: bool,
|
2018-05-16 14:50:23 -07:00
|
|
|
|
/// The outgoing message queue.
|
|
|
|
|
messages: VecDeque<AgreementMessage>,
|
2018-05-23 15:00:19 -07:00
|
|
|
|
/// The CONF message value in the current epoch.
|
|
|
|
|
sent_conf: Option<BinValues>,
|
2018-05-16 14:50:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<NodeUid: Clone + Debug + Eq + Hash + Ord> DistAlgorithm for Agreement<NodeUid> {
|
|
|
|
|
type NodeUid = NodeUid;
|
|
|
|
|
type Input = bool;
|
|
|
|
|
type Output = bool;
|
|
|
|
|
type Message = AgreementMessage;
|
|
|
|
|
type Error = Error;
|
|
|
|
|
|
2018-05-20 04:51:33 -07:00
|
|
|
|
fn input(&mut self, input: Self::Input) -> AgreementResult<()> {
|
2018-05-16 14:50:23 -07:00
|
|
|
|
self.set_input(input)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Receive input from a remote node.
|
|
|
|
|
fn handle_message(
|
|
|
|
|
&mut self,
|
|
|
|
|
sender_id: &Self::NodeUid,
|
|
|
|
|
message: Self::Message,
|
2018-05-20 04:51:33 -07:00
|
|
|
|
) -> AgreementResult<()> {
|
2018-05-17 08:38:45 -07:00
|
|
|
|
if self.terminated {
|
2018-05-20 04:51:33 -07:00
|
|
|
|
return Err(ErrorKind::Terminated.into());
|
2018-05-17 08:38:45 -07:00
|
|
|
|
}
|
|
|
|
|
if message.epoch() < self.epoch {
|
|
|
|
|
return Ok(()); // Message is obsolete: We are already in a later epoch.
|
|
|
|
|
}
|
|
|
|
|
if message.epoch() > self.epoch {
|
|
|
|
|
// Message is for a later epoch. We can't handle that yet.
|
|
|
|
|
self.incoming_queue.push((sender_id.clone(), message));
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
2018-05-16 14:50:23 -07:00
|
|
|
|
match message {
|
2018-05-17 08:38:45 -07:00
|
|
|
|
AgreementMessage::BVal(_, b) => self.handle_bval(sender_id, b),
|
|
|
|
|
AgreementMessage::Aux(_, b) => self.handle_aux(sender_id, b),
|
2018-05-23 10:38:33 -07:00
|
|
|
|
AgreementMessage::Conf(_, v) => self.handle_conf(sender_id, v),
|
2018-05-16 14:50:23 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Take the next Agreement message for multicast to all other nodes.
|
|
|
|
|
fn next_message(&mut self) -> Option<TargetedMessage<Self::Message, Self::NodeUid>> {
|
|
|
|
|
self.messages
|
|
|
|
|
.pop_front()
|
|
|
|
|
.map(|msg| Target::All.message(msg))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Consume the output. Once consumed, the output stays `None` forever.
|
|
|
|
|
fn next_output(&mut self) -> Option<Self::Output> {
|
|
|
|
|
self.output.take()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Whether the algorithm has terminated.
|
|
|
|
|
fn terminated(&self) -> bool {
|
|
|
|
|
self.terminated
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn our_id(&self) -> &Self::NodeUid {
|
|
|
|
|
&self.uid
|
|
|
|
|
}
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 08:38:45 -07:00
|
|
|
|
impl<NodeUid: Clone + Debug + Eq + Hash + Ord> Agreement<NodeUid> {
|
2018-05-06 14:39:01 -07:00
|
|
|
|
pub fn new(uid: NodeUid, num_nodes: usize) -> Self {
|
|
|
|
|
let num_faulty_nodes = (num_nodes - 1) / 3;
|
|
|
|
|
|
2018-05-03 01:05:26 -07:00
|
|
|
|
Agreement {
|
2018-05-06 14:39:01 -07:00
|
|
|
|
uid,
|
|
|
|
|
num_nodes,
|
|
|
|
|
num_faulty_nodes,
|
|
|
|
|
epoch: 0,
|
2018-05-23 10:38:33 -07:00
|
|
|
|
bin_values: BinValues::new(),
|
|
|
|
|
received_bval: BTreeMap::new(),
|
2018-05-06 14:39:01 -07:00
|
|
|
|
sent_bval: BTreeSet::new(),
|
2018-05-23 10:38:33 -07:00
|
|
|
|
received_aux: BTreeMap::new(),
|
|
|
|
|
received_conf: BTreeMap::new(),
|
2018-05-10 04:09:22 -07:00
|
|
|
|
estimated: None,
|
|
|
|
|
output: None,
|
2018-05-17 02:43:56 -07:00
|
|
|
|
decision: None,
|
2018-05-17 08:38:45 -07:00
|
|
|
|
incoming_queue: Vec::new(),
|
2018-05-06 14:39:01 -07:00
|
|
|
|
terminated: false,
|
2018-05-16 14:50:23 -07:00
|
|
|
|
messages: VecDeque::new(),
|
2018-05-23 15:00:19 -07:00
|
|
|
|
sent_conf: None,
|
2018-05-03 01:05:26 -07:00
|
|
|
|
}
|
2018-05-02 06:10:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 04:09:22 -07:00
|
|
|
|
/// Sets the input value for agreement.
|
2018-05-20 04:51:33 -07:00
|
|
|
|
pub fn set_input(&mut self, input: bool) -> AgreementResult<()> {
|
2018-05-16 14:50:23 -07:00
|
|
|
|
if self.epoch != 0 || self.estimated.is_some() {
|
2018-05-20 04:51:33 -07:00
|
|
|
|
return Err(ErrorKind::InputNotAccepted.into());
|
2018-05-09 07:27:31 -07:00
|
|
|
|
}
|
2018-05-17 08:38:45 -07:00
|
|
|
|
if self.num_nodes == 1 {
|
|
|
|
|
self.decision = Some(input);
|
|
|
|
|
self.output = Some(input);
|
|
|
|
|
self.terminated = true;
|
|
|
|
|
}
|
2018-05-09 07:27:31 -07:00
|
|
|
|
|
|
|
|
|
// Set the initial estimated value to the input value.
|
2018-05-10 04:09:22 -07:00
|
|
|
|
self.estimated = Some(input);
|
2018-05-15 07:16:07 -07:00
|
|
|
|
// Record the input value as sent.
|
2018-05-17 08:38:45 -07:00
|
|
|
|
self.send_bval(input)
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
2018-05-02 03:57:28 -07:00
|
|
|
|
|
2018-05-10 04:09:22 -07:00
|
|
|
|
/// Acceptance check to be performed before setting the input value.
|
|
|
|
|
pub fn accepts_input(&self) -> bool {
|
|
|
|
|
self.epoch == 0 && self.estimated.is_none()
|
2018-05-02 03:57:28 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-20 04:51:33 -07:00
|
|
|
|
fn handle_bval(&mut self, sender_id: &NodeUid, b: bool) -> AgreementResult<()> {
|
2018-05-08 09:25:57 -07:00
|
|
|
|
self.received_bval
|
2018-05-10 04:09:22 -07:00
|
|
|
|
.entry(sender_id.clone())
|
2018-05-08 09:25:57 -07:00
|
|
|
|
.or_insert_with(BTreeSet::new)
|
|
|
|
|
.insert(b);
|
2018-05-21 02:01:49 -07:00
|
|
|
|
let count_bval = self
|
|
|
|
|
.received_bval
|
2018-05-08 09:25:57 -07:00
|
|
|
|
.values()
|
|
|
|
|
.filter(|values| values.contains(&b))
|
|
|
|
|
.count();
|
|
|
|
|
|
|
|
|
|
// upon receiving BVAL_r(b) messages from 2f + 1 nodes,
|
|
|
|
|
// bin_values_r := bin_values_r ∪ {b}
|
|
|
|
|
if count_bval == 2 * self.num_faulty_nodes + 1 {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
let previous_bin_values = self.bin_values.clone();
|
2018-05-23 15:00:19 -07:00
|
|
|
|
let _bin_values_changed = self.bin_values.insert(b);
|
2018-05-07 02:59:14 -07:00
|
|
|
|
|
2018-05-08 09:25:57 -07:00
|
|
|
|
// wait until bin_values_r != 0, then multicast AUX_r(w)
|
|
|
|
|
// where w ∈ bin_values_r
|
2018-05-23 10:38:33 -07:00
|
|
|
|
if previous_bin_values == BinValues::None {
|
2018-05-08 09:25:57 -07:00
|
|
|
|
// Send an AUX message at most once per epoch.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.send_aux(b)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(())
|
2018-05-06 14:39:01 -07:00
|
|
|
|
}
|
2018-05-19 05:29:31 -07:00
|
|
|
|
} else if count_bval == self.num_faulty_nodes + 1 && !self.sent_bval.contains(&b) {
|
|
|
|
|
// upon receiving BVAL_r(b) messages from f + 1 nodes, if
|
|
|
|
|
// BVAL_r(b) has not been sent, multicast BVAL_r(b)
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.send_bval(b)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(())
|
2018-05-08 09:25:57 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-06 14:39:01 -07:00
|
|
|
|
|
2018-05-20 04:51:33 -07:00
|
|
|
|
fn send_bval(&mut self, b: bool) -> AgreementResult<()> {
|
2018-05-17 08:38:45 -07:00
|
|
|
|
// Record the value `b` as sent.
|
|
|
|
|
self.sent_bval.insert(b);
|
|
|
|
|
// Multicast BVAL.
|
|
|
|
|
self.messages
|
|
|
|
|
.push_back(AgreementMessage::BVal(self.epoch, b));
|
|
|
|
|
// Receive the BVAL message locally.
|
|
|
|
|
let our_uid = self.uid.clone();
|
|
|
|
|
self.handle_bval(&our_uid, b)
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 10:38:33 -07:00
|
|
|
|
fn send_conf(&mut self) -> AgreementResult<()> {
|
2018-05-23 15:00:19 -07:00
|
|
|
|
if self.sent_conf.is_some() {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
// Only one CONF message is allowed in an epoch.
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let v = &self.bin_values.clone();
|
|
|
|
|
// Multicast CONF.
|
|
|
|
|
self.messages
|
|
|
|
|
.push_back(AgreementMessage::Conf(self.epoch, v.clone()));
|
2018-05-23 15:00:19 -07:00
|
|
|
|
// Trigger the start of the CONF round.
|
|
|
|
|
self.sent_conf = Some(v.clone());
|
2018-05-23 10:38:33 -07:00
|
|
|
|
// Receive the CONF message locally.
|
|
|
|
|
let our_uid = self.uid.clone();
|
|
|
|
|
self.handle_conf(&our_uid, v.clone())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Waits until at least (N − f) AUX_r messages have been received, such that
|
|
|
|
|
/// the set of values carried by these messages, vals, are a subset of
|
|
|
|
|
/// bin_values_r (note that bin_values_r may continue to change as BVAL_r
|
|
|
|
|
/// messages are received, thus this condition may be triggered upon arrival
|
|
|
|
|
/// of either an AUX_r or a BVAL_r message).
|
2018-05-20 04:51:33 -07:00
|
|
|
|
fn handle_aux(&mut self, sender_id: &NodeUid, b: bool) -> AgreementResult<()> {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
// Perform the AUX message round only if a CONF round hasn't started yet.
|
2018-05-23 15:00:19 -07:00
|
|
|
|
if self.sent_conf.is_some() {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
2018-05-10 04:09:22 -07:00
|
|
|
|
self.received_aux.insert(sender_id.clone(), b);
|
2018-05-23 10:38:33 -07:00
|
|
|
|
if self.bin_values == BinValues::None {
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
2018-05-23 15:00:19 -07:00
|
|
|
|
if self.count_aux().0 < self.num_nodes - self.num_faulty_nodes {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
// Continue waiting for the (N - f) AUX messages.
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
// Start the CONF message round.
|
|
|
|
|
self.send_conf()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn handle_conf(&mut self, sender_id: &NodeUid, v: BinValues) -> AgreementResult<()> {
|
|
|
|
|
self.received_conf.insert(sender_id.clone(), v);
|
2018-05-23 15:00:19 -07:00
|
|
|
|
if let Some(sent_conf) = self.sent_conf.clone() {
|
|
|
|
|
let (count_vals, vals) = self.count_conf(&sent_conf);
|
|
|
|
|
if count_vals < self.num_nodes - self.num_faulty_nodes {
|
|
|
|
|
// Continue waiting for (N - f) CONF messages
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
self.invoke_coin(vals)
|
|
|
|
|
} else {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
2018-05-19 05:29:31 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-20 04:51:33 -07:00
|
|
|
|
fn send_aux(&mut self, b: bool) -> AgreementResult<()> {
|
2018-05-19 05:29:31 -07:00
|
|
|
|
// Multicast AUX.
|
|
|
|
|
self.messages
|
|
|
|
|
.push_back(AgreementMessage::Aux(self.epoch, b));
|
|
|
|
|
// Receive the AUX message locally.
|
|
|
|
|
let our_uid = self.uid.clone();
|
|
|
|
|
self.handle_aux(&our_uid, b)
|
2018-05-02 03:57:28 -07:00
|
|
|
|
}
|
2018-05-06 14:39:01 -07:00
|
|
|
|
|
2018-05-23 10:38:33 -07:00
|
|
|
|
/// The count of AUX_r messages such that the set of values carried by those messages is a
|
|
|
|
|
/// subset of bin_values_r. Outputs this subset.
|
2018-05-06 14:39:01 -07:00
|
|
|
|
///
|
|
|
|
|
/// FIXME: Clarify whether the values of AUX messages should be the same or
|
|
|
|
|
/// not. It is assumed in `count_aux` that they can differ.
|
2018-05-08 09:25:57 -07:00
|
|
|
|
///
|
|
|
|
|
/// In general, we can't expect every good node to send the same AUX value,
|
|
|
|
|
/// so waiting for N - f agreeing messages would not always terminate. We
|
|
|
|
|
/// can, however, expect every good node to send an AUX value that will
|
|
|
|
|
/// eventually end up in our bin_values.
|
2018-05-23 15:00:19 -07:00
|
|
|
|
fn count_aux(&self) -> (usize, BinValues) {
|
|
|
|
|
let (vals_cnt, vals) = self
|
|
|
|
|
.received_aux
|
2018-05-09 08:39:45 -07:00
|
|
|
|
.values()
|
2018-05-23 10:38:33 -07:00
|
|
|
|
.filter(|&&b| self.bin_values.contains(b))
|
2018-05-23 15:00:19 -07:00
|
|
|
|
.map(|&b| BinValues::single(b))
|
|
|
|
|
.tee();
|
|
|
|
|
|
|
|
|
|
(vals_cnt.count(), vals.collect())
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
2018-05-10 01:57:58 -07:00
|
|
|
|
|
2018-05-23 15:00:19 -07:00
|
|
|
|
/// Counts the number of received CONF messages. The argument `sent_conf` contains the values
|
|
|
|
|
/// committed committed previously at the start of the CONF round.
|
|
|
|
|
fn count_conf(&self, sent_conf: &BinValues) -> (usize, BinValues) {
|
|
|
|
|
let (vals_cnt, vals) = self
|
|
|
|
|
.received_conf
|
2018-05-23 10:38:33 -07:00
|
|
|
|
.values()
|
2018-05-23 15:00:19 -07:00
|
|
|
|
.filter(|&conf| conf.is_subset(sent_conf))
|
|
|
|
|
.tee();
|
|
|
|
|
|
|
|
|
|
(vals_cnt.count(), vals.cloned().collect())
|
2018-05-06 14:39:01 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 10:38:33 -07:00
|
|
|
|
fn start_next_epoch(&mut self) {
|
|
|
|
|
self.bin_values.clear();
|
|
|
|
|
self.received_bval.clear();
|
|
|
|
|
self.sent_bval.clear();
|
|
|
|
|
self.received_aux.clear();
|
|
|
|
|
self.received_conf.clear();
|
2018-05-23 15:00:19 -07:00
|
|
|
|
self.sent_conf = None;
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.epoch += 1;
|
|
|
|
|
}
|
2018-05-09 07:27:31 -07:00
|
|
|
|
|
2018-05-23 10:38:33 -07:00
|
|
|
|
/// Gets a common coin and uses it to compute the next decision estimate and outputs the
|
|
|
|
|
/// optional decision value. The function may start the next epoch. In that case, it also
|
|
|
|
|
/// returns a message for broadcast.
|
2018-05-23 15:00:19 -07:00
|
|
|
|
fn invoke_coin(&mut self, vals: BinValues) -> AgreementResult<()> {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
debug!("{:?} invoke_coin in epoch {}", self.uid, self.epoch);
|
2018-05-09 07:27:31 -07:00
|
|
|
|
// FIXME: Implement the Common Coin algorithm. At the moment the
|
|
|
|
|
// coin value is common across different nodes but not random.
|
2018-05-10 04:09:22 -07:00
|
|
|
|
let coin = (self.epoch % 2) == 0;
|
2018-05-09 07:27:31 -07:00
|
|
|
|
|
|
|
|
|
// Check the termination condition: "continue looping until both a
|
|
|
|
|
// value b is output in some round r, and the value Coin_r' = b for
|
|
|
|
|
// some round r' > r."
|
2018-05-17 02:43:56 -07:00
|
|
|
|
self.terminated = self.terminated || self.decision == Some(coin);
|
2018-05-15 10:18:05 -07:00
|
|
|
|
if self.terminated {
|
|
|
|
|
debug!("Agreement instance {:?} terminated", self.uid);
|
2018-05-17 08:38:45 -07:00
|
|
|
|
return Ok(());
|
2018-05-15 10:18:05 -07:00
|
|
|
|
}
|
2018-05-09 07:27:31 -07:00
|
|
|
|
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.start_next_epoch();
|
2018-05-15 14:25:41 -07:00
|
|
|
|
debug!(
|
|
|
|
|
"Agreement instance {:?} started epoch {}",
|
|
|
|
|
self.uid, self.epoch
|
|
|
|
|
);
|
2018-05-09 07:27:31 -07:00
|
|
|
|
|
2018-05-23 15:00:19 -07:00
|
|
|
|
if let Some(b) = vals.definite() {
|
2018-05-10 04:33:01 -07:00
|
|
|
|
self.estimated = Some(b);
|
2018-05-10 04:44:33 -07:00
|
|
|
|
// Outputting a value is allowed only once.
|
2018-05-17 03:36:09 -07:00
|
|
|
|
if self.decision.is_none() && b == coin {
|
2018-05-10 04:33:01 -07:00
|
|
|
|
// Output the agreement value.
|
|
|
|
|
self.output = Some(b);
|
2018-05-17 03:36:09 -07:00
|
|
|
|
// Latch the decided state.
|
|
|
|
|
self.decision = Some(b);
|
2018-05-15 10:18:05 -07:00
|
|
|
|
debug!("Agreement instance {:?} output: {}", self.uid, b);
|
2018-05-10 04:33:01 -07:00
|
|
|
|
}
|
2018-05-23 10:38:33 -07:00
|
|
|
|
} else {
|
|
|
|
|
self.estimated = Some(coin);
|
|
|
|
|
}
|
2018-05-10 04:44:33 -07:00
|
|
|
|
|
2018-05-15 07:15:41 -07:00
|
|
|
|
let b = self.estimated.unwrap();
|
2018-05-17 08:38:45 -07:00
|
|
|
|
self.send_bval(b)?;
|
2018-05-23 10:38:33 -07:00
|
|
|
|
let queued_msgs = replace(&mut self.incoming_queue, Vec::new());
|
2018-05-17 08:38:45 -07:00
|
|
|
|
for (sender_id, msg) in queued_msgs {
|
|
|
|
|
self.handle_message(&sender_id, msg)?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
2018-05-06 14:39:01 -07:00
|
|
|
|
}
|
|
|
|
|
}
|