2018-07-02 06:42:31 -07:00
|
|
|
|
//! # Binary Byzantine agreement protocol
|
|
|
|
|
//!
|
|
|
|
|
//! 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
|
|
|
|
|
//! node, and all correct nodes will have the same output.
|
|
|
|
|
//!
|
|
|
|
|
//! ## How it works
|
|
|
|
|
//!
|
|
|
|
|
//! The algorithm proceeds in _epochs_, and the number of epochs it takes until it terminates is
|
|
|
|
|
//! unbounded in theory but has a finite expected value. Each node keeps track of an _estimate_
|
|
|
|
|
//! value `e`, which is initialized to the node's own input. Let's call a value `v`
|
|
|
|
|
//! that has been input by at least one correct node and such that `!v` hasn't been _output_ by any
|
|
|
|
|
//! correct node yet, a _viable output_. The estimate will always be a viable output.
|
|
|
|
|
//!
|
|
|
|
|
//! All messages are annotated with the epoch they belong to, but we omit that here for brevity.
|
|
|
|
|
//!
|
|
|
|
|
//! * At the beginning of each epoch, we multicast `BVal(e)`. It translates to: "I know that `e` is
|
|
|
|
|
//! a viable output."
|
|
|
|
|
//!
|
|
|
|
|
//! * Once we receive `BVal(v)` with the same value from _f + 1_ different validators, we know that
|
|
|
|
|
//! at least one of them must be correct. So we know that `v` is a viable output. If we haven't
|
|
|
|
|
//! done so already we multicast `BVal(v)`. (Even if we already multicast `BVal(!v)`).
|
|
|
|
|
//!
|
|
|
|
|
//! * Let's say a node _believes in `v`_ if it received `BVal(v)` from _2 f + 1_ validators.
|
|
|
|
|
//! For the _first_ value `v` we believe in, we multicast `Aux(v)`. It translates to:
|
|
|
|
|
//! "I know that all correct nodes will eventually know that `v` is a viable output.
|
|
|
|
|
//! I'm not sure about `!v` yet."
|
|
|
|
|
//!
|
|
|
|
|
//! * Since every node will receive at least _2 f + 1_ `BVal` messages from correct validators,
|
|
|
|
|
//! there is at least one value `v`, such that every node receives _f + 1_ `BVal(v)` messages.
|
|
|
|
|
//! As a consequence, every correct validator will multicast `BVal(v)` itself. Hence we are
|
|
|
|
|
//! guaranteed to receive _2 f + 1_ `BVal(v)` messages.
|
|
|
|
|
//! In short: If _any_ correct node believes in `v`, _every_ correct node will.
|
|
|
|
|
//!
|
|
|
|
|
//! * Every correct node will eventually send exactly one `Aux`, so we will receive at least
|
|
|
|
|
//! _2 f + 1_ `Aux` messages with values we believe in. At that point, we define the set `vals`
|
|
|
|
|
//! of _candidate values_: the set of values we believe in _and_ have received in an `Aux`.
|
|
|
|
|
//!
|
|
|
|
|
//! * Once we have the set of candidate values, we obtain a _coin value_ `s` (see below).
|
|
|
|
|
//!
|
|
|
|
|
//! * If there is only a single candidate value `b`, we set our estimate `e = b`. If `s == b`,
|
|
|
|
|
//! we _output_ and send a `Term(b)` message which is interpreted as `BVal(b)` and `Aux(b)` for
|
|
|
|
|
//! all future epochs. If `s != b`, we just proceed to the next epoch.
|
|
|
|
|
//!
|
|
|
|
|
//! * If both values are candidates, we set `e = s` and proceed to the next epoch.
|
|
|
|
|
//!
|
|
|
|
|
//! In epochs that are 0 modulo 3, the value `s` is `true`. In 1 modulo 3, it is `false`. In the
|
2018-07-05 08:51:55 -07:00
|
|
|
|
//! case 2 modulo 3, we flip a common coin to determine a pseudorandom `s`.
|
2018-07-02 06:42:31 -07:00
|
|
|
|
//!
|
|
|
|
|
//! An adversary that knows each coin value, controls a few validators and controls network
|
|
|
|
|
//! scheduling can delay the delivery of `Aux` and `BVal` messages to influence which candidate
|
|
|
|
|
//! values the nodes will end up with. In some circumstances that allows them to stall the network.
|
|
|
|
|
//! This is even true if the coin is flipped too early: the adversary must not learn about the coin
|
|
|
|
|
//! value early enough to delay enough `Aux` messages. That's why in the third case, the value `s`
|
|
|
|
|
//! is determined as follows:
|
|
|
|
|
//!
|
|
|
|
|
//! * We multicast a `Conf` message containing our candidate values.
|
|
|
|
|
//!
|
|
|
|
|
//! * Since every good node believes in all values it puts into its `Conf` message, we will
|
|
|
|
|
//! eventually receive _2 f + 1_ `Conf` messages containing only values we believe in. Then we
|
|
|
|
|
//! trigger the common coin.
|
|
|
|
|
//!
|
|
|
|
|
//! * After _f + 1_ nodes have sent us their coin shares, we receive the coin output and assign it
|
|
|
|
|
//! to `s`.
|
2018-05-01 10:12:05 -07:00
|
|
|
|
|
2018-05-24 11:11:56 -07:00
|
|
|
|
pub mod bin_values;
|
2018-05-24 10:52:58 -07:00
|
|
|
|
|
2018-07-05 09:20:53 -07:00
|
|
|
|
use rand;
|
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-23 10:38:33 -07:00
|
|
|
|
use std::mem::replace;
|
2018-07-11 12:15:08 -07:00
|
|
|
|
use std::sync::Arc;
|
2018-05-06 14:39:01 -07:00
|
|
|
|
|
2018-05-23 15:00:19 -07:00
|
|
|
|
use itertools::Itertools;
|
|
|
|
|
|
2018-05-24 10:52:58 -07:00
|
|
|
|
use agreement::bin_values::BinValues;
|
2018-06-08 11:43:27 -07:00
|
|
|
|
use common_coin;
|
2018-07-10 04:23:50 -07:00
|
|
|
|
use common_coin::{CommonCoin, CommonCoinMessage, CommonCoinStep};
|
2018-07-10 14:27:18 -07:00
|
|
|
|
use fault_log::FaultLog;
|
2018-07-09 04:35:26 -07:00
|
|
|
|
use messaging::{DistAlgorithm, NetworkInfo, Step, Target, TargetedMessage};
|
2018-05-08 09:25:57 -07:00
|
|
|
|
|
2018-05-20 04:51:33 -07:00
|
|
|
|
error_chain!{
|
2018-06-08 11:43:27 -07:00
|
|
|
|
links {
|
|
|
|
|
CommonCoin(common_coin::Error, common_coin::ErrorKind);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-20 04:51:33 -07:00
|
|
|
|
types {
|
|
|
|
|
Error, ErrorKind, ResultExt, AgreementResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errors {
|
2018-06-14 05:36:48 -07:00
|
|
|
|
UnknownProposer {
|
|
|
|
|
description("unknown proposer")
|
|
|
|
|
}
|
|
|
|
|
InputNotAccepted {
|
|
|
|
|
description("input not accepted")
|
|
|
|
|
}
|
2018-05-20 04:51:33 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-20 01:21:52 -07:00
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
2018-05-24 10:52:58 -07:00
|
|
|
|
pub enum AgreementContent {
|
2018-05-28 03:54:37 -07:00
|
|
|
|
/// `BVal` message.
|
2018-05-24 10:52:58 -07:00
|
|
|
|
BVal(bool),
|
2018-05-28 03:54:37 -07:00
|
|
|
|
/// `Aux` message.
|
2018-05-24 10:52:58 -07:00
|
|
|
|
Aux(bool),
|
2018-05-28 03:54:37 -07:00
|
|
|
|
/// `Conf` message.
|
2018-05-24 10:52:58 -07:00
|
|
|
|
Conf(BinValues),
|
2018-06-07 12:06:44 -07:00
|
|
|
|
/// `Term` message.
|
|
|
|
|
Term(bool),
|
2018-06-08 11:43:27 -07:00
|
|
|
|
/// Common Coin message,
|
|
|
|
|
Coin(Box<CommonCoinMessage>),
|
2018-06-07 12:06:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AgreementContent {
|
|
|
|
|
/// Creates an message with a given epoch number.
|
|
|
|
|
pub fn with_epoch(self, epoch: u32) -> AgreementMessage {
|
|
|
|
|
AgreementMessage {
|
|
|
|
|
epoch,
|
|
|
|
|
content: self,
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-24 10:52:58 -07:00
|
|
|
|
/// Messages sent during the binary Byzantine agreement stage.
|
2018-07-05 09:20:53 -07:00
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
|
2018-05-24 10:52:58 -07:00
|
|
|
|
pub struct AgreementMessage {
|
2018-05-24 11:11:56 -07:00
|
|
|
|
pub epoch: u32,
|
|
|
|
|
pub content: AgreementContent,
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 09:20:53 -07:00
|
|
|
|
// 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 {
|
|
|
|
|
fn rand<R: rand::Rng>(rng: &mut R) -> Self {
|
|
|
|
|
let message_type = *rng
|
|
|
|
|
.choose(&["bval", "aux", "conf", "term", "coin"])
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
match message_type {
|
|
|
|
|
"bval" => AgreementContent::BVal(rand::random()),
|
|
|
|
|
"aux" => AgreementContent::Aux(rand::random()),
|
|
|
|
|
"conf" => AgreementContent::Conf(rand::random()),
|
|
|
|
|
"term" => AgreementContent::Term(rand::random()),
|
|
|
|
|
"coin" => AgreementContent::Coin(Box::new(rand::random())),
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-21 03:42:17 -07:00
|
|
|
|
/// Possible values of the common coin schedule defining the method to derive the common coin in a
|
|
|
|
|
/// given epoch: as a constant value or a distributed computation.
|
|
|
|
|
enum CoinSchedule {
|
|
|
|
|
False,
|
|
|
|
|
True,
|
|
|
|
|
Random,
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-15 10:18:05 -07:00
|
|
|
|
/// Binary Agreement instance
|
2018-06-18 07:37:07 -07:00
|
|
|
|
pub struct Agreement<NodeUid> {
|
2018-05-29 05:17:30 -07:00
|
|
|
|
/// Shared network information.
|
2018-07-11 12:15:08 -07:00
|
|
|
|
netinfo: Arc<NetworkInfo<NodeUid>>,
|
2018-06-14 04:28:38 -07:00
|
|
|
|
/// Session ID, e.g, the Honey Badger algorithm epoch.
|
|
|
|
|
session_id: u64,
|
2018-06-14 05:36:48 -07:00
|
|
|
|
/// The ID of the proposer of the value for this agreement instance.
|
|
|
|
|
proposer_id: NodeUid,
|
2018-05-29 05:17:30 -07:00
|
|
|
|
/// Agreement algorithm epoch.
|
2018-05-06 14:39:01 -07:00
|
|
|
|
epoch: u32,
|
|
|
|
|
/// Bin values. Reset on every epoch update.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
bin_values: BinValues,
|
2018-05-24 10:52:58 -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-28 03:54:37 -07:00
|
|
|
|
/// Sent `BVal` values. Reset on every epoch update.
|
2018-05-06 14:39:01 -07:00
|
|
|
|
sent_bval: BTreeSet<bool>,
|
2018-05-24 10:52:58 -07:00
|
|
|
|
/// Values received in `Aux` messages. Reset on every epoch update.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
received_aux: BTreeMap<NodeUid, bool>,
|
2018-05-28 03:54:37 -07:00
|
|
|
|
/// Received `Conf` messages. Reset on every epoch update.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
received_conf: BTreeMap<NodeUid, BinValues>,
|
2018-06-07 12:06:44 -07:00
|
|
|
|
/// Received `Term` messages. Kept throughout epoch updates.
|
|
|
|
|
received_term: BTreeMap<NodeUid, bool>,
|
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-07-10 04:23:50 -07:00
|
|
|
|
/// Termination flag. Once the instance determines that all the remote nodes have reached
|
|
|
|
|
/// agreement or have the necessary information to reach 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-24 10:52:58 -07:00
|
|
|
|
/// Whether the `Conf` message round has started in the current epoch.
|
|
|
|
|
conf_round: bool,
|
2018-06-08 11:43:27 -07:00
|
|
|
|
/// A common coin instance. It is reset on epoch update.
|
2018-06-14 02:05:05 -07:00
|
|
|
|
common_coin: CommonCoin<NodeUid, Nonce>,
|
2018-06-21 03:42:17 -07:00
|
|
|
|
/// Common coin schedule computed at the start of each epoch.
|
|
|
|
|
coin_schedule: CoinSchedule,
|
2018-05-16 14:50:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-13 14:53:44 -07:00
|
|
|
|
pub type AgreementStep<NodeUid> = Step<NodeUid, bool>;
|
2018-07-09 04:35:26 -07:00
|
|
|
|
|
2018-06-18 07:37:07 -07:00
|
|
|
|
impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for Agreement<NodeUid> {
|
2018-05-16 14:50:23 -07:00
|
|
|
|
type NodeUid = NodeUid;
|
|
|
|
|
type Input = bool;
|
|
|
|
|
type Output = bool;
|
|
|
|
|
type Message = AgreementMessage;
|
|
|
|
|
type Error = Error;
|
|
|
|
|
|
2018-07-09 04:35:26 -07:00
|
|
|
|
fn input(&mut self, input: Self::Input) -> AgreementResult<AgreementStep<NodeUid>> {
|
2018-07-10 14:27:18 -07:00
|
|
|
|
let fault_log = self.set_input(input)?;
|
|
|
|
|
self.step(fault_log)
|
2018-05-16 14:50:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Receive input from a remote node.
|
|
|
|
|
fn handle_message(
|
|
|
|
|
&mut self,
|
|
|
|
|
sender_id: &Self::NodeUid,
|
|
|
|
|
message: Self::Message,
|
2018-07-09 04:35:26 -07:00
|
|
|
|
) -> AgreementResult<AgreementStep<NodeUid>> {
|
2018-07-10 14:27:18 -07:00
|
|
|
|
let fault_log = if self.terminated || message.epoch < self.epoch {
|
2018-07-09 04:35:26 -07:00
|
|
|
|
// Message is obsolete: We are already in a later epoch or terminated.
|
2018-07-13 14:53:44 -07:00
|
|
|
|
FaultLog::new()
|
2018-07-10 14:27:18 -07:00
|
|
|
|
} else if message.epoch > self.epoch {
|
2018-05-17 08:38:45 -07:00
|
|
|
|
// Message is for a later epoch. We can't handle that yet.
|
|
|
|
|
self.incoming_queue.push((sender_id.clone(), message));
|
2018-07-13 14:53:44 -07:00
|
|
|
|
FaultLog::new()
|
2018-07-10 14:27:18 -07:00
|
|
|
|
} else {
|
|
|
|
|
match message.content {
|
|
|
|
|
AgreementContent::BVal(b) => self.handle_bval(sender_id, b)?,
|
|
|
|
|
AgreementContent::Aux(b) => self.handle_aux(sender_id, b)?,
|
|
|
|
|
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)?,
|
|
|
|
|
}
|
2018-07-09 04:35:26 -07:00
|
|
|
|
};
|
2018-07-10 14:27:18 -07:00
|
|
|
|
self.step(fault_log)
|
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))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Whether the algorithm has terminated.
|
|
|
|
|
fn terminated(&self) -> bool {
|
|
|
|
|
self.terminated
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn our_id(&self) -> &Self::NodeUid {
|
2018-05-29 05:17:30 -07:00
|
|
|
|
self.netinfo.our_uid()
|
2018-05-16 14:50:23 -07:00
|
|
|
|
}
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-18 07:37:07 -07:00
|
|
|
|
impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
2018-06-14 06:10:05 -07:00
|
|
|
|
pub fn new(
|
2018-07-11 12:15:08 -07:00
|
|
|
|
netinfo: Arc<NetworkInfo<NodeUid>>,
|
2018-06-14 06:10:05 -07:00
|
|
|
|
session_id: u64,
|
|
|
|
|
proposer_id: NodeUid,
|
|
|
|
|
) -> AgreementResult<Self> {
|
2018-06-12 02:24:09 -07:00
|
|
|
|
let invocation_id = netinfo.invocation_id();
|
2018-06-14 05:36:48 -07:00
|
|
|
|
if let Some(&proposer_i) = netinfo.node_index(&proposer_id) {
|
|
|
|
|
Ok(Agreement {
|
|
|
|
|
netinfo: netinfo.clone(),
|
|
|
|
|
session_id,
|
|
|
|
|
proposer_id,
|
|
|
|
|
epoch: 0,
|
|
|
|
|
bin_values: BinValues::new(),
|
|
|
|
|
received_bval: BTreeMap::new(),
|
|
|
|
|
sent_bval: BTreeSet::new(),
|
|
|
|
|
received_aux: BTreeMap::new(),
|
|
|
|
|
received_conf: BTreeMap::new(),
|
|
|
|
|
received_term: BTreeMap::new(),
|
|
|
|
|
estimated: None,
|
|
|
|
|
output: None,
|
|
|
|
|
decision: None,
|
|
|
|
|
incoming_queue: Vec::new(),
|
|
|
|
|
terminated: false,
|
|
|
|
|
messages: VecDeque::new(),
|
|
|
|
|
conf_round: false,
|
|
|
|
|
common_coin: CommonCoin::new(
|
|
|
|
|
netinfo,
|
|
|
|
|
Nonce::new(invocation_id.as_ref(), session_id, proposer_i, 0),
|
|
|
|
|
),
|
2018-06-22 09:39:55 -07:00
|
|
|
|
coin_schedule: CoinSchedule::True,
|
2018-06-14 05:36:48 -07:00
|
|
|
|
})
|
2018-06-14 06:10:05 -07:00
|
|
|
|
} else {
|
2018-06-14 05:36:48 -07:00
|
|
|
|
Err(ErrorKind::UnknownProposer.into())
|
2018-05-03 01:05:26 -07:00
|
|
|
|
}
|
2018-05-02 06:10:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-10 14:27:18 -07:00
|
|
|
|
fn step(&mut self, fault_log: FaultLog<NodeUid>) -> AgreementResult<AgreementStep<NodeUid>> {
|
|
|
|
|
Ok(Step::new(
|
|
|
|
|
self.output.take().into_iter().collect(),
|
|
|
|
|
fault_log,
|
|
|
|
|
))
|
2018-07-09 04:35:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 04:09:22 -07:00
|
|
|
|
/// Sets the input value for agreement.
|
2018-07-09 04:35:26 -07:00
|
|
|
|
fn set_input(&mut self, input: bool) -> AgreementResult<FaultLog<NodeUid>> {
|
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-29 05:17:30 -07:00
|
|
|
|
if self.netinfo.num_nodes() == 1 {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
let mut fault_log = self.send_bval(input)?;
|
|
|
|
|
self.send_aux(input)?.merge_into(&mut fault_log);
|
2018-07-09 02:36:30 -07:00
|
|
|
|
self.decide(input);
|
2018-07-08 09:41:50 -07:00
|
|
|
|
Ok(fault_log)
|
2018-06-25 12:09:45 -07:00
|
|
|
|
} else {
|
|
|
|
|
// Set the initial estimated value to the input value.
|
|
|
|
|
self.estimated = Some(input);
|
|
|
|
|
// Record the input value as sent.
|
2018-07-10 14:27:18 -07:00
|
|
|
|
self.send_bval(input)
|
2018-05-17 08:38:45 -07:00
|
|
|
|
}
|
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-07-08 09:41:50 -07:00
|
|
|
|
fn handle_bval(&mut self, sender_id: &NodeUid, b: bool) -> AgreementResult<FaultLog<NodeUid>> {
|
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();
|
|
|
|
|
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// upon receiving `BVal(b)` messages from 2f + 1 nodes,
|
|
|
|
|
// bin_values := bin_values ∪ {b}
|
2018-05-29 05:17:30 -07:00
|
|
|
|
if count_bval == 2 * self.netinfo.num_faulty() + 1 {
|
2018-05-23 15:19:48 -07:00
|
|
|
|
let previous_bin_values = self.bin_values;
|
2018-05-24 10:52:58 -07:00
|
|
|
|
let bin_values_changed = self.bin_values.insert(b);
|
2018-05-07 02:59:14 -07:00
|
|
|
|
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// wait until bin_values != 0, then multicast `Aux(w)`
|
|
|
|
|
// where w ∈ bin_values
|
2018-05-23 10:38:33 -07:00
|
|
|
|
if previous_bin_values == BinValues::None {
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Send an `Aux` message at most once per epoch.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.send_aux(b)
|
2018-05-24 10:52:58 -07:00
|
|
|
|
} else if bin_values_changed {
|
2018-06-21 03:42:17 -07:00
|
|
|
|
self.on_bin_values_changed()
|
2018-05-23 10:38:33 -07:00
|
|
|
|
} else {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
Ok(FaultLog::new())
|
2018-05-06 14:39:01 -07:00
|
|
|
|
}
|
2018-05-29 05:17:30 -07:00
|
|
|
|
} else if count_bval == self.netinfo.num_faulty() + 1 && !self.sent_bval.contains(&b) {
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// upon receiving `BVal(b)` messages from f + 1 nodes, if
|
|
|
|
|
// `BVal(b)` has not been sent, multicast `BVal(b)`
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.send_bval(b)
|
|
|
|
|
} else {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
Ok(FaultLog::new())
|
2018-05-08 09:25:57 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-06 14:39:01 -07:00
|
|
|
|
|
2018-06-21 03:42:17 -07:00
|
|
|
|
/// Called when `bin_values` changes as a result of receiving a `BVal` message. Tries to update
|
|
|
|
|
/// the epoch.
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn on_bin_values_changed(&mut self) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-06-21 03:42:17 -07:00
|
|
|
|
match self.coin_schedule {
|
|
|
|
|
CoinSchedule::False => {
|
|
|
|
|
let (aux_count, aux_vals) = self.count_aux();
|
|
|
|
|
if aux_count >= self.netinfo.num_nodes() - self.netinfo.num_faulty() {
|
|
|
|
|
self.on_coin(false, aux_vals.definite())
|
|
|
|
|
} else {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
Ok(FaultLog::new())
|
2018-06-21 03:42:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CoinSchedule::True => {
|
|
|
|
|
let (aux_count, aux_vals) = self.count_aux();
|
|
|
|
|
if aux_count >= self.netinfo.num_nodes() - self.netinfo.num_faulty() {
|
|
|
|
|
self.on_coin(true, aux_vals.definite())
|
|
|
|
|
} else {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
Ok(FaultLog::new())
|
2018-06-21 03:42:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CoinSchedule::Random => {
|
|
|
|
|
// If the `Conf` round has already started, a change in `bin_values` can lead to its
|
|
|
|
|
// end. Try if it has indeed finished.
|
|
|
|
|
self.try_finish_conf_round()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn send_bval(&mut self, b: bool) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-06-29 08:20:54 -07:00
|
|
|
|
if !self.netinfo.is_validator() {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
return Ok(FaultLog::new());
|
2018-06-25 12:09:45 -07:00
|
|
|
|
}
|
2018-05-17 08:38:45 -07:00
|
|
|
|
// Record the value `b` as sent.
|
|
|
|
|
self.sent_bval.insert(b);
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Multicast `BVal`.
|
2018-05-17 08:38:45 -07:00
|
|
|
|
self.messages
|
2018-06-07 12:06:44 -07:00
|
|
|
|
.push_back(AgreementContent::BVal(b).with_epoch(self.epoch));
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Receive the `BVal` message locally.
|
2018-05-29 05:17:30 -07:00
|
|
|
|
let our_uid = &self.netinfo.our_uid().clone();
|
|
|
|
|
self.handle_bval(our_uid, b)
|
2018-05-17 08:38:45 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn send_conf(&mut self) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-05-24 10:52:58 -07:00
|
|
|
|
if self.conf_round {
|
|
|
|
|
// Only one `Conf` message is allowed in an epoch.
|
2018-07-08 09:41:50 -07:00
|
|
|
|
return Ok(FaultLog::new());
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-25 12:09:45 -07:00
|
|
|
|
// Trigger the start of the `Conf` round.
|
|
|
|
|
self.conf_round = true;
|
|
|
|
|
|
2018-06-29 08:20:54 -07:00
|
|
|
|
if !self.netinfo.is_validator() {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
return Ok(FaultLog::new());
|
2018-06-25 12:09:45 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 15:19:48 -07:00
|
|
|
|
let v = self.bin_values;
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Multicast `Conf`.
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.messages
|
2018-06-07 12:06:44 -07:00
|
|
|
|
.push_back(AgreementContent::Conf(v).with_epoch(self.epoch));
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Receive the `Conf` message locally.
|
2018-05-29 05:17:30 -07:00
|
|
|
|
let our_uid = &self.netinfo.our_uid().clone();
|
|
|
|
|
self.handle_conf(our_uid, v)
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-24 10:52:58 -07:00
|
|
|
|
/// Waits until at least (N − f) `Aux` messages have been received, such that
|
2018-05-23 10:38:33 -07:00
|
|
|
|
/// the set of values carried by these messages, vals, are a subset of
|
2018-05-24 10:52:58 -07:00
|
|
|
|
/// bin_values (note that bin_values_r may continue to change as `BVal`
|
2018-05-23 10:38:33 -07:00
|
|
|
|
/// messages are received, thus this condition may be triggered upon arrival
|
2018-05-24 10:52:58 -07:00
|
|
|
|
/// of either an `Aux` or a `BVal` message).
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn handle_aux(&mut self, sender_id: &NodeUid, b: bool) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Perform the `Aux` message round only if a `Conf` round hasn't started yet.
|
|
|
|
|
if self.conf_round {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
return Ok(FaultLog::new());
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
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 {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
return Ok(FaultLog::new());
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
2018-06-21 03:42:17 -07:00
|
|
|
|
let (aux_count, aux_vals) = self.count_aux();
|
|
|
|
|
if aux_count < self.netinfo.num_nodes() - self.netinfo.num_faulty() {
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Continue waiting for the (N - f) `Aux` messages.
|
2018-07-08 09:41:50 -07:00
|
|
|
|
return Ok(FaultLog::new());
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
2018-06-21 03:42:17 -07:00
|
|
|
|
|
|
|
|
|
// Execute the Common Coin schedule `false, true, get_coin(), false, true, get_coin(), ...`
|
|
|
|
|
match self.coin_schedule {
|
|
|
|
|
CoinSchedule::False => self.on_coin(false, aux_vals.definite()),
|
|
|
|
|
CoinSchedule::True => self.on_coin(true, aux_vals.definite()),
|
|
|
|
|
CoinSchedule::Random => {
|
|
|
|
|
// Start the `Conf` message round.
|
|
|
|
|
self.send_conf()
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn handle_conf(
|
|
|
|
|
&mut self,
|
|
|
|
|
sender_id: &NodeUid,
|
|
|
|
|
v: BinValues,
|
|
|
|
|
) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.received_conf.insert(sender_id.clone(), v);
|
2018-05-24 10:52:58 -07:00
|
|
|
|
self.try_finish_conf_round()
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-07 12:06:44 -07:00
|
|
|
|
/// Receives a `Term(v)` message. If we haven't yet decided on a value and there are more than
|
|
|
|
|
/// `num_faulty` such messages with the same value from different nodes, performs expedite
|
|
|
|
|
/// termination: decides on `v`, broadcasts `Term(v)` and terminates the instance.
|
2018-07-10 14:27:18 -07:00
|
|
|
|
fn handle_term(&mut self, sender_id: &NodeUid, b: bool) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-06-07 12:06:44 -07:00
|
|
|
|
self.received_term.insert(sender_id.clone(), b);
|
|
|
|
|
// Check for the expedite termination condition.
|
|
|
|
|
if self.decision.is_none()
|
|
|
|
|
&& self.received_term.iter().filter(|(_, &c)| b == c).count()
|
|
|
|
|
> self.netinfo.num_faulty()
|
|
|
|
|
{
|
|
|
|
|
self.decide(b);
|
|
|
|
|
}
|
2018-07-10 14:27:18 -07:00
|
|
|
|
Ok(FaultLog::new())
|
2018-06-07 12:06:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-09 02:03:38 -07:00
|
|
|
|
/// Handles a Common Coin message. If there is output from Common Coin, starts the next
|
|
|
|
|
/// epoch. The function may output a decision value.
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn handle_coin(
|
|
|
|
|
&mut self,
|
|
|
|
|
sender_id: &NodeUid,
|
|
|
|
|
msg: CommonCoinMessage,
|
|
|
|
|
) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-07-10 04:23:50 -07:00
|
|
|
|
let coin_step = self.common_coin.handle_message(sender_id, msg)?;
|
2018-06-10 02:44:12 -07:00
|
|
|
|
self.extend_common_coin();
|
2018-07-10 04:23:50 -07:00
|
|
|
|
self.on_coin_step(coin_step)
|
|
|
|
|
}
|
2018-06-10 02:44:12 -07:00
|
|
|
|
|
2018-07-10 14:27:18 -07:00
|
|
|
|
fn on_coin_step(
|
|
|
|
|
&mut self,
|
|
|
|
|
coin_step: CommonCoinStep<NodeUid>,
|
|
|
|
|
) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-07-13 14:53:44 -07:00
|
|
|
|
let mut fault_log = coin_step.fault_log.clone();
|
2018-07-10 14:27:18 -07:00
|
|
|
|
if let Some(coin) = coin_step.output.into_iter().next() {
|
2018-06-21 03:42:17 -07:00
|
|
|
|
let def_bin_value = self.count_conf().1.definite();
|
2018-07-10 04:23:50 -07:00
|
|
|
|
fault_log.extend(self.on_coin(coin, def_bin_value)?);
|
2018-06-21 03:42:17 -07:00
|
|
|
|
}
|
2018-07-10 04:23:50 -07:00
|
|
|
|
Ok(fault_log)
|
2018-06-21 03:42:17 -07:00
|
|
|
|
}
|
2018-06-12 11:36:50 -07:00
|
|
|
|
|
2018-06-21 03:42:17 -07:00
|
|
|
|
/// When the common coin has been computed, tries to decide on an output value, updates the
|
|
|
|
|
/// `Agreement` epoch and handles queued messages for the new epoch.
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn on_coin(
|
|
|
|
|
&mut self,
|
|
|
|
|
coin: bool,
|
|
|
|
|
def_bin_value: Option<bool>,
|
|
|
|
|
) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-07-10 04:23:50 -07:00
|
|
|
|
let mut fault_log = FaultLog::new();
|
|
|
|
|
if self.terminated {
|
|
|
|
|
// Avoid an infinite regression without making an Agreement step.
|
|
|
|
|
return Ok(fault_log);
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-21 03:42:17 -07:00
|
|
|
|
let b = if let Some(b) = def_bin_value {
|
|
|
|
|
// Outputting a value is allowed only once.
|
|
|
|
|
if self.decision.is_none() && b == coin {
|
|
|
|
|
self.decide(b);
|
2018-06-08 11:43:27 -07:00
|
|
|
|
}
|
2018-06-21 03:42:17 -07:00
|
|
|
|
b
|
|
|
|
|
} else {
|
|
|
|
|
coin
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.update_epoch();
|
|
|
|
|
|
|
|
|
|
self.estimated = Some(b);
|
2018-07-10 04:23:50 -07:00
|
|
|
|
fault_log.extend(self.send_bval(b)?);
|
2018-06-21 03:42:17 -07:00
|
|
|
|
let queued_msgs = replace(&mut self.incoming_queue, Vec::new());
|
|
|
|
|
for (sender_id, msg) in queued_msgs {
|
2018-07-09 04:35:26 -07:00
|
|
|
|
let step = self.handle_message(&sender_id, msg)?;
|
|
|
|
|
fault_log.extend(step.fault_log);
|
|
|
|
|
// Save the output of the internal call.
|
2018-07-10 14:27:18 -07:00
|
|
|
|
self.output = step.output.into_iter().next();
|
2018-07-09 04:35:26 -07:00
|
|
|
|
if self.terminated {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-06-08 11:43:27 -07:00
|
|
|
|
}
|
2018-07-08 09:41:50 -07:00
|
|
|
|
Ok(fault_log)
|
2018-06-08 11:43:27 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-21 03:42:17 -07:00
|
|
|
|
/// Computes the coin schedule for the current `Agreement` epoch.
|
|
|
|
|
fn coin_schedule(&self) -> CoinSchedule {
|
|
|
|
|
match self.epoch % 3 {
|
2018-06-22 09:39:55 -07:00
|
|
|
|
0 => CoinSchedule::True,
|
|
|
|
|
1 => CoinSchedule::False,
|
2018-06-21 03:42:17 -07:00
|
|
|
|
_ => CoinSchedule::Random,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Propagates Common Coin messages to the top level.
|
2018-06-10 02:44:12 -07:00
|
|
|
|
fn extend_common_coin(&mut self) {
|
|
|
|
|
let epoch = self.epoch;
|
|
|
|
|
self.messages.extend(self.common_coin.message_iter().map(
|
|
|
|
|
|msg: TargetedMessage<CommonCoinMessage, NodeUid>| {
|
|
|
|
|
AgreementContent::Coin(Box::new(msg.message)).with_epoch(epoch)
|
|
|
|
|
},
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-07 12:06:44 -07:00
|
|
|
|
/// Decides on a value and broadcasts a `Term` message with that value.
|
|
|
|
|
fn decide(&mut self, b: bool) {
|
2018-07-09 02:36:30 -07:00
|
|
|
|
if self.terminated {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-06-07 12:06:44 -07:00
|
|
|
|
// Output the agreement value.
|
|
|
|
|
self.output = Some(b);
|
|
|
|
|
// Latch the decided state.
|
|
|
|
|
self.decision = Some(b);
|
2018-07-10 04:23:50 -07:00
|
|
|
|
debug!(
|
|
|
|
|
"{:?}/{:?} (is_validator: {}) decision: {}",
|
|
|
|
|
self.netinfo.our_uid(),
|
|
|
|
|
self.proposer_id,
|
|
|
|
|
self.netinfo.is_validator(),
|
|
|
|
|
b
|
|
|
|
|
);
|
2018-06-29 08:20:54 -07:00
|
|
|
|
if self.netinfo.is_validator() {
|
2018-06-27 04:45:25 -07:00
|
|
|
|
self.messages
|
|
|
|
|
.push_back(AgreementContent::Term(b).with_epoch(self.epoch));
|
|
|
|
|
self.received_term.insert(self.netinfo.our_uid().clone(), b);
|
|
|
|
|
}
|
2018-06-07 12:06:44 -07:00
|
|
|
|
self.terminated = true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn try_finish_conf_round(&mut self) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-07-10 14:27:18 -07:00
|
|
|
|
if self.conf_round
|
|
|
|
|
&& self.count_conf().0 >= self.netinfo.num_nodes() - self.netinfo.num_faulty()
|
|
|
|
|
{
|
2018-07-10 04:23:50 -07:00
|
|
|
|
// Invoke the comon coin.
|
|
|
|
|
let coin_step = self.common_coin.input(())?;
|
2018-06-10 02:44:12 -07:00
|
|
|
|
self.extend_common_coin();
|
2018-07-10 14:27:18 -07:00
|
|
|
|
self.on_coin_step(coin_step)
|
|
|
|
|
} else {
|
|
|
|
|
// Continue waiting for (N - f) `Conf` messages
|
2018-07-13 14:53:44 -07:00
|
|
|
|
Ok(FaultLog::new())
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
2018-05-19 05:29:31 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-08 09:41:50 -07:00
|
|
|
|
fn send_aux(&mut self, b: bool) -> AgreementResult<FaultLog<NodeUid>> {
|
2018-06-29 08:20:54 -07:00
|
|
|
|
if !self.netinfo.is_validator() {
|
2018-07-08 09:41:50 -07:00
|
|
|
|
return Ok(FaultLog::new());
|
2018-06-27 04:45:25 -07:00
|
|
|
|
}
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Multicast `Aux`.
|
2018-05-19 05:29:31 -07:00
|
|
|
|
self.messages
|
2018-06-07 12:06:44 -07:00
|
|
|
|
.push_back(AgreementContent::Aux(b).with_epoch(self.epoch));
|
2018-05-24 10:52:58 -07:00
|
|
|
|
// Receive the `Aux` message locally.
|
2018-05-29 05:17:30 -07:00
|
|
|
|
let our_uid = &self.netinfo.our_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-24 10:52:58 -07:00
|
|
|
|
/// The count of `Aux` messages such that the set of values carried by those messages is a
|
2018-06-07 12:06:44 -07:00
|
|
|
|
/// subset of bin_values_r. The count of matching `Term` messages from terminated nodes is also
|
|
|
|
|
/// added to the count of `Aux` messages as witnesses of the terminated nodes' decision.
|
2018-05-06 14:39:01 -07:00
|
|
|
|
///
|
2018-05-24 10:52:58 -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
|
2018-05-28 03:54:37 -07:00
|
|
|
|
/// send an `Aux` value that will eventually end up in our `bin_values`.
|
2018-06-21 03:42:17 -07:00
|
|
|
|
fn count_aux(&self) -> (usize, BinValues) {
|
2018-06-07 12:29:51 -07:00
|
|
|
|
let mut aux: BTreeMap<_, _> = self
|
2018-06-07 12:06:44 -07:00
|
|
|
|
.received_aux
|
2018-06-07 12:29:51 -07:00
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(_, &b)| self.bin_values.contains(b))
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let term: BTreeMap<_, _> = self
|
2018-06-07 12:06:44 -07:00
|
|
|
|
.received_term
|
2018-06-07 12:29:51 -07:00
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(_, &b)| self.bin_values.contains(b))
|
|
|
|
|
.collect();
|
2018-06-07 12:06:44 -07:00
|
|
|
|
|
2018-06-07 12:29:51 -07:00
|
|
|
|
// Ensure that nodes are not counted twice.
|
|
|
|
|
aux.extend(term);
|
2018-06-21 03:42:17 -07:00
|
|
|
|
let bin: BinValues = aux.values().map(|&&v| BinValues::from_bool(v)).collect();
|
|
|
|
|
(aux.len(), bin)
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
2018-05-10 01:57:58 -07:00
|
|
|
|
|
2018-05-24 10:52:58 -07:00
|
|
|
|
/// Counts the number of received `Conf` messages.
|
|
|
|
|
fn count_conf(&self) -> (usize, BinValues) {
|
2018-05-23 15:00:19 -07:00
|
|
|
|
let (vals_cnt, vals) = self
|
|
|
|
|
.received_conf
|
2018-05-23 10:38:33 -07:00
|
|
|
|
.values()
|
2018-05-24 10:52:58 -07:00
|
|
|
|
.filter(|&conf| conf.is_subset(self.bin_values))
|
2018-05-23 15:00:19 -07:00
|
|
|
|
.tee();
|
|
|
|
|
|
|
|
|
|
(vals_cnt.count(), vals.cloned().collect())
|
2018-05-06 14:39:01 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-21 03:42:17 -07:00
|
|
|
|
fn update_epoch(&mut self) {
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.bin_values.clear();
|
|
|
|
|
self.received_bval.clear();
|
|
|
|
|
self.sent_bval.clear();
|
|
|
|
|
self.received_aux.clear();
|
|
|
|
|
self.received_conf.clear();
|
2018-05-24 10:52:58 -07:00
|
|
|
|
self.conf_round = false;
|
2018-05-23 10:38:33 -07:00
|
|
|
|
self.epoch += 1;
|
2018-06-12 02:24:09 -07:00
|
|
|
|
let nonce = Nonce::new(
|
|
|
|
|
self.netinfo.invocation_id().as_ref(),
|
2018-06-14 04:28:38 -07:00
|
|
|
|
self.session_id,
|
2018-06-14 05:36:48 -07:00
|
|
|
|
*self.netinfo.node_index(&self.proposer_id).unwrap(),
|
2018-06-12 02:24:09 -07:00
|
|
|
|
self.epoch,
|
|
|
|
|
);
|
2018-06-21 03:42:17 -07:00
|
|
|
|
// TODO: Don't spend time creating a `CommonCoin` instance in epochs where the common coin
|
|
|
|
|
// is known.
|
2018-06-08 11:43:27 -07:00
|
|
|
|
self.common_coin = CommonCoin::new(self.netinfo.clone(), nonce);
|
2018-06-21 03:42:17 -07:00
|
|
|
|
self.coin_schedule = self.coin_schedule();
|
2018-06-07 12:06:44 -07:00
|
|
|
|
debug!(
|
2018-06-14 05:36:48 -07:00
|
|
|
|
"{:?} Agreement instance {:?} started epoch {}",
|
2018-06-07 12:06:44 -07:00
|
|
|
|
self.netinfo.our_uid(),
|
2018-06-14 05:36:48 -07:00
|
|
|
|
self.proposer_id,
|
2018-06-07 12:06:44 -07:00
|
|
|
|
self.epoch
|
|
|
|
|
);
|
2018-05-23 10:38:33 -07:00
|
|
|
|
}
|
2018-05-06 14:39:01 -07:00
|
|
|
|
}
|
2018-06-12 02:24:09 -07:00
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
struct Nonce(Vec<u8>);
|
|
|
|
|
|
|
|
|
|
impl Nonce {
|
2018-06-14 05:36:48 -07:00
|
|
|
|
pub fn new(
|
|
|
|
|
invocation_id: &[u8],
|
|
|
|
|
session_id: u64,
|
|
|
|
|
proposer_id: usize,
|
|
|
|
|
agreement_epoch: u32,
|
|
|
|
|
) -> Self {
|
2018-06-12 02:24:09 -07:00
|
|
|
|
Nonce(Vec::from(format!(
|
2018-06-14 05:36:48 -07:00
|
|
|
|
"Nonce for Honey Badger {:?}@{}:{}:{}",
|
|
|
|
|
invocation_id, session_id, agreement_epoch, proposer_id
|
2018-06-12 02:24:09 -07:00
|
|
|
|
)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AsRef<[u8]> for Nonce {
|
|
|
|
|
fn as_ref(&self) -> &[u8] {
|
|
|
|
|
self.0.as_ref()
|
|
|
|
|
}
|
|
|
|
|
}
|