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()`.
This commit is contained in:
Marc Brinkmann 2018-07-05 18:20:53 +02:00
parent 510c4478d4
commit 5336fbe707
22 changed files with 377 additions and 96 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"] }
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"

View File

@ -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.

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
/// 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,

View File

@ -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<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
/// given epoch: as a constant value or a distributed computation.
enum CoinSchedule {

View File

@ -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<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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
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);
impl CommonCoinMessage {

View File

@ -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<u8>;
/// Message from Common Subset to remote nodes.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum Message<NodeUid> {
#[derive(Serialize, Deserialize, Clone, Debug, Rand)]
pub enum Message<NodeUid: Rand> {
/// 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<NodeUid> {
/// The queue of outgoing messages in a `CommonSubset` instance.
#[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`.
fn extend_agreement(&mut self, proposer_id: &NodeUid, agr: &mut Agreement<NodeUid>) {
let convert = |msg: TargetedMessage<AgreementMessage, NodeUid>| {
@ -86,7 +87,7 @@ impl<NodeUid: Clone + Debug + Ord> MessageQueue<NodeUid> {
}
/// Asynchronous Common Subset algorithm instance
pub struct CommonSubset<NodeUid> {
pub struct CommonSubset<NodeUid: Rand> {
/// Shared network information.
netinfo: Arc<NetworkInfo<NodeUid>>,
broadcast_instances: BTreeMap<NodeUid, Broadcast<NodeUid>>,
@ -101,7 +102,7 @@ pub struct CommonSubset<NodeUid> {
decided: bool,
}
impl<NodeUid: Clone + Debug + Ord> DistAlgorithm for CommonSubset<NodeUid> {
impl<NodeUid: Clone + Debug + Ord + Rand> DistAlgorithm for CommonSubset<NodeUid> {
type NodeUid = NodeUid;
type Input = 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> {
// Create all broadcast instances.
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 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<R: Rng>(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 {

View File

@ -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<C, 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.
pub fn new(epoch: u64) -> Self {
Batch {

View File

@ -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<C, NodeUid> {
impl<C, NodeUid> DynamicHoneyBadgerBuilder<C, NodeUid>
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`.

View File

@ -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<C, NodeUid> {
}
/// A Honey Badger instance that can handle adding and removing nodes.
pub struct DynamicHoneyBadger<C, NodeUid>
pub struct DynamicHoneyBadger<C, NodeUid: Rand>
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<C, NodeUid> DistAlgorithm for DynamicHoneyBadger<C, NodeUid>
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<C, NodeUid>;
@ -178,7 +179,7 @@ where
impl<C, NodeUid> DynamicHoneyBadger<C, NodeUid>
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<NodeUid> {
pub enum Message<NodeUid: Rand> {
/// A message belonging to the `HoneyBadger` algorithm started in the given epoch.
HoneyBadger(u64, HbMessage<NodeUid>),
/// A transaction to be committed, signed by a node.
@ -496,7 +497,7 @@ pub enum Message<NodeUid> {
SignedVote(SignedVote<NodeUid>),
}
impl<NodeUid> Message<NodeUid> {
impl<NodeUid: Rand> Message<NodeUid> {
pub fn epoch(&self) -> u64 {
match *self {
Message::HoneyBadger(epoch, _) => epoch,
@ -508,11 +509,11 @@ impl<NodeUid> Message<NodeUid> {
/// The queue of outgoing messages in a `HoneyBadger` instance.
#[derive(Deref, DerefMut)]
struct MessageQueue<NodeUid>(VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>);
struct MessageQueue<NodeUid: Rand>(VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>);
impl<NodeUid> MessageQueue<NodeUid>
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<Tx>(&mut self, epoch: u64, hb: &mut HoneyBadger<Tx, NodeUid>)

View File

@ -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<C, NodeUid> {
impl<C, NodeUid> HoneyBadgerBuilder<C, NodeUid>
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<C, NodeUid> {
pub struct HoneyBadger<C, NodeUid: Rand> {
/// Shared network data.
netinfo: Arc<NetworkInfo<NodeUid>>,
/// The earliest epoch from which we have not yet received output.
@ -113,7 +114,7 @@ pub struct HoneyBadger<C, NodeUid> {
impl<C, NodeUid> DistAlgorithm for HoneyBadger<C, NodeUid>
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<C, NodeUid> HoneyBadger<C, NodeUid>
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<C, NodeUid: Ord> Batch<C, NodeUid> {
}
/// The content of a `HoneyBadger` message. It should be further annotated with an epoch.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum MessageContent<NodeUid> {
#[derive(Clone, Debug, Deserialize, Rand, Serialize)]
pub enum MessageContent<NodeUid: Rand> {
/// A message belonging to the common subset algorithm in the given epoch.
CommonSubset(common_subset::Message<NodeUid>),
/// A decrypted share of the output of `proposer_id`.
@ -640,7 +641,7 @@ pub enum MessageContent<NodeUid> {
},
}
impl<NodeUid> MessageContent<NodeUid> {
impl<NodeUid: Rand> MessageContent<NodeUid> {
pub fn with_epoch(self, epoch: u64) -> Message<NodeUid> {
Message {
epoch,
@ -650,13 +651,13 @@ impl<NodeUid> MessageContent<NodeUid> {
}
/// A message sent to or received from another node's Honey Badger instance.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Message<NodeUid> {
#[derive(Clone, Debug, Deserialize, Rand, Serialize)]
pub struct Message<NodeUid: Rand> {
epoch: u64,
content: MessageContent<NodeUid>,
}
impl<NodeUid> Message<NodeUid> {
impl<NodeUid: Rand> Message<NodeUid> {
pub fn epoch(&self) -> u64 {
self.epoch
}
@ -664,9 +665,9 @@ impl<NodeUid> Message<NodeUid> {
/// The queue of outgoing messages in a `HoneyBadger` instance.
#[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`.
fn extend_with_epoch(&mut self, epoch: u64, cs: &mut CommonSubset<NodeUid>) {
let convert = |msg: TargetedMessage<common_subset::Message<NodeUid>, NodeUid>| {

View File

@ -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;

View File

@ -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<Tx, NodeUid> {
impl<Tx, NodeUid> QueueingHoneyBadgerBuilder<Tx, NodeUid>
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<Tx, NodeUid>
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<Tx, NodeUid> DistAlgorithm for QueueingHoneyBadger<Tx, NodeUid>
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<Tx, NodeUid>;
@ -180,7 +181,7 @@ where
impl<Tx, NodeUid> QueueingHoneyBadger<Tx, NodeUid>
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`.

View File

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

View File

@ -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<Broadcast<NodeUid>> for ProposeAdversary {
fn pick_node(&mut self, nodes: &BTreeMap<NodeUid, TestNode<Broadcast<NodeUid>>>) -> NodeUid {
fn pick_node(&self, nodes: &BTreeMap<NodeUid, TestNode<Broadcast<NodeUid>>>) -> NodeUid {
self.scheduler.pick_node(nodes)
}
@ -55,7 +60,7 @@ impl Adversary<Broadcast<NodeUid>> for ProposeAdversary {
// 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 {
return vec![];
}
@ -84,7 +89,9 @@ impl Adversary<Broadcast<NodeUid>> 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");
}

View File

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

View File

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

View File

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

View File

@ -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<Vec<usize>, NodeUid>;
@ -56,7 +59,7 @@ impl FaultyShareAdversary {
}
impl Adversary<UsizeHoneyBadger> for FaultyShareAdversary {
fn pick_node(&mut self, nodes: &BTreeMap<NodeUid, TestNode<UsizeHoneyBadger>>) -> NodeUid {
fn pick_node(&self, nodes: &BTreeMap<NodeUid, TestNode<UsizeHoneyBadger>>) -> NodeUid {
self.scheduler.pick_node(nodes)
}
@ -100,7 +103,7 @@ impl Adversary<UsizeHoneyBadger> 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);
}

View File

@ -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<D> = (
<D as DistAlgorithm>::NodeUid,
TargetedMessage<<D as DistAlgorithm>::Message, <D as DistAlgorithm>::NodeUid>,
);
/// A message combined with a sender.
pub struct MessageWithSender<D: DistAlgorithm> {
/// 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.
///
/// See `TestNetwork::step()` for a more detailed description of its capabilities.
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(&mut self, nodes: &BTreeMap<D::NodeUid, TestNode<D>>) -> D::NodeUid;
fn pick_node(&self, nodes: &BTreeMap<D::NodeUid, TestNode<D>>) -> 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<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>>;
/// 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.
@ -145,7 +181,7 @@ impl SilentAdversary {
}
impl<D: DistAlgorithm> Adversary<D> for SilentAdversary {
fn pick_node(&mut self, nodes: &BTreeMap<D::NodeUid, TestNode<D>>) -> D::NodeUid {
fn pick_node(&self, nodes: &BTreeMap<D::NodeUid, TestNode<D>>) -> D::NodeUid {
self.scheduler.pick_node(nodes)
}
@ -158,19 +194,164 @@ impl<D: DistAlgorithm> Adversary<D> 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<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
/// 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<A: Adversary<D>, D: DistAlgorithm>
where
<D as DistAlgorithm>::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()"

View File

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