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
|
2018-07-28 08:31:17 -07:00
|
|
|
//! a viable output."
|
2018-07-02 06:42:31 -07:00
|
|
|
//!
|
|
|
|
//! * 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.
|
2018-07-28 08:31:17 -07:00
|
|
|
//! 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."
|
2018-07-02 06:42:31 -07:00
|
|
|
//!
|
|
|
|
//! * Since every node will receive at least _2 f + 1_ `BVal` messages from correct validators,
|
2018-07-28 08:31:17 -07:00
|
|
|
//! 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.
|
2018-07-02 06:42:31 -07:00
|
|
|
//!
|
|
|
|
//! * Every correct node will eventually send exactly one `Aux`, so we will receive at least
|
2018-07-28 08:31:17 -07:00
|
|
|
//! _N - f_ `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`.
|
2018-07-02 06:42:31 -07:00
|
|
|
//!
|
|
|
|
//! * 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`,
|
2018-07-28 08:31:17 -07:00
|
|
|
//! 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.
|
2018-07-02 06:42:31 -07:00
|
|
|
//!
|
|
|
|
//! * 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
|
2018-07-28 08:31:17 -07:00
|
|
|
//! eventually receive _N - f_ `Conf` messages containing only values we believe in. Then we
|
2018-07-02 06:42:31 -07:00
|
|
|
//! 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-08-02 11:11:32 -07:00
|
|
|
mod agreement;
|
2018-08-01 09:51:43 -07:00
|
|
|
mod bool_multimap;
|
2018-07-31 00:38:52 -07:00
|
|
|
pub mod bool_set;
|
2018-08-01 09:51:43 -07:00
|
|
|
mod sbv_broadcast;
|
2018-05-24 10:52:58 -07:00
|
|
|
|
2018-07-05 09:20:53 -07:00
|
|
|
use rand;
|
2018-05-06 14:39:01 -07:00
|
|
|
|
2018-08-02 11:11:32 -07:00
|
|
|
use self::bool_set::BoolSet;
|
|
|
|
use common_coin::{self, CommonCoinMessage};
|
|
|
|
use messaging;
|
2018-05-23 15:00:19 -07:00
|
|
|
|
2018-08-02 11:11:32 -07:00
|
|
|
pub use self::agreement::Agreement;
|
2018-05-08 09:25:57 -07:00
|
|
|
|
2018-07-25 14:38:33 -07:00
|
|
|
/// An agreement error.
|
|
|
|
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
|
|
|
|
pub enum Error {
|
|
|
|
#[fail(display = "HandleCoinCommonCoin error: {}", _0)]
|
|
|
|
HandleCoinCommonCoin(common_coin::Error),
|
|
|
|
#[fail(display = "TryFinishConfRoundCommonCoin error: {}", _0)]
|
|
|
|
TryFinishConfRoundCommonCoin(common_coin::Error),
|
|
|
|
#[fail(display = "Unknown proposer")]
|
|
|
|
UnknownProposer,
|
|
|
|
#[fail(display = "Input not accepted")]
|
|
|
|
InputNotAccepted,
|
2018-05-20 04:51:33 -07:00
|
|
|
}
|
|
|
|
|
2018-07-25 14:38:33 -07:00
|
|
|
/// An agreement result.
|
|
|
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
|
|
|
|
2018-08-02 11:11:32 -07:00
|
|
|
pub type Step<NodeUid> = messaging::Step<Agreement<NodeUid>>;
|
|
|
|
|
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-08-01 09:51:43 -07:00
|
|
|
/// Synchronized Binary Value Broadcast message.
|
|
|
|
SbvBroadcast(sbv_broadcast::Message),
|
2018-05-28 03:54:37 -07:00
|
|
|
/// `Conf` message.
|
2018-07-31 00:38:52 -07:00
|
|
|
Conf(BoolSet),
|
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-07-28 08:31:17 -07:00
|
|
|
|
|
|
|
/// 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,
|
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
}
|
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 {
|
2018-08-01 09:51:43 -07:00
|
|
|
let message_type = *rng.choose(&["sbvb", "conf", "term", "coin"]).unwrap();
|
2018-07-05 09:20:53 -07:00
|
|
|
|
|
|
|
match message_type {
|
2018-08-01 09:51:43 -07:00
|
|
|
"sbvb" => AgreementContent::SbvBroadcast(rand::random()),
|
2018-07-05 09:20:53 -07:00
|
|
|
"conf" => AgreementContent::Conf(rand::random()),
|
|
|
|
"term" => AgreementContent::Term(rand::random()),
|
|
|
|
"coin" => AgreementContent::Coin(Box::new(rand::random())),
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-24 04:12:06 -07:00
|
|
|
#[derive(Clone, Debug)]
|
2018-06-12 02:24:09 -07:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|