From 0e6a8aa27234939e95ac3ad72c1676c73a0cb0d6 Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Fri, 13 Jul 2018 10:52:44 +0200 Subject: [PATCH 1/6] Fix clippy lints. Extra lints have been added in the latest version of clippy; this fixes four instances of two of the new lints. --- src/agreement/bin_values.rs | 6 +++--- src/dynamic_honey_badger/votes.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/agreement/bin_values.rs b/src/agreement/bin_values.rs index ac2b931..b948acd 100644 --- a/src/agreement/bin_values.rs +++ b/src/agreement/bin_values.rs @@ -63,7 +63,7 @@ impl BinValues { } } - pub fn contains(&self, b: bool) -> bool { + pub fn contains(self, b: bool) -> bool { match self { BinValues::None => false, BinValues::Both => true, @@ -73,7 +73,7 @@ impl BinValues { } } - pub fn is_subset(&self, other: BinValues) -> bool { + pub fn is_subset(self, other: BinValues) -> bool { match self { BinValues::None => true, BinValues::False if other == BinValues::False || other == BinValues::Both => true, @@ -83,7 +83,7 @@ impl BinValues { } } - pub fn definite(&self) -> Option { + pub fn definite(self) -> Option { match self { BinValues::False => Some(false), BinValues::True => Some(true), diff --git a/src/dynamic_honey_badger/votes.rs b/src/dynamic_honey_badger/votes.rs index 67acfbb..3f1433c 100644 --- a/src/dynamic_honey_badger/votes.rs +++ b/src/dynamic_honey_badger/votes.rs @@ -89,7 +89,7 @@ where /// Returns an iterator over all pending votes that are newer than their voter's committed /// vote. - pub fn pending_votes<'a>(&'a self) -> impl Iterator> { + pub fn pending_votes(&self) -> impl Iterator> { self.pending.values().filter(move |signed_vote| { self.committed .get(&signed_vote.voter) From a0da8b7385a1fe2ef7462fb366250adc3e7fc937 Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Thu, 5 Jul 2018 14:01:56 +0200 Subject: [PATCH 2/6] Added extended example for broadcast api and other docstrings. --- src/broadcast.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++ tests/network/mod.rs | 41 ++++++++++++++++--- 2 files changed, 132 insertions(+), 5 deletions(-) diff --git a/src/broadcast.rs b/src/broadcast.rs index 3a2704b..f6304d0 100644 --- a/src/broadcast.rs +++ b/src/broadcast.rs @@ -40,6 +40,102 @@ //! node will eventually be able to decode (i.e. receive at least _2 f + 1_ `Echo` messages). //! * So a node with _2 f + 1_ `Ready`s and _2 f + 1_ `Echos` will decode and _output_ the value, //! knowing that every other correct node will eventually do the same. +//! +//! ## Example usage +//! +//! ```rust +//!# extern crate clear_on_drop; +//!# extern crate hbbft; +//!# extern crate rand; +//!# fn main() { +//!# +//! use clear_on_drop::ClearOnDrop; +//! use hbbft::broadcast::Broadcast; +//! use hbbft::crypto::SecretKeySet; +//! use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage}; +//! use rand::{Rng, thread_rng}; +//! use std::collections::{BTreeSet, BTreeMap}; +//! use std::sync::Arc; +//! +//! // in the example, we will "simulate" a network by passing messages by hand between +//! // instantiated nodes. we use u64 as network ids, and start by creating a common +//! // network info +//! +//! // our simulated network will use seven nodes in total, node 3 will be the proposer +//! const NUM_NODES: u64 = 7; +//! const PROPOSER_ID: u64 = 3; +//! +//! // create set of node ids +//! let all_uids: BTreeSet<_> = (0..NUM_NODES).collect(); +//! +//! // secret keys are required to complete the NetworkInfo structure, but not used in the +//! // broadcast algorithm +//! let mut rng = thread_rng(); +//! let secret_keys = SecretKeySet::random(4, &mut rng); +//! +//! // create initial nodes by instantiating a `NetworkInfo` for each +//! let mut nodes: BTreeMap<_, _> = all_uids.iter().cloned().map(|i| { +//! let netinfo = NetworkInfo::new( +//! i, +//! all_uids.clone(), +//! secret_keys.secret_key_share(i), +//! secret_keys.public_keys(), +//! ); +//! +//! let bc = Broadcast::new(Arc::new(netinfo), PROPOSER_ID) +//! .expect("could not instantiate Broadcast"); +//! +//! (i, bc) +//! }).collect(); +//! +//! // we are ready to start. first, we generate a payload to broadcast: +//! let mut payload: Vec<_> = vec![0; 128]; +//! rng.fill_bytes(&mut payload[..]); +//! +//! // now we can start the algorithm, its input is the payload to be broadcast +//! let mut next_message = { +//! let proposer = nodes.get_mut(&PROPOSER_ID).unwrap(); +//! proposer.input(payload.clone()).unwrap(); +//! +//! // attach the sender to the resulting message +//! proposer.next_message().map(|tm| (PROPOSER_ID, tm)) +//! }; +//! +//! // we can sanity-check that a message scheduled by the proposer +//! assert!(next_message.is_some()); +//! +//! // the network is simulated by passing messages around from node to node +//! while let Some((sender, TargetedMessage { target, message })) = next_message { +//! println!("Message [{:?} -> {:?}]: {:?}", sender, target, message); +//! +//! match target { +//! Target::All => { +//! let msg = &message; +//! nodes.iter_mut() +//! .for_each(|(_, node)| { node.handle_message(&sender, msg.clone()) +//! .expect("could not handle message"); }); +//! }, +//! Target::Node(ref dest) => { +//! let dest_node = nodes.get_mut(dest).expect("destination node not found"); +//! dest_node.handle_message(&sender, message) +//! .expect("could not handle message"); +//! }, +//! } +//! +//! // we have handled the message, now we check all nodes in order for new messages +//! next_message = nodes +//! .iter_mut() +//! .filter_map(|(&id, node)| node.next_message() +//! .map(|tm| (id, tm))) +//! .next(); +//! } +//! +//! // the algorithm output of every node will be the original payload +//! for (_, mut node) in nodes { +//! assert_eq!(node.next_output().expect("missing output"), payload); +//! } +//!# } +//! ``` use std::collections::{BTreeMap, VecDeque}; use std::fmt::{self, Debug}; diff --git a/tests/network/mod.rs b/tests/network/mod.rs index 714643d..0c6ed7c 100644 --- a/tests/network/mod.rs +++ b/tests/network/mod.rs @@ -111,14 +111,19 @@ pub type MessageWithSender = ( ); /// An adversary that can control a set of nodes and pick the next good node to receive a message. +/// +/// See `TestNetwork::step()` for a more detailed description of its capabilities. pub trait Adversary { - /// Chooses a node to be the next one to handle a message. + /// Chooses a node to be the next one to handle a message + /// + /// Starvation is illegal, i.e. in every iteration a node that has pending incoming messages + /// must be chosen. fn pick_node(&self, nodes: &BTreeMap>) -> D::NodeUid; - /// Adds a message sent to one of the adversary's nodes. + /// Called when a node controlled by the adversary receives a message fn push_message(&mut self, sender_id: D::NodeUid, msg: TargetedMessage); - /// Produces a list of messages to be sent from the adversary's nodes. + /// Produces a list of messages to be sent from the adversary's nodes fn step(&mut self) -> Vec>; } @@ -149,6 +154,18 @@ impl Adversary for SilentAdversary { } /// A collection of `TestNode`s representing a network. +/// +/// Each TestNetwork type is tied to a specific adversary and a distributed algorithm. It consists +/// of a set of nodes, some of which are controlled by the adversary and some of which may be +/// observer nodes, as well as a set of threshold-cryptography public keys. +/// +/// In addition to being able to participate correctly in the network using his nodes, the +/// adversary can: +/// +/// 1. decide which node is the next one to make progress. +/// 2. send arbitrary messages to any node originating from one of the nodes they control +/// +/// See the `step` function for details on actual operation of the network pub struct TestNetwork, D: DistAlgorithm> where ::NodeUid: Hash, @@ -266,20 +283,34 @@ where } } - /// Handles a queued message in a randomly selected node and returns the selected node's ID. + /// Performs one iteration of the network, consisting of the following steps: + /// + /// 1. Give the adversary a chance to send messages of his choosing through `Adversary::step()` + /// 2. Let the adversary pick a node that receives its next message through + /// `Adversary::pick_node()` + /// + /// Returns the node id of the node that made progress pub fn step(&mut self) -> NodeUid { + // we let the adversary send out messages to any number of nodes let msgs = self.adversary.step(); for (sender_id, msg) in msgs { self.dispatch_messages(sender_id, Some(msg)); } - // Pick a random non-idle node.. + + // now one node is chosen to make progress. we let the adversary decide which node let id = self.adversary.pick_node(&self.nodes); + + // TODO: ensure the adversary is honest and does pick a node that has actual messages to + // process + + // the node handles the incoming message and creates new outgoing ones to be dispatched let msgs: Vec<_> = { let node = self.nodes.get_mut(&id).unwrap(); node.handle_message(); node.algo.message_iter().collect() }; self.dispatch_messages(id, msgs); + id } From 510c4478d4c49b8424eb5dce6f63e7cd03011777 Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Thu, 5 Jul 2018 16:47:45 +0200 Subject: [PATCH 3/6] Ensure the adversary is playing fair and give a more helpful error message if they are not. --- tests/broadcast.rs | 2 +- tests/honey_badger.rs | 2 +- tests/network/mod.rs | 21 ++++++++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 4103097..b2f911a 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -47,7 +47,7 @@ impl ProposeAdversary { } impl Adversary> for ProposeAdversary { - fn pick_node(&self, nodes: &BTreeMap>>) -> NodeUid { + fn pick_node(&mut self, nodes: &BTreeMap>>) -> NodeUid { self.scheduler.pick_node(nodes) } diff --git a/tests/honey_badger.rs b/tests/honey_badger.rs index 7a391a5..e2092fe 100644 --- a/tests/honey_badger.rs +++ b/tests/honey_badger.rs @@ -56,7 +56,7 @@ impl FaultyShareAdversary { } impl Adversary for FaultyShareAdversary { - fn pick_node(&self, nodes: &BTreeMap>) -> NodeUid { + fn pick_node(&mut self, nodes: &BTreeMap>) -> NodeUid { self.scheduler.pick_node(nodes) } diff --git a/tests/network/mod.rs b/tests/network/mod.rs index 0c6ed7c..913be3b 100644 --- a/tests/network/mod.rs +++ b/tests/network/mod.rs @@ -68,6 +68,11 @@ impl TestNode { .expect("handling message"); self.outputs.extend(self.algo.output_iter()); } + + /// Checks whether the node has messages to process + fn is_idle(&self) -> bool { + self.queue.is_empty() + } } /// A strategy for picking the next good node to handle a message. @@ -118,7 +123,7 @@ pub trait Adversary { /// /// Starvation is illegal, i.e. in every iteration a node that has pending incoming messages /// must be chosen. - fn pick_node(&self, nodes: &BTreeMap>) -> D::NodeUid; + fn pick_node(&mut self, nodes: &BTreeMap>) -> D::NodeUid; /// Called when a node controlled by the adversary receives a message fn push_message(&mut self, sender_id: D::NodeUid, msg: TargetedMessage); @@ -140,7 +145,7 @@ impl SilentAdversary { } impl Adversary for SilentAdversary { - fn pick_node(&self, nodes: &BTreeMap>) -> D::NodeUid { + fn pick_node(&mut self, nodes: &BTreeMap>) -> D::NodeUid { self.scheduler.pick_node(nodes) } @@ -300,12 +305,18 @@ where // now one node is chosen to make progress. we let the adversary decide which node let id = self.adversary.pick_node(&self.nodes); - // TODO: ensure the adversary is honest and does pick a node that has actual messages to - // process - // the node handles the incoming message and creates new outgoing ones to be dispatched let msgs: Vec<_> = { let node = self.nodes.get_mut(&id).unwrap(); + + // ensure the adversary is playing fair by selecting a node that will result in actual + // progress being made. otherwise `TestNode::handle_message()` will panic on `expect()` + // with a much more cryptic error message + assert!( + !node.is_idle(), + "adversary illegally selected an idle node in pick_node()" + ); + node.handle_message(); node.algo.message_iter().collect() }; From 5336fbe7075864000d3856b49c1eb2c9765cc175 Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Thu, 5 Jul 2018 18:20:53 +0200 Subject: [PATCH 4/6] Added `RandomAdversary` and the necessary auxiliary functions. Random adversaries are created for `broadcast` and `honey_badger`. Random value generation was added for all type-dependencies of these algorithms, causing the `Rand` trait to be implement for a large portion of the codebase. Additionally, `MessageWithSender` turned into an actual struct, making it much easier to handle. Tuple-like construction is still available through `MessageWithSender::new()`. --- Cargo.toml | 1 + examples/simulation.rs | 4 +- src/agreement/bin_values.rs | 2 +- src/agreement/mod.rs | 23 ++- src/broadcast.rs | 52 ++++-- src/common_coin.rs | 2 +- src/common_subset.rs | 15 +- src/crypto/mod.rs | 15 +- src/dynamic_honey_badger/batch.rs | 3 +- src/dynamic_honey_badger/builder.rs | 3 +- src/dynamic_honey_badger/mod.rs | 15 +- src/honey_badger.rs | 25 +-- src/lib.rs | 2 + src/queueing_honey_badger.rs | 9 +- tests/agreement.rs | 2 + tests/broadcast.rs | 29 +++- tests/common_coin.rs | 2 + tests/common_subset.rs | 2 + tests/dynamic_honey_badger.rs | 2 + tests/honey_badger.rs | 21 ++- tests/network/mod.rs | 242 ++++++++++++++++++++++++---- tests/queueing_honey_badger.rs | 2 + 22 files changed, 377 insertions(+), 96 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ba6f4ab..d213493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ merkle = { git = "https://github.com/afck/merkle.rs", branch = "public-proof", f pairing = { version = "0.14.2", features = ["u128-support"] } protobuf = { version = "2.0.0", optional = true } rand = "0.4.2" +rand_derive = "0.3.1" reed-solomon-erasure = "3.1.0" ring = "^0.12" serde = "1.0.55" diff --git a/examples/simulation.rs b/examples/simulation.rs index 87850cf..5fb8efc 100644 --- a/examples/simulation.rs +++ b/examples/simulation.rs @@ -6,6 +6,8 @@ extern crate hbbft; extern crate itertools; extern crate pairing; extern crate rand; +#[macro_use] +extern crate rand_derive; extern crate serde; #[macro_use(Deserialize, Serialize)] extern crate serde_derive; @@ -62,7 +64,7 @@ struct Args { } /// A node identifier. In the simulation, nodes are simply numbered. -#[derive(Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, Rand)] pub struct NodeUid(pub usize); /// A transaction. diff --git a/src/agreement/bin_values.rs b/src/agreement/bin_values.rs index b948acd..12081bc 100644 --- a/src/agreement/bin_values.rs +++ b/src/agreement/bin_values.rs @@ -3,7 +3,7 @@ use std::mem::replace; /// A lattice-valued description of the state of `bin_values`, essentially the same as the set of /// subsets of `bool`. -#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Rand)] pub enum BinValues { None, False, diff --git a/src/agreement/mod.rs b/src/agreement/mod.rs index 8dfdb0c..1c7b65b 100644 --- a/src/agreement/mod.rs +++ b/src/agreement/mod.rs @@ -65,6 +65,7 @@ pub mod bin_values; +use rand; use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::fmt::Debug; use std::mem::replace; @@ -122,12 +123,32 @@ impl AgreementContent { } /// Messages sent during the binary Byzantine agreement stage. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)] pub struct AgreementMessage { pub epoch: u32, pub content: AgreementContent, } +// NOTE: Extending rand_derive to correctly generate random values from boxes would make this +// implementation obsolete; however at the time of this writing, `rand::Rand` is already deprecated +// with no replacement in sight. +impl rand::Rand for AgreementContent { + fn rand(rng: &mut R) -> Self { + let message_type = *rng + .choose(&["bval", "aux", "conf", "term", "coin"]) + .unwrap(); + + match message_type { + "bval" => AgreementContent::BVal(rand::random()), + "aux" => AgreementContent::Aux(rand::random()), + "conf" => AgreementContent::Conf(rand::random()), + "term" => AgreementContent::Term(rand::random()), + "coin" => AgreementContent::Coin(Box::new(rand::random())), + _ => unreachable!(), + } + } +} + /// Possible values of the common coin schedule defining the method to derive the common coin in a /// given epoch: as a constant value or a distributed computation. enum CoinSchedule { diff --git a/src/broadcast.rs b/src/broadcast.rs index f6304d0..1aacb40 100644 --- a/src/broadcast.rs +++ b/src/broadcast.rs @@ -57,23 +57,23 @@ //! use std::collections::{BTreeSet, BTreeMap}; //! use std::sync::Arc; //! -//! // in the example, we will "simulate" a network by passing messages by hand between -//! // instantiated nodes. we use u64 as network ids, and start by creating a common -//! // network info +//! // In the example, we will "simulate" a network by passing messages by hand between +//! // instantiated nodes. We use u64 as network ids, and start by creating a common +//! // network info. //! -//! // our simulated network will use seven nodes in total, node 3 will be the proposer +//! // Our simulated network will use seven nodes in total, node 3 will be the proposer. //! const NUM_NODES: u64 = 7; //! const PROPOSER_ID: u64 = 3; //! -//! // create set of node ids +//! // Create set of node ids. //! let all_uids: BTreeSet<_> = (0..NUM_NODES).collect(); //! -//! // secret keys are required to complete the NetworkInfo structure, but not used in the -//! // broadcast algorithm +//! // Secret keys are required to complete the NetworkInfo structure, but not used in the +//! // broadcast algorithm. //! let mut rng = thread_rng(); //! let secret_keys = SecretKeySet::random(4, &mut rng); //! -//! // create initial nodes by instantiating a `NetworkInfo` for each +//! // Create initial nodes by instantiating a `NetworkInfo` for each: //! let mut nodes: BTreeMap<_, _> = all_uids.iter().cloned().map(|i| { //! let netinfo = NetworkInfo::new( //! i, @@ -88,11 +88,11 @@ //! (i, bc) //! }).collect(); //! -//! // we are ready to start. first, we generate a payload to broadcast: +//! // We are ready to start. First we generate a payload to broadcast: //! let mut payload: Vec<_> = vec![0; 128]; //! rng.fill_bytes(&mut payload[..]); //! -//! // now we can start the algorithm, its input is the payload to be broadcast +//! // Now we can start the algorithm, its input is the payload to be broadcast. //! let mut next_message = { //! let proposer = nodes.get_mut(&PROPOSER_ID).unwrap(); //! proposer.input(payload.clone()).unwrap(); @@ -101,10 +101,10 @@ //! proposer.next_message().map(|tm| (PROPOSER_ID, tm)) //! }; //! -//! // we can sanity-check that a message scheduled by the proposer +//! // We can sanity-check that a message is scheduled by the proposer: //! assert!(next_message.is_some()); //! -//! // the network is simulated by passing messages around from node to node +//! // The network is simulated by passing messages around from node to node. //! while let Some((sender, TargetedMessage { target, message })) = next_message { //! println!("Message [{:?} -> {:?}]: {:?}", sender, target, message); //! @@ -122,7 +122,7 @@ //! }, //! } //! -//! // we have handled the message, now we check all nodes in order for new messages +//! // We have handled the message, now we check all nodes for new messages, in order: //! next_message = nodes //! .iter_mut() //! .filter_map(|(&id, node)| node.next_message() @@ -130,7 +130,7 @@ //! .next(); //! } //! -//! // the algorithm output of every node will be the original payload +//! // The algorithm output of every node will be the original payload. //! for (_, mut node) in nodes { //! assert_eq!(node.next_output().expect("missing output"), payload); //! } @@ -144,6 +144,7 @@ use std::sync::Arc; use byteorder::{BigEndian, ByteOrder}; use merkle::{MerkleTree, Proof}; +use rand; use reed_solomon_erasure as rse; use reed_solomon_erasure::ReedSolomon; use ring::digest; @@ -180,6 +181,29 @@ pub enum BroadcastMessage { Ready(Vec), } +// A random generation impl is provided for test cases. Unfortunately `#[cfg(test)]` does not work +// for integration tests. +impl rand::Rand for BroadcastMessage { + fn rand(rng: &mut R) -> Self { + let message_type = *rng.choose(&["value", "echo", "ready"]).unwrap(); + + // Create a random buffer for our proof. + let mut buffer: [u8; 32] = [0; 32]; + rng.fill_bytes(&mut buffer); + + // Generate a dummy proof to fill broadcast messages with. + let tree = MerkleTree::from_vec(&digest::SHA256, vec![buffer.to_vec()]); + let proof = tree.gen_proof(buffer.to_vec()).unwrap(); + + match message_type { + "value" => BroadcastMessage::Value(proof), + "echo" => BroadcastMessage::Echo(proof), + "ready" => BroadcastMessage::Ready(b"dummy-ready".to_vec()), + _ => unreachable!(), + } + } +} + impl Debug for BroadcastMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/src/common_coin.rs b/src/common_coin.rs index 0939ae7..4d210b9 100644 --- a/src/common_coin.rs +++ b/src/common_coin.rs @@ -45,7 +45,7 @@ error_chain! { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)] pub struct CommonCoinMessage(Signature); impl CommonCoinMessage { diff --git a/src/common_subset.rs b/src/common_subset.rs index 4dadeed..4936aed 100644 --- a/src/common_subset.rs +++ b/src/common_subset.rs @@ -32,6 +32,7 @@ use broadcast::{self, Broadcast, BroadcastMessage, BroadcastResult}; use fault_log::FaultLog; use fmt::HexBytes; use messaging::{DistAlgorithm, NetworkInfo, TargetedMessage}; +use rand::Rand; error_chain!{ types { @@ -54,8 +55,8 @@ error_chain!{ type ProposedValue = Vec; /// Message from Common Subset to remote nodes. -#[derive(Serialize, Deserialize, Clone, Debug)] -pub enum Message { +#[derive(Serialize, Deserialize, Clone, Debug, Rand)] +pub enum Message { /// A message for the broadcast algorithm concerning the set element proposed by the given node. Broadcast(NodeUid, BroadcastMessage), /// A message for the agreement algorithm concerning the set element proposed by the given @@ -65,9 +66,9 @@ pub enum Message { /// The queue of outgoing messages in a `CommonSubset` instance. #[derive(Deref, DerefMut)] -struct MessageQueue(VecDeque, NodeUid>>); +struct MessageQueue(VecDeque, NodeUid>>); -impl MessageQueue { +impl MessageQueue { /// Appends to the queue the messages from `agr`, wrapped with `proposer_id`. fn extend_agreement(&mut self, proposer_id: &NodeUid, agr: &mut Agreement) { let convert = |msg: TargetedMessage| { @@ -86,7 +87,7 @@ impl MessageQueue { } /// Asynchronous Common Subset algorithm instance -pub struct CommonSubset { +pub struct CommonSubset { /// Shared network information. netinfo: Arc>, broadcast_instances: BTreeMap>, @@ -101,7 +102,7 @@ pub struct CommonSubset { decided: bool, } -impl DistAlgorithm for CommonSubset { +impl DistAlgorithm for CommonSubset { type NodeUid = NodeUid; type Input = ProposedValue; type Output = BTreeMap; @@ -145,7 +146,7 @@ impl DistAlgorithm for CommonSubset { } } -impl CommonSubset { +impl CommonSubset { pub fn new(netinfo: Arc>, session_id: u64) -> CommonSubsetResult { // Create all broadcast instances. let mut broadcast_instances: BTreeMap> = BTreeMap::new(); diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index d25a40d..a5ce582 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -16,7 +16,7 @@ use clear_on_drop::ClearOnDrop; use init_with::InitWith; use pairing::bls12_381::{Bls12, Fr, FrRepr, G1, G1Affine, G2, G2Affine}; use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField}; -use rand::{ChaChaRng, OsRng, Rand, Rng, SeedableRng}; +use rand::{ChaChaRng, OsRng, Rng, SeedableRng}; use ring::digest; use self::error::{ErrorKind, Result}; @@ -83,7 +83,8 @@ impl PublicKey { } /// A signature, or a signature share. -#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)] +// note: random signatures can be generated for testing +#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Rand)] pub struct Signature(#[serde(with = "serde_impl::projective")] G2); impl fmt::Debug for Signature { @@ -112,7 +113,7 @@ impl Signature { } /// A secret key, or a secret key share. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Rand)] pub struct SecretKey(Fr); impl fmt::Debug for SecretKey { @@ -129,12 +130,6 @@ impl Default for SecretKey { } } -impl Rand for SecretKey { - fn rand(rng: &mut R) -> Self { - SecretKey(rng.gen()) - } -} - impl SecretKey { /// Creates a secret key from an existing value pub fn from_value(f: Fr) -> Self { @@ -203,7 +198,7 @@ impl Ciphertext { } /// A decryption share. A threshold of decryption shares can be used to decrypt a message. -#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq)] +#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Eq, Rand)] pub struct DecryptionShare(#[serde(with = "serde_impl::projective")] G1); impl Hash for DecryptionShare { diff --git a/src/dynamic_honey_badger/batch.rs b/src/dynamic_honey_badger/batch.rs index e0a4ecb..4271948 100644 --- a/src/dynamic_honey_badger/batch.rs +++ b/src/dynamic_honey_badger/batch.rs @@ -1,4 +1,5 @@ use super::ChangeState; +use rand::Rand; use std::collections::BTreeMap; /// A batch of transactions the algorithm has output. @@ -13,7 +14,7 @@ pub struct Batch { pub change: ChangeState, } -impl Batch { +impl Batch { /// Returns a new, empty batch with the given epoch. pub fn new(epoch: u64) -> Self { Batch { diff --git a/src/dynamic_honey_badger/builder.rs b/src/dynamic_honey_badger/builder.rs index aff47fc..2be5b1f 100644 --- a/src/dynamic_honey_badger/builder.rs +++ b/src/dynamic_honey_badger/builder.rs @@ -4,6 +4,7 @@ use std::hash::Hash; use std::marker::PhantomData; use std::sync::Arc; +use rand::Rand; use serde::{Deserialize, Serialize}; use super::{DynamicHoneyBadger, MessageQueue, VoteCounter}; @@ -25,7 +26,7 @@ pub struct DynamicHoneyBadgerBuilder { impl DynamicHoneyBadgerBuilder where C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, - NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash, + NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash + Rand, { /// Returns a new `DynamicHoneyBadgerBuilder` configured to use the node IDs and cryptographic /// keys specified by `netinfo`. diff --git a/src/dynamic_honey_badger/mod.rs b/src/dynamic_honey_badger/mod.rs index 148537a..ad6a52c 100644 --- a/src/dynamic_honey_badger/mod.rs +++ b/src/dynamic_honey_badger/mod.rs @@ -44,6 +44,7 @@ //! `SyncKeyGen` instance is dropped, and a new one is started to create keys according to the new //! pending change. +use rand::Rand; use std::collections::VecDeque; use std::fmt::Debug; use std::hash::Hash; @@ -84,7 +85,7 @@ pub enum Input { } /// A Honey Badger instance that can handle adding and removing nodes. -pub struct DynamicHoneyBadger +pub struct DynamicHoneyBadger where C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, NodeUid: Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug, @@ -114,7 +115,7 @@ where impl DistAlgorithm for DynamicHoneyBadger where C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, - NodeUid: Eq + Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug + Hash, + NodeUid: Eq + Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Rand, { type NodeUid = NodeUid; type Input = Input; @@ -178,7 +179,7 @@ where impl DynamicHoneyBadger where C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, - NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash, + NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash + Rand, { /// Returns a new `DynamicHoneyBadgerBuilder` configured to use the node IDs and cryptographic /// keys specified by `netinfo`. @@ -487,7 +488,7 @@ pub enum KeyGenMessage { /// A message sent to or received from another node's Honey Badger instance. #[derive(Serialize, Deserialize, Debug, Clone)] -pub enum Message { +pub enum Message { /// A message belonging to the `HoneyBadger` algorithm started in the given epoch. HoneyBadger(u64, HbMessage), /// A transaction to be committed, signed by a node. @@ -496,7 +497,7 @@ pub enum Message { SignedVote(SignedVote), } -impl Message { +impl Message { pub fn epoch(&self) -> u64 { match *self { Message::HoneyBadger(epoch, _) => epoch, @@ -508,11 +509,11 @@ impl Message { /// The queue of outgoing messages in a `HoneyBadger` instance. #[derive(Deref, DerefMut)] -struct MessageQueue(VecDeque, NodeUid>>); +struct MessageQueue(VecDeque, NodeUid>>); impl MessageQueue where - NodeUid: Eq + Hash + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r>, + NodeUid: Eq + Hash + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Rand, { /// Appends to the queue the messages from `hb`, wrapped with `epoch`. fn extend_with_epoch(&mut self, epoch: u64, hb: &mut HoneyBadger) diff --git a/src/honey_badger.rs b/src/honey_badger.rs index 04f1f25..9751cb0 100644 --- a/src/honey_badger.rs +++ b/src/honey_badger.rs @@ -1,3 +1,4 @@ +use rand::Rand; use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::fmt::Debug; @@ -45,7 +46,7 @@ pub struct HoneyBadgerBuilder { impl HoneyBadgerBuilder where C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq, - NodeUid: Ord + Clone + Debug, + NodeUid: Ord + Clone + Debug + Rand, { /// Returns a new `HoneyBadgerBuilder` configured to use the node IDs and cryptographic keys /// specified by `netinfo`. @@ -82,7 +83,7 @@ where } /// An instance of the Honey Badger Byzantine fault tolerant consensus algorithm. -pub struct HoneyBadger { +pub struct HoneyBadger { /// Shared network data. netinfo: Arc>, /// The earliest epoch from which we have not yet received output. @@ -113,7 +114,7 @@ pub struct HoneyBadger { impl DistAlgorithm for HoneyBadger where C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq, - NodeUid: Ord + Clone + Debug, + NodeUid: Ord + Clone + Debug + Rand, { type NodeUid = NodeUid; type Input = C; @@ -169,7 +170,7 @@ where impl HoneyBadger where C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq, - NodeUid: Ord + Clone + Debug, + NodeUid: Ord + Clone + Debug + Rand, { /// Returns a new `HoneyBadgerBuilder` configured to use the node IDs and cryptographic keys /// specified by `netinfo`. @@ -629,8 +630,8 @@ impl Batch { } /// The content of a `HoneyBadger` message. It should be further annotated with an epoch. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub enum MessageContent { +#[derive(Clone, Debug, Deserialize, Rand, Serialize)] +pub enum MessageContent { /// A message belonging to the common subset algorithm in the given epoch. CommonSubset(common_subset::Message), /// A decrypted share of the output of `proposer_id`. @@ -640,7 +641,7 @@ pub enum MessageContent { }, } -impl MessageContent { +impl MessageContent { pub fn with_epoch(self, epoch: u64) -> Message { Message { epoch, @@ -650,13 +651,13 @@ impl MessageContent { } /// A message sent to or received from another node's Honey Badger instance. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Message { +#[derive(Clone, Debug, Deserialize, Rand, Serialize)] +pub struct Message { epoch: u64, content: MessageContent, } -impl Message { +impl Message { pub fn epoch(&self) -> u64 { self.epoch } @@ -664,9 +665,9 @@ impl Message { /// The queue of outgoing messages in a `HoneyBadger` instance. #[derive(Deref, DerefMut)] -struct MessageQueue(VecDeque, NodeUid>>); +struct MessageQueue(VecDeque, NodeUid>>); -impl MessageQueue { +impl MessageQueue { /// Appends to the queue the messages from `cs`, wrapped with `epoch`. fn extend_with_epoch(&mut self, epoch: u64, cs: &mut CommonSubset) { let convert = |msg: TargetedMessage, NodeUid>| { diff --git a/src/lib.rs b/src/lib.rs index e815b87..8e14f0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,8 @@ extern crate pairing; #[cfg(feature = "serialization-protobuf")] extern crate protobuf; extern crate rand; +#[macro_use] +extern crate rand_derive; extern crate reed_solomon_erasure; extern crate ring; extern crate serde; diff --git a/src/queueing_honey_badger.rs b/src/queueing_honey_badger.rs index db2c749..a3363df 100644 --- a/src/queueing_honey_badger.rs +++ b/src/queueing_honey_badger.rs @@ -2,6 +2,7 @@ //! //! This works exactly like Dynamic Honey Badger, but it has a transaction queue built in. +use rand::Rand; use std::collections::VecDeque; use std::fmt::Debug; use std::hash::Hash; @@ -39,7 +40,7 @@ pub struct QueueingHoneyBadgerBuilder { impl QueueingHoneyBadgerBuilder where Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone, - NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash, + NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash + Rand, { /// Returns a new `QueueingHoneyBadgerBuilder` configured to use the node IDs and cryptographic /// keys specified by `netinfo`. @@ -109,7 +110,7 @@ where pub struct QueueingHoneyBadger where Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, - NodeUid: Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug, + NodeUid: Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug + Rand, { /// The target number of transactions to be included in each batch. batch_size: usize, @@ -124,7 +125,7 @@ where impl DistAlgorithm for QueueingHoneyBadger where Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone, - NodeUid: Eq + Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug + Hash, + NodeUid: Eq + Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Rand, { type NodeUid = NodeUid; type Input = Input; @@ -180,7 +181,7 @@ where impl QueueingHoneyBadger where Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone, - NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash, + NodeUid: Eq + Ord + Clone + Debug + Serialize + for<'r> Deserialize<'r> + Hash + Rand, { /// Returns a new `QueueingHoneyBadgerBuilder` configured to use the node IDs and cryptographic /// keys specified by `netinfo`. diff --git a/tests/agreement.rs b/tests/agreement.rs index 4f9b52f..e40fee1 100644 --- a/tests/agreement.rs +++ b/tests/agreement.rs @@ -21,6 +21,8 @@ extern crate pairing; extern crate rand; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate rand_derive; mod network; diff --git a/tests/broadcast.rs b/tests/broadcast.rs index b2f911a..7f10d0e 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -8,6 +8,8 @@ extern crate pairing; extern crate rand; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate rand_derive; mod network; @@ -19,8 +21,11 @@ use rand::Rng; use hbbft::broadcast::{Broadcast, BroadcastMessage}; use hbbft::crypto::SecretKeySet; -use hbbft::messaging::{DistAlgorithm, NetworkInfo, TargetedMessage}; -use network::{Adversary, MessageScheduler, NodeUid, SilentAdversary, TestNetwork, TestNode}; +use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage}; +use network::{ + Adversary, MessageScheduler, MessageWithSender, NodeUid, RandomAdversary, SilentAdversary, + TestNetwork, TestNode, +}; /// An adversary that inputs an alternate value. struct ProposeAdversary { @@ -47,7 +52,7 @@ impl ProposeAdversary { } impl Adversary> for ProposeAdversary { - fn pick_node(&mut self, nodes: &BTreeMap>>) -> NodeUid { + fn pick_node(&self, nodes: &BTreeMap>>) -> NodeUid { self.scheduler.pick_node(nodes) } @@ -55,7 +60,7 @@ impl Adversary> for ProposeAdversary { // All messages are ignored. } - fn step(&mut self) -> Vec<(NodeUid, TargetedMessage)> { + fn step(&mut self) -> Vec>> { if self.has_sent { return vec![]; } @@ -84,7 +89,9 @@ impl Adversary> for ProposeAdversary { )); let mut bc = Broadcast::new(netinfo, id).expect("broadcast instance"); bc.input(b"Fake news".to_vec()).expect("propose"); - bc.message_iter().map(|msg| (id, msg)).collect() + bc.message_iter() + .map(|msg| MessageWithSender::new(id, msg)) + .collect() } } @@ -182,3 +189,15 @@ fn test_broadcast_first_delivery_adv_propose() { }; test_broadcast_different_sizes(new_adversary, b"Foo"); } + +#[test] +fn test_broadcast_random_adversary() { + let new_adversary = |_, _| { + // Note: Set this to 0.8 to watch 30 gigs of RAM disappear. + RandomAdversary::new(0.2, 0.2, || TargetedMessage { + target: Target::All, + message: rand::random(), + }) + }; + test_broadcast_different_sizes(new_adversary, b"RandomFoo"); +} diff --git a/tests/common_coin.rs b/tests/common_coin.rs index 7b0e9d3..23ab884 100644 --- a/tests/common_coin.rs +++ b/tests/common_coin.rs @@ -8,6 +8,8 @@ extern crate pairing; extern crate rand; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate rand_derive; mod network; diff --git a/tests/common_subset.rs b/tests/common_subset.rs index 658bd93..7af95c7 100644 --- a/tests/common_subset.rs +++ b/tests/common_subset.rs @@ -8,6 +8,8 @@ extern crate pairing; extern crate rand; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate rand_derive; mod network; diff --git a/tests/dynamic_honey_badger.rs b/tests/dynamic_honey_badger.rs index 51cbb10..2f163c2 100644 --- a/tests/dynamic_honey_badger.rs +++ b/tests/dynamic_honey_badger.rs @@ -8,6 +8,8 @@ extern crate pairing; extern crate rand; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate rand_derive; mod network; diff --git a/tests/honey_badger.rs b/tests/honey_badger.rs index e2092fe..7b09385 100644 --- a/tests/honey_badger.rs +++ b/tests/honey_badger.rs @@ -8,6 +8,8 @@ extern crate env_logger; extern crate pairing; extern crate rand; #[macro_use] +extern crate rand_derive; +#[macro_use] extern crate serde_derive; mod network; @@ -23,7 +25,8 @@ use hbbft::messaging::{NetworkInfo, Target, TargetedMessage}; use hbbft::transaction_queue::TransactionQueue; use network::{ - Adversary, MessageScheduler, MessageWithSender, NodeUid, SilentAdversary, TestNetwork, TestNode, + Adversary, MessageScheduler, MessageWithSender, NodeUid, RandomAdversary, SilentAdversary, + TestNetwork, TestNode, }; type UsizeHoneyBadger = HoneyBadger, NodeUid>; @@ -56,7 +59,7 @@ impl FaultyShareAdversary { } impl Adversary for FaultyShareAdversary { - fn pick_node(&mut self, nodes: &BTreeMap>) -> NodeUid { + fn pick_node(&self, nodes: &BTreeMap>) -> NodeUid { self.scheduler.pick_node(nodes) } @@ -100,7 +103,7 @@ impl Adversary for FaultyShareAdversary { .expect("decryption share"); // Send the share to remote nodes. for proposer_id in 0..self.num_good + self.num_adv { - outgoing.push(( + outgoing.push(MessageWithSender::new( NodeUid(sender_id), Target::All.message( MessageContent::DecryptionShare { @@ -237,3 +240,15 @@ fn test_honey_badger_faulty_share() { }; test_honey_badger_different_sizes(new_adversary, 8); } + +#[test] +fn test_honey_badger_random_adversary() { + let new_adversary = |_, _, _| { + // A 10% injection chance is roughly ~13k extra messages added. + RandomAdversary::new(0.1, 0.1, || TargetedMessage { + target: Target::All, + message: rand::random(), + }) + }; + test_honey_badger_different_sizes(new_adversary, 8); +} diff --git a/tests/network/mod.rs b/tests/network/mod.rs index 913be3b..f966c35 100644 --- a/tests/network/mod.rs +++ b/tests/network/mod.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque}; -use std::fmt::Debug; +use std::fmt::{self, Debug}; use std::hash::Hash; +use std::mem; use std::sync::Arc; use rand::{self, Rng}; @@ -9,7 +10,7 @@ use hbbft::crypto::{PublicKeySet, SecretKeySet}; use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage}; /// A node identifier. In the tests, nodes are simply numbered. -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, Serialize, Deserialize, Rand)] pub struct NodeUid(pub usize); /// A "node" running an instance of the algorithm `D`. @@ -110,26 +111,61 @@ impl MessageScheduler { } } -pub type MessageWithSender = ( - ::NodeUid, - TargetedMessage<::Message, ::NodeUid>, -); +/// A message combined with a sender. +pub struct MessageWithSender { + /// The sender of the message. + pub sender: ::NodeUid, + /// The targeted message (recipient and message body). + pub tm: TargetedMessage<::Message, ::NodeUid>, +} + +// The Debug implementation cannot be derived automatically, possibly due to a compiler bug. For +// this reason, it is implemented manually here. +impl fmt::Debug for MessageWithSender { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "MessageWithSender {{ sender: {:?}, tm: {:?} }}", + self.sender, self.tm.target + ) + } +} + +impl MessageWithSender { + /// Creates a new message with a sender. + pub fn new( + sender: D::NodeUid, + tm: TargetedMessage, + ) -> MessageWithSender { + MessageWithSender { sender, tm } + } +} /// An adversary that can control a set of nodes and pick the next good node to receive a message. /// /// See `TestNetwork::step()` for a more detailed description of its capabilities. pub trait Adversary { - /// Chooses a node to be the next one to handle a message + /// Chooses a node to be the next one to handle a message. /// /// Starvation is illegal, i.e. in every iteration a node that has pending incoming messages /// must be chosen. - fn pick_node(&mut self, nodes: &BTreeMap>) -> D::NodeUid; + fn pick_node(&self, nodes: &BTreeMap>) -> D::NodeUid; - /// Called when a node controlled by the adversary receives a message + /// Called when a node controlled by the adversary receives a message. fn push_message(&mut self, sender_id: D::NodeUid, msg: TargetedMessage); - /// Produces a list of messages to be sent from the adversary's nodes + /// Produces a list of messages to be sent from the adversary's nodes. fn step(&mut self) -> Vec>; + + /// Initialize an adversary. This function's primary purpose is to inform the adversary over + /// some aspects of the network, such as which nodes they control. + fn init( + &mut self, + _all_nodes: &BTreeMap>, + _adv_nodes: &BTreeMap>>, + ) { + // default: does nothing + } } /// An adversary whose nodes never send any messages. @@ -145,7 +181,7 @@ impl SilentAdversary { } impl Adversary for SilentAdversary { - fn pick_node(&mut self, nodes: &BTreeMap>) -> D::NodeUid { + fn pick_node(&self, nodes: &BTreeMap>) -> D::NodeUid { self.scheduler.pick_node(nodes) } @@ -158,19 +194,164 @@ impl Adversary for SilentAdversary { } } +/// Return true with a certain `probability` ([0 .. 1.0]). +fn randomly(probability: f32) -> bool { + assert!(probability <= 1.0); + assert!(probability >= 0.0); + + let mut rng = rand::thread_rng(); + rng.gen_range(0.0, 1.0) <= probability +} + +#[test] +fn test_randomly() { + assert!(randomly(1.0)); + assert!(!randomly(0.0)); +} + +/// An adversary that performs naive replay attacks. +/// +/// The adversary will randomly take a message that is sent to one of its nodes and re-send it to +/// a different node. Additionally, it will inject unrelated messages at random. +#[allow(unused)] // not used in all tests +pub struct RandomAdversary { + /// The underlying scheduler used + scheduler: MessageScheduler, + + /// Node ids seen by the adversary. + known_node_ids: Vec, + /// Node ids under control of adversary + known_adversarial_ids: Vec, + + /// Internal queue for messages to be returned on the next `Adversary::step()` call + outgoing: Vec>, + /// Generates random messages to be injected + generator: F, + + /// Probability of a message replay + p_replay: f32, + /// Probability of a message injection + p_inject: f32, +} + +impl RandomAdversary { + /// Creates a new random adversary instance. + #[allow(unused)] + pub fn new(p_replay: f32, p_inject: f32, generator: F) -> RandomAdversary { + assert!( + p_inject < 0.95, + "injections are repeated, p_inject must be smaller than 0.95" + ); + + RandomAdversary { + // The random adversary, true to its name, always schedules randomly. + scheduler: MessageScheduler::Random, + known_node_ids: Vec::new(), + known_adversarial_ids: Vec::new(), + outgoing: Vec::new(), + generator, + p_replay, + p_inject, + } + } +} + +impl TargetedMessage> Adversary + for RandomAdversary +{ + fn init( + &mut self, + all_nodes: &BTreeMap>, + nodes: &BTreeMap>>, + ) { + self.known_adversarial_ids = nodes.keys().cloned().collect(); + self.known_node_ids = all_nodes.keys().cloned().collect(); + } + + fn pick_node(&self, nodes: &BTreeMap>) -> D::NodeUid { + // Just let the scheduler pick a node. + self.scheduler.pick_node(nodes) + } + + fn push_message(&mut self, _: D::NodeUid, msg: TargetedMessage) { + // If we have not discovered the network topology yet, abort. + if self.known_node_ids.is_empty() { + return; + } + + // only replay a message in some cases + if !randomly(self.p_replay) { + return; + } + + let TargetedMessage { message, target } = msg; + + match target { + Target::All => { + // Ideally, we would want to handle broadcast messages as well; however the + // adversary API is quite cumbersome at the moment in regards to access to the + // network topology. To re-send a broadcast message from one of the attacker + // controlled nodes, we would have to get a list of attacker controlled nodes + // here and use a random one as the origin/sender, this is not done here. + return; + } + Target::Node(our_node_id) => { + // Choose a new target to send the message to. The unwrap never fails, because we + // ensured that `known_node_ids` is non-empty earlier. + let mut rng = rand::thread_rng(); + let new_target_node = rng.choose(&self.known_node_ids).unwrap().clone(); + + // TODO: We could randomly broadcast it instead, if we had access to topology + // information. + self.outgoing.push(MessageWithSender::new( + our_node_id, + TargetedMessage { + target: Target::Node(new_target_node), + message, + }, + )); + } + } + } + + fn step(&mut self) -> Vec> { + // Clear messages. + let mut tmp = Vec::new(); + mem::swap(&mut tmp, &mut self.outgoing); + + // Possibly inject more messages: + while randomly(self.p_inject) { + let mut rng = rand::thread_rng(); + + // Pick a random adversarial node and create a message using the generator. + if let Some(sender) = rng.choose(&self.known_adversarial_ids[..]) { + let tm = (self.generator)(); + + // Add to outgoing queue. + tmp.push(MessageWithSender::new(sender.clone(), tm)); + } + } + + if !tmp.is_empty() { + println!("Injecting random messages: {:?}", tmp); + } + tmp + } +} + /// A collection of `TestNode`s representing a network. /// -/// Each TestNetwork type is tied to a specific adversary and a distributed algorithm. It consists +/// Each `TestNetwork` type is tied to a specific adversary and a distributed algorithm. It consists /// of a set of nodes, some of which are controlled by the adversary and some of which may be /// observer nodes, as well as a set of threshold-cryptography public keys. /// /// In addition to being able to participate correctly in the network using his nodes, the /// adversary can: /// -/// 1. decide which node is the next one to make progress. -/// 2. send arbitrary messages to any node originating from one of the nodes they control +/// 1. Decide which node is the next one to make progress, +/// 2. Send arbitrary messages to any node originating from one of the nodes they control. /// -/// See the `step` function for details on actual operation of the network +/// See the `step` function for details on actual operation of the network. pub struct TestNetwork, D: DistAlgorithm> where ::NodeUid: Hash, @@ -230,6 +411,7 @@ where .map(NodeUid) .map(new_adv_node_by_id) .collect(); + let mut network = TestNetwork { nodes: (0..good_num).map(NodeUid).map(new_node_by_id).collect(), observer: new_node_by_id(NodeUid(good_num + adv_num)).1, @@ -237,9 +419,13 @@ where pk_set: pk_set.clone(), adv_nodes, }; + + // Inform the adversary about their nodes. + network.adversary.init(&network.nodes, &network.adv_nodes); + let msgs = network.adversary.step(); - for (sender_id, msg) in msgs { - network.dispatch_messages(sender_id, vec![msg]); + for MessageWithSender { sender, tm } in msgs { + network.dispatch_messages(sender, vec![tm]); } let mut initial_msgs: Vec<(D::NodeUid, Vec<_>)> = Vec::new(); for (id, node) in &mut network.nodes { @@ -290,28 +476,28 @@ where /// Performs one iteration of the network, consisting of the following steps: /// - /// 1. Give the adversary a chance to send messages of his choosing through `Adversary::step()` + /// 1. Give the adversary a chance to send messages of his choosing through `Adversary::step()`, /// 2. Let the adversary pick a node that receives its next message through - /// `Adversary::pick_node()` + /// `Adversary::pick_node()`. /// - /// Returns the node id of the node that made progress + /// Returns the node ID of the node that made progress. pub fn step(&mut self) -> NodeUid { - // we let the adversary send out messages to any number of nodes + // We let the adversary send out messages to any number of nodes. let msgs = self.adversary.step(); - for (sender_id, msg) in msgs { - self.dispatch_messages(sender_id, Some(msg)); + for MessageWithSender { sender, tm } in msgs { + self.dispatch_messages(sender, Some(tm)); } - // now one node is chosen to make progress. we let the adversary decide which node + // Now one node is chosen to make progress, we let the adversary decide which. let id = self.adversary.pick_node(&self.nodes); - // the node handles the incoming message and creates new outgoing ones to be dispatched + // The node handles the incoming message and creates new outgoing ones to be dispatched. let msgs: Vec<_> = { let node = self.nodes.get_mut(&id).unwrap(); - // ensure the adversary is playing fair by selecting a node that will result in actual - // progress being made. otherwise `TestNode::handle_message()` will panic on `expect()` - // with a much more cryptic error message + // Ensure the adversary is playing fair by selecting a node that will result in actual + // progress being made, otherwise `TestNode::handle_message()` will panic on `expect()` + // with a much more cryptic error message. assert!( !node.is_idle(), "adversary illegally selected an idle node in pick_node()" diff --git a/tests/queueing_honey_badger.rs b/tests/queueing_honey_badger.rs index 6d7a4d6..042dc8a 100644 --- a/tests/queueing_honey_badger.rs +++ b/tests/queueing_honey_badger.rs @@ -8,6 +8,8 @@ extern crate pairing; extern crate rand; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate rand_derive; mod network; From 591b5464029b76bee9390fc31c76428c8503438b Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Fri, 13 Jul 2018 11:08:48 +0200 Subject: [PATCH 5/6] Removed unnecessary `Hash` where clause. --- tests/network/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/network/mod.rs b/tests/network/mod.rs index f966c35..821aa01 100644 --- a/tests/network/mod.rs +++ b/tests/network/mod.rs @@ -1,6 +1,5 @@ use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::fmt::{self, Debug}; -use std::hash::Hash; use std::mem; use std::sync::Arc; @@ -352,10 +351,7 @@ impl TargetedMessage> Adver /// 2. Send arbitrary messages to any node originating from one of the nodes they control. /// /// See the `step` function for details on actual operation of the network. -pub struct TestNetwork, D: DistAlgorithm> -where - ::NodeUid: Hash, -{ +pub struct TestNetwork, D: DistAlgorithm> { pub nodes: BTreeMap>, pub observer: TestNode, pub adv_nodes: BTreeMap>>, From 28da78c709942cd3550343038716820666f7ac93 Mon Sep 17 00:00:00 2001 From: c0gent Date: Fri, 13 Jul 2018 08:30:02 -0700 Subject: [PATCH 6/6] Add QueueingHoneyBadger::dyn_hb. Needed to retrieve netinfo. --- src/queueing_honey_badger.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/queueing_honey_badger.rs b/src/queueing_honey_badger.rs index a3363df..8f0de66 100644 --- a/src/queueing_honey_badger.rs +++ b/src/queueing_honey_badger.rs @@ -189,6 +189,11 @@ where QueueingHoneyBadgerBuilder::new(netinfo) } + /// Returns a reference to the internal `DynamicHoneyBadger` instance. + pub fn dyn_hb(&self) -> &DynamicHoneyBadger, NodeUid> { + &self.dyn_hb + } + /// Initiates the next epoch by proposing a batch from the queue. fn propose(&mut self) -> Result> { let amount = self.batch_size / self.dyn_hb.netinfo().num_nodes();