Merge branch 'master' into afck-dhb-votes

This commit is contained in:
Vladimir Komendantskiy 2018-07-14 08:20:02 +01:00 committed by GitHub
commit 72f7b1c06d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 494 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"] }
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,
@ -63,7 +63,7 @@ impl BinValues {
}
}
pub fn contains(&self, b: bool) -> bool {
pub fn contains(self, b: bool) -> bool {
match self {
BinValues::None => false,
BinValues::Both => true,
@ -73,7 +73,7 @@ impl BinValues {
}
}
pub fn is_subset(&self, other: BinValues) -> bool {
pub fn is_subset(self, other: BinValues) -> bool {
match self {
BinValues::None => true,
BinValues::False if other == BinValues::False || other == BinValues::Both => true,
@ -83,7 +83,7 @@ impl BinValues {
}
}
pub fn definite(&self) -> Option<bool> {
pub fn definite(self) -> Option<bool> {
match self {
BinValues::False => Some(false),
BinValues::True => Some(true),

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

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

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> {
fn start_epoch(&self) -> u64 {
match *self {
Message::HoneyBadger(epoch, _) => epoch,
@ -516,11 +517,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

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

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;
@ -44,7 +45,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`.
@ -81,7 +82,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.
@ -112,7 +113,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;
@ -168,7 +169,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`.
@ -634,8 +635,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`.
@ -645,7 +646,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,
@ -655,13 +656,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
}
@ -669,9 +670,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

@ -8,6 +8,7 @@ use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use rand::Rand;
use serde::{Deserialize, Serialize};
use dynamic_honey_badger::{self, Batch as DhbBatch, DynamicHoneyBadger, Message};
@ -40,7 +41,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`.
@ -110,7 +111,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,
@ -125,7 +126,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>;
@ -179,7 +180,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`.
@ -187,6 +188,11 @@ where
QueueingHoneyBadgerBuilder::new(netinfo)
}
/// Returns a reference to the internal `DynamicHoneyBadger` instance.
pub fn dyn_hb(&self) -> &DynamicHoneyBadger<Vec<Tx>, NodeUid> {
&self.dyn_hb
}
/// Initiates the next epoch by proposing a batch from the queue.
fn propose(&mut self) -> Result<FaultLog<NodeUid>> {
let amount = cmp::max(1, self.batch_size / self.dyn_hb.netinfo().num_nodes());

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 {
@ -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;
@ -22,7 +24,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>;
@ -99,7 +102,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,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
}

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;