Merge branch 'master' into vk-api-refactor66

This commit is contained in:
Vladimir Komendantskiy 2018-07-13 13:53:21 +01:00 committed by GitHub
commit d7a2808774
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 489 additions and 74 deletions

View File

@ -30,6 +30,7 @@ merkle = { git = "https://github.com/afck/merkle.rs", branch = "public-proof", f
pairing = { version = "0.14.2", features = ["u128-support"] } pairing = { version = "0.14.2", features = ["u128-support"] }
protobuf = { version = "2.0.0", optional = true } protobuf = { version = "2.0.0", optional = true }
rand = "0.4.2" rand = "0.4.2"
rand_derive = "0.3.1"
reed-solomon-erasure = "3.1.0" reed-solomon-erasure = "3.1.0"
ring = "^0.12" ring = "^0.12"
serde = "1.0.55" serde = "1.0.55"

View File

@ -6,6 +6,8 @@ extern crate hbbft;
extern crate itertools; extern crate itertools;
extern crate pairing; extern crate pairing;
extern crate rand; extern crate rand;
#[macro_use]
extern crate rand_derive;
extern crate serde; extern crate serde;
#[macro_use(Deserialize, Serialize)] #[macro_use(Deserialize, Serialize)]
extern crate serde_derive; extern crate serde_derive;
@ -62,7 +64,7 @@ struct Args {
} }
/// A node identifier. In the simulation, nodes are simply numbered. /// 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); pub struct NodeUid(pub usize);
/// A transaction. /// A transaction.

View File

@ -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 /// A lattice-valued description of the state of `bin_values`, essentially the same as the set of
/// subsets of `bool`. /// 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 { pub enum BinValues {
None, None,
False, False,
@ -63,7 +63,7 @@ impl BinValues {
} }
} }
pub fn contains(&self, b: bool) -> bool { pub fn contains(self, b: bool) -> bool {
match self { match self {
BinValues::None => false, BinValues::None => false,
BinValues::Both => true, 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 { match self {
BinValues::None => true, BinValues::None => true,
BinValues::False if other == BinValues::False || other == BinValues::Both => true, BinValues::False if other == BinValues::False || other == BinValues::Both => true,
@ -83,7 +83,7 @@ impl BinValues {
} }
} }
pub fn definite(&self) -> Option<bool> { pub fn definite(self) -> Option<bool> {
match self { match self {
BinValues::False => Some(false), BinValues::False => Some(false),
BinValues::True => Some(true), BinValues::True => Some(true),

View File

@ -65,6 +65,7 @@
pub mod bin_values; pub mod bin_values;
use rand;
use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::fmt::Debug; use std::fmt::Debug;
use std::mem::replace; use std::mem::replace;
@ -122,12 +123,32 @@ impl AgreementContent {
} }
/// Messages sent during the binary Byzantine agreement stage. /// 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 struct AgreementMessage {
pub epoch: u32, pub epoch: u32,
pub content: AgreementContent, 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<R: rand::Rng>(rng: &mut R) -> Self {
let message_type = *rng
.choose(&["bval", "aux", "conf", "term", "coin"])
.unwrap();
match message_type {
"bval" => AgreementContent::BVal(rand::random()),
"aux" => AgreementContent::Aux(rand::random()),
"conf" => AgreementContent::Conf(rand::random()),
"term" => AgreementContent::Term(rand::random()),
"coin" => AgreementContent::Coin(Box::new(rand::random())),
_ => unreachable!(),
}
}
}
/// Possible values of the common coin schedule defining the method to derive the common coin in a /// 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. /// given epoch: as a constant value or a distributed computation.
enum CoinSchedule { enum CoinSchedule {

View File

@ -40,6 +40,102 @@
//! node will eventually be able to decode (i.e. receive at least _2 f + 1_ `Echo` messages). //! 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, //! * 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. //! 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 is 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 for new messages, in order:
//! 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::collections::{BTreeMap, VecDeque};
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
@ -48,6 +144,7 @@ use std::sync::Arc;
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use merkle::{MerkleTree, Proof}; use merkle::{MerkleTree, Proof};
use rand;
use reed_solomon_erasure as rse; use reed_solomon_erasure as rse;
use reed_solomon_erasure::ReedSolomon; use reed_solomon_erasure::ReedSolomon;
use ring::digest; use ring::digest;
@ -84,6 +181,29 @@ pub enum BroadcastMessage {
Ready(Vec<u8>), Ready(Vec<u8>),
} }
// 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<R: rand::Rng>(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 { impl Debug for BroadcastMessage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {

View File

@ -45,7 +45,7 @@ error_chain! {
} }
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
pub struct CommonCoinMessage(Signature); pub struct CommonCoinMessage(Signature);
impl CommonCoinMessage { impl CommonCoinMessage {

View File

@ -32,6 +32,7 @@ use broadcast::{self, Broadcast, BroadcastMessage, BroadcastStep};
use fault_log::FaultLog; use fault_log::FaultLog;
use fmt::HexBytes; use fmt::HexBytes;
use messaging::{DistAlgorithm, NetworkInfo, Step, TargetedMessage}; use messaging::{DistAlgorithm, NetworkInfo, Step, TargetedMessage};
use rand::Rand;
error_chain!{ error_chain!{
types { types {
@ -54,8 +55,8 @@ error_chain!{
type ProposedValue = Vec<u8>; type ProposedValue = Vec<u8>;
/// Message from Common Subset to remote nodes. /// Message from Common Subset to remote nodes.
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug, Rand)]
pub enum Message<NodeUid> { pub enum Message<NodeUid: Rand> {
/// A message for the broadcast algorithm concerning the set element proposed by the given node. /// A message for the broadcast algorithm concerning the set element proposed by the given node.
Broadcast(NodeUid, BroadcastMessage), Broadcast(NodeUid, BroadcastMessage),
/// A message for the agreement algorithm concerning the set element proposed by the given /// A message for the agreement algorithm concerning the set element proposed by the given
@ -65,9 +66,9 @@ pub enum Message<NodeUid> {
/// The queue of outgoing messages in a `CommonSubset` instance. /// The queue of outgoing messages in a `CommonSubset` instance.
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]
struct MessageQueue<NodeUid>(VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>); struct MessageQueue<NodeUid: Rand>(VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>);
impl<NodeUid: Clone + Debug + Ord> MessageQueue<NodeUid> { impl<NodeUid: Clone + Debug + Ord + Rand> MessageQueue<NodeUid> {
/// Appends to the queue the messages from `agr`, wrapped with `proposer_id`. /// Appends to the queue the messages from `agr`, wrapped with `proposer_id`.
fn extend_agreement(&mut self, proposer_id: &NodeUid, agr: &mut Agreement<NodeUid>) { fn extend_agreement(&mut self, proposer_id: &NodeUid, agr: &mut Agreement<NodeUid>) {
let convert = |msg: TargetedMessage<AgreementMessage, NodeUid>| { let convert = |msg: TargetedMessage<AgreementMessage, NodeUid>| {
@ -86,7 +87,7 @@ impl<NodeUid: Clone + Debug + Ord> MessageQueue<NodeUid> {
} }
/// Asynchronous Common Subset algorithm instance /// Asynchronous Common Subset algorithm instance
pub struct CommonSubset<NodeUid> { pub struct CommonSubset<NodeUid: Rand> {
/// Shared network information. /// Shared network information.
netinfo: Arc<NetworkInfo<NodeUid>>, netinfo: Arc<NetworkInfo<NodeUid>>,
broadcast_instances: BTreeMap<NodeUid, Broadcast<NodeUid>>, broadcast_instances: BTreeMap<NodeUid, Broadcast<NodeUid>>,
@ -103,7 +104,7 @@ pub struct CommonSubset<NodeUid> {
pub type CommonSubsetStep<NodeUid> = Step<NodeUid, BTreeMap<NodeUid, ProposedValue>>; pub type CommonSubsetStep<NodeUid> = Step<NodeUid, BTreeMap<NodeUid, ProposedValue>>;
impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for CommonSubset<NodeUid> { impl<NodeUid: Clone + Debug + Ord + Rand> DistAlgorithm for CommonSubset<NodeUid> {
type NodeUid = NodeUid; type NodeUid = NodeUid;
type Input = ProposedValue; type Input = ProposedValue;
type Output = BTreeMap<NodeUid, ProposedValue>; type Output = BTreeMap<NodeUid, ProposedValue>;
@ -145,7 +146,7 @@ impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for CommonSubset<NodeUid> {
} }
} }
impl<NodeUid: Clone + Debug + Ord> CommonSubset<NodeUid> { impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>, session_id: u64) -> CommonSubsetResult<Self> { pub fn new(netinfo: Arc<NetworkInfo<NodeUid>>, session_id: u64) -> CommonSubsetResult<Self> {
// Create all broadcast instances. // Create all broadcast instances.
let mut broadcast_instances: BTreeMap<NodeUid, Broadcast<NodeUid>> = BTreeMap::new(); let mut broadcast_instances: BTreeMap<NodeUid, Broadcast<NodeUid>> = BTreeMap::new();

View File

@ -16,7 +16,7 @@ use clear_on_drop::ClearOnDrop;
use init_with::InitWith; use init_with::InitWith;
use pairing::bls12_381::{Bls12, Fr, FrRepr, G1, G1Affine, G2, G2Affine}; use pairing::bls12_381::{Bls12, Fr, FrRepr, G1, G1Affine, G2, G2Affine};
use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField}; use pairing::{CurveAffine, CurveProjective, Engine, Field, PrimeField};
use rand::{ChaChaRng, OsRng, Rand, Rng, SeedableRng}; use rand::{ChaChaRng, OsRng, Rng, SeedableRng};
use ring::digest; use ring::digest;
use self::error::{ErrorKind, Result}; use self::error::{ErrorKind, Result};
@ -83,7 +83,8 @@ impl PublicKey {
} }
/// A signature, or a signature share. /// 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); pub struct Signature(#[serde(with = "serde_impl::projective")] G2);
impl fmt::Debug for Signature { impl fmt::Debug for Signature {
@ -112,7 +113,7 @@ impl Signature {
} }
/// A secret key, or a secret key share. /// A secret key, or a secret key share.
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq, Rand)]
pub struct SecretKey(Fr); pub struct SecretKey(Fr);
impl fmt::Debug for SecretKey { impl fmt::Debug for SecretKey {
@ -129,12 +130,6 @@ impl Default for SecretKey {
} }
} }
impl Rand for SecretKey {
fn rand<R: Rng>(rng: &mut R) -> Self {
SecretKey(rng.gen())
}
}
impl SecretKey { impl SecretKey {
/// Creates a secret key from an existing value /// Creates a secret key from an existing value
pub fn from_value(f: Fr) -> Self { 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. /// 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); pub struct DecryptionShare(#[serde(with = "serde_impl::projective")] G1);
impl Hash for DecryptionShare { impl Hash for DecryptionShare {

View File

@ -1,4 +1,5 @@
use super::ChangeState; use super::ChangeState;
use rand::Rand;
use std::collections::BTreeMap; use std::collections::BTreeMap;
/// A batch of transactions the algorithm has output. /// A batch of transactions the algorithm has output.
@ -13,7 +14,7 @@ pub struct Batch<C, NodeUid> {
pub change: ChangeState<NodeUid>, pub change: ChangeState<NodeUid>,
} }
impl<C, NodeUid: Ord> Batch<C, NodeUid> { impl<C, NodeUid: Ord + Rand> Batch<C, NodeUid> {
/// Returns a new, empty batch with the given epoch. /// Returns a new, empty batch with the given epoch.
pub fn new(epoch: u64) -> Self { pub fn new(epoch: u64) -> Self {
Batch { Batch {

View File

@ -4,6 +4,7 @@ use std::hash::Hash;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use rand::Rand;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::{DynamicHoneyBadger, MessageQueue, VoteCounter}; use super::{DynamicHoneyBadger, MessageQueue, VoteCounter};
@ -25,7 +26,7 @@ pub struct DynamicHoneyBadgerBuilder<C, NodeUid> {
impl<C, NodeUid> DynamicHoneyBadgerBuilder<C, NodeUid> impl<C, NodeUid> DynamicHoneyBadgerBuilder<C, NodeUid>
where where
C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, 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 /// Returns a new `DynamicHoneyBadgerBuilder` configured to use the node IDs and cryptographic
/// keys specified by `netinfo`. /// keys specified by `netinfo`.

View File

@ -44,6 +44,7 @@
//! `SyncKeyGen` instance is dropped, and a new one is started to create keys according to the new //! `SyncKeyGen` instance is dropped, and a new one is started to create keys according to the new
//! pending change. //! pending change.
use rand::Rand;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::Hash; use std::hash::Hash;
@ -84,7 +85,7 @@ pub enum Input<C, NodeUid> {
} }
/// A Honey Badger instance that can handle adding and removing nodes. /// A Honey Badger instance that can handle adding and removing nodes.
pub struct DynamicHoneyBadger<C, NodeUid> pub struct DynamicHoneyBadger<C, NodeUid: Rand>
where where
C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, C: 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,
@ -116,7 +117,7 @@ pub type DynamicHoneyBadgerStep<C, NodeUid> = Step<NodeUid, Batch<C, NodeUid>>;
impl<C, NodeUid> DistAlgorithm for DynamicHoneyBadger<C, NodeUid> impl<C, NodeUid> DistAlgorithm for DynamicHoneyBadger<C, NodeUid>
where where
C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, 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 NodeUid = NodeUid;
type Input = Input<C, NodeUid>; type Input = Input<C, NodeUid>;
@ -180,7 +181,7 @@ where
impl<C, NodeUid> DynamicHoneyBadger<C, NodeUid> impl<C, NodeUid> DynamicHoneyBadger<C, NodeUid>
where where
C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, 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,
{ {
fn step(&mut self, fault_log: FaultLog<NodeUid>) -> Result<DynamicHoneyBadgerStep<C, NodeUid>> { fn step(&mut self, fault_log: FaultLog<NodeUid>) -> Result<DynamicHoneyBadgerStep<C, NodeUid>> {
Ok(Step::new(self.output.drain(0..).collect(), fault_log)) Ok(Step::new(self.output.drain(0..).collect(), fault_log))
@ -498,7 +499,7 @@ pub enum KeyGenMessage {
/// A message sent to or received from another node's Honey Badger instance. /// A message sent to or received from another node's Honey Badger instance.
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Message<NodeUid> { pub enum Message<NodeUid: Rand> {
/// A message belonging to the `HoneyBadger` algorithm started in the given epoch. /// A message belonging to the `HoneyBadger` algorithm started in the given epoch.
HoneyBadger(u64, HbMessage<NodeUid>), HoneyBadger(u64, HbMessage<NodeUid>),
/// A transaction to be committed, signed by a node. /// A transaction to be committed, signed by a node.
@ -507,7 +508,7 @@ pub enum Message<NodeUid> {
SignedVote(SignedVote<NodeUid>), SignedVote(SignedVote<NodeUid>),
} }
impl<NodeUid> Message<NodeUid> { impl<NodeUid: Rand> Message<NodeUid> {
pub fn epoch(&self) -> u64 { pub fn epoch(&self) -> u64 {
match *self { match *self {
Message::HoneyBadger(epoch, _) => epoch, Message::HoneyBadger(epoch, _) => epoch,
@ -519,11 +520,11 @@ impl<NodeUid> Message<NodeUid> {
/// The queue of outgoing messages in a `HoneyBadger` instance. /// The queue of outgoing messages in a `HoneyBadger` instance.
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]
struct MessageQueue<NodeUid>(VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>); struct MessageQueue<NodeUid: Rand>(VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>);
impl<NodeUid> MessageQueue<NodeUid> impl<NodeUid> MessageQueue<NodeUid>
where 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`. /// Appends to the queue the messages from `hb`, wrapped with `epoch`.
fn extend_with_epoch<Tx>(&mut self, epoch: u64, hb: &mut HoneyBadger<Tx, NodeUid>) fn extend_with_epoch<Tx>(&mut self, epoch: u64, hb: &mut HoneyBadger<Tx, NodeUid>)

View File

@ -89,7 +89,7 @@ where
/// Returns an iterator over all pending votes that are newer than their voter's committed /// Returns an iterator over all pending votes that are newer than their voter's committed
/// vote. /// vote.
pub fn pending_votes<'a>(&'a self) -> impl Iterator<Item = &'a SignedVote<NodeUid>> { pub fn pending_votes(&self) -> impl Iterator<Item = &SignedVote<NodeUid>> {
self.pending.values().filter(move |signed_vote| { self.pending.values().filter(move |signed_vote| {
self.committed self.committed
.get(&signed_vote.voter) .get(&signed_vote.voter)

View File

@ -1,3 +1,4 @@
use rand::Rand;
use std::collections::btree_map::Entry; use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::fmt::Debug; use std::fmt::Debug;
@ -45,7 +46,7 @@ pub struct HoneyBadgerBuilder<C, NodeUid> {
impl<C, NodeUid> HoneyBadgerBuilder<C, NodeUid> impl<C, NodeUid> HoneyBadgerBuilder<C, NodeUid>
where where
C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq, 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 /// Returns a new `HoneyBadgerBuilder` configured to use the node IDs and cryptographic keys
/// specified by `netinfo`. /// specified by `netinfo`.
@ -82,7 +83,7 @@ where
} }
/// An instance of the Honey Badger Byzantine fault tolerant consensus algorithm. /// An instance of the Honey Badger Byzantine fault tolerant consensus algorithm.
pub struct HoneyBadger<C, NodeUid> { pub struct HoneyBadger<C, NodeUid: Rand> {
/// Shared network data. /// Shared network data.
netinfo: Arc<NetworkInfo<NodeUid>>, netinfo: Arc<NetworkInfo<NodeUid>>,
/// The earliest epoch from which we have not yet received output. /// The earliest epoch from which we have not yet received output.
@ -115,7 +116,7 @@ pub type HoneyBadgerStep<C, NodeUid> = Step<NodeUid, Batch<C, NodeUid>>;
impl<C, NodeUid> DistAlgorithm for HoneyBadger<C, NodeUid> impl<C, NodeUid> DistAlgorithm for HoneyBadger<C, NodeUid>
where where
C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq, C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq,
NodeUid: Ord + Clone + Debug, NodeUid: Ord + Clone + Debug + Rand,
{ {
type NodeUid = NodeUid; type NodeUid = NodeUid;
type Input = C; type Input = C;
@ -166,7 +167,7 @@ where
impl<C, NodeUid> HoneyBadger<C, NodeUid> impl<C, NodeUid> HoneyBadger<C, NodeUid>
where where
C: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq, 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 /// Returns a new `HoneyBadgerBuilder` configured to use the node IDs and cryptographic keys
/// specified by `netinfo`. /// specified by `netinfo`.
@ -630,8 +631,8 @@ impl<C, NodeUid: Ord> Batch<C, NodeUid> {
} }
/// The content of a `HoneyBadger` message. It should be further annotated with an epoch. /// The content of a `HoneyBadger` message. It should be further annotated with an epoch.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Rand, Serialize)]
pub enum MessageContent<NodeUid> { pub enum MessageContent<NodeUid: Rand> {
/// A message belonging to the common subset algorithm in the given epoch. /// A message belonging to the common subset algorithm in the given epoch.
CommonSubset(common_subset::Message<NodeUid>), CommonSubset(common_subset::Message<NodeUid>),
/// A decrypted share of the output of `proposer_id`. /// A decrypted share of the output of `proposer_id`.
@ -641,7 +642,7 @@ pub enum MessageContent<NodeUid> {
}, },
} }
impl<NodeUid> MessageContent<NodeUid> { impl<NodeUid: Rand> MessageContent<NodeUid> {
pub fn with_epoch(self, epoch: u64) -> Message<NodeUid> { pub fn with_epoch(self, epoch: u64) -> Message<NodeUid> {
Message { Message {
epoch, epoch,
@ -651,13 +652,13 @@ impl<NodeUid> MessageContent<NodeUid> {
} }
/// A message sent to or received from another node's Honey Badger instance. /// A message sent to or received from another node's Honey Badger instance.
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Rand, Serialize)]
pub struct Message<NodeUid> { pub struct Message<NodeUid: Rand> {
epoch: u64, epoch: u64,
content: MessageContent<NodeUid>, content: MessageContent<NodeUid>,
} }
impl<NodeUid> Message<NodeUid> { impl<NodeUid: Rand> Message<NodeUid> {
pub fn epoch(&self) -> u64 { pub fn epoch(&self) -> u64 {
self.epoch self.epoch
} }
@ -665,9 +666,9 @@ impl<NodeUid> Message<NodeUid> {
/// The queue of outgoing messages in a `HoneyBadger` instance. /// The queue of outgoing messages in a `HoneyBadger` instance.
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]
struct MessageQueue<NodeUid>(VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>); struct MessageQueue<NodeUid: Rand>(VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>);
impl<NodeUid: Clone + Debug + Ord> MessageQueue<NodeUid> { impl<NodeUid: Clone + Debug + Ord + Rand> MessageQueue<NodeUid> {
/// Appends to the queue the messages from `cs`, wrapped with `epoch`. /// Appends to the queue the messages from `cs`, wrapped with `epoch`.
fn extend_with_epoch(&mut self, epoch: u64, cs: &mut CommonSubset<NodeUid>) { fn extend_with_epoch(&mut self, epoch: u64, cs: &mut CommonSubset<NodeUid>) {
let convert = |msg: TargetedMessage<common_subset::Message<NodeUid>, NodeUid>| { let convert = |msg: TargetedMessage<common_subset::Message<NodeUid>, NodeUid>| {

View File

@ -114,6 +114,8 @@ extern crate pairing;
#[cfg(feature = "serialization-protobuf")] #[cfg(feature = "serialization-protobuf")]
extern crate protobuf; extern crate protobuf;
extern crate rand; extern crate rand;
#[macro_use]
extern crate rand_derive;
extern crate reed_solomon_erasure; extern crate reed_solomon_erasure;
extern crate ring; extern crate ring;
extern crate serde; extern crate serde;

View File

@ -2,6 +2,7 @@
//! //!
//! This works exactly like Dynamic Honey Badger, but it has a transaction queue built in. //! This works exactly like Dynamic Honey Badger, but it has a transaction queue built in.
use rand::Rand;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::Hash; use std::hash::Hash;
@ -39,7 +40,7 @@ pub struct QueueingHoneyBadgerBuilder<Tx, NodeUid> {
impl<Tx, NodeUid> QueueingHoneyBadgerBuilder<Tx, NodeUid> impl<Tx, NodeUid> QueueingHoneyBadgerBuilder<Tx, NodeUid>
where where
Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone, 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 /// Returns a new `QueueingHoneyBadgerBuilder` configured to use the node IDs and cryptographic
/// keys specified by `netinfo`. /// keys specified by `netinfo`.
@ -109,7 +110,7 @@ where
pub struct QueueingHoneyBadger<Tx, NodeUid> pub struct QueueingHoneyBadger<Tx, NodeUid>
where where
Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash, 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. /// The target number of transactions to be included in each batch.
batch_size: usize, batch_size: usize,
@ -126,7 +127,7 @@ pub type QueueingHoneyBadgerStep<Tx, NodeUid> = Step<NodeUid, Batch<Tx, NodeUid>
impl<Tx, NodeUid> DistAlgorithm for QueueingHoneyBadger<Tx, NodeUid> impl<Tx, NodeUid> DistAlgorithm for QueueingHoneyBadger<Tx, NodeUid>
where where
Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone, 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 NodeUid = NodeUid;
type Input = Input<Tx, NodeUid>; type Input = Input<Tx, NodeUid>;
@ -185,7 +186,7 @@ where
impl<Tx, NodeUid> QueueingHoneyBadger<Tx, NodeUid> impl<Tx, NodeUid> QueueingHoneyBadger<Tx, NodeUid>
where where
Tx: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash + Clone, 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 /// Returns a new `QueueingHoneyBadgerBuilder` configured to use the node IDs and cryptographic
/// keys specified by `netinfo`. /// keys specified by `netinfo`.

View File

@ -21,6 +21,8 @@ extern crate pairing;
extern crate rand; extern crate rand;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[macro_use]
extern crate rand_derive;
mod network; mod network;

View File

@ -8,6 +8,8 @@ extern crate pairing;
extern crate rand; extern crate rand;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[macro_use]
extern crate rand_derive;
mod network; mod network;
@ -19,8 +21,11 @@ use rand::Rng;
use hbbft::broadcast::{Broadcast, BroadcastMessage}; use hbbft::broadcast::{Broadcast, BroadcastMessage};
use hbbft::crypto::SecretKeySet; use hbbft::crypto::SecretKeySet;
use hbbft::messaging::{DistAlgorithm, NetworkInfo, TargetedMessage}; use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage};
use network::{Adversary, MessageScheduler, NodeUid, SilentAdversary, TestNetwork, TestNode}; use network::{
Adversary, MessageScheduler, MessageWithSender, NodeUid, RandomAdversary, SilentAdversary,
TestNetwork, TestNode,
};
/// An adversary that inputs an alternate value. /// An adversary that inputs an alternate value.
struct ProposeAdversary { struct ProposeAdversary {
@ -55,7 +60,7 @@ impl Adversary<Broadcast<NodeUid>> for ProposeAdversary {
// All messages are ignored. // All messages are ignored.
} }
fn step(&mut self) -> Vec<(NodeUid, TargetedMessage<BroadcastMessage, NodeUid>)> { fn step(&mut self) -> Vec<MessageWithSender<Broadcast<NodeUid>>> {
if self.has_sent { if self.has_sent {
return vec![]; return vec![];
} }
@ -84,7 +89,9 @@ impl Adversary<Broadcast<NodeUid>> for ProposeAdversary {
)); ));
let mut bc = Broadcast::new(netinfo, id).expect("broadcast instance"); let mut bc = Broadcast::new(netinfo, id).expect("broadcast instance");
bc.input(b"Fake news".to_vec()).expect("propose"); 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_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");
}

View File

@ -8,6 +8,8 @@ extern crate pairing;
extern crate rand; extern crate rand;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[macro_use]
extern crate rand_derive;
mod network; mod network;

View File

@ -8,6 +8,8 @@ extern crate pairing;
extern crate rand; extern crate rand;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[macro_use]
extern crate rand_derive;
mod network; mod network;

View File

@ -8,6 +8,8 @@ extern crate pairing;
extern crate rand; extern crate rand;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[macro_use]
extern crate rand_derive;
mod network; mod network;

View File

@ -8,6 +8,8 @@ extern crate env_logger;
extern crate pairing; extern crate pairing;
extern crate rand; extern crate rand;
#[macro_use] #[macro_use]
extern crate rand_derive;
#[macro_use]
extern crate serde_derive; extern crate serde_derive;
mod network; mod network;
@ -23,7 +25,8 @@ use hbbft::messaging::{NetworkInfo, Target, TargetedMessage};
use hbbft::transaction_queue::TransactionQueue; use hbbft::transaction_queue::TransactionQueue;
use network::{ use network::{
Adversary, MessageScheduler, MessageWithSender, NodeUid, SilentAdversary, TestNetwork, TestNode, Adversary, MessageScheduler, MessageWithSender, NodeUid, RandomAdversary, SilentAdversary,
TestNetwork, TestNode,
}; };
type UsizeHoneyBadger = HoneyBadger<Vec<usize>, NodeUid>; type UsizeHoneyBadger = HoneyBadger<Vec<usize>, NodeUid>;
@ -100,7 +103,7 @@ impl Adversary<UsizeHoneyBadger> for FaultyShareAdversary {
.expect("decryption share"); .expect("decryption share");
// Send the share to remote nodes. // Send the share to remote nodes.
for proposer_id in 0..self.num_good + self.num_adv { for proposer_id in 0..self.num_good + self.num_adv {
outgoing.push(( outgoing.push(MessageWithSender::new(
NodeUid(sender_id), NodeUid(sender_id),
Target::All.message( Target::All.message(
MessageContent::DecryptionShare { MessageContent::DecryptionShare {
@ -237,3 +240,15 @@ fn test_honey_badger_faulty_share() {
}; };
test_honey_badger_different_sizes(new_adversary, 8); 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);
}

View File

@ -1,6 +1,6 @@
use std::collections::{BTreeMap, BTreeSet, VecDeque}; 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 std::sync::Arc;
use rand::{self, Rng}; use rand::{self, Rng};
@ -9,7 +9,7 @@ use hbbft::crypto::{PublicKeySet, SecretKeySet};
use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage}; use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage};
/// A node identifier. In the tests, nodes are simply numbered. /// 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); pub struct NodeUid(pub usize);
/// A "node" running an instance of the algorithm `D`. /// A "node" running an instance of the algorithm `D`.
@ -68,6 +68,11 @@ impl<D: DistAlgorithm> TestNode<D> {
.expect("handling message"); .expect("handling message");
self.outputs.extend(step.output); self.outputs.extend(step.output);
} }
/// 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. /// A strategy for picking the next good node to handle a message.
@ -105,21 +110,61 @@ impl MessageScheduler {
} }
} }
pub type MessageWithSender<D> = ( /// A message combined with a sender.
<D as DistAlgorithm>::NodeUid, pub struct MessageWithSender<D: DistAlgorithm> {
TargetedMessage<<D as DistAlgorithm>::Message, <D as DistAlgorithm>::NodeUid>, /// The sender of the message.
); pub sender: <D as DistAlgorithm>::NodeUid,
/// The targeted message (recipient and message body).
pub tm: TargetedMessage<<D as DistAlgorithm>::Message, <D as DistAlgorithm>::NodeUid>,
}
// The Debug implementation cannot be derived automatically, possibly due to a compiler bug. For
// this reason, it is implemented manually here.
impl<D: DistAlgorithm> fmt::Debug for MessageWithSender<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"MessageWithSender {{ sender: {:?}, tm: {:?} }}",
self.sender, self.tm.target
)
}
}
impl<D: DistAlgorithm> MessageWithSender<D> {
/// Creates a new message with a sender.
pub fn new(
sender: D::NodeUid,
tm: TargetedMessage<D::Message, D::NodeUid>,
) -> MessageWithSender<D> {
MessageWithSender { sender, tm }
}
}
/// An adversary that can control a set of nodes and pick the next good node to receive a message. /// 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<D: DistAlgorithm> { pub trait Adversary<D: DistAlgorithm> {
/// 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, TestNode<D>>) -> D::NodeUid; fn pick_node(&self, nodes: &BTreeMap<D::NodeUid, TestNode<D>>) -> 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<D::Message, D::NodeUid>); fn push_message(&mut self, sender_id: D::NodeUid, msg: TargetedMessage<D::Message, D::NodeUid>);
/// 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<MessageWithSender<D>>; fn step(&mut self) -> Vec<MessageWithSender<D>>;
/// 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<D::NodeUid, TestNode<D>>,
_adv_nodes: &BTreeMap<D::NodeUid, Arc<NetworkInfo<D::NodeUid>>>,
) {
// default: does nothing
}
} }
/// An adversary whose nodes never send any messages. /// An adversary whose nodes never send any messages.
@ -148,11 +193,165 @@ impl<D: DistAlgorithm> Adversary<D> for SilentAdversary {
} }
} }
/// A collection of `TestNode`s representing a network. /// Return true with a certain `probability` ([0 .. 1.0]).
pub struct TestNetwork<A: Adversary<D>, D: DistAlgorithm> fn randomly(probability: f32) -> bool {
where assert!(probability <= 1.0);
<D as DistAlgorithm>::NodeUid: Hash, 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<D: DistAlgorithm, F> {
/// The underlying scheduler used
scheduler: MessageScheduler,
/// Node ids seen by the adversary.
known_node_ids: Vec<D::NodeUid>,
/// Node ids under control of adversary
known_adversarial_ids: Vec<D::NodeUid>,
/// Internal queue for messages to be returned on the next `Adversary::step()` call
outgoing: Vec<MessageWithSender<D>>,
/// 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<D: DistAlgorithm, F> RandomAdversary<D, F> {
/// Creates a new random adversary instance.
#[allow(unused)]
pub fn new(p_replay: f32, p_inject: f32, generator: F) -> RandomAdversary<D, F> {
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<D: DistAlgorithm, F: Fn() -> TargetedMessage<D::Message, D::NodeUid>> Adversary<D>
for RandomAdversary<D, F>
{ {
fn init(
&mut self,
all_nodes: &BTreeMap<D::NodeUid, TestNode<D>>,
nodes: &BTreeMap<D::NodeUid, Arc<NetworkInfo<D::NodeUid>>>,
) {
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, TestNode<D>>) -> D::NodeUid {
// Just let the scheduler pick a node.
self.scheduler.pick_node(nodes)
}
fn push_message(&mut self, _: D::NodeUid, msg: TargetedMessage<D::Message, D::NodeUid>) {
// 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<MessageWithSender<D>> {
// 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
/// 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<A: Adversary<D>, D: DistAlgorithm> {
pub nodes: BTreeMap<D::NodeUid, TestNode<D>>, pub nodes: BTreeMap<D::NodeUid, TestNode<D>>,
pub observer: TestNode<D>, pub observer: TestNode<D>,
pub adv_nodes: BTreeMap<D::NodeUid, Arc<NetworkInfo<D::NodeUid>>>, pub adv_nodes: BTreeMap<D::NodeUid, Arc<NetworkInfo<D::NodeUid>>>,
@ -208,6 +407,7 @@ where
.map(NodeUid) .map(NodeUid)
.map(new_adv_node_by_id) .map(new_adv_node_by_id)
.collect(); .collect();
let mut network = TestNetwork { let mut network = TestNetwork {
nodes: (0..good_num).map(NodeUid).map(new_node_by_id).collect(), nodes: (0..good_num).map(NodeUid).map(new_node_by_id).collect(),
observer: new_node_by_id(NodeUid(good_num + adv_num)).1, observer: new_node_by_id(NodeUid(good_num + adv_num)).1,
@ -215,9 +415,13 @@ where
pk_set: pk_set.clone(), pk_set: pk_set.clone(),
adv_nodes, adv_nodes,
}; };
// Inform the adversary about their nodes.
network.adversary.init(&network.nodes, &network.adv_nodes);
let msgs = network.adversary.step(); let msgs = network.adversary.step();
for (sender_id, msg) in msgs { for MessageWithSender { sender, tm } in msgs {
network.dispatch_messages(sender_id, vec![msg]); network.dispatch_messages(sender, vec![tm]);
} }
let mut initial_msgs: Vec<(D::NodeUid, Vec<_>)> = Vec::new(); let mut initial_msgs: Vec<(D::NodeUid, Vec<_>)> = Vec::new();
for (id, node) in &mut network.nodes { for (id, node) in &mut network.nodes {
@ -266,20 +470,40 @@ 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 { pub fn step(&mut self) -> NodeUid {
// We let the adversary send out messages to any number of nodes.
let msgs = self.adversary.step(); let msgs = self.adversary.step();
for (sender_id, msg) in msgs { for MessageWithSender { sender, tm } in msgs {
self.dispatch_messages(sender_id, Some(msg)); self.dispatch_messages(sender, Some(tm));
} }
// Pick a random non-idle node..
// Now one node is chosen to make progress, we let the adversary decide which.
let id = self.adversary.pick_node(&self.nodes); let id = self.adversary.pick_node(&self.nodes);
// The node handles the incoming message and creates new outgoing ones to be dispatched.
let msgs: Vec<_> = { let msgs: Vec<_> = {
let node = self.nodes.get_mut(&id).unwrap(); 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.handle_message();
node.algo.message_iter().collect() node.algo.message_iter().collect()
}; };
self.dispatch_messages(id, msgs); self.dispatch_messages(id, msgs);
id id
} }

View File

@ -8,6 +8,8 @@ extern crate pairing;
extern crate rand; extern crate rand;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[macro_use]
extern crate rand_derive;
mod network; mod network;