mirror of https://github.com/poanetwork/hbbft.git
Merge pull request #112 from poanetwork/mbr--random-adversary-model
Implement a random adversary
This commit is contained in:
commit
c12bb58ac0
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
120
src/broadcast.rs
120
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 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::fmt::{self, Debug};
|
||||
|
@ -48,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;
|
||||
|
@ -84,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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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>| {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -21,6 +21,8 @@ extern crate pairing;
|
|||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate rand_derive;
|
||||
|
||||
mod network;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ extern crate pairing;
|
|||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate rand_derive;
|
||||
|
||||
mod network;
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ extern crate pairing;
|
|||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate rand_derive;
|
||||
|
||||
mod network;
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ extern crate pairing;
|
|||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate rand_derive;
|
||||
|
||||
mod network;
|
||||
|
||||
|
|
|
@ -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>;
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rand::{self, Rng};
|
||||
|
@ -9,7 +9,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`.
|
||||
|
@ -68,6 +68,11 @@ impl<D: DistAlgorithm> TestNode<D> {
|
|||
.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.
|
||||
|
@ -105,21 +110,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.
|
||||
///
|
||||
/// 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;
|
||||
|
||||
/// 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>);
|
||||
|
||||
/// 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.
|
||||
|
@ -148,11 +193,165 @@ impl<D: DistAlgorithm> Adversary<D> for SilentAdversary {
|
|||
}
|
||||
}
|
||||
|
||||
/// A collection of `TestNode`s representing a network.
|
||||
pub struct TestNetwork<A: Adversary<D>, D: DistAlgorithm>
|
||||
where
|
||||
<D as DistAlgorithm>::NodeUid: Hash,
|
||||
/// 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
|
||||
/// 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 observer: TestNode<D>,
|
||||
pub adv_nodes: BTreeMap<D::NodeUid, Arc<NetworkInfo<D::NodeUid>>>,
|
||||
|
@ -208,6 +407,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,
|
||||
|
@ -215,9 +415,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 {
|
||||
|
@ -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 {
|
||||
// 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));
|
||||
}
|
||||
// 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);
|
||||
|
||||
// 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()
|
||||
};
|
||||
self.dispatch_messages(id, msgs);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ extern crate pairing;
|
|||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate rand_derive;
|
||||
|
||||
mod network;
|
||||
|
||||
|
|
Loading…
Reference in New Issue