mirror of https://github.com/poanetwork/hbbft.git
Add common supertraits and rename related type parameters.
* Add the `Contribution`, `NodeUidT`, and `Message` supertraits. * Rename type parameters: * `Tx` -> `T` or `C` * `NodeUid` -> `N`
This commit is contained in:
parent
b1aa82b1c9
commit
697ba6f0a8
|
@ -1,5 +1,4 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
@ -10,18 +9,19 @@ use super::{AgreementContent, AgreementMessage, Error, Nonce, Result, Step};
|
|||
use agreement::bool_set::BoolSet;
|
||||
use common_coin::{self, CommonCoin, CommonCoinMessage};
|
||||
use messaging::{DistAlgorithm, NetworkInfo, Target};
|
||||
use traits::NodeUidT;
|
||||
|
||||
/// The state of the current epoch's common coin. In some epochs this is fixed, in others it starts
|
||||
/// with in `InProgress`.
|
||||
#[derive(Debug)]
|
||||
enum CoinState<NodeUid> {
|
||||
enum CoinState<N> {
|
||||
/// The value was fixed in the current epoch, or the coin has already terminated.
|
||||
Decided(bool),
|
||||
/// The coin value is not known yet.
|
||||
InProgress(CommonCoin<NodeUid, Nonce>),
|
||||
InProgress(CommonCoin<N, Nonce>),
|
||||
}
|
||||
|
||||
impl<NodeUid> CoinState<NodeUid> {
|
||||
impl<N> CoinState<N> {
|
||||
/// Returns the value, if this coin has already decided.
|
||||
fn value(&self) -> Option<bool> {
|
||||
match self {
|
||||
|
@ -31,7 +31,7 @@ impl<NodeUid> CoinState<NodeUid> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<NodeUid> From<bool> for CoinState<NodeUid> {
|
||||
impl<N> From<bool> for CoinState<N> {
|
||||
fn from(value: bool) -> Self {
|
||||
CoinState::Decided(value)
|
||||
}
|
||||
|
@ -39,22 +39,22 @@ impl<NodeUid> From<bool> for CoinState<NodeUid> {
|
|||
|
||||
/// Binary Agreement instance
|
||||
#[derive(Debug)]
|
||||
pub struct Agreement<NodeUid> {
|
||||
pub struct Agreement<N> {
|
||||
/// Shared network information.
|
||||
netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
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.
|
||||
proposer_id: NodeUid,
|
||||
proposer_id: N,
|
||||
/// Agreement algorithm epoch.
|
||||
epoch: u32,
|
||||
/// This epoch's Synchronized Binary Value Broadcast instance.
|
||||
sbv_broadcast: SbvBroadcast<NodeUid>,
|
||||
sbv_broadcast: SbvBroadcast<N>,
|
||||
/// Received `Conf` messages. Reset on every epoch update.
|
||||
received_conf: BTreeMap<NodeUid, BoolSet>,
|
||||
received_conf: BTreeMap<N, BoolSet>,
|
||||
/// Received `Term` messages. Kept throughout epoch updates. These count as `BVal`, `Aux` and
|
||||
/// `Conf` messages for all future epochs.
|
||||
received_term: BoolMultimap<NodeUid>,
|
||||
received_term: BoolMultimap<N>,
|
||||
/// The estimate of the decision value in the current epoch.
|
||||
estimated: Option<bool>,
|
||||
/// A permanent, latching copy of the output value. This copy is required because `output` can
|
||||
|
@ -65,21 +65,21 @@ pub struct Agreement<NodeUid> {
|
|||
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<(NodeUid, AgreementContent)>>,
|
||||
incoming_queue: BTreeMap<u32, Vec<(N, AgreementContent)>>,
|
||||
/// 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 common coin.
|
||||
coin_state: CoinState<NodeUid>,
|
||||
coin_state: CoinState<N>,
|
||||
}
|
||||
|
||||
impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for Agreement<NodeUid> {
|
||||
type NodeUid = NodeUid;
|
||||
impl<N: NodeUidT> DistAlgorithm for Agreement<N> {
|
||||
type NodeUid = N;
|
||||
type Input = bool;
|
||||
type Output = bool;
|
||||
type Message = AgreementMessage;
|
||||
type Error = Error;
|
||||
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<NodeUid>> {
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<N>> {
|
||||
self.set_input(input)
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for Agreement<NodeUid> {
|
|||
&mut self,
|
||||
sender_id: &Self::NodeUid,
|
||||
AgreementMessage { epoch, content }: Self::Message,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
) -> Result<Step<N>> {
|
||||
if self.decision.is_some() || (epoch < self.epoch && content.can_expire()) {
|
||||
// Message is obsolete: We are already in a later epoch or terminated.
|
||||
Ok(Step::default())
|
||||
|
@ -112,12 +112,8 @@ impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for Agreement<NodeUid> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
||||
pub fn new(
|
||||
netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
session_id: u64,
|
||||
proposer_id: NodeUid,
|
||||
) -> Result<Self> {
|
||||
impl<N: NodeUidT> Agreement<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);
|
||||
}
|
||||
|
@ -138,7 +134,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
}
|
||||
|
||||
/// Sets the input value for agreement.
|
||||
fn set_input(&mut self, input: bool) -> Result<Step<NodeUid>> {
|
||||
fn set_input(&mut self, input: bool) -> Result<Step<N>> {
|
||||
if self.epoch != 0 || self.estimated.is_some() {
|
||||
return Err(Error::InputNotAccepted);
|
||||
}
|
||||
|
@ -157,9 +153,9 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
/// Dispatches the message content to the corresponding handling method.
|
||||
fn handle_message_content(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
content: AgreementContent,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
) -> Result<Step<N>> {
|
||||
match content {
|
||||
AgreementContent::SbvBroadcast(msg) => self.handle_sbv_broadcast(sender_id, msg),
|
||||
AgreementContent::Conf(v) => self.handle_conf(sender_id, v),
|
||||
|
@ -171,19 +167,16 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
/// Handles a Synchroniced Binary Value Broadcast message.
|
||||
fn handle_sbv_broadcast(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
msg: sbv_broadcast::Message,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
) -> Result<Step<N>> {
|
||||
let sbvb_step = self.sbv_broadcast.handle_message(sender_id, msg)?;
|
||||
self.handle_sbvb_step(sbvb_step)
|
||||
}
|
||||
|
||||
/// Handles a Synchronized Binary Value Broadcast step. On output, starts the `Conf` round or
|
||||
/// decides.
|
||||
fn handle_sbvb_step(
|
||||
&mut self,
|
||||
sbvb_step: sbv_broadcast::Step<NodeUid>,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
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)
|
||||
|
@ -209,7 +202,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
|
||||
/// Handles a `Conf` message. When _N - f_ `Conf` messages with values in `bin_values` have
|
||||
/// been received, updates the epoch or decides.
|
||||
fn handle_conf(&mut self, sender_id: &NodeUid, v: BoolSet) -> Result<Step<NodeUid>> {
|
||||
fn handle_conf(&mut self, sender_id: &N, v: BoolSet) -> Result<Step<N>> {
|
||||
self.received_conf.insert(sender_id.clone(), v);
|
||||
self.try_finish_conf_round()
|
||||
}
|
||||
|
@ -217,7 +210,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
/// Handles a `Term(v)` message. If we haven't yet decided on a value and there are more than
|
||||
/// _f_ such messages with the same value from different nodes, performs expedite termination:
|
||||
/// decides on `v`, broadcasts `Term(v)` and terminates the instance.
|
||||
fn handle_term(&mut self, sender_id: &NodeUid, b: bool) -> Result<Step<NodeUid>> {
|
||||
fn handle_term(&mut self, sender_id: &N, b: bool) -> Result<Step<N>> {
|
||||
self.received_term[b].insert(sender_id.clone());
|
||||
// Check for the expedite termination condition.
|
||||
if self.decision.is_some() {
|
||||
|
@ -236,11 +229,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
|
||||
/// Handles a Common Coin message. If there is output from Common Coin, starts the next
|
||||
/// epoch. The function may output a decision value.
|
||||
fn handle_coin(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
msg: CommonCoinMessage,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
fn handle_coin(&mut self, sender_id: &N, msg: CommonCoinMessage) -> Result<Step<N>> {
|
||||
let coin_step = match self.coin_state {
|
||||
CoinState::Decided(_) => return Ok(Step::default()), // Coin value is already decided.
|
||||
CoinState::InProgress(ref mut common_coin) => common_coin
|
||||
|
@ -251,7 +240,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
}
|
||||
|
||||
/// Multicasts a `Conf(values)` message, and handles it.
|
||||
fn send_conf(&mut self, values: BoolSet) -> Result<Step<NodeUid>> {
|
||||
fn send_conf(&mut self, values: BoolSet) -> Result<Step<N>> {
|
||||
if self.conf_values.is_some() {
|
||||
// Only one `Conf` message is allowed in an epoch.
|
||||
return Ok(Step::default());
|
||||
|
@ -268,7 +257,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
}
|
||||
|
||||
/// Multicasts and handles a message. Does nothing if we are only an observer.
|
||||
fn send(&mut self, content: AgreementContent) -> Result<Step<NodeUid>> {
|
||||
fn send(&mut self, content: AgreementContent) -> Result<Step<N>> {
|
||||
if !self.netinfo.is_validator() {
|
||||
return Ok(Step::default());
|
||||
}
|
||||
|
@ -281,10 +270,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
}
|
||||
|
||||
/// Handles a step returned from the `CommonCoin`.
|
||||
fn on_coin_step(
|
||||
&mut self,
|
||||
coin_step: common_coin::Step<NodeUid, Nonce>,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
fn on_coin_step(&mut self, coin_step: common_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);
|
||||
|
@ -302,7 +288,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
/// With two conf values, the next epoch's estimate is the coin value. If there is only one conf
|
||||
/// value and that disagrees with the coin, the conf value is the next epoch's estimate. If
|
||||
/// the unique conf value agrees with the coin, terminates and decides on that value.
|
||||
fn try_update_epoch(&mut self) -> Result<Step<NodeUid>> {
|
||||
fn try_update_epoch(&mut self) -> Result<Step<N>> {
|
||||
if self.decision.is_some() {
|
||||
// Avoid an infinite regression without making an Agreement step.
|
||||
return Ok(Step::default());
|
||||
|
@ -325,7 +311,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
|
||||
/// Creates the initial coin state for the current epoch, i.e. sets it to the predetermined
|
||||
/// value, or initializes a `CommonCoin` instance.
|
||||
fn coin_state(&self) -> CoinState<NodeUid> {
|
||||
fn coin_state(&self) -> CoinState<N> {
|
||||
match self.epoch % 3 {
|
||||
0 => CoinState::Decided(true),
|
||||
1 => CoinState::Decided(false),
|
||||
|
@ -342,7 +328,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
}
|
||||
|
||||
/// Decides on a value and broadcasts a `Term` message with that value.
|
||||
fn decide(&mut self, b: bool) -> Step<NodeUid> {
|
||||
fn decide(&mut self, b: bool) -> Step<N> {
|
||||
if self.decision.is_some() {
|
||||
return Step::default();
|
||||
}
|
||||
|
@ -366,7 +352,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
}
|
||||
|
||||
/// Checks whether the _N - f_ `Conf` messages have arrived, and if so, activates the coin.
|
||||
fn try_finish_conf_round(&mut self) -> Result<Step<NodeUid>> {
|
||||
fn try_finish_conf_round(&mut self) -> Result<Step<N>> {
|
||||
if self.conf_values.is_none() || self.count_conf() < self.netinfo.num_correct() {
|
||||
return Ok(Step::default());
|
||||
}
|
||||
|
@ -390,7 +376,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
}
|
||||
|
||||
/// Increments the epoch, sets the new estimate and handles queued messages.
|
||||
fn update_epoch(&mut self, b: bool) -> Result<Step<NodeUid>> {
|
||||
fn update_epoch(&mut self, b: bool) -> Result<Step<N>> {
|
||||
self.sbv_broadcast.clear(&self.received_term);
|
||||
self.received_conf.clear();
|
||||
for (v, id) in &self.received_term {
|
||||
|
|
|
@ -92,7 +92,7 @@ pub enum Error {
|
|||
/// An agreement result.
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
pub type Step<NodeUid> = messaging::Step<Agreement<NodeUid>>;
|
||||
pub type Step<N> = messaging::Step<Agreement<N>>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub enum AgreementContent {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
//! `bin_values` of values for which _2 f + 1_ `BVal`s were received.
|
||||
|
||||
use rand;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::bool_multimap::BoolMultimap;
|
||||
|
@ -18,8 +17,9 @@ use super::bool_set::{self, BoolSet};
|
|||
use super::{Error, Result};
|
||||
use fault_log::{Fault, FaultKind};
|
||||
use messaging::{self, DistAlgorithm, NetworkInfo, Target};
|
||||
use traits::NodeUidT;
|
||||
|
||||
pub type Step<NodeUid> = messaging::Step<SbvBroadcast<NodeUid>>;
|
||||
pub type Step<N> = messaging::Step<SbvBroadcast<N>>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub enum Message {
|
||||
|
@ -43,37 +43,33 @@ impl rand::Rand for Message {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SbvBroadcast<NodeUid> {
|
||||
pub struct SbvBroadcast<N> {
|
||||
/// Shared network information.
|
||||
netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
netinfo: Arc<NetworkInfo<N>>,
|
||||
/// The set of values for which _2 f + 1_ `BVal`s have been received.
|
||||
bin_values: BoolSet,
|
||||
/// The nodes that sent us a `BVal(b)`, by `b`.
|
||||
received_bval: BoolMultimap<NodeUid>,
|
||||
received_bval: BoolMultimap<N>,
|
||||
/// The values `b` for which we already sent `BVal(b)`.
|
||||
sent_bval: BoolSet,
|
||||
/// The nodes that sent us an `Aux(b)`, by `b`.
|
||||
received_aux: BoolMultimap<NodeUid>,
|
||||
received_aux: BoolMultimap<N>,
|
||||
/// Whether we have already output.
|
||||
terminated: bool,
|
||||
}
|
||||
|
||||
impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for SbvBroadcast<NodeUid> {
|
||||
type NodeUid = NodeUid;
|
||||
impl<N: NodeUidT> DistAlgorithm for SbvBroadcast<N> {
|
||||
type NodeUid = N;
|
||||
type Input = bool;
|
||||
type Output = BoolSet;
|
||||
type Message = Message;
|
||||
type Error = Error;
|
||||
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<NodeUid>> {
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<N>> {
|
||||
self.send_bval(input)
|
||||
}
|
||||
|
||||
fn handle_message(
|
||||
&mut self,
|
||||
sender_id: &Self::NodeUid,
|
||||
msg: Self::Message,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
fn handle_message(&mut self, sender_id: &Self::NodeUid, msg: Self::Message) -> Result<Step<N>> {
|
||||
match msg {
|
||||
Message::BVal(b) => self.handle_bval(sender_id, b),
|
||||
Message::Aux(b) => self.handle_aux(sender_id, b),
|
||||
|
@ -89,8 +85,8 @@ impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for SbvBroadcast<NodeUid> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<NodeUid: Clone + Debug + Ord> SbvBroadcast<NodeUid> {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>) -> Self {
|
||||
impl<N: NodeUidT> SbvBroadcast<N> {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<N>>) -> Self {
|
||||
SbvBroadcast {
|
||||
netinfo,
|
||||
bin_values: bool_set::NONE,
|
||||
|
@ -103,7 +99,7 @@ impl<NodeUid: Clone + Debug + Ord> SbvBroadcast<NodeUid> {
|
|||
|
||||
/// Resets the algorithm, but assumes the given `init` values have already been received as
|
||||
/// both `BVal` and `Aux` messages.
|
||||
pub fn clear(&mut self, init: &BoolMultimap<NodeUid>) {
|
||||
pub fn clear(&mut self, init: &BoolMultimap<N>) {
|
||||
self.bin_values = bool_set::NONE;
|
||||
self.received_bval = init.clone();
|
||||
self.sent_bval = bool_set::NONE;
|
||||
|
@ -115,7 +111,7 @@ impl<NodeUid: Clone + Debug + Ord> SbvBroadcast<NodeUid> {
|
|||
///
|
||||
/// Upon receiving _f + 1_ `BVal(b)`, multicasts `BVal(b)`. Upon receiving _2 f + 1_ `BVal(b)`,
|
||||
/// updates `bin_values`. When `bin_values` gets its first entry, multicasts `Aux(b)`.
|
||||
pub fn handle_bval(&mut self, sender_id: &NodeUid, b: bool) -> Result<Step<NodeUid>> {
|
||||
pub fn handle_bval(&mut self, sender_id: &N, b: bool) -> Result<Step<N>> {
|
||||
let count_bval = {
|
||||
if !self.received_bval[b].insert(sender_id.clone()) {
|
||||
return Ok(Fault::new(sender_id.clone(), FaultKind::DuplicateBVal).into());
|
||||
|
@ -148,7 +144,7 @@ impl<NodeUid: Clone + Debug + Ord> SbvBroadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Multicasts and handles a message. Does nothing if we are only an observer.
|
||||
fn send(&mut self, msg: Message) -> Result<Step<NodeUid>> {
|
||||
fn send(&mut self, msg: Message) -> Result<Step<N>> {
|
||||
if !self.netinfo.is_validator() {
|
||||
return Ok(Step::default());
|
||||
}
|
||||
|
@ -159,7 +155,7 @@ impl<NodeUid: Clone + Debug + Ord> SbvBroadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Multicasts a `BVal(b)` message, and handles it.
|
||||
fn send_bval(&mut self, b: bool) -> Result<Step<NodeUid>> {
|
||||
fn send_bval(&mut self, b: bool) -> Result<Step<N>> {
|
||||
// Record the value `b` as sent. If it was already there, don't send it again.
|
||||
if !self.sent_bval.insert(b) {
|
||||
return Ok(Step::default());
|
||||
|
@ -168,7 +164,7 @@ impl<NodeUid: Clone + Debug + Ord> SbvBroadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Handles an `Aux` message.
|
||||
pub fn handle_aux(&mut self, sender_id: &NodeUid, b: bool) -> Result<Step<NodeUid>> {
|
||||
pub fn handle_aux(&mut self, sender_id: &N, b: bool) -> Result<Step<N>> {
|
||||
if !self.received_aux[b].insert(sender_id.clone()) {
|
||||
return Ok(Fault::new(sender_id.clone(), FaultKind::DuplicateAux).into());
|
||||
}
|
||||
|
@ -176,7 +172,7 @@ impl<NodeUid: Clone + Debug + Ord> SbvBroadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Checks whether there are _N - f_ `Aux` messages with values in `bin_values`, and outputs.
|
||||
fn try_output(&mut self) -> Result<Step<NodeUid>> {
|
||||
fn try_output(&mut self) -> Result<Step<N>> {
|
||||
if self.terminated || self.bin_values == bool_set::NONE {
|
||||
return Ok(Step::default());
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ use ring::digest;
|
|||
use fault_log::{Fault, FaultKind};
|
||||
use fmt::{HexBytes, HexList, HexProof};
|
||||
use messaging::{self, DistAlgorithm, NetworkInfo, Target};
|
||||
use traits::NodeUidT;
|
||||
|
||||
/// A broadcast error.
|
||||
#[derive(Clone, PartialEq, Debug, Fail)]
|
||||
|
@ -239,11 +240,11 @@ impl Debug for BroadcastMessage {
|
|||
|
||||
/// Reliable Broadcast algorithm instance.
|
||||
#[derive(Debug)]
|
||||
pub struct Broadcast<NodeUid> {
|
||||
pub struct Broadcast<N> {
|
||||
/// Shared network data.
|
||||
netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
netinfo: Arc<NetworkInfo<N>>,
|
||||
/// The UID of the sending node.
|
||||
proposer_id: NodeUid,
|
||||
proposer_id: N,
|
||||
data_shard_num: usize,
|
||||
coding: Coding,
|
||||
/// Whether we have already multicast `Echo`.
|
||||
|
@ -253,15 +254,15 @@ pub struct Broadcast<NodeUid> {
|
|||
/// Whether we have already output a value.
|
||||
decided: bool,
|
||||
/// The proofs we have received via `Echo` messages, by sender ID.
|
||||
echos: BTreeMap<NodeUid, Proof<Vec<u8>>>,
|
||||
echos: BTreeMap<N, Proof<Vec<u8>>>,
|
||||
/// The root hashes we received via `Ready` messages, by sender ID.
|
||||
readys: BTreeMap<NodeUid, Vec<u8>>,
|
||||
readys: BTreeMap<N, Vec<u8>>,
|
||||
}
|
||||
|
||||
pub type Step<NodeUid> = messaging::Step<Broadcast<NodeUid>>;
|
||||
pub type Step<N> = messaging::Step<Broadcast<N>>;
|
||||
|
||||
impl<NodeUid: Debug + Clone + Ord> DistAlgorithm for Broadcast<NodeUid> {
|
||||
type NodeUid = NodeUid;
|
||||
impl<N: NodeUidT> DistAlgorithm for Broadcast<N> {
|
||||
type NodeUid = N;
|
||||
// TODO: Allow anything serializable and deserializable, i.e. make this a type parameter
|
||||
// T: Serialize + DeserializeOwned
|
||||
type Input = Vec<u8>;
|
||||
|
@ -269,7 +270,7 @@ impl<NodeUid: Debug + Clone + Ord> DistAlgorithm for Broadcast<NodeUid> {
|
|||
type Message = BroadcastMessage;
|
||||
type Error = Error;
|
||||
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<NodeUid>> {
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<N>> {
|
||||
if *self.netinfo.our_uid() != self.proposer_id {
|
||||
return Err(Error::InstanceCannotPropose);
|
||||
}
|
||||
|
@ -282,11 +283,7 @@ impl<NodeUid: Debug + Clone + Ord> DistAlgorithm for Broadcast<NodeUid> {
|
|||
Ok(step)
|
||||
}
|
||||
|
||||
fn handle_message(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
message: Self::Message,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<N>> {
|
||||
if !self.netinfo.is_node_validator(sender_id) {
|
||||
return Err(Error::UnknownSender);
|
||||
}
|
||||
|
@ -301,15 +298,15 @@ impl<NodeUid: Debug + Clone + Ord> DistAlgorithm for Broadcast<NodeUid> {
|
|||
self.decided
|
||||
}
|
||||
|
||||
fn our_id(&self) -> &NodeUid {
|
||||
fn our_id(&self) -> &N {
|
||||
self.netinfo.our_uid()
|
||||
}
|
||||
}
|
||||
|
||||
impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
||||
impl<N: NodeUidT> Broadcast<N> {
|
||||
/// Creates a new broadcast instance to be used by node `our_id` which expects a value proposal
|
||||
/// from node `proposer_id`.
|
||||
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>, proposer_id: NodeUid) -> Result<Self> {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<N>>, proposer_id: N) -> Result<Self> {
|
||||
let parity_shard_num = 2 * netinfo.num_faulty();
|
||||
let data_shard_num = netinfo.num_nodes() - parity_shard_num;
|
||||
let coding = Coding::new(data_shard_num, parity_shard_num)?;
|
||||
|
@ -332,7 +329,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
/// scheme. The returned value contains the shard assigned to this
|
||||
/// node. That shard doesn't need to be sent anywhere. It gets recorded in
|
||||
/// the broadcast instance.
|
||||
fn send_shards(&mut self, mut value: Vec<u8>) -> Result<(Proof<Vec<u8>>, Step<NodeUid>)> {
|
||||
fn send_shards(&mut self, mut value: Vec<u8>) -> Result<(Proof<Vec<u8>>, Step<N>)> {
|
||||
let data_shard_num = self.coding.data_shard_count();
|
||||
let parity_shard_num = self.coding.parity_shard_count();
|
||||
|
||||
|
@ -407,7 +404,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Handles a received echo and verifies the proof it contains.
|
||||
fn handle_value(&mut self, sender_id: &NodeUid, p: Proof<Vec<u8>>) -> Result<Step<NodeUid>> {
|
||||
fn handle_value(&mut self, sender_id: &N, p: Proof<Vec<u8>>) -> Result<Step<N>> {
|
||||
// If the sender is not the proposer or if this is not the first `Value`, ignore.
|
||||
if *sender_id != self.proposer_id {
|
||||
info!(
|
||||
|
@ -439,7 +436,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Handles a received `Echo` message.
|
||||
fn handle_echo(&mut self, sender_id: &NodeUid, p: Proof<Vec<u8>>) -> Result<Step<NodeUid>> {
|
||||
fn handle_echo(&mut self, sender_id: &N, p: Proof<Vec<u8>>) -> Result<Step<N>> {
|
||||
// If the sender has already sent `Echo`, ignore.
|
||||
if self.echos.contains_key(sender_id) {
|
||||
info!(
|
||||
|
@ -469,7 +466,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Handles a received `Ready` message.
|
||||
fn handle_ready(&mut self, sender_id: &NodeUid, hash: &[u8]) -> Result<Step<NodeUid>> {
|
||||
fn handle_ready(&mut self, sender_id: &N, hash: &[u8]) -> Result<Step<N>> {
|
||||
// If the sender has already sent a `Ready` before, ignore.
|
||||
if self.readys.contains_key(sender_id) {
|
||||
info!(
|
||||
|
@ -494,7 +491,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Sends an `Echo` message and handles it. Does nothing if we are only an observer.
|
||||
fn send_echo(&mut self, p: Proof<Vec<u8>>) -> Result<Step<NodeUid>> {
|
||||
fn send_echo(&mut self, p: Proof<Vec<u8>>) -> Result<Step<N>> {
|
||||
self.echo_sent = true;
|
||||
if !self.netinfo.is_validator() {
|
||||
return Ok(Step::default());
|
||||
|
@ -507,7 +504,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
}
|
||||
|
||||
/// Sends a `Ready` message and handles it. Does nothing if we are only an observer.
|
||||
fn send_ready(&mut self, hash: &[u8]) -> Result<Step<NodeUid>> {
|
||||
fn send_ready(&mut self, hash: &[u8]) -> Result<Step<N>> {
|
||||
self.ready_sent = true;
|
||||
if !self.netinfo.is_validator() {
|
||||
return Ok(Step::default());
|
||||
|
@ -521,7 +518,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
|
||||
/// Checks whether the conditions for output are met for this hash, and if so, sets the output
|
||||
/// value.
|
||||
fn compute_output(&mut self, hash: &[u8]) -> Result<Step<NodeUid>> {
|
||||
fn compute_output(&mut self, hash: &[u8]) -> Result<Step<N>> {
|
||||
if self.decided
|
||||
|| self.count_readys(hash) <= 2 * self.netinfo.num_faulty()
|
||||
|| self.count_echos(hash) < self.coding.data_shard_count()
|
||||
|
@ -555,7 +552,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
|
||||
/// Returns `true` if the proof is valid and has the same index as the node ID. Otherwise
|
||||
/// logs an info message.
|
||||
fn validate_proof(&self, p: &Proof<Vec<u8>>, id: &NodeUid) -> bool {
|
||||
fn validate_proof(&self, p: &Proof<Vec<u8>>, id: &N) -> bool {
|
||||
if !p.validate(&p.root_hash) {
|
||||
info!(
|
||||
"Node {:?} received invalid proof: {:?}",
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
//! of its bits.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crypto::error as cerror;
|
||||
use crypto::{Signature, SignatureShare};
|
||||
use fault_log::{Fault, FaultKind};
|
||||
use messaging::{self, DistAlgorithm, NetworkInfo, Target};
|
||||
use traits::NodeUidT;
|
||||
|
||||
/// A common coin error.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
|
||||
|
@ -61,33 +61,33 @@ impl CommonCoinMessage {
|
|||
/// receiving at least `num_faulty + 1` shares, attempts to combine them into a signature. If that
|
||||
/// signature is valid, the instance outputs it and terminates; otherwise the instance aborts.
|
||||
#[derive(Debug)]
|
||||
pub struct CommonCoin<NodeUid, T> {
|
||||
netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
pub struct CommonCoin<N, T> {
|
||||
netinfo: Arc<NetworkInfo<N>>,
|
||||
/// The name of this common coin. It is required to be unique for each common coin round.
|
||||
nonce: T,
|
||||
/// All received threshold signature shares.
|
||||
received_shares: BTreeMap<NodeUid, SignatureShare>,
|
||||
received_shares: BTreeMap<N, SignatureShare>,
|
||||
/// Whether we provided input to the common coin.
|
||||
had_input: bool,
|
||||
/// Termination flag.
|
||||
terminated: bool,
|
||||
}
|
||||
|
||||
pub type Step<NodeUid, T> = messaging::Step<CommonCoin<NodeUid, T>>;
|
||||
pub type Step<N, T> = messaging::Step<CommonCoin<N, T>>;
|
||||
|
||||
impl<NodeUid, T> DistAlgorithm for CommonCoin<NodeUid, T>
|
||||
impl<N, T> DistAlgorithm for CommonCoin<N, T>
|
||||
where
|
||||
NodeUid: Clone + Debug + Ord,
|
||||
N: NodeUidT,
|
||||
T: Clone + AsRef<[u8]>,
|
||||
{
|
||||
type NodeUid = NodeUid;
|
||||
type NodeUid = N;
|
||||
type Input = ();
|
||||
type Output = bool;
|
||||
type Message = CommonCoinMessage;
|
||||
type Error = Error;
|
||||
|
||||
/// Sends our threshold signature share if not yet sent.
|
||||
fn input(&mut self, _input: Self::Input) -> Result<Step<NodeUid, T>> {
|
||||
fn input(&mut self, _input: Self::Input) -> Result<Step<N, T>> {
|
||||
if !self.had_input {
|
||||
self.had_input = true;
|
||||
self.get_coin()
|
||||
|
@ -101,7 +101,7 @@ where
|
|||
&mut self,
|
||||
sender_id: &Self::NodeUid,
|
||||
message: Self::Message,
|
||||
) -> Result<Step<NodeUid, T>> {
|
||||
) -> Result<Step<N, T>> {
|
||||
if !self.terminated {
|
||||
let CommonCoinMessage(share) = message;
|
||||
self.handle_share(sender_id, share)
|
||||
|
@ -120,12 +120,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<NodeUid, T> CommonCoin<NodeUid, T>
|
||||
impl<N, T> CommonCoin<N, T>
|
||||
where
|
||||
NodeUid: Clone + Debug + Ord,
|
||||
N: NodeUidT,
|
||||
T: Clone + AsRef<[u8]>,
|
||||
{
|
||||
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>, nonce: T) -> Self {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<N>>, nonce: T) -> Self {
|
||||
CommonCoin {
|
||||
netinfo,
|
||||
nonce,
|
||||
|
@ -135,7 +135,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn get_coin(&mut self) -> Result<Step<NodeUid, T>> {
|
||||
fn get_coin(&mut self) -> Result<Step<N, T>> {
|
||||
if !self.netinfo.is_validator() {
|
||||
return self.try_output();
|
||||
}
|
||||
|
@ -146,11 +146,7 @@ where
|
|||
Ok(step)
|
||||
}
|
||||
|
||||
fn handle_share(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
share: SignatureShare,
|
||||
) -> Result<Step<NodeUid, T>> {
|
||||
fn handle_share(&mut self, sender_id: &N, share: SignatureShare) -> Result<Step<N, T>> {
|
||||
if let Some(pk_i) = self.netinfo.public_key_share(sender_id) {
|
||||
if !pk_i.verify(&share, &self.nonce) {
|
||||
// Log the faulty node and ignore the invalid share.
|
||||
|
@ -164,7 +160,7 @@ where
|
|||
self.try_output()
|
||||
}
|
||||
|
||||
fn try_output(&mut self) -> Result<Step<NodeUid, T>> {
|
||||
fn try_output(&mut self) -> Result<Step<N, T>> {
|
||||
debug!(
|
||||
"{:?} received {} shares, had_input = {}",
|
||||
self.netinfo.our_uid(),
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
//! values for which the decision was "yes".
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Debug;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -33,6 +32,7 @@ use broadcast::{self, Broadcast, BroadcastMessage};
|
|||
use fmt::HexBytes;
|
||||
use messaging::{self, DistAlgorithm, NetworkInfo};
|
||||
use rand::Rand;
|
||||
use traits::NodeUidT;
|
||||
|
||||
/// A common subset error.
|
||||
#[derive(Clone, PartialEq, Debug, Fail)]
|
||||
|
@ -63,37 +63,37 @@ type ProposedValue = Vec<u8>;
|
|||
|
||||
/// Message from Common Subset to remote nodes.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Rand)]
|
||||
pub enum Message<NodeUid: Rand> {
|
||||
pub enum Message<N: Rand> {
|
||||
/// A message for the broadcast algorithm concerning the set element proposed by the given node.
|
||||
Broadcast(NodeUid, BroadcastMessage),
|
||||
Broadcast(N, BroadcastMessage),
|
||||
/// A message for the agreement algorithm concerning the set element proposed by the given
|
||||
/// node.
|
||||
Agreement(NodeUid, AgreementMessage),
|
||||
Agreement(N, AgreementMessage),
|
||||
}
|
||||
|
||||
/// Asynchronous Common Subset algorithm instance
|
||||
#[derive(Debug)]
|
||||
pub struct CommonSubset<NodeUid: Rand> {
|
||||
pub struct CommonSubset<N: Rand> {
|
||||
/// Shared network information.
|
||||
netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
broadcast_instances: BTreeMap<NodeUid, Broadcast<NodeUid>>,
|
||||
agreement_instances: BTreeMap<NodeUid, Agreement<NodeUid>>,
|
||||
broadcast_results: BTreeMap<NodeUid, ProposedValue>,
|
||||
agreement_results: BTreeMap<NodeUid, bool>,
|
||||
netinfo: Arc<NetworkInfo<N>>,
|
||||
broadcast_instances: BTreeMap<N, Broadcast<N>>,
|
||||
agreement_instances: BTreeMap<N, Agreement<N>>,
|
||||
broadcast_results: BTreeMap<N, ProposedValue>,
|
||||
agreement_results: BTreeMap<N, bool>,
|
||||
/// Whether the instance has decided on a value.
|
||||
decided: bool,
|
||||
}
|
||||
|
||||
pub type Step<NodeUid> = messaging::Step<CommonSubset<NodeUid>>;
|
||||
pub type Step<N> = messaging::Step<CommonSubset<N>>;
|
||||
|
||||
impl<NodeUid: Clone + Debug + Ord + Rand> DistAlgorithm for CommonSubset<NodeUid> {
|
||||
type NodeUid = NodeUid;
|
||||
impl<N: NodeUidT + Rand> DistAlgorithm for CommonSubset<N> {
|
||||
type NodeUid = N;
|
||||
type Input = ProposedValue;
|
||||
type Output = BTreeMap<NodeUid, ProposedValue>;
|
||||
type Message = Message<NodeUid>;
|
||||
type Output = BTreeMap<N, ProposedValue>;
|
||||
type Message = Message<N>;
|
||||
type Error = Error;
|
||||
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<NodeUid>> {
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<N>> {
|
||||
debug!(
|
||||
"{:?} Proposing {:?}",
|
||||
self.netinfo.our_uid(),
|
||||
|
@ -106,7 +106,7 @@ impl<NodeUid: Clone + Debug + Ord + Rand> DistAlgorithm for CommonSubset<NodeUid
|
|||
&mut self,
|
||||
sender_id: &Self::NodeUid,
|
||||
message: Self::Message,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
) -> 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),
|
||||
|
@ -122,10 +122,10 @@ impl<NodeUid: Clone + Debug + Ord + Rand> DistAlgorithm for CommonSubset<NodeUid
|
|||
}
|
||||
}
|
||||
|
||||
impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>, session_id: u64) -> Result<Self> {
|
||||
impl<N: NodeUidT + Rand> CommonSubset<N> {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<N>>, session_id: u64) -> Result<Self> {
|
||||
// Create all broadcast instances.
|
||||
let mut broadcast_instances: BTreeMap<NodeUid, Broadcast<NodeUid>> = BTreeMap::new();
|
||||
let mut broadcast_instances: BTreeMap<N, Broadcast<N>> = BTreeMap::new();
|
||||
for proposer_id in netinfo.all_uids() {
|
||||
broadcast_instances.insert(
|
||||
proposer_id.clone(),
|
||||
|
@ -134,7 +134,7 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
}
|
||||
|
||||
// Create all agreement instances.
|
||||
let mut agreement_instances: BTreeMap<NodeUid, Agreement<NodeUid>> = BTreeMap::new();
|
||||
let mut agreement_instances: BTreeMap<N, Agreement<N>> = BTreeMap::new();
|
||||
for proposer_id in netinfo.all_uids() {
|
||||
agreement_instances.insert(
|
||||
proposer_id.clone(),
|
||||
|
@ -155,7 +155,7 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
|
||||
/// Common Subset input message handler. It receives a value for broadcast
|
||||
/// and redirects it to the corresponding broadcast instance.
|
||||
pub fn send_proposed_value(&mut self, value: ProposedValue) -> Result<Step<NodeUid>> {
|
||||
pub fn send_proposed_value(&mut self, value: ProposedValue) -> Result<Step<N>> {
|
||||
if !self.netinfo.is_validator() {
|
||||
return Ok(Step::default());
|
||||
}
|
||||
|
@ -173,10 +173,10 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
/// value proposed by the node `proposer_id`.
|
||||
fn handle_broadcast(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
proposer_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
proposer_id: &N,
|
||||
bmessage: BroadcastMessage,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
) -> Result<Step<N>> {
|
||||
self.process_broadcast(proposer_id, |bc| bc.handle_message(sender_id, bmessage))
|
||||
}
|
||||
|
||||
|
@ -184,10 +184,10 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
/// a value proposed by the node `proposer_id`.
|
||||
fn handle_agreement(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
proposer_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
proposer_id: &N,
|
||||
amessage: AgreementMessage,
|
||||
) -> Result<Step<NodeUid>> {
|
||||
) -> Result<Step<N>> {
|
||||
// Send the message to the local instance of Agreement
|
||||
self.process_agreement(proposer_id, |agreement| {
|
||||
agreement.handle_message(sender_id, amessage)
|
||||
|
@ -196,10 +196,9 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
|
||||
/// Upon delivery of v_j from RBC_j, if input has not yet been provided to
|
||||
/// BA_j, then provide input 1 to BA_j. See Figure 11.
|
||||
fn process_broadcast<F>(&mut self, proposer_id: &NodeUid, f: F) -> Result<Step<NodeUid>>
|
||||
fn process_broadcast<F>(&mut self, proposer_id: &N, f: F) -> Result<Step<N>>
|
||||
where
|
||||
F: FnOnce(&mut Broadcast<NodeUid>)
|
||||
-> result::Result<broadcast::Step<NodeUid>, broadcast::Error>,
|
||||
F: FnOnce(&mut Broadcast<N>) -> result::Result<broadcast::Step<N>, broadcast::Error>,
|
||||
{
|
||||
let mut step = Step::default();
|
||||
let value = {
|
||||
|
@ -219,7 +218,7 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
}
|
||||
};
|
||||
self.broadcast_results.insert(proposer_id.clone(), value);
|
||||
let set_agreement_input = |agreement: &mut Agreement<NodeUid>| {
|
||||
let set_agreement_input = |agreement: &mut Agreement<N>| {
|
||||
if agreement.accepts_input() {
|
||||
agreement.input(true)
|
||||
} else {
|
||||
|
@ -232,10 +231,9 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
|
||||
/// Callback to be invoked on receipt of the decision value of the Agreement
|
||||
/// instance `uid`.
|
||||
fn process_agreement<F>(&mut self, proposer_id: &NodeUid, f: F) -> Result<Step<NodeUid>>
|
||||
fn process_agreement<F>(&mut self, proposer_id: &N, f: F) -> Result<Step<N>>
|
||||
where
|
||||
F: FnOnce(&mut Agreement<NodeUid>)
|
||||
-> result::Result<agreement::Step<NodeUid>, agreement::Error>,
|
||||
F: FnOnce(&mut Agreement<N>) -> result::Result<agreement::Step<N>, agreement::Error>,
|
||||
{
|
||||
let mut step = Step::default();
|
||||
let value = {
|
||||
|
@ -298,7 +296,7 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
self.agreement_results.values().filter(|v| **v).count()
|
||||
}
|
||||
|
||||
fn try_agreement_completion(&mut self) -> Option<BTreeMap<NodeUid, ProposedValue>> {
|
||||
fn try_agreement_completion(&mut self) -> Option<BTreeMap<N, ProposedValue>> {
|
||||
if self.decided || self.count_true() < self.netinfo.num_correct() {
|
||||
return None;
|
||||
}
|
||||
|
@ -313,7 +311,7 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
self.netinfo.our_uid()
|
||||
);
|
||||
// All instances of Agreement that delivered `true` (or "1" in the paper).
|
||||
let delivered_1: BTreeSet<&NodeUid> = self
|
||||
let delivered_1: BTreeSet<&N> = self
|
||||
.agreement_results
|
||||
.iter()
|
||||
.filter(|(_, v)| **v)
|
||||
|
@ -322,7 +320,7 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
debug!("Agreement instances that delivered 1: {:?}", delivered_1);
|
||||
|
||||
// Results of Broadcast instances in `delivered_1`
|
||||
let broadcast_results: BTreeMap<NodeUid, ProposedValue> = self
|
||||
let broadcast_results: BTreeMap<N, ProposedValue> = self
|
||||
.broadcast_results
|
||||
.iter()
|
||||
.filter(|(k, _)| delivered_1.contains(k))
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use rand::Rand;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -7,22 +6,23 @@ use serde::{Deserialize, Serialize};
|
|||
use super::{ChangeState, JoinPlan};
|
||||
use crypto::{PublicKey, PublicKeySet};
|
||||
use messaging::NetworkInfo;
|
||||
use traits::NodeUidT;
|
||||
|
||||
/// A batch of transactions the algorithm has output.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Batch<C, NodeUid> {
|
||||
pub struct Batch<C, N> {
|
||||
/// The sequence number: there is exactly one batch in each epoch.
|
||||
pub(super) epoch: u64,
|
||||
/// The user contributions committed in this epoch.
|
||||
pub(super) contributions: BTreeMap<NodeUid, C>,
|
||||
pub(super) contributions: BTreeMap<N, C>,
|
||||
/// The current state of adding or removing a node: whether any is in progress, or completed
|
||||
/// this epoch.
|
||||
change: ChangeState<NodeUid>,
|
||||
change: ChangeState<N>,
|
||||
/// The public network info, if `change` is not `None`.
|
||||
pub_netinfo: Option<(PublicKeySet, BTreeMap<NodeUid, PublicKey>)>,
|
||||
pub_netinfo: Option<(PublicKeySet, BTreeMap<N, PublicKey>)>,
|
||||
}
|
||||
|
||||
impl<C, NodeUid: Ord + Rand + Clone + Debug> Batch<C, NodeUid> {
|
||||
impl<C, N: NodeUidT + Rand> Batch<C, N> {
|
||||
/// Returns a new, empty batch with the given epoch.
|
||||
pub fn new(epoch: u64) -> Self {
|
||||
Batch {
|
||||
|
@ -39,7 +39,7 @@ impl<C, NodeUid: Ord + Rand + Clone + Debug> Batch<C, NodeUid> {
|
|||
|
||||
/// Returns whether any change to the set of participating nodes is in progress or was
|
||||
/// completed in this epoch.
|
||||
pub fn change(&self) -> &ChangeState<NodeUid> {
|
||||
pub fn change(&self) -> &ChangeState<N> {
|
||||
&self.change
|
||||
}
|
||||
|
||||
|
@ -60,33 +60,33 @@ impl<C, NodeUid: Ord + Rand + Clone + Debug> Batch<C, NodeUid> {
|
|||
}
|
||||
|
||||
/// Returns the number of transactions in the batch (without detecting duplicates).
|
||||
pub fn len<Tx>(&self) -> usize
|
||||
pub fn len<T>(&self) -> usize
|
||||
where
|
||||
C: AsRef<[Tx]>,
|
||||
C: AsRef<[T]>,
|
||||
{
|
||||
self.contributions
|
||||
.values()
|
||||
.map(C::as_ref)
|
||||
.map(<[Tx]>::len)
|
||||
.map(<[T]>::len)
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Returns `true` if the batch contains no transactions.
|
||||
pub fn is_empty<Tx>(&self) -> bool
|
||||
pub fn is_empty<T>(&self) -> bool
|
||||
where
|
||||
C: AsRef<[Tx]>,
|
||||
C: AsRef<[T]>,
|
||||
{
|
||||
self.contributions
|
||||
.values()
|
||||
.map(C::as_ref)
|
||||
.all(<[Tx]>::is_empty)
|
||||
.all(<[T]>::is_empty)
|
||||
}
|
||||
|
||||
/// Returns the `JoinPlan` to be sent to new observer nodes, if it is possible to join in the
|
||||
/// next epoch.
|
||||
pub fn join_plan(&self) -> Option<JoinPlan<NodeUid>>
|
||||
pub fn join_plan(&self) -> Option<JoinPlan<N>>
|
||||
where
|
||||
NodeUid: Serialize + for<'r> Deserialize<'r>,
|
||||
N: Serialize + for<'r> Deserialize<'r>,
|
||||
{
|
||||
self.pub_netinfo
|
||||
.as_ref()
|
||||
|
@ -100,11 +100,7 @@ impl<C, NodeUid: Ord + Rand + Clone + Debug> Batch<C, NodeUid> {
|
|||
|
||||
/// Sets the current change state, and if it is not `None`, inserts the network information so
|
||||
/// that a `JoinPlan` can be generated for the next epoch.
|
||||
pub(super) fn set_change(
|
||||
&mut self,
|
||||
change: ChangeState<NodeUid>,
|
||||
netinfo: &NetworkInfo<NodeUid>,
|
||||
) {
|
||||
pub(super) fn set_change(&mut self, change: ChangeState<N>, netinfo: &NetworkInfo<N>) {
|
||||
self.change = change;
|
||||
if self.change != ChangeState::None {
|
||||
self.pub_netinfo = Some((
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use std::default::Default;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::iter::once;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
@ -12,16 +10,17 @@ use serde::{Deserialize, Serialize};
|
|||
use super::{ChangeState, DynamicHoneyBadger, JoinPlan, Result, Step, VoteCounter};
|
||||
use honey_badger::HoneyBadger;
|
||||
use messaging::NetworkInfo;
|
||||
use traits::{Contribution, NodeUidT};
|
||||
|
||||
/// A Dynamic Honey Badger builder, to configure the parameters and create new instances of
|
||||
/// `DynamicHoneyBadger`.
|
||||
pub struct DynamicHoneyBadgerBuilder<C, NodeUid> {
|
||||
pub struct DynamicHoneyBadgerBuilder<C, N> {
|
||||
/// The maximum number of future epochs for which we handle messages simultaneously.
|
||||
max_future_epochs: usize,
|
||||
_phantom: PhantomData<(C, NodeUid)>,
|
||||
_phantom: PhantomData<(C, N)>,
|
||||
}
|
||||
|
||||
impl<C, NodeUid> Default for DynamicHoneyBadgerBuilder<C, NodeUid> {
|
||||
impl<C, N> Default for DynamicHoneyBadgerBuilder<C, N> {
|
||||
fn default() -> Self {
|
||||
// TODO: Use the defaults from `HoneyBadgerBuilder`.
|
||||
DynamicHoneyBadgerBuilder {
|
||||
|
@ -31,10 +30,10 @@ impl<C, NodeUid> Default for DynamicHoneyBadgerBuilder<C, NodeUid> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<C, NodeUid> DynamicHoneyBadgerBuilder<C, NodeUid>
|
||||
impl<C, N> DynamicHoneyBadgerBuilder<C, N>
|
||||
where
|
||||
C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash,
|
||||
NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash + Rand,
|
||||
C: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
N: NodeUidT + Serialize + for<'r> Deserialize<'r> + Rand,
|
||||
{
|
||||
/// Returns a new `DynamicHoneyBadgerBuilder` configured to use the node IDs and cryptographic
|
||||
/// keys specified by `netinfo`.
|
||||
|
@ -49,7 +48,7 @@ where
|
|||
}
|
||||
|
||||
/// Creates a new Dynamic Honey Badger instance with an empty buffer.
|
||||
pub fn build(&self, netinfo: NetworkInfo<NodeUid>) -> DynamicHoneyBadger<C, NodeUid> {
|
||||
pub fn build(&self, netinfo: NetworkInfo<N>) -> DynamicHoneyBadger<C, N> {
|
||||
let arc_netinfo = Arc::new(netinfo.clone());
|
||||
let honey_badger = HoneyBadger::builder(arc_netinfo.clone())
|
||||
.max_future_epochs(self.max_future_epochs)
|
||||
|
@ -67,7 +66,7 @@ where
|
|||
}
|
||||
|
||||
/// Creates a new `DynamicHoneyBadger` configured to start a new network as a single validator.
|
||||
pub fn build_first_node(&self, our_uid: NodeUid) -> DynamicHoneyBadger<C, NodeUid> {
|
||||
pub fn build_first_node(&self, our_uid: N) -> DynamicHoneyBadger<C, N> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let sk_set = SecretKeySet::random(0, &mut rng);
|
||||
let pk_set = sk_set.public_keys();
|
||||
|
@ -82,10 +81,10 @@ where
|
|||
/// the `JoinPlan`.
|
||||
pub fn build_joining(
|
||||
&self,
|
||||
our_uid: NodeUid,
|
||||
our_uid: N,
|
||||
secret_key: SecretKey,
|
||||
join_plan: JoinPlan<NodeUid>,
|
||||
) -> Result<(DynamicHoneyBadger<C, NodeUid>, Step<C, NodeUid>)> {
|
||||
join_plan: JoinPlan<N>,
|
||||
) -> Result<(DynamicHoneyBadger<C, N>, Step<C, N>)> {
|
||||
let netinfo = NetworkInfo::new(
|
||||
our_uid,
|
||||
SecretKeyShare::default(), // TODO: Should be an option?
|
||||
|
|
|
@ -2,16 +2,16 @@ use crypto::PublicKey;
|
|||
|
||||
/// A node change action: adding or removing a node.
|
||||
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Hash, Debug)]
|
||||
pub enum Change<NodeUid> {
|
||||
pub enum Change<N> {
|
||||
/// Add a node. The public key is used only temporarily, for key generation.
|
||||
Add(NodeUid, PublicKey),
|
||||
Add(N, PublicKey),
|
||||
/// Remove a node.
|
||||
Remove(NodeUid),
|
||||
Remove(N),
|
||||
}
|
||||
|
||||
impl<NodeUid> Change<NodeUid> {
|
||||
impl<N> Change<N> {
|
||||
/// Returns the ID of the current candidate for being added, if any.
|
||||
pub fn candidate(&self) -> Option<&NodeUid> {
|
||||
pub fn candidate(&self) -> Option<&N> {
|
||||
match *self {
|
||||
Change::Add(ref id, _) => Some(id),
|
||||
Change::Remove(_) => None,
|
||||
|
@ -21,13 +21,13 @@ impl<NodeUid> Change<NodeUid> {
|
|||
|
||||
/// A change status: whether a node addition or removal is currently in progress or completed.
|
||||
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Hash, Debug)]
|
||||
pub enum ChangeState<NodeUid> {
|
||||
pub enum ChangeState<N> {
|
||||
/// No node is currently being considered for addition or removal.
|
||||
None,
|
||||
/// A change is currently in progress. If it is an addition, all broadcast messages must be
|
||||
/// sent to the new node, too.
|
||||
InProgress(Change<NodeUid>),
|
||||
InProgress(Change<N>),
|
||||
/// A change has been completed in this epoch. From the next epoch on, the new composition of
|
||||
/// the network will perform the consensus process.
|
||||
Complete(Change<NodeUid>),
|
||||
Complete(Change<N>),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use rand::Rand;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -17,40 +15,41 @@ use fault_log::{Fault, FaultKind, FaultLog};
|
|||
use honey_badger::{self, HoneyBadger, Message as HbMessage};
|
||||
use messaging::{DistAlgorithm, NetworkInfo, Target};
|
||||
use sync_key_gen::{Ack, Part, PartOutcome, SyncKeyGen};
|
||||
use traits::{Contribution, NodeUidT};
|
||||
|
||||
/// A Honey Badger instance that can handle adding and removing nodes.
|
||||
#[derive(Debug)]
|
||||
pub struct DynamicHoneyBadger<C, NodeUid: Rand> {
|
||||
pub struct DynamicHoneyBadger<C, N: Rand> {
|
||||
/// Shared network data.
|
||||
pub(super) netinfo: NetworkInfo<NodeUid>,
|
||||
pub(super) netinfo: NetworkInfo<N>,
|
||||
/// The maximum number of future epochs for which we handle messages simultaneously.
|
||||
pub(super) max_future_epochs: usize,
|
||||
/// The first epoch after the latest node change.
|
||||
pub(super) start_epoch: u64,
|
||||
/// The buffer and counter for the pending and committed change votes.
|
||||
pub(super) vote_counter: VoteCounter<NodeUid>,
|
||||
pub(super) vote_counter: VoteCounter<N>,
|
||||
/// Pending node transactions that we will propose in the next epoch.
|
||||
pub(super) key_gen_msg_buffer: Vec<SignedKeyGenMsg<NodeUid>>,
|
||||
pub(super) key_gen_msg_buffer: Vec<SignedKeyGenMsg<N>>,
|
||||
/// The `HoneyBadger` instance with the current set of nodes.
|
||||
pub(super) honey_badger: HoneyBadger<InternalContrib<C, NodeUid>, NodeUid>,
|
||||
pub(super) honey_badger: HoneyBadger<InternalContrib<C, N>, N>,
|
||||
/// The current key generation process, and the change it applies to.
|
||||
pub(super) key_gen_state: Option<KeyGenState<NodeUid>>,
|
||||
pub(super) key_gen_state: Option<KeyGenState<N>>,
|
||||
/// A queue for messages from future epochs that cannot be handled yet.
|
||||
pub(super) incoming_queue: Vec<(NodeUid, Message<NodeUid>)>,
|
||||
pub(super) incoming_queue: Vec<(N, Message<N>)>,
|
||||
}
|
||||
|
||||
impl<C, NodeUid> DistAlgorithm for DynamicHoneyBadger<C, NodeUid>
|
||||
impl<C, N> DistAlgorithm for DynamicHoneyBadger<C, N>
|
||||
where
|
||||
C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash,
|
||||
NodeUid: Eq + Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Rand,
|
||||
C: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
N: NodeUidT + Serialize + for<'r> Deserialize<'r> + Rand,
|
||||
{
|
||||
type NodeUid = NodeUid;
|
||||
type Input = Input<C, NodeUid>;
|
||||
type Output = Batch<C, NodeUid>;
|
||||
type Message = Message<NodeUid>;
|
||||
type NodeUid = N;
|
||||
type Input = Input<C, N>;
|
||||
type Output = Batch<C, N>;
|
||||
type Message = Message<N>;
|
||||
type Error = Error;
|
||||
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<C, NodeUid>> {
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<C, N>> {
|
||||
// User contributions are forwarded to `HoneyBadger` right away. Votes are signed and
|
||||
// broadcast.
|
||||
match input {
|
||||
|
@ -59,11 +58,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_message(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
message: Self::Message,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<C, N>> {
|
||||
let epoch = message.start_epoch();
|
||||
if epoch < self.start_epoch {
|
||||
// Obsolete message.
|
||||
|
@ -93,18 +88,18 @@ where
|
|||
false
|
||||
}
|
||||
|
||||
fn our_id(&self) -> &NodeUid {
|
||||
fn our_id(&self) -> &N {
|
||||
self.netinfo.our_uid()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, NodeUid> DynamicHoneyBadger<C, NodeUid>
|
||||
impl<C, N> DynamicHoneyBadger<C, N>
|
||||
where
|
||||
C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash,
|
||||
NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash + Rand,
|
||||
C: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
N: NodeUidT + Serialize + for<'r> Deserialize<'r> + Rand,
|
||||
{
|
||||
/// Returns a new `DynamicHoneyBadgerBuilder`.
|
||||
pub fn builder() -> DynamicHoneyBadgerBuilder<C, NodeUid> {
|
||||
pub fn builder() -> DynamicHoneyBadgerBuilder<C, N> {
|
||||
DynamicHoneyBadgerBuilder::new()
|
||||
}
|
||||
|
||||
|
@ -114,7 +109,7 @@ where
|
|||
}
|
||||
|
||||
/// Proposes a contribution in the current epoch.
|
||||
pub fn propose(&mut self, contrib: C) -> Result<Step<C, NodeUid>> {
|
||||
pub fn propose(&mut self, contrib: C) -> Result<Step<C, N>> {
|
||||
let step = self
|
||||
.honey_badger
|
||||
.input(InternalContrib {
|
||||
|
@ -127,7 +122,7 @@ where
|
|||
}
|
||||
|
||||
/// Cast a vote to change the set of validators.
|
||||
pub fn vote_for(&mut self, change: Change<NodeUid>) -> Result<Step<C, NodeUid>> {
|
||||
pub fn vote_for(&mut self, change: Change<N>) -> Result<Step<C, N>> {
|
||||
if !self.netinfo.is_validator() {
|
||||
return Ok(Step::default()); // TODO: Return an error?
|
||||
}
|
||||
|
@ -137,7 +132,7 @@ where
|
|||
}
|
||||
|
||||
/// Returns the information about the node IDs in the network, and the cryptographic keys.
|
||||
pub fn netinfo(&self) -> &NetworkInfo<NodeUid> {
|
||||
pub fn netinfo(&self) -> &NetworkInfo<N> {
|
||||
&self.netinfo
|
||||
}
|
||||
|
||||
|
@ -172,9 +167,9 @@ where
|
|||
/// Handles a message for the `HoneyBadger` instance.
|
||||
fn handle_honey_badger_message(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
message: HbMessage<NodeUid>,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
sender_id: &N,
|
||||
message: HbMessage<N>,
|
||||
) -> Result<Step<C, N>> {
|
||||
if !self.netinfo.is_node_validator(sender_id) {
|
||||
info!("Unknown sender {:?} of message {:?}", sender_id, message);
|
||||
return Err(ErrorKind::UnknownSender.into());
|
||||
|
@ -191,10 +186,10 @@ where
|
|||
/// messages are only handled once they appear in a batch output from Honey Badger.
|
||||
fn handle_key_gen_message(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
kg_msg: KeyGenMessage,
|
||||
sig: Signature,
|
||||
) -> Result<FaultLog<NodeUid>> {
|
||||
) -> Result<FaultLog<N>> {
|
||||
if !self.verify_signature(sender_id, &sig, &kg_msg)? {
|
||||
info!("Invalid signature from {:?} for: {:?}.", sender_id, kg_msg);
|
||||
let fault_kind = FaultKind::InvalidKeyGenMessageSignature;
|
||||
|
@ -234,9 +229,9 @@ where
|
|||
/// Processes all pending batches output by Honey Badger.
|
||||
fn process_output(
|
||||
&mut self,
|
||||
hb_step: honey_badger::Step<InternalContrib<C, NodeUid>, NodeUid>,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
let mut step: Step<C, NodeUid> = Step::default();
|
||||
hb_step: honey_badger::Step<InternalContrib<C, N>, N>,
|
||||
) -> Result<Step<C, N>> {
|
||||
let mut step: Step<C, N> = Step::default();
|
||||
let start_epoch = self.start_epoch;
|
||||
let output = step.extend_with(hb_step, |hb_msg| Message::HoneyBadger(start_epoch, hb_msg));
|
||||
for hb_batch in output {
|
||||
|
@ -302,11 +297,7 @@ where
|
|||
|
||||
/// If the winner of the vote has changed, restarts Key Generation for the set of nodes implied
|
||||
/// by the current change.
|
||||
pub(super) fn update_key_gen(
|
||||
&mut self,
|
||||
epoch: u64,
|
||||
change: &Change<NodeUid>,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
pub(super) fn update_key_gen(&mut self, epoch: u64, change: &Change<N>) -> Result<Step<C, N>> {
|
||||
if self.key_gen_state.as_ref().map(|kgs| &kgs.change) == Some(change) {
|
||||
return Ok(Step::default()); // The change is the same as before. Continue DKG as is.
|
||||
}
|
||||
|
@ -347,8 +338,8 @@ where
|
|||
}
|
||||
|
||||
/// Handles a `Part` message that was output by Honey Badger.
|
||||
fn handle_part(&mut self, sender_id: &NodeUid, part: Part) -> Result<Step<C, NodeUid>> {
|
||||
let handle = |kgs: &mut KeyGenState<NodeUid>| kgs.key_gen.handle_part(&sender_id, part);
|
||||
fn handle_part(&mut self, sender_id: &N, part: Part) -> Result<Step<C, N>> {
|
||||
let handle = |kgs: &mut KeyGenState<N>| kgs.key_gen.handle_part(&sender_id, part);
|
||||
match self.key_gen_state.as_mut().and_then(handle) {
|
||||
Some(PartOutcome::Valid(ack)) => self.send_transaction(KeyGenMessage::Ack(ack)),
|
||||
Some(PartOutcome::Invalid(fault_log)) => Ok(fault_log.into()),
|
||||
|
@ -357,7 +348,7 @@ where
|
|||
}
|
||||
|
||||
/// Handles an `Ack` message that was output by Honey Badger.
|
||||
fn handle_ack(&mut self, sender_id: &NodeUid, ack: Ack) -> Result<FaultLog<NodeUid>> {
|
||||
fn handle_ack(&mut self, sender_id: &N, ack: Ack) -> Result<FaultLog<N>> {
|
||||
if let Some(kgs) = self.key_gen_state.as_mut() {
|
||||
Ok(kgs.key_gen.handle_ack(sender_id, ack))
|
||||
} else {
|
||||
|
@ -366,7 +357,7 @@ where
|
|||
}
|
||||
|
||||
/// Signs and sends a `KeyGenMessage` and also tries to commit it.
|
||||
fn send_transaction(&mut self, kg_msg: KeyGenMessage) -> Result<Step<C, NodeUid>> {
|
||||
fn send_transaction(&mut self, kg_msg: KeyGenMessage) -> Result<Step<C, N>> {
|
||||
let ser =
|
||||
bincode::serialize(&kg_msg).map_err(|err| ErrorKind::SendTransactionBincode(*err))?;
|
||||
let sig = Box::new(self.netinfo.secret_key().sign(ser));
|
||||
|
@ -385,7 +376,7 @@ where
|
|||
/// We require the minimum number of completed proposals (`SyncKeyGen::is_ready`) and if a new
|
||||
/// node is joining, we require in addition that the new node's proposal is complete. That way
|
||||
/// the new node knows that it's key is secret, without having to trust any number of nodes.
|
||||
fn take_ready_key_gen(&mut self) -> Option<KeyGenState<NodeUid>> {
|
||||
fn take_ready_key_gen(&mut self) -> Option<KeyGenState<N>> {
|
||||
if self
|
||||
.key_gen_state
|
||||
.as_ref()
|
||||
|
@ -403,7 +394,7 @@ where
|
|||
/// This accepts signatures from both validators and the currently joining candidate, if any.
|
||||
fn verify_signature(
|
||||
&self,
|
||||
node_id: &NodeUid,
|
||||
node_id: &N,
|
||||
sig: &Signature,
|
||||
kg_msg: &KeyGenMessage,
|
||||
) -> Result<bool> {
|
||||
|
|
|
@ -65,12 +65,12 @@ mod votes;
|
|||
use crypto::{PublicKey, PublicKeySet, Signature};
|
||||
use rand::Rand;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use self::votes::{SignedVote, VoteCounter};
|
||||
use honey_badger::Message as HbMessage;
|
||||
use messaging;
|
||||
use sync_key_gen::{Ack, Part, SyncKeyGen};
|
||||
use traits::NodeUidT;
|
||||
|
||||
pub use self::batch::Batch;
|
||||
pub use self::builder::DynamicHoneyBadgerBuilder;
|
||||
|
@ -78,15 +78,15 @@ pub use self::change::{Change, ChangeState};
|
|||
pub use self::dynamic_honey_badger::DynamicHoneyBadger;
|
||||
pub use self::error::{Error, ErrorKind, Result};
|
||||
|
||||
pub type Step<C, NodeUid> = messaging::Step<DynamicHoneyBadger<C, NodeUid>>;
|
||||
pub type Step<C, N> = messaging::Step<DynamicHoneyBadger<C, N>>;
|
||||
|
||||
/// The user input for `DynamicHoneyBadger`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Input<C, NodeUid> {
|
||||
pub enum Input<C, N> {
|
||||
/// A user-defined contribution for the next epoch.
|
||||
User(C),
|
||||
/// A vote to change the set of validators.
|
||||
Change(Change<NodeUid>),
|
||||
Change(Change<N>),
|
||||
}
|
||||
|
||||
/// An internal message containing a vote for adding or removing a validator, or a message for key
|
||||
|
@ -102,16 +102,16 @@ pub enum KeyGenMessage {
|
|||
|
||||
/// A message sent to or received from another node's Honey Badger instance.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum Message<NodeUid: Rand> {
|
||||
pub enum Message<N: Rand> {
|
||||
/// A message belonging to the `HoneyBadger` algorithm started in the given epoch.
|
||||
HoneyBadger(u64, HbMessage<NodeUid>),
|
||||
HoneyBadger(u64, HbMessage<N>),
|
||||
/// A transaction to be committed, signed by a node.
|
||||
KeyGen(u64, KeyGenMessage, Box<Signature>),
|
||||
/// A vote to be committed, signed by a validator.
|
||||
SignedVote(SignedVote<NodeUid>),
|
||||
SignedVote(SignedVote<N>),
|
||||
}
|
||||
|
||||
impl<NodeUid: Rand> Message<NodeUid> {
|
||||
impl<N: Rand> Message<N> {
|
||||
fn start_epoch(&self) -> u64 {
|
||||
match *self {
|
||||
Message::HoneyBadger(epoch, _) => epoch,
|
||||
|
@ -133,31 +133,31 @@ impl<NodeUid: Rand> Message<NodeUid> {
|
|||
/// of voting and key generation after a specific epoch, so that the new node will be in sync if it
|
||||
/// joins in the next one.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct JoinPlan<NodeUid: Ord> {
|
||||
pub struct JoinPlan<N: Ord> {
|
||||
/// The first epoch the new node will observe.
|
||||
epoch: u64,
|
||||
/// The current change. If `InProgress`, key generation for it is beginning at `epoch`.
|
||||
change: ChangeState<NodeUid>,
|
||||
change: ChangeState<N>,
|
||||
/// The current public key set for threshold cryptography.
|
||||
pub_key_set: PublicKeySet,
|
||||
/// The public keys of the nodes taking part in key generation.
|
||||
pub_keys: BTreeMap<NodeUid, PublicKey>,
|
||||
pub_keys: BTreeMap<N, PublicKey>,
|
||||
}
|
||||
|
||||
/// The ongoing key generation, together with information about the validator change.
|
||||
#[derive(Debug)]
|
||||
struct KeyGenState<NodeUid> {
|
||||
struct KeyGenState<N> {
|
||||
/// The key generation instance.
|
||||
key_gen: SyncKeyGen<NodeUid>,
|
||||
key_gen: SyncKeyGen<N>,
|
||||
/// The change for which key generation is performed.
|
||||
change: Change<NodeUid>,
|
||||
change: Change<N>,
|
||||
/// The number of key generation messages received from the candidate. At most _N² + 1_ are
|
||||
/// accepted.
|
||||
candidate_msg_count: usize,
|
||||
}
|
||||
|
||||
impl<NodeUid: Ord + Clone + Debug> KeyGenState<NodeUid> {
|
||||
fn new(key_gen: SyncKeyGen<NodeUid>, change: Change<NodeUid>) -> Self {
|
||||
impl<N: NodeUidT> KeyGenState<N> {
|
||||
fn new(key_gen: SyncKeyGen<N>, change: Change<N>) -> Self {
|
||||
KeyGenState {
|
||||
key_gen,
|
||||
change,
|
||||
|
@ -168,12 +168,12 @@ impl<NodeUid: Ord + Clone + Debug> KeyGenState<NodeUid> {
|
|||
/// Returns `true` if the candidate's, if any, as well as enough validators' key generation
|
||||
/// parts have been completed.
|
||||
fn is_ready(&self) -> bool {
|
||||
let candidate_ready = |id: &NodeUid| self.key_gen.is_node_ready(id);
|
||||
let candidate_ready = |id: &N| self.key_gen.is_node_ready(id);
|
||||
self.key_gen.is_ready() && self.change.candidate().map_or(true, candidate_ready)
|
||||
}
|
||||
|
||||
/// If the node `node_id` is the currently joining candidate, returns its public key.
|
||||
fn candidate_key(&self, node_id: &NodeUid) -> Option<&PublicKey> {
|
||||
fn candidate_key(&self, node_id: &N) -> Option<&PublicKey> {
|
||||
match self.change {
|
||||
Change::Add(ref id, ref pk) if id == node_id => Some(pk),
|
||||
Change::Add(_, _) | Change::Remove(_) => None,
|
||||
|
@ -184,15 +184,15 @@ impl<NodeUid: Ord + Clone + Debug> KeyGenState<NodeUid> {
|
|||
/// The contribution for the internal `HoneyBadger` instance: this includes a user-defined
|
||||
/// application-level contribution as well as internal signed messages.
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Hash)]
|
||||
struct InternalContrib<C, NodeUid> {
|
||||
struct InternalContrib<C, N> {
|
||||
/// A user-defined contribution.
|
||||
contrib: C,
|
||||
/// Key generation messages that get committed via Honey Badger to communicate synchronously.
|
||||
key_gen_messages: Vec<SignedKeyGenMsg<NodeUid>>,
|
||||
key_gen_messages: Vec<SignedKeyGenMsg<N>>,
|
||||
/// Signed votes for validator set changes.
|
||||
votes: Vec<SignedVote<NodeUid>>,
|
||||
votes: Vec<SignedVote<N>>,
|
||||
}
|
||||
|
||||
/// A signed internal message.
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Hash, Clone)]
|
||||
struct SignedKeyGenMsg<NodeUid>(u64, NodeUid, KeyGenMessage, Signature);
|
||||
struct SignedKeyGenMsg<N>(u64, N, KeyGenMessage, Signature);
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bincode;
|
||||
|
@ -10,30 +8,31 @@ use serde::{Deserialize, Serialize};
|
|||
use super::{Change, ErrorKind, Result};
|
||||
use fault_log::{FaultKind, FaultLog};
|
||||
use messaging::NetworkInfo;
|
||||
use traits::NodeUidT;
|
||||
|
||||
/// A buffer and counter collecting pending and committed votes for validator set changes.
|
||||
///
|
||||
/// This is reset whenever the set of validators changes or a change reaches _f + 1_ votes. We call
|
||||
/// the epochs since the last reset the current _era_.
|
||||
#[derive(Debug)]
|
||||
pub struct VoteCounter<NodeUid> {
|
||||
pub struct VoteCounter<N> {
|
||||
/// Shared network data.
|
||||
netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
netinfo: Arc<NetworkInfo<N>>,
|
||||
/// The epoch when voting was reset.
|
||||
era: u64,
|
||||
/// Pending node transactions that we will propose in the next epoch.
|
||||
pending: BTreeMap<NodeUid, SignedVote<NodeUid>>,
|
||||
pending: BTreeMap<N, SignedVote<N>>,
|
||||
/// Collected votes for adding or removing nodes. Each node has one vote, and casting another
|
||||
/// vote revokes the previous one.
|
||||
committed: BTreeMap<NodeUid, Vote<NodeUid>>,
|
||||
committed: BTreeMap<N, Vote<N>>,
|
||||
}
|
||||
|
||||
impl<NodeUid> VoteCounter<NodeUid>
|
||||
impl<N> VoteCounter<N>
|
||||
where
|
||||
NodeUid: Eq + Hash + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r>,
|
||||
N: NodeUidT + Serialize + for<'r> Deserialize<'r>,
|
||||
{
|
||||
/// Creates a new `VoteCounter` object with empty buffer and counter.
|
||||
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>, era: u64) -> Self {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<N>>, era: u64) -> Self {
|
||||
VoteCounter {
|
||||
era,
|
||||
netinfo,
|
||||
|
@ -43,7 +42,7 @@ where
|
|||
}
|
||||
|
||||
/// Creates a signed vote for the given change, and inserts it into the pending votes buffer.
|
||||
pub fn sign_vote_for(&mut self, change: Change<NodeUid>) -> Result<&SignedVote<NodeUid>> {
|
||||
pub fn sign_vote_for(&mut self, change: Change<N>) -> Result<&SignedVote<N>> {
|
||||
let voter = self.netinfo.our_uid().clone();
|
||||
let vote = Vote {
|
||||
change,
|
||||
|
@ -64,9 +63,9 @@ where
|
|||
/// Inserts a pending vote into the buffer, if it has a higher number than the existing one.
|
||||
pub fn add_pending_vote(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
signed_vote: SignedVote<NodeUid>,
|
||||
) -> Result<FaultLog<NodeUid>> {
|
||||
sender_id: &N,
|
||||
signed_vote: SignedVote<N>,
|
||||
) -> Result<FaultLog<N>> {
|
||||
if signed_vote.vote.era != self.era
|
||||
|| self
|
||||
.pending
|
||||
|
@ -87,7 +86,7 @@ where
|
|||
|
||||
/// Returns an iterator over all pending votes that are newer than their voter's committed
|
||||
/// vote.
|
||||
pub fn pending_votes(&self) -> impl Iterator<Item = &SignedVote<NodeUid>> {
|
||||
pub fn pending_votes(&self) -> impl Iterator<Item = &SignedVote<N>> {
|
||||
self.pending.values().filter(move |signed_vote| {
|
||||
self.committed
|
||||
.get(&signed_vote.voter)
|
||||
|
@ -98,11 +97,11 @@ where
|
|||
// TODO: Document and return fault logs?
|
||||
pub fn add_committed_votes<I>(
|
||||
&mut self,
|
||||
proposer_id: &NodeUid,
|
||||
proposer_id: &N,
|
||||
signed_votes: I,
|
||||
) -> Result<FaultLog<NodeUid>>
|
||||
) -> Result<FaultLog<N>>
|
||||
where
|
||||
I: IntoIterator<Item = SignedVote<NodeUid>>,
|
||||
I: IntoIterator<Item = SignedVote<N>>,
|
||||
{
|
||||
let mut fault_log = FaultLog::new();
|
||||
for signed_vote in signed_votes {
|
||||
|
@ -114,9 +113,9 @@ where
|
|||
/// Inserts a committed vote into the counter, if it has a higher number than the existing one.
|
||||
pub fn add_committed_vote(
|
||||
&mut self,
|
||||
proposer_id: &NodeUid,
|
||||
signed_vote: SignedVote<NodeUid>,
|
||||
) -> Result<FaultLog<NodeUid>> {
|
||||
proposer_id: &N,
|
||||
signed_vote: SignedVote<N>,
|
||||
) -> Result<FaultLog<N>> {
|
||||
if self
|
||||
.committed
|
||||
.get(&signed_vote.voter)
|
||||
|
@ -135,8 +134,8 @@ where
|
|||
}
|
||||
|
||||
/// Returns the change that has at least _f + 1_ votes, if any.
|
||||
pub fn compute_winner(&self) -> Option<&Change<NodeUid>> {
|
||||
let mut vote_counts: HashMap<&Change<NodeUid>, usize> = HashMap::new();
|
||||
pub fn compute_winner(&self) -> Option<&Change<N>> {
|
||||
let mut vote_counts: HashMap<&Change<N>, usize> = HashMap::new();
|
||||
for vote in self.committed.values() {
|
||||
let change = &vote.change;
|
||||
let entry = vote_counts.entry(change).or_insert(0);
|
||||
|
@ -149,7 +148,7 @@ where
|
|||
}
|
||||
|
||||
/// Returns `true` if the signature is valid.
|
||||
fn validate(&self, signed_vote: &SignedVote<NodeUid>) -> Result<bool> {
|
||||
fn validate(&self, signed_vote: &SignedVote<N>) -> Result<bool> {
|
||||
let ser_vote =
|
||||
bincode::serialize(&signed_vote.vote).map_err(|err| ErrorKind::ValidateBincode(*err))?;
|
||||
let pk_opt = self.netinfo.public_key(&signed_vote.voter);
|
||||
|
@ -159,9 +158,9 @@ where
|
|||
|
||||
/// A vote fore removing or adding a validator.
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Hash, Clone)]
|
||||
struct Vote<NodeUid> {
|
||||
struct Vote<N> {
|
||||
/// The change this vote is for.
|
||||
change: Change<NodeUid>,
|
||||
change: Change<N>,
|
||||
/// The epoch in which the current era began.
|
||||
era: u64,
|
||||
/// The vote number: VoteCounter can be changed by casting another vote with a higher number.
|
||||
|
@ -170,18 +169,18 @@ struct Vote<NodeUid> {
|
|||
|
||||
/// A signed vote for removing or adding a validator.
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Hash, Clone)]
|
||||
pub struct SignedVote<NodeUid> {
|
||||
vote: Vote<NodeUid>,
|
||||
voter: NodeUid,
|
||||
pub struct SignedVote<N> {
|
||||
vote: Vote<N>,
|
||||
voter: N,
|
||||
sig: Signature,
|
||||
}
|
||||
|
||||
impl<NodeUid> SignedVote<NodeUid> {
|
||||
impl<N> SignedVote<N> {
|
||||
pub fn era(&self) -> u64 {
|
||||
self.vote.era
|
||||
}
|
||||
|
||||
pub fn voter(&self) -> &NodeUid {
|
||||
pub fn voter(&self) -> &N {
|
||||
&self.voter
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,52 +52,52 @@ pub enum FaultKind {
|
|||
/// describes which node is faulty (`node_id`) and which faulty behavior
|
||||
/// that the node exhibited ('kind').
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Fault<NodeUid> {
|
||||
pub node_id: NodeUid,
|
||||
pub struct Fault<N> {
|
||||
pub node_id: N,
|
||||
pub kind: FaultKind,
|
||||
}
|
||||
|
||||
impl<NodeUid> Fault<NodeUid> {
|
||||
pub fn new(node_id: NodeUid, kind: FaultKind) -> Self {
|
||||
impl<N> Fault<N> {
|
||||
pub fn new(node_id: N, kind: FaultKind) -> Self {
|
||||
Fault { node_id, kind }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `FaultLog` where `self` is the first element in the log
|
||||
/// vector.
|
||||
impl<NodeUid> Into<FaultLog<NodeUid>> for Fault<NodeUid> {
|
||||
fn into(self) -> FaultLog<NodeUid> {
|
||||
impl<N> Into<FaultLog<N>> for Fault<N> {
|
||||
fn into(self) -> FaultLog<N> {
|
||||
FaultLog(vec![self])
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure used to contain reports of faulty node behavior.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct FaultLog<NodeUid>(pub Vec<Fault<NodeUid>>);
|
||||
pub struct FaultLog<N>(pub Vec<Fault<N>>);
|
||||
|
||||
impl<NodeUid> FaultLog<NodeUid> {
|
||||
impl<N> FaultLog<N> {
|
||||
/// Creates an empty `FaultLog`.
|
||||
pub fn new() -> Self {
|
||||
FaultLog::default()
|
||||
}
|
||||
|
||||
/// Creates a new `FaultLog` initialized with a single log.
|
||||
pub fn init(node_id: NodeUid, kind: FaultKind) -> Self {
|
||||
pub fn init(node_id: N, kind: FaultKind) -> Self {
|
||||
Fault::new(node_id, kind).into()
|
||||
}
|
||||
|
||||
/// Creates a new `Fault` and pushes it onto the fault log.
|
||||
pub fn append(&mut self, node_id: NodeUid, kind: FaultKind) {
|
||||
pub fn append(&mut self, node_id: N, kind: FaultKind) {
|
||||
self.0.push(Fault::new(node_id, kind));
|
||||
}
|
||||
|
||||
/// Consumes `new_logs`, appending its logs onto the end of `self`.
|
||||
pub fn extend(&mut self, new_logs: FaultLog<NodeUid>) {
|
||||
pub fn extend(&mut self, new_logs: FaultLog<N>) {
|
||||
self.0.extend(new_logs.0);
|
||||
}
|
||||
|
||||
/// Consumes `self`, appending its logs onto the end of `logs`.
|
||||
pub fn merge_into(self, logs: &mut FaultLog<NodeUid>) {
|
||||
pub fn merge_into(self, logs: &mut FaultLog<N>) {
|
||||
logs.extend(self);
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ impl<NodeUid> FaultLog<NodeUid> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<NodeUid> Default for FaultLog<NodeUid> {
|
||||
impl<N> Default for FaultLog<N> {
|
||||
fn default() -> Self {
|
||||
FaultLog(vec![])
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use traits::NodeUidT;
|
||||
|
||||
/// A batch of contributions the algorithm has output.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Batch<C, NodeUid> {
|
||||
pub struct Batch<C, N> {
|
||||
pub epoch: u64,
|
||||
pub contributions: BTreeMap<NodeUid, C>,
|
||||
pub contributions: BTreeMap<N, C>,
|
||||
}
|
||||
|
||||
impl<C, NodeUid: Ord> Batch<C, NodeUid> {
|
||||
impl<C, N: NodeUidT> Batch<C, N> {
|
||||
/// Returns an iterator over references to all transactions included in the batch.
|
||||
pub fn iter<'a>(&'a self) -> impl Iterator<Item = <&'a C as IntoIterator>::Item>
|
||||
where
|
||||
|
@ -25,25 +27,25 @@ impl<C, NodeUid: Ord> Batch<C, NodeUid> {
|
|||
}
|
||||
|
||||
/// Returns the number of transactions in the batch (without detecting duplicates).
|
||||
pub fn len<Tx>(&self) -> usize
|
||||
pub fn len<T>(&self) -> usize
|
||||
where
|
||||
C: AsRef<[Tx]>,
|
||||
C: AsRef<[T]>,
|
||||
{
|
||||
self.contributions
|
||||
.values()
|
||||
.map(C::as_ref)
|
||||
.map(<[Tx]>::len)
|
||||
.map(<[T]>::len)
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Returns `true` if the batch contains no transactions.
|
||||
pub fn is_empty<Tx>(&self) -> bool
|
||||
pub fn is_empty<T>(&self) -> bool
|
||||
where
|
||||
C: AsRef<[Tx]>,
|
||||
C: AsRef<[T]>,
|
||||
{
|
||||
self.contributions
|
||||
.values()
|
||||
.map(C::as_ref)
|
||||
.all(<[Tx]>::is_empty)
|
||||
.all(<[T]>::is_empty)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -9,24 +7,25 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use super::HoneyBadger;
|
||||
use messaging::NetworkInfo;
|
||||
use traits::{Contribution, NodeUidT};
|
||||
|
||||
/// A Honey Badger builder, to configure the parameters and create new instances of `HoneyBadger`.
|
||||
pub struct HoneyBadgerBuilder<C, NodeUid> {
|
||||
pub struct HoneyBadgerBuilder<C, N> {
|
||||
/// Shared network data.
|
||||
netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
netinfo: Arc<NetworkInfo<N>>,
|
||||
/// The maximum number of future epochs for which we handle messages simultaneously.
|
||||
max_future_epochs: usize,
|
||||
_phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C, NodeUid> HoneyBadgerBuilder<C, NodeUid>
|
||||
impl<C, N> HoneyBadgerBuilder<C, N>
|
||||
where
|
||||
C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq,
|
||||
NodeUid: Ord + Clone + Debug + Rand,
|
||||
C: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
N: NodeUidT + Rand,
|
||||
{
|
||||
/// Returns a new `HoneyBadgerBuilder` configured to use the node IDs and cryptographic keys
|
||||
/// specified by `netinfo`.
|
||||
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>) -> Self {
|
||||
pub fn new(netinfo: Arc<NetworkInfo<N>>) -> Self {
|
||||
HoneyBadgerBuilder {
|
||||
netinfo,
|
||||
max_future_epochs: 3,
|
||||
|
@ -41,7 +40,7 @@ where
|
|||
}
|
||||
|
||||
/// Creates a new Honey Badger instance.
|
||||
pub fn build(&self) -> HoneyBadger<C, NodeUid> {
|
||||
pub fn build(&self) -> HoneyBadger<C, N> {
|
||||
HoneyBadger {
|
||||
netinfo: self.netinfo.clone(),
|
||||
epoch: 0,
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use std::collections::btree_map::Entry;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
@ -16,57 +14,53 @@ use super::{Batch, Error, ErrorKind, HoneyBadgerBuilder, Message, MessageContent
|
|||
use common_subset::{self, CommonSubset};
|
||||
use fault_log::{Fault, FaultKind, FaultLog};
|
||||
use messaging::{self, DistAlgorithm, NetworkInfo, Target};
|
||||
use traits::{Contribution, NodeUidT};
|
||||
|
||||
/// An instance of the Honey Badger Byzantine fault tolerant consensus algorithm.
|
||||
#[derive(Debug)]
|
||||
pub struct HoneyBadger<C, NodeUid: Rand> {
|
||||
pub struct HoneyBadger<C, N: Rand> {
|
||||
/// Shared network data.
|
||||
pub(super) netinfo: Arc<NetworkInfo<NodeUid>>,
|
||||
pub(super) netinfo: Arc<NetworkInfo<N>>,
|
||||
/// The earliest epoch from which we have not yet received output.
|
||||
pub(super) epoch: u64,
|
||||
/// Whether we have already submitted a proposal for the current epoch.
|
||||
pub(super) has_input: bool,
|
||||
/// The Asynchronous Common Subset instance that decides which nodes' transactions to include,
|
||||
/// indexed by epoch.
|
||||
pub(super) common_subsets: BTreeMap<u64, CommonSubset<NodeUid>>,
|
||||
pub(super) common_subsets: BTreeMap<u64, CommonSubset<N>>,
|
||||
/// The maximum number of `CommonSubset` instances that we run simultaneously.
|
||||
pub(super) max_future_epochs: u64,
|
||||
/// Messages for future epochs that couldn't be handled yet.
|
||||
pub(super) incoming_queue: BTreeMap<u64, Vec<(NodeUid, MessageContent<NodeUid>)>>,
|
||||
pub(super) incoming_queue: BTreeMap<u64, Vec<(N, MessageContent<N>)>>,
|
||||
/// Received decryption shares for an epoch. Each decryption share has a sender and a
|
||||
/// proposer. The outer `BTreeMap` has epochs as its key. The next `BTreeMap` has proposers as
|
||||
/// its key. The inner `BTreeMap` has the sender as its key.
|
||||
pub(super) received_shares:
|
||||
BTreeMap<u64, BTreeMap<NodeUid, BTreeMap<NodeUid, DecryptionShare>>>,
|
||||
pub(super) received_shares: BTreeMap<u64, BTreeMap<N, BTreeMap<N, DecryptionShare>>>,
|
||||
/// Decoded accepted proposals.
|
||||
pub(super) decrypted_contributions: BTreeMap<NodeUid, Vec<u8>>,
|
||||
pub(super) decrypted_contributions: BTreeMap<N, Vec<u8>>,
|
||||
/// Ciphertexts output by Common Subset in an epoch.
|
||||
pub(super) ciphertexts: BTreeMap<u64, BTreeMap<NodeUid, Ciphertext>>,
|
||||
pub(super) ciphertexts: BTreeMap<u64, BTreeMap<N, Ciphertext>>,
|
||||
pub(super) _phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
pub type Step<C, NodeUid> = messaging::Step<HoneyBadger<C, NodeUid>>;
|
||||
pub type Step<C, N> = messaging::Step<HoneyBadger<C, N>>;
|
||||
|
||||
impl<C, NodeUid> DistAlgorithm for HoneyBadger<C, NodeUid>
|
||||
impl<C, N> DistAlgorithm for HoneyBadger<C, N>
|
||||
where
|
||||
C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq,
|
||||
NodeUid: Ord + Clone + Debug + Rand,
|
||||
C: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
N: NodeUidT + Rand,
|
||||
{
|
||||
type NodeUid = NodeUid;
|
||||
type NodeUid = N;
|
||||
type Input = C;
|
||||
type Output = Batch<C, NodeUid>;
|
||||
type Message = Message<NodeUid>;
|
||||
type Output = Batch<C, N>;
|
||||
type Message = Message<N>;
|
||||
type Error = Error;
|
||||
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<C, NodeUid>> {
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<C, N>> {
|
||||
self.propose(&input)
|
||||
}
|
||||
|
||||
fn handle_message(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
message: Self::Message,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<C, N>> {
|
||||
if !self.netinfo.is_node_validator(sender_id) {
|
||||
return Err(ErrorKind::UnknownSender.into());
|
||||
}
|
||||
|
@ -87,24 +81,24 @@ where
|
|||
false
|
||||
}
|
||||
|
||||
fn our_id(&self) -> &NodeUid {
|
||||
fn our_id(&self) -> &N {
|
||||
self.netinfo.our_uid()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, NodeUid> HoneyBadger<C, NodeUid>
|
||||
impl<C, N> HoneyBadger<C, N>
|
||||
where
|
||||
C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq,
|
||||
NodeUid: Ord + Clone + Debug + Rand,
|
||||
C: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
N: NodeUidT + Rand,
|
||||
{
|
||||
/// Returns a new `HoneyBadgerBuilder` configured to use the node IDs and cryptographic keys
|
||||
/// specified by `netinfo`.
|
||||
pub fn builder(netinfo: Arc<NetworkInfo<NodeUid>>) -> HoneyBadgerBuilder<C, NodeUid> {
|
||||
pub fn builder(netinfo: Arc<NetworkInfo<N>>) -> HoneyBadgerBuilder<C, N> {
|
||||
HoneyBadgerBuilder::new(netinfo)
|
||||
}
|
||||
|
||||
/// Proposes a new item in the current epoch.
|
||||
pub fn propose(&mut self, proposal: &C) -> Result<Step<C, NodeUid>> {
|
||||
pub fn propose(&mut self, proposal: &C) -> Result<Step<C, N>> {
|
||||
if !self.netinfo.is_validator() {
|
||||
return Ok(Step::default());
|
||||
}
|
||||
|
@ -143,10 +137,10 @@ where
|
|||
/// Handles a message for the given epoch.
|
||||
fn handle_message_content(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
epoch: u64,
|
||||
content: MessageContent<NodeUid>,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
content: MessageContent<N>,
|
||||
) -> Result<Step<C, N>> {
|
||||
match content {
|
||||
MessageContent::CommonSubset(cs_msg) => {
|
||||
self.handle_common_subset_message(sender_id, epoch, cs_msg)
|
||||
|
@ -160,10 +154,10 @@ where
|
|||
/// Handles a message for the common subset sub-algorithm.
|
||||
fn handle_common_subset_message(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
epoch: u64,
|
||||
message: common_subset::Message<NodeUid>,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
message: common_subset::Message<N>,
|
||||
) -> Result<Step<C, N>> {
|
||||
let cs_step = {
|
||||
// Borrow the instance for `epoch`, or create it.
|
||||
let cs = match self.common_subsets.entry(epoch) {
|
||||
|
@ -191,11 +185,11 @@ where
|
|||
/// Handles decryption shares sent by `HoneyBadger` instances.
|
||||
fn handle_decryption_share_message(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
epoch: u64,
|
||||
proposer_id: NodeUid,
|
||||
proposer_id: N,
|
||||
share: DecryptionShare,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
) -> Result<Step<C, N>> {
|
||||
if let Some(ciphertext) = self
|
||||
.ciphertexts
|
||||
.get(&epoch)
|
||||
|
@ -227,7 +221,7 @@ where
|
|||
/// has failed.
|
||||
fn verify_decryption_share(
|
||||
&self,
|
||||
sender_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
share: &DecryptionShare,
|
||||
ciphertext: &Ciphertext,
|
||||
) -> bool {
|
||||
|
@ -240,7 +234,7 @@ where
|
|||
|
||||
/// When contributions of transactions have been decrypted for all valid proposers in this
|
||||
/// epoch, moves those contributions into a batch, outputs the batch and updates the epoch.
|
||||
fn try_output_batch(&mut self) -> Result<Option<Step<C, NodeUid>>> {
|
||||
fn try_output_batch(&mut self) -> Result<Option<Step<C, N>>> {
|
||||
// Return if we don't have ciphertexts yet.
|
||||
let proposer_ids = match self.ciphertexts.get(&self.epoch) {
|
||||
Some(cts) => cts.keys().cloned().collect_vec(),
|
||||
|
@ -258,7 +252,7 @@ where
|
|||
let mut step = Step::default();
|
||||
|
||||
// Deserialize the output.
|
||||
let contributions: BTreeMap<NodeUid, C> =
|
||||
let contributions: BTreeMap<N, C> =
|
||||
mem::replace(&mut self.decrypted_contributions, BTreeMap::new())
|
||||
.into_iter()
|
||||
.flat_map(|(proposer_id, ser_contrib)| {
|
||||
|
@ -289,7 +283,7 @@ where
|
|||
}
|
||||
|
||||
/// Increments the epoch number and clears any state that is local to the finished epoch.
|
||||
fn update_epoch(&mut self) -> Result<Step<C, NodeUid>> {
|
||||
fn update_epoch(&mut self) -> Result<Step<C, N>> {
|
||||
// Clear the state of the old epoch.
|
||||
self.ciphertexts.remove(&self.epoch);
|
||||
self.received_shares.remove(&self.epoch);
|
||||
|
@ -309,7 +303,7 @@ where
|
|||
}
|
||||
|
||||
/// Tries to decrypt contributions from all proposers and output those in a batch.
|
||||
fn try_output_batches(&mut self) -> Result<Step<C, NodeUid>> {
|
||||
fn try_output_batches(&mut self) -> Result<Step<C, N>> {
|
||||
let mut step = Step::default();
|
||||
while let Some(new_step) = self.try_output_batch()? {
|
||||
step.extend(new_step);
|
||||
|
@ -318,7 +312,7 @@ where
|
|||
}
|
||||
|
||||
/// Tries to decrypt the contribution from a given proposer.
|
||||
fn try_decrypt_proposer_contribution(&mut self, proposer_id: NodeUid) -> bool {
|
||||
fn try_decrypt_proposer_contribution(&mut self, proposer_id: N) -> bool {
|
||||
if self.decrypted_contributions.contains_key(&proposer_id) {
|
||||
return true; // Already decrypted.
|
||||
}
|
||||
|
@ -356,9 +350,9 @@ where
|
|||
|
||||
fn send_decryption_shares(
|
||||
&mut self,
|
||||
cs_output: BTreeMap<NodeUid, Vec<u8>>,
|
||||
cs_output: BTreeMap<N, Vec<u8>>,
|
||||
epoch: u64,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
) -> Result<Step<C, N>> {
|
||||
let mut step = Step::default();
|
||||
let mut ciphertexts = BTreeMap::new();
|
||||
for (proposer_id, v) in cs_output {
|
||||
|
@ -399,10 +393,10 @@ where
|
|||
/// Sends decryption shares without verifying the ciphertext.
|
||||
fn send_decryption_share(
|
||||
&mut self,
|
||||
proposer_id: &NodeUid,
|
||||
proposer_id: &N,
|
||||
ciphertext: &Ciphertext,
|
||||
epoch: u64,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
) -> Result<Step<C, N>> {
|
||||
let share = self
|
||||
.netinfo
|
||||
.secret_key_share()
|
||||
|
@ -427,10 +421,10 @@ where
|
|||
/// senders with incorrect pending shares.
|
||||
fn verify_pending_decryption_shares(
|
||||
&self,
|
||||
proposer_id: &NodeUid,
|
||||
proposer_id: &N,
|
||||
ciphertext: &Ciphertext,
|
||||
epoch: u64,
|
||||
) -> (BTreeSet<NodeUid>, FaultLog<NodeUid>) {
|
||||
) -> (BTreeSet<N>, FaultLog<N>) {
|
||||
let mut incorrect_senders = BTreeSet::new();
|
||||
let mut fault_log = FaultLog::new();
|
||||
if let Some(sender_shares) = self
|
||||
|
@ -451,8 +445,8 @@ where
|
|||
|
||||
fn remove_incorrect_decryption_shares(
|
||||
&mut self,
|
||||
proposer_id: &NodeUid,
|
||||
incorrect_senders: BTreeSet<NodeUid>,
|
||||
proposer_id: &N,
|
||||
incorrect_senders: BTreeSet<N>,
|
||||
epoch: u64,
|
||||
) {
|
||||
if let Some(sender_shares) = self
|
||||
|
@ -472,9 +466,9 @@ where
|
|||
/// `epoch == Some(given_epoch)`.
|
||||
fn process_output(
|
||||
&mut self,
|
||||
cs_step: common_subset::Step<NodeUid>,
|
||||
cs_step: common_subset::Step<N>,
|
||||
epoch: u64,
|
||||
) -> Result<Step<C, NodeUid>> {
|
||||
) -> Result<Step<C, N>> {
|
||||
let mut step = Step::default();
|
||||
let mut cs_outputs = step.extend_with(cs_step, |cs_msg| {
|
||||
MessageContent::CommonSubset(cs_msg).with_epoch(epoch)
|
||||
|
|
|
@ -5,18 +5,18 @@ use common_subset;
|
|||
|
||||
/// The content of a `HoneyBadger` message. It should be further annotated with an epoch.
|
||||
#[derive(Clone, Debug, Deserialize, Rand, Serialize)]
|
||||
pub enum MessageContent<NodeUid: Rand> {
|
||||
pub enum MessageContent<N: Rand> {
|
||||
/// A message belonging to the common subset algorithm in the given epoch.
|
||||
CommonSubset(common_subset::Message<NodeUid>),
|
||||
CommonSubset(common_subset::Message<N>),
|
||||
/// A decrypted share of the output of `proposer_id`.
|
||||
DecryptionShare {
|
||||
proposer_id: NodeUid,
|
||||
proposer_id: N,
|
||||
share: DecryptionShare,
|
||||
},
|
||||
}
|
||||
|
||||
impl<NodeUid: Rand> MessageContent<NodeUid> {
|
||||
pub fn with_epoch(self, epoch: u64) -> Message<NodeUid> {
|
||||
impl<N: Rand> MessageContent<N> {
|
||||
pub fn with_epoch(self, epoch: u64) -> Message<N> {
|
||||
Message {
|
||||
epoch,
|
||||
content: self,
|
||||
|
@ -26,12 +26,12 @@ impl<NodeUid: Rand> MessageContent<NodeUid> {
|
|||
|
||||
/// A message sent to or received from another node's Honey Badger instance.
|
||||
#[derive(Clone, Debug, Deserialize, Rand, Serialize)]
|
||||
pub struct Message<NodeUid: Rand> {
|
||||
pub struct Message<N: Rand> {
|
||||
pub(super) epoch: u64,
|
||||
pub(super) content: MessageContent<NodeUid>,
|
||||
pub(super) content: MessageContent<N>,
|
||||
}
|
||||
|
||||
impl<NodeUid: Rand> Message<NodeUid> {
|
||||
impl<N: Rand> Message<N> {
|
||||
pub fn epoch(&self) -> u64 {
|
||||
self.epoch
|
||||
}
|
||||
|
|
18
src/lib.rs
18
src/lib.rs
|
@ -135,3 +135,21 @@ pub mod messaging;
|
|||
pub mod queueing_honey_badger;
|
||||
pub mod sync_key_gen;
|
||||
pub mod transaction_queue;
|
||||
|
||||
/// Common supertraits.
|
||||
pub mod traits {
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
/// A transaction, user message, etc.
|
||||
pub trait Contribution: Eq + Debug + Hash + Send + Sync {}
|
||||
impl<C> Contribution for C where C: Eq + Debug + Hash + Send + Sync {}
|
||||
|
||||
/// A peer node's unique identifier.
|
||||
pub trait NodeUidT: Eq + Ord + Clone + Debug + Hash + Send + Sync {}
|
||||
impl<N> NodeUidT for N where N: Eq + Ord + Clone + Debug + Hash + Send + Sync {}
|
||||
|
||||
/// Messages.
|
||||
pub trait Message: Debug + Send + Sync {}
|
||||
impl<M> Message for M where M: Debug + Send + Sync {}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::iter::once;
|
|||
|
||||
use crypto::{PublicKey, PublicKeySet, PublicKeyShare, SecretKey, SecretKeyShare};
|
||||
use fault_log::{Fault, FaultLog};
|
||||
use traits::{Message, NodeUidT};
|
||||
|
||||
/// Message sent by a given source.
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -57,7 +58,7 @@ impl<M, N> TargetedMessage<M, N> {
|
|||
pub struct Step<D>
|
||||
where
|
||||
D: DistAlgorithm,
|
||||
<D as DistAlgorithm>::NodeUid: Clone,
|
||||
<D as DistAlgorithm>::NodeUid: NodeUidT,
|
||||
{
|
||||
pub output: VecDeque<D::Output>,
|
||||
pub fault_log: FaultLog<D::NodeUid>,
|
||||
|
@ -67,7 +68,7 @@ where
|
|||
impl<D> Default for Step<D>
|
||||
where
|
||||
D: DistAlgorithm,
|
||||
<D as DistAlgorithm>::NodeUid: Clone,
|
||||
<D as DistAlgorithm>::NodeUid: NodeUidT,
|
||||
{
|
||||
fn default() -> Step<D> {
|
||||
Step {
|
||||
|
@ -80,7 +81,7 @@ where
|
|||
|
||||
impl<D: DistAlgorithm> Step<D>
|
||||
where
|
||||
<D as DistAlgorithm>::NodeUid: Clone,
|
||||
<D as DistAlgorithm>::NodeUid: NodeUidT,
|
||||
{
|
||||
/// Creates a new `Step` from the given collections.
|
||||
pub fn new(
|
||||
|
@ -184,14 +185,14 @@ impl<D: DistAlgorithm> From<TargetedMessage<D::Message, D::NodeUid>> for Step<D>
|
|||
/// A distributed algorithm that defines a message flow.
|
||||
pub trait DistAlgorithm {
|
||||
/// Unique node identifier.
|
||||
type NodeUid: Debug + Clone + Ord + Eq;
|
||||
type NodeUid: NodeUidT;
|
||||
/// The input provided by the user.
|
||||
type Input;
|
||||
/// The output type. Some algorithms return an output exactly once, others return multiple
|
||||
/// times.
|
||||
type Output;
|
||||
/// The messages that need to be exchanged between the instances in the participating nodes.
|
||||
type Message: Debug;
|
||||
type Message: Message;
|
||||
/// The errors that can occur during execution.
|
||||
type Error: Debug;
|
||||
|
||||
|
@ -218,8 +219,8 @@ pub trait DistAlgorithm {
|
|||
|
||||
/// Common data shared between algorithms: the nodes' IDs and key shares.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkInfo<NodeUid> {
|
||||
our_uid: NodeUid,
|
||||
pub struct NetworkInfo<N> {
|
||||
our_uid: N,
|
||||
num_nodes: usize,
|
||||
num_faulty: usize,
|
||||
is_validator: bool,
|
||||
|
@ -227,22 +228,22 @@ pub struct NetworkInfo<NodeUid> {
|
|||
secret_key_share: SecretKeyShare,
|
||||
secret_key: SecretKey,
|
||||
public_key_set: PublicKeySet,
|
||||
public_key_shares: BTreeMap<NodeUid, PublicKeyShare>,
|
||||
public_keys: BTreeMap<NodeUid, PublicKey>,
|
||||
node_indices: BTreeMap<NodeUid, usize>,
|
||||
public_key_shares: BTreeMap<N, PublicKeyShare>,
|
||||
public_keys: BTreeMap<N, PublicKey>,
|
||||
node_indices: BTreeMap<N, usize>,
|
||||
}
|
||||
|
||||
impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
||||
impl<N: NodeUidT> NetworkInfo<N> {
|
||||
pub fn new(
|
||||
our_uid: NodeUid,
|
||||
our_uid: N,
|
||||
secret_key_share: SecretKeyShare,
|
||||
public_key_set: PublicKeySet,
|
||||
secret_key: SecretKey,
|
||||
public_keys: BTreeMap<NodeUid, PublicKey>,
|
||||
public_keys: BTreeMap<N, PublicKey>,
|
||||
) -> Self {
|
||||
let num_nodes = public_keys.len();
|
||||
let is_validator = public_keys.contains_key(&our_uid);
|
||||
let node_indices: BTreeMap<NodeUid, usize> = public_keys
|
||||
let node_indices: BTreeMap<N, usize> = public_keys
|
||||
.keys()
|
||||
.enumerate()
|
||||
.map(|(n, id)| (id.clone(), n))
|
||||
|
@ -266,12 +267,12 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
|||
}
|
||||
|
||||
/// The ID of the node the algorithm runs on.
|
||||
pub fn our_uid(&self) -> &NodeUid {
|
||||
pub fn our_uid(&self) -> &N {
|
||||
&self.our_uid
|
||||
}
|
||||
|
||||
/// ID of all nodes in the network.
|
||||
pub fn all_uids(&self) -> impl Iterator<Item = &NodeUid> {
|
||||
pub fn all_uids(&self) -> impl Iterator<Item = &N> {
|
||||
self.public_keys.keys()
|
||||
}
|
||||
|
||||
|
@ -308,27 +309,27 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
|||
}
|
||||
|
||||
/// Returns the public key share if a node with that ID exists, otherwise `None`.
|
||||
pub fn public_key_share(&self, id: &NodeUid) -> Option<&PublicKeyShare> {
|
||||
pub fn public_key_share(&self, id: &N) -> Option<&PublicKeyShare> {
|
||||
self.public_key_shares.get(id)
|
||||
}
|
||||
|
||||
/// Returns a map of all node IDs to their public key shares.
|
||||
pub fn public_key_share_map(&self) -> &BTreeMap<NodeUid, PublicKeyShare> {
|
||||
pub fn public_key_share_map(&self) -> &BTreeMap<N, PublicKeyShare> {
|
||||
&self.public_key_shares
|
||||
}
|
||||
|
||||
/// Returns a map of all node IDs to their public keys.
|
||||
pub fn public_key(&self, id: &NodeUid) -> Option<&PublicKey> {
|
||||
pub fn public_key(&self, id: &N) -> Option<&PublicKey> {
|
||||
self.public_keys.get(id)
|
||||
}
|
||||
|
||||
/// Returns a map of all node IDs to their public keys.
|
||||
pub fn public_key_map(&self) -> &BTreeMap<NodeUid, PublicKey> {
|
||||
pub fn public_key_map(&self) -> &BTreeMap<N, PublicKey> {
|
||||
&self.public_keys
|
||||
}
|
||||
|
||||
/// The index of a node in a canonical numbering of all nodes.
|
||||
pub fn node_index(&self, id: &NodeUid) -> Option<usize> {
|
||||
pub fn node_index(&self, id: &N) -> Option<usize> {
|
||||
self.node_indices.get(id).cloned()
|
||||
}
|
||||
|
||||
|
@ -350,14 +351,14 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
|||
|
||||
/// Returns `true` if the given node takes part in the consensus itself. If not, it is only an
|
||||
/// observer.
|
||||
pub fn is_node_validator(&self, uid: &NodeUid) -> bool {
|
||||
pub fn is_node_validator(&self, uid: &N) -> bool {
|
||||
self.public_keys.contains_key(uid)
|
||||
}
|
||||
|
||||
/// Generates a map of matching `NetworkInfo`s for testing.
|
||||
pub fn generate_map<I>(uids: I) -> BTreeMap<NodeUid, NetworkInfo<NodeUid>>
|
||||
pub fn generate_map<I>(uids: I) -> BTreeMap<N, NetworkInfo<N>>
|
||||
where
|
||||
I: IntoIterator<Item = NodeUid>,
|
||||
I: IntoIterator<Item = N>,
|
||||
{
|
||||
use rand::{self, Rng};
|
||||
|
||||
|
@ -365,7 +366,7 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
|||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let all_uids: BTreeSet<NodeUid> = uids.into_iter().collect();
|
||||
let all_uids: BTreeSet<N> = uids.into_iter().collect();
|
||||
let num_faulty = (all_uids.len() - 1) / 3;
|
||||
|
||||
// Generate the keys for threshold cryptography.
|
||||
|
@ -381,7 +382,7 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
|||
.collect();
|
||||
|
||||
// Create the corresponding `NetworkInfo` for each node.
|
||||
let create_netinfo = |(i, uid): (usize, NodeUid)| {
|
||||
let create_netinfo = |(i, uid): (usize, N)| {
|
||||
let netinfo = NetworkInfo::new(
|
||||
uid.clone(),
|
||||
sk_set.secret_key_share(i),
|
||||
|
|
|
@ -23,9 +23,7 @@
|
|||
//! the same transaction multiple times.
|
||||
|
||||
use std::cmp;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::{self, Display};
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use failure::{Backtrace, Context, Fail};
|
||||
|
@ -34,6 +32,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use dynamic_honey_badger::{self, Batch as DhbBatch, DynamicHoneyBadger, Message};
|
||||
use messaging::{self, DistAlgorithm};
|
||||
use traits::{Contribution, NodeUidT};
|
||||
use transaction_queue::TransactionQueue;
|
||||
|
||||
pub use dynamic_honey_badger::{Change, ChangeState, Input};
|
||||
|
@ -95,24 +94,24 @@ pub type Result<T> = ::std::result::Result<T, Error>;
|
|||
|
||||
/// A Queueing Honey Badger builder, to configure the parameters and create new instances of
|
||||
/// `QueueingHoneyBadger`.
|
||||
pub struct QueueingHoneyBadgerBuilder<Tx, NodeUid: Rand> {
|
||||
pub struct QueueingHoneyBadgerBuilder<T, N: Rand> {
|
||||
/// Shared network data.
|
||||
dyn_hb: DynamicHoneyBadger<Vec<Tx>, NodeUid>,
|
||||
dyn_hb: DynamicHoneyBadger<Vec<T>, N>,
|
||||
/// The target number of transactions to be included in each batch.
|
||||
batch_size: usize,
|
||||
_phantom: PhantomData<Tx>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<Tx, NodeUid> QueueingHoneyBadgerBuilder<Tx, NodeUid>
|
||||
impl<T, N> QueueingHoneyBadgerBuilder<T, N>
|
||||
where
|
||||
Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone,
|
||||
NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash + Rand,
|
||||
T: Contribution + Serialize + for<'r> Deserialize<'r> + Clone,
|
||||
N: NodeUidT + Serialize + for<'r> Deserialize<'r> + Rand,
|
||||
{
|
||||
/// Returns a new `QueueingHoneyBadgerBuilder` configured to use the node IDs and cryptographic
|
||||
/// keys specified by `netinfo`.
|
||||
// TODO: Make it easier to build a `QueueingHoneyBadger` with a `JoinPlan`. Handle `Step`
|
||||
// conversion internally.
|
||||
pub fn new(dyn_hb: DynamicHoneyBadger<Vec<Tx>, NodeUid>) -> Self {
|
||||
pub fn new(dyn_hb: DynamicHoneyBadger<Vec<T>, N>) -> Self {
|
||||
// TODO: Use the defaults from `HoneyBadgerBuilder`.
|
||||
QueueingHoneyBadgerBuilder {
|
||||
dyn_hb,
|
||||
|
@ -128,9 +127,9 @@ where
|
|||
}
|
||||
|
||||
/// Creates a new Queueing Honey Badger instance with an empty buffer.
|
||||
pub fn build(self) -> (QueueingHoneyBadger<Tx, NodeUid>, Step<Tx, NodeUid>)
|
||||
pub fn build(self) -> (QueueingHoneyBadger<T, N>, Step<T, N>)
|
||||
where
|
||||
Tx: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq,
|
||||
T: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
{
|
||||
self.build_with_transactions(None)
|
||||
.expect("building without transactions cannot fail")
|
||||
|
@ -141,10 +140,10 @@ where
|
|||
pub fn build_with_transactions<TI>(
|
||||
self,
|
||||
txs: TI,
|
||||
) -> Result<(QueueingHoneyBadger<Tx, NodeUid>, Step<Tx, NodeUid>)>
|
||||
) -> Result<(QueueingHoneyBadger<T, N>, Step<T, N>)>
|
||||
where
|
||||
TI: IntoIterator<Item = Tx>,
|
||||
Tx: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq,
|
||||
TI: IntoIterator<Item = T>,
|
||||
T: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
{
|
||||
let queue = TransactionQueue(txs.into_iter().collect());
|
||||
let mut qhb = QueueingHoneyBadger {
|
||||
|
@ -160,33 +159,33 @@ where
|
|||
/// A Honey Badger instance that can handle adding and removing nodes and manages a transaction
|
||||
/// queue.
|
||||
#[derive(Debug)]
|
||||
pub struct QueueingHoneyBadger<Tx, NodeUid>
|
||||
pub struct QueueingHoneyBadger<T, N>
|
||||
where
|
||||
Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash,
|
||||
NodeUid: Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug + Rand,
|
||||
T: Contribution + Serialize + for<'r> Deserialize<'r>,
|
||||
N: NodeUidT + Serialize + for<'r> Deserialize<'r> + Rand,
|
||||
{
|
||||
/// The target number of transactions to be included in each batch.
|
||||
batch_size: usize,
|
||||
/// The internal `DynamicHoneyBadger` instance.
|
||||
dyn_hb: DynamicHoneyBadger<Vec<Tx>, NodeUid>,
|
||||
dyn_hb: DynamicHoneyBadger<Vec<T>, N>,
|
||||
/// The queue of pending transactions that haven't been output in a batch yet.
|
||||
queue: TransactionQueue<Tx>,
|
||||
queue: TransactionQueue<T>,
|
||||
}
|
||||
|
||||
pub type Step<Tx, NodeUid> = messaging::Step<QueueingHoneyBadger<Tx, NodeUid>>;
|
||||
pub type Step<T, N> = messaging::Step<QueueingHoneyBadger<T, N>>;
|
||||
|
||||
impl<Tx, NodeUid> DistAlgorithm for QueueingHoneyBadger<Tx, NodeUid>
|
||||
impl<T, N> DistAlgorithm for QueueingHoneyBadger<T, N>
|
||||
where
|
||||
Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone,
|
||||
NodeUid: Eq + Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Rand,
|
||||
T: Contribution + Serialize + for<'r> Deserialize<'r> + Clone,
|
||||
N: NodeUidT + Serialize + for<'r> Deserialize<'r> + Rand,
|
||||
{
|
||||
type NodeUid = NodeUid;
|
||||
type Input = Input<Tx, NodeUid>;
|
||||
type Output = Batch<Tx, NodeUid>;
|
||||
type Message = Message<NodeUid>;
|
||||
type NodeUid = N;
|
||||
type Input = Input<T, N>;
|
||||
type Output = Batch<T, N>;
|
||||
type Message = Message<N>;
|
||||
type Error = Error;
|
||||
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<Tx, NodeUid>> {
|
||||
fn input(&mut self, input: Self::Input) -> Result<Step<T, N>> {
|
||||
// User transactions are forwarded to `HoneyBadger` right away. Internal messages are
|
||||
// in addition signed and broadcast.
|
||||
let mut step = match input {
|
||||
|
@ -204,11 +203,7 @@ where
|
|||
Ok(step)
|
||||
}
|
||||
|
||||
fn handle_message(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
message: Self::Message,
|
||||
) -> Result<Step<Tx, NodeUid>> {
|
||||
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<T, N>> {
|
||||
let mut step = self
|
||||
.dyn_hb
|
||||
.handle_message(sender_id, message)
|
||||
|
@ -225,26 +220,24 @@ where
|
|||
false
|
||||
}
|
||||
|
||||
fn our_id(&self) -> &NodeUid {
|
||||
fn our_id(&self) -> &N {
|
||||
self.dyn_hb.our_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tx, NodeUid> QueueingHoneyBadger<Tx, NodeUid>
|
||||
impl<T, N> QueueingHoneyBadger<T, N>
|
||||
where
|
||||
Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone,
|
||||
NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash + Rand,
|
||||
T: Contribution + Serialize + for<'r> Deserialize<'r> + Clone,
|
||||
N: NodeUidT + Serialize + for<'r> Deserialize<'r> + Rand,
|
||||
{
|
||||
/// Returns a new `QueueingHoneyBadgerBuilder` configured to use the node IDs and cryptographic
|
||||
/// keys specified by `netinfo`.
|
||||
pub fn builder(
|
||||
dyn_hb: DynamicHoneyBadger<Vec<Tx>, NodeUid>,
|
||||
) -> QueueingHoneyBadgerBuilder<Tx, NodeUid> {
|
||||
pub fn builder(dyn_hb: DynamicHoneyBadger<Vec<T>, N>) -> QueueingHoneyBadgerBuilder<T, N> {
|
||||
QueueingHoneyBadgerBuilder::new(dyn_hb)
|
||||
}
|
||||
|
||||
/// Returns a reference to the internal `DynamicHoneyBadger` instance.
|
||||
pub fn dyn_hb(&self) -> &DynamicHoneyBadger<Vec<Tx>, NodeUid> {
|
||||
pub fn dyn_hb(&self) -> &DynamicHoneyBadger<Vec<T>, N> {
|
||||
&self.dyn_hb
|
||||
}
|
||||
|
||||
|
@ -259,7 +252,7 @@ where
|
|||
}
|
||||
|
||||
/// Initiates the next epoch by proposing a batch from the queue.
|
||||
fn propose(&mut self) -> Result<Step<Tx, NodeUid>> {
|
||||
fn propose(&mut self) -> Result<Step<T, N>> {
|
||||
let mut step = Step::default();
|
||||
while self.can_propose() {
|
||||
let amount = cmp::max(1, self.batch_size / self.dyn_hb.netinfo().num_nodes());
|
||||
|
@ -275,4 +268,4 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub type Batch<Tx, NodeUid> = DhbBatch<Vec<Tx>, NodeUid>;
|
||||
pub type Batch<T, N> = DhbBatch<Vec<T>, N>;
|
||||
|
|
|
@ -166,6 +166,7 @@ use crypto::serde_impl::field_vec::FieldWrap;
|
|||
use crypto::{Ciphertext, PublicKey, PublicKeySet, SecretKey, SecretKeyShare};
|
||||
use fault_log::{FaultKind, FaultLog};
|
||||
use messaging::NetworkInfo;
|
||||
use traits::NodeUidT;
|
||||
|
||||
// TODO: No need to send our own row and value to ourselves.
|
||||
|
||||
|
@ -228,7 +229,7 @@ impl ProposalState {
|
|||
}
|
||||
|
||||
/// The outcome of handling and verifying a `Part` message.
|
||||
pub enum PartOutcome<NodeUid: Clone> {
|
||||
pub enum PartOutcome<N: Clone> {
|
||||
/// The message was valid: the part of it that was encrypted to us matched the public
|
||||
/// commitment, so we can multicast an `Ack` message for it.
|
||||
Valid(Ack),
|
||||
|
@ -236,40 +237,40 @@ pub enum PartOutcome<NodeUid: Clone> {
|
|||
// fault is logged and passed onto the caller.
|
||||
/// The message was invalid: the part encrypted to us was malformed or didn't match the
|
||||
/// commitment. We now know that the proposer is faulty, and dont' send an `Ack`.
|
||||
Invalid(FaultLog<NodeUid>),
|
||||
Invalid(FaultLog<N>),
|
||||
}
|
||||
|
||||
/// A synchronous algorithm for dealerless distributed key generation.
|
||||
///
|
||||
/// It requires that all nodes handle all messages in the exact same order.
|
||||
#[derive(Debug)]
|
||||
pub struct SyncKeyGen<NodeUid> {
|
||||
pub struct SyncKeyGen<N> {
|
||||
/// Our node ID.
|
||||
our_uid: NodeUid,
|
||||
our_uid: N,
|
||||
/// Our node index.
|
||||
our_idx: Option<u64>,
|
||||
/// Our secret key.
|
||||
sec_key: SecretKey,
|
||||
/// The public keys of all nodes, by node index.
|
||||
pub_keys: BTreeMap<NodeUid, PublicKey>,
|
||||
pub_keys: BTreeMap<N, PublicKey>,
|
||||
/// Proposed bivariate polynomials.
|
||||
parts: BTreeMap<u64, ProposalState>,
|
||||
/// The degree of the generated polynomial.
|
||||
threshold: usize,
|
||||
}
|
||||
|
||||
impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
|
||||
impl<N: NodeUidT> SyncKeyGen<N> {
|
||||
/// Creates a new `SyncKeyGen` instance, together with the `Part` message that should be
|
||||
/// multicast to all nodes.
|
||||
///
|
||||
/// If we are not a validator but only an observer, no `Part` message is produced and no
|
||||
/// messages need to be sent.
|
||||
pub fn new(
|
||||
our_uid: NodeUid,
|
||||
our_uid: N,
|
||||
sec_key: SecretKey,
|
||||
pub_keys: BTreeMap<NodeUid, PublicKey>,
|
||||
pub_keys: BTreeMap<N, PublicKey>,
|
||||
threshold: usize,
|
||||
) -> (SyncKeyGen<NodeUid>, Option<Part>) {
|
||||
) -> (SyncKeyGen<N>, Option<Part>) {
|
||||
let our_idx = pub_keys
|
||||
.keys()
|
||||
.position(|uid| *uid == our_uid)
|
||||
|
@ -305,9 +306,9 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
|
|||
/// Note that `handle_part` also needs to explicitly be called with this instance's own `Part`.
|
||||
pub fn handle_part(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
sender_id: &N,
|
||||
Part(commit, rows): Part,
|
||||
) -> Option<PartOutcome<NodeUid>> {
|
||||
) -> Option<PartOutcome<N>> {
|
||||
let sender_idx = self.node_index(sender_id)?;
|
||||
let opt_commit_row = self.our_idx.map(|idx| commit.row(idx + 1));
|
||||
match self.parts.entry(sender_idx) {
|
||||
|
@ -351,7 +352,7 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
|
|||
///
|
||||
/// All participating nodes must handle the exact same sequence of messages.
|
||||
/// Note that `handle_ack` also needs to explicitly be called with this instance's own `Ack`s.
|
||||
pub fn handle_ack(&mut self, sender_id: &NodeUid, ack: Ack) -> FaultLog<NodeUid> {
|
||||
pub fn handle_ack(&mut self, sender_id: &N, ack: Ack) -> FaultLog<N> {
|
||||
let mut fault_log = FaultLog::new();
|
||||
if let Some(sender_idx) = self.node_index(sender_id) {
|
||||
if let Err(err) = self.handle_ack_or_err(sender_idx, ack) {
|
||||
|
@ -372,7 +373,7 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
|
|||
}
|
||||
|
||||
/// Returns `true` if the part of the given node is complete.
|
||||
pub fn is_node_ready(&self, proposer_id: &NodeUid) -> bool {
|
||||
pub fn is_node_ready(&self, proposer_id: &N) -> bool {
|
||||
self.node_index(proposer_id)
|
||||
.and_then(|proposer_idx| self.parts.get(&proposer_idx))
|
||||
.map_or(false, |part| part.is_complete(self.threshold))
|
||||
|
@ -412,7 +413,7 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
|
|||
///
|
||||
/// All participating nodes must have handled the exact same sequence of `Part` and `Ack`
|
||||
/// messages before calling this method. Otherwise their key shares will not match.
|
||||
pub fn into_network_info(self) -> NetworkInfo<NodeUid> {
|
||||
pub fn into_network_info(self) -> NetworkInfo<N> {
|
||||
let (pk_set, opt_sk_share) = self.generate();
|
||||
let sk_share = opt_sk_share.unwrap_or_default(); // TODO: Make this an option.
|
||||
NetworkInfo::new(self.our_uid, sk_share, pk_set, self.sec_key, self.pub_keys)
|
||||
|
@ -453,7 +454,7 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
|
|||
}
|
||||
|
||||
/// Returns the index of the node, or `None` if it is unknown.
|
||||
fn node_index(&self, node_id: &NodeUid) -> Option<u64> {
|
||||
fn node_index(&self, node_id: &N) -> Option<u64> {
|
||||
if let Some(node_idx) = self.pub_keys.keys().position(|uid| uid == node_id) {
|
||||
Some(node_idx as u64)
|
||||
} else {
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
use std::cmp;
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
use std::hash::Hash;
|
||||
|
||||
use rand;
|
||||
|
||||
use traits::Contribution;
|
||||
|
||||
/// A wrapper providing a few convenience methods for a queue of pending transactions.
|
||||
#[derive(Debug)]
|
||||
pub struct TransactionQueue<Tx>(pub VecDeque<Tx>);
|
||||
pub struct TransactionQueue<T>(pub VecDeque<T>);
|
||||
|
||||
impl<Tx: Clone> TransactionQueue<Tx> {
|
||||
impl<T: Clone> TransactionQueue<T> {
|
||||
/// Removes the given transactions from the queue.
|
||||
pub fn remove_all<'a, I>(&mut self, txs: I)
|
||||
where
|
||||
I: IntoIterator<Item = &'a Tx>,
|
||||
Tx: Eq + Hash + 'a,
|
||||
I: IntoIterator<Item = &'a T>,
|
||||
T: 'a + Contribution,
|
||||
{
|
||||
let tx_set: HashSet<_> = txs.into_iter().collect();
|
||||
self.0.retain(|tx| !tx_set.contains(tx));
|
||||
|
@ -22,7 +23,7 @@ impl<Tx: Clone> TransactionQueue<Tx> {
|
|||
/// Returns a new set of `amount` transactions, randomly chosen from the first `batch_size`.
|
||||
/// No transactions are removed from the queue.
|
||||
// TODO: Return references, once the `HoneyBadger` API accepts them. Remove `Clone` bound.
|
||||
pub fn choose(&self, amount: usize, batch_size: usize) -> Vec<Tx> {
|
||||
pub fn choose(&self, amount: usize, batch_size: usize) -> Vec<T> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let limit = cmp::min(batch_size, self.0.len());
|
||||
let sample = match rand::seq::sample_iter(&mut rng, self.0.iter().take(limit), amount) {
|
||||
|
|
Loading…
Reference in New Issue