OsRng / external RNG Refactoring (#357)

* Use `OsRng` in place of `thread_rng`.

This changes the defaults of any builder by instantiating an `OsRng` instead of
a `thread_rng`, the former being much more secure than the latter.

Additionally, all the unit tests that still instantiate RNGs manually used `OsRng`s
as well; while there is no actual need for this level of security in tests, the performance overhead is very small and random number generation complexity has such a small impact on these tests that the convenience of being able to ban `thread_rng` from the codebase altogether, setting a good example and avoid issues when refactoring later greatly outweigh the negatives.

* Instead of storing random number generators in the various consensus algorithm instances, pass them in from the outside whenever they are needed.

This changes a large amount of interfaces (and in this commit is only partially done, since `DistAlgorithm` needs to be fundamentally altered as well.

It also obsoletes parts of the `util` module.

* Added an `R: Rng` type parameter to both methods of `DistAlgorithm`, forcing callers to pass in their own Rngs.

* Fixed documentation grammar and spelling in some of the altered interfaces due to RNG refactoring.

* Move `rng` argument to the end of the argument for most functions.

Also includes a reformatting due to Rust 1.30.

* Updated tests, accomodate `rng`-API changes.

* Fixed remaining compilation issues with new RNG code.

* Fix illegal `self` import outside curly braces.

* Cleaned up comments and fixed broken definition of `broadcast_input`.

* Updated existing test cases to properly work with static dispatch randomness.

* Do not use boxed `Rng`s for key generation in test networks.

* Use the passed-in `Rng` in `ReorderingAdversary`, instead of storing a boxed one.

* Fixed clippy lints after refactoring.

* Removed some no-longer necessary manual `fmt::Debug` implementations in test framework.

* Use `OsRng` even in tests in `binary_agreement_mitm`.

* Use a proper deterministic RNG in tests `binary_agreement_mitm`.

* Refactor `examples/simulation.rs` by not using `ThreadRng`, passing generic `Rng` parameters throughout and using a type alias instead of a newtype as the `Transaction`.

* Remove `thread_rng` use from `examples/node.rs`.

* Explicitly construct `InternalContrib` in `DynamicHoneyBadger::propose`.

* Fixed typo in description of `DistAlgorithm` trait.
This commit is contained in:
Marc Brinkmann 2018-12-14 13:51:09 +01:00 committed by Vladimir Komendantskiy
parent 1c7fc60db9
commit eafa77d5fc
31 changed files with 462 additions and 439 deletions

View File

@ -108,6 +108,8 @@ impl<T: Clone + Debug + AsRef<[u8]> + PartialEq + Send + Sync + From<Vec<u8>> +
let tx_from_algo = messaging.tx_from_algo();
let stop_tx = messaging.stop_tx();
let mut rng = rand::OsRng::new().unwrap();
// All spawned threads will have exited by the end of the scope.
crossbeam::scope(|scope| {
// Start the centralised message delivery system.
@ -124,7 +126,7 @@ impl<T: Clone + Debug + AsRef<[u8]> + PartialEq + Send + Sync + From<Vec<u8>> +
if let Some(v) = value {
// FIXME: Use the output.
let step = broadcast
.handle_input(v.clone().into())
.handle_input(v.clone().into(), &mut rng)
.expect("propose value");
for msg in step.messages {
tx_from_algo.send(msg).expect("send from algo");

View File

@ -5,7 +5,7 @@ use std::{cmp, u64};
use colored::*;
use docopt::Docopt;
use itertools::Itertools;
use rand::{Isaac64Rng, Rng};
use rand::Rng;
use rand_derive::Rand;
use serde::de::DeserializeOwned;
use serde::Serialize;
@ -56,14 +56,7 @@ struct Args {
pub struct NodeId(pub usize);
/// A transaction.
#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)]
pub struct Transaction(pub Vec<u8>);
impl Transaction {
fn new(len: usize) -> Transaction {
Transaction(rand::thread_rng().gen_iter().take(len).collect())
}
}
type Transaction = Vec<u8>;
/// A serialized message with a sender and the timestamp of arrival.
#[derive(Eq, PartialEq, Debug)]
@ -168,7 +161,7 @@ where
}
/// Handles the first message in the node's queue.
fn handle_message(&mut self) {
fn handle_message<R: Rng>(&mut self, rng: &mut R) {
let ts_msg = self.in_queue.pop_front().expect("message not found");
self.time = cmp::max(self.time, ts_msg.time);
self.message_count += 1;
@ -177,7 +170,7 @@ where
let msg = bincode::deserialize::<D::Message>(&ts_msg.message).expect("deserialize");
let step = self
.algo
.handle_message(&ts_msg.sender_id, msg)
.handle_message(&ts_msg.sender_id, msg, rng)
.expect("handling message");
self.time += start.elapsed() * self.hw_quality.cpu_factor / 100;
self.send_output_and_msgs(step)
@ -247,20 +240,21 @@ where
D::Message: Serialize + DeserializeOwned + Clone,
{
/// Creates a new network with `good_num` good nodes, and `dead_num` dead nodes.
pub fn new<F>(
pub fn new<F, R: Rng>(
good_num: usize,
adv_num: usize,
new_algo: F,
hw_quality: HwQuality,
rng: &mut R,
) -> TestNetwork<D>
where
F: Fn(NetworkInfo<NodeId>) -> (D, DaStep<D>),
F: Fn(NetworkInfo<NodeId>, &mut R) -> (D, DaStep<D>),
{
let node_ids = (0..(good_num + adv_num)).map(NodeId);
let netinfos = NetworkInfo::generate_map(node_ids, &mut rand::thread_rng())
.expect("Failed to create `NetworkInfo` map");
let netinfos =
NetworkInfo::generate_map(node_ids, rng).expect("Failed to create `NetworkInfo` map");
let new_node = |(id, netinfo): (NodeId, NetworkInfo<_>)| {
(id, TestNode::new(new_algo(netinfo), hw_quality))
(id, TestNode::new(new_algo(netinfo, rng), hw_quality))
};
let mut network = TestNetwork {
nodes: netinfos.into_iter().map(new_node).collect(),
@ -299,7 +293,7 @@ where
/// Handles a queued message in one of the nodes with the earliest timestamp, if any. Returns
/// the recipient's ID.
pub fn step(&mut self) -> Option<NodeId> {
pub fn step<R: Rng>(&mut self, rng: &mut R) -> Option<NodeId> {
let min_time = self
.nodes
.values()
@ -311,10 +305,10 @@ where
.filter(|(_, node)| node.next_event_time() == Some(min_time))
.map(|(id, _)| *id)
.collect();
let next_id = *rand::thread_rng().choose(&min_ids).unwrap();
let next_id = *rng.choose(&min_ids).unwrap();
let msgs: Vec<_> = {
let node = self.nodes.get_mut(&next_id).unwrap();
node.handle_message();
node.handle_message(rng);
node.out_queue.drain(..).collect()
};
self.dispatch_messages(msgs);
@ -378,14 +372,14 @@ impl EpochInfo {
}
/// Proposes `num_txs` values and expects nodes to output and order them.
fn simulate_honey_badger(mut network: TestNetwork<QHB>) {
fn simulate_honey_badger<R: Rng>(mut network: TestNetwork<QHB>, rng: &mut R) {
// Handle messages until all nodes have output all transactions.
println!(
"{}",
"Epoch Min/Max Time Txs Msgs/Node Size/Node".bold()
);
let mut epochs = Vec::new();
while let Some(id) = network.step() {
while let Some(id) = network.step(rng) {
for &(time, ref batch) in &network.nodes[&id].outputs {
let epoch = batch.epoch() as usize;
if epochs.len() <= epoch {
@ -406,11 +400,14 @@ fn parse_args() -> Result<Args, docopt::Error> {
fn main() {
env_logger::init();
let mut rng = rand::OsRng::new().expect("Could not initialize OS random number generator.");
let args = parse_args().unwrap_or_else(|e| e.exit());
if args.flag_n <= 3 * args.flag_f {
let msg = "Honey Badger only works if less than one third of the nodes are faulty.";
println!("{}", msg.red().bold());
}
println!("Simulating Honey Badger with:");
println!("{} nodes, {} faulty", args.flag_n, args.flag_f);
println!(
@ -422,11 +419,14 @@ fn main() {
args.flag_lag, args.flag_bw, args.flag_cpu
);
println!();
let num_good_nodes = args.flag_n - args.flag_f;
let txs: Vec<_> = (0..args.flag_txs)
.map(|_| Transaction::new(args.flag_tx_size))
.map(|_| rng.gen_iter().take(args.flag_tx_size).collect())
.collect();
let new_honey_badger = |netinfo: NetworkInfo<NodeId>| {
let new_honey_badger = |netinfo: NetworkInfo<NodeId>, rng: &mut rand::OsRng| {
let our_id = *netinfo.our_id();
let peer_ids: Vec<_> = netinfo
.all_ids()
@ -436,17 +436,25 @@ fn main() {
let dhb = DynamicHoneyBadger::builder().build(netinfo);
let (qhb, qhb_step) = QueueingHoneyBadger::builder(dhb)
.batch_size(args.flag_b)
.build_with_transactions(txs.clone(), rand::thread_rng().gen::<Isaac64Rng>())
.build_with_transactions(txs.clone(), rng)
.expect("instantiate QueueingHoneyBadger");
let (sq, mut step) = SenderQueue::builder(qhb, peer_ids.into_iter()).build(our_id);
step.extend_with(qhb_step, Message::from);
(sq, step)
};
let hw_quality = HwQuality {
latency: Duration::from_millis(args.flag_lag),
inv_bw: Duration::new(0, 8_000_000 / args.flag_bw),
cpu_factor: (10_000f32 / args.flag_cpu) as u32,
};
let network = TestNetwork::new(num_good_nodes, args.flag_f, new_honey_badger, hw_quality);
simulate_honey_badger(network);
let network = TestNetwork::new(
num_good_nodes,
args.flag_f,
new_honey_badger,
hw_quality,
&mut rng,
);
simulate_honey_badger(network, &mut rng);
}

View File

@ -5,6 +5,7 @@ use std::{fmt, result};
use crate::crypto::SignatureShare;
use bincode;
use log::debug;
use rand::Rng;
use super::bool_multimap::BoolMultimap;
use super::bool_set::{self, BoolSet};
@ -177,13 +178,18 @@ impl<N: NodeIdT, S: SessionIdT> DistAlgorithm for BinaryAgreement<N, S> {
type Message = Message;
type Error = Error;
fn handle_input(&mut self, input: Self::Input) -> Result<Step<N>> {
fn handle_input<R: Rng>(&mut self, input: Self::Input, _rng: &mut R) -> Result<Step<N>> {
self.propose(input)
}
/// Receive input from a remote node.
fn handle_message(&mut self, sender_id: &Self::NodeId, msg: Message) -> Result<Step<N>> {
self.handle_message(sender_id, msg)
fn handle_message<R: Rng>(
&mut self,
sender_id: &Self::NodeId,
message: Message,
_rng: &mut R,
) -> Result<Step<N>> {
self.handle_message(sender_id, message)
}
/// Whether the algorithm has terminated.

View File

@ -5,6 +5,7 @@ use std::{fmt, result};
use byteorder::{BigEndian, ByteOrder};
use hex_fmt::{HexFmt, HexList};
use log::{debug, warn};
use rand::Rng;
use reed_solomon_erasure as rse;
use reed_solomon_erasure::ReedSolomon;
@ -47,11 +48,16 @@ impl<N: NodeIdT> DistAlgorithm for Broadcast<N> {
type Message = Message;
type Error = Error;
fn handle_input(&mut self, input: Self::Input) -> Result<Step<N>> {
fn handle_input<R: Rng>(&mut self, input: Self::Input, _rng: &mut R) -> Result<Step<N>> {
self.broadcast(input)
}
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<N>> {
fn handle_message<R: Rng>(
&mut self,
sender_id: &Self::NodeId,
message: Message,
_rng: &mut R,
) -> Result<Step<N>> {
self.handle_message(sender_id, message)
}

View File

@ -92,7 +92,7 @@
//! ```
//! use hbbft::broadcast::{Broadcast, Error, Step};
//! use hbbft::{NetworkInfo, SourcedMessage, Target, TargetedMessage};
//! use rand::{thread_rng, Rng};
//! use rand::{OsRng, Rng};
//! use std::collections::{BTreeMap, BTreeSet, VecDeque};
//! use std::iter::once;
//! use std::sync::Arc;
@ -102,7 +102,7 @@
//! const NUM_NODES: u64 = 7;
//! const PROPOSER_ID: u64 = 3;
//!
//! let mut rng = thread_rng();
//! let mut rng = OsRng::new().expect("Could not initialize OS random number generator.");
//!
//! // Create a random set of keys for testing.
//! let netinfos = NetworkInfo::generate_map(0..NUM_NODES, &mut rng)

View File

@ -4,12 +4,11 @@ use std::marker::PhantomData;
use std::sync::Arc;
use crate::crypto::{SecretKey, SecretKeySet};
use rand::{self, Rand, Rng};
use rand::Rand;
use serde::{de::DeserializeOwned, Serialize};
use super::{DynamicHoneyBadger, EncryptionSchedule, JoinPlan, Result, Step, VoteCounter};
use crate::honey_badger::{HoneyBadger, Params, SubsetHandlingStrategy};
use crate::util::SubRng;
use crate::{Contribution, NetworkInfo, NodeIdT};
/// A Dynamic Honey Badger builder, to configure the parameters and create new instances of
@ -17,9 +16,6 @@ use crate::{Contribution, NetworkInfo, NodeIdT};
pub struct DynamicHoneyBadgerBuilder<C, N> {
/// Start in this era.
era: u64,
/// Random number generator passed on to algorithm instance for key generation. Also used to
/// instantiate `HoneyBadger`.
rng: Box<dyn rand::Rng>,
/// Parameters controlling Honey Badger's behavior and performance.
params: Params,
_phantom: PhantomData<(C, N)>,
@ -32,7 +28,6 @@ where
fn default() -> Self {
DynamicHoneyBadgerBuilder {
era: 0,
rng: Box::new(rand::thread_rng()),
params: Params::default(),
_phantom: PhantomData,
}
@ -62,12 +57,6 @@ where
self
}
/// Sets the random number generator to be used to instantiate cryptographic structures.
pub fn rng<R: rand::Rng + 'static>(&mut self, rng: R) -> &mut Self {
self.rng = Box::new(rng);
self
}
/// Sets the strategy to use when handling `Subset` output.
pub fn subset_handling_strategy(
&mut self,
@ -93,16 +82,16 @@ where
pub fn build(&mut self, netinfo: NetworkInfo<N>) -> DynamicHoneyBadger<C, N> {
let DynamicHoneyBadgerBuilder {
era,
rng,
params,
_phantom,
} = self;
let arc_netinfo = Arc::new(netinfo.clone());
let honey_badger = HoneyBadger::builder(arc_netinfo.clone())
.session_id(*era)
.params(params.clone())
.rng(rng.sub_rng())
.build();
DynamicHoneyBadger {
netinfo,
max_future_epochs: params.max_future_epochs,
@ -111,16 +100,19 @@ where
key_gen_msg_buffer: Vec::new(),
honey_badger,
key_gen_state: None,
rng: Box::new(rng.sub_rng()),
}
}
/// Creates a new `DynamicHoneyBadger` configured to start a new network as a single validator.
pub fn build_first_node(&mut self, our_id: N) -> Result<DynamicHoneyBadger<C, N>> {
let sk_set = SecretKeySet::random(0, &mut self.rng);
pub fn build_first_node<R: rand::Rng>(
&mut self,
our_id: N,
rng: &mut R,
) -> Result<DynamicHoneyBadger<C, N>> {
let sk_set = SecretKeySet::random(0, rng);
let pk_set = sk_set.public_keys();
let sks = sk_set.secret_key_share(0);
let sk: SecretKey = self.rng.gen();
let sk: SecretKey = rng.gen();
let pub_keys = once((our_id.clone(), sk.public_key())).collect();
let netinfo = NetworkInfo::new(our_id, sks, pk_set, sk, pub_keys);
Ok(self.build(netinfo))
@ -131,12 +123,13 @@ where
///
/// **Deprecated**: Please use `DynamicHoneyBadger::new_joining` instead.
#[deprecated]
pub fn build_joining(
pub fn build_joining<R: rand::Rng>(
&mut self,
our_id: N,
secret_key: SecretKey,
join_plan: JoinPlan<N>,
rng: &mut R,
) -> Result<(DynamicHoneyBadger<C, N>, Step<C, N>)> {
DynamicHoneyBadger::new_joining(our_id, secret_key, join_plan, self.rng.sub_rng())
DynamicHoneyBadger::new_joining(our_id, secret_key, join_plan, rng)
}
}

View File

@ -6,7 +6,7 @@ use crate::crypto::{PublicKey, SecretKey, Signature};
use bincode;
use derivative::Derivative;
use log::debug;
use rand::{self, Rand, Rng};
use rand::{Rand, Rng};
use serde::{de::DeserializeOwned, Serialize};
use super::votes::{SignedVote, VoteCounter};
@ -19,7 +19,7 @@ use crate::fault_log::{Fault, FaultKind, FaultLog};
use crate::honey_badger::{self, HoneyBadger, Message as HbMessage};
use crate::sync_key_gen::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen};
use crate::util::{self, SubRng};
use crate::util;
use crate::{Contribution, DistAlgorithm, Epoched, NetworkInfo, NodeIdT, Target};
/// A Honey Badger instance that can handle adding and removing nodes.
@ -40,10 +40,6 @@ pub struct DynamicHoneyBadger<C, N: Rand + Ord> {
pub(super) honey_badger: HoneyBadger<InternalContrib<C, N>, N>,
/// The current key generation process, and the change it applies to.
pub(super) key_gen_state: Option<KeyGenState<N>>,
/// A random number generator used for secret key generation.
// Boxed to avoid overloading the algorithm's type with more generics.
#[derivative(Debug(format_with = "util::fmt_rng"))]
pub(super) rng: Box<dyn rand::Rng + Send + Sync>,
}
impl<C, N> DistAlgorithm for DynamicHoneyBadger<C, N>
@ -57,17 +53,22 @@ where
type Message = Message<N>;
type Error = Error;
fn handle_input(&mut self, input: Self::Input) -> Result<Step<C, N>> {
fn handle_input<R: Rng>(&mut self, input: Self::Input, rng: &mut R) -> Result<Step<C, N>> {
// User contributions are forwarded to `HoneyBadger` right away. Votes are signed and
// broadcast.
match input {
Input::User(contrib) => self.propose(contrib),
Input::User(contrib) => self.propose(contrib, rng),
Input::Change(change) => self.vote_for(change),
}
}
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<C, N>> {
self.handle_message(sender_id, message)
fn handle_message<R: Rng>(
&mut self,
sender_id: &Self::NodeId,
msg: Self::Message,
rng: &mut R,
) -> Result<Step<C, N>> {
self.handle_message(sender_id, msg, rng)
}
fn terminated(&self) -> bool {
@ -90,11 +91,11 @@ where
}
/// Creates a new `DynamicHoneyBadger` ready to join the network specified in the `JoinPlan`.
pub fn new_joining<R: Rng + Send + Sync + 'static>(
pub fn new_joining<R: Rng>(
our_id: N,
secret_key: SecretKey,
join_plan: JoinPlan<N>,
mut rng: R,
rng: &mut R,
) -> Result<(Self, Step<C, N>)> {
let netinfo = NetworkInfo::new(
our_id,
@ -108,7 +109,6 @@ where
let honey_badger = HoneyBadger::builder(arc_netinfo.clone())
.session_id(join_plan.era)
.params(join_plan.params)
.rng(rng.sub_rng())
.build();
let mut dhb = DynamicHoneyBadger {
netinfo,
@ -118,11 +118,10 @@ where
key_gen_msg_buffer: Vec::new(),
honey_badger,
key_gen_state: None,
rng: Box::new(rng),
};
let step = match join_plan.change {
ChangeState::InProgress(ref change) => match change {
Change::NodeChange(change) => dhb.update_key_gen(join_plan.era, change)?,
Change::NodeChange(change) => dhb.update_key_gen(join_plan.era, change, rng)?,
_ => Step::default(),
},
ChangeState::None | ChangeState::Complete(..) => Step::default(),
@ -141,22 +140,25 @@ where
///
/// If we are the only validator, this will immediately output a batch, containing our
/// proposal.
pub fn propose(&mut self, contrib: C) -> Result<Step<C, N>> {
pub fn propose<R: Rng>(&mut self, contrib: C, rng: &mut R) -> Result<Step<C, N>> {
let key_gen_messages = self
.key_gen_msg_buffer
.iter()
.filter(|kg_msg| kg_msg.era() == self.era)
.cloned()
.collect();
let contrib = InternalContrib {
contrib,
key_gen_messages,
votes: self.vote_counter.pending_votes().cloned().collect(),
};
let step = self
.honey_badger
.propose(&InternalContrib {
contrib,
key_gen_messages,
votes: self.vote_counter.pending_votes().cloned().collect(),
})
.propose(&contrib, rng)
.map_err(Error::ProposeHoneyBadger)?;
self.process_output(step)
self.process_output(step, rng)
}
/// Casts a vote to change the set of validators or parameters.
@ -195,11 +197,16 @@ where
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<C, N>> {
pub fn handle_message<R: Rng>(
&mut self,
sender_id: &N,
message: Message<N>,
rng: &mut R,
) -> Result<Step<C, N>> {
if message.era() == self.era {
match message {
Message::HoneyBadger(_, hb_msg) => {
self.handle_honey_badger_message(sender_id, hb_msg)
self.handle_honey_badger_message(sender_id, hb_msg, rng)
}
Message::KeyGen(_, kg_msg, sig) => self
.handle_key_gen_message(sender_id, kg_msg, *sig)
@ -249,10 +256,11 @@ where
}
/// Handles a message for the `HoneyBadger` instance.
fn handle_honey_badger_message(
fn handle_honey_badger_message<R: Rng>(
&mut self,
sender_id: &N,
message: HbMessage<N>,
rng: &mut R,
) -> Result<Step<C, N>> {
if !self.netinfo.is_node_validator(sender_id) {
return Err(Error::UnknownSender);
@ -262,7 +270,7 @@ where
.honey_badger
.handle_message(sender_id, message)
.map_err(Error::HandleHoneyBadgerMessage)?;
self.process_output(step)
self.process_output(step, rng)
}
/// Handles a vote or key generation message and tries to commit it as a transaction. These
@ -297,9 +305,10 @@ where
}
/// Processes all pending batches output by Honey Badger.
fn process_output(
fn process_output<R: Rng>(
&mut self,
hb_step: honey_badger::Step<InternalContrib<C, N>, N>,
rng: &mut R,
) -> Result<Step<C, N>> {
let mut step: Step<C, N> = Step::default();
let output = step.extend_with(hb_step, |hb_msg| Message::HoneyBadger(self.era, hb_msg));
@ -329,7 +338,7 @@ where
step.fault_log.append(id.clone(), fault_kind);
} else {
step.extend(match kg_msg {
KeyGenMessage::Part(part) => self.handle_part(&s_id, part)?,
KeyGenMessage::Part(part) => self.handle_part(&s_id, part, rng)?,
KeyGenMessage::Ack(ack) => self.handle_ack(&s_id, ack)?,
});
}
@ -346,7 +355,7 @@ where
// If there is a new change, restart DKG. Inform the user about the current change.
match change {
Change::NodeChange(ref pub_keys) => {
step.extend(self.update_key_gen(batch_epoch + 1, pub_keys)?);
step.extend(self.update_key_gen(batch_epoch + 1, pub_keys, rng)?);
}
Change::EncryptionSchedule(schedule) => {
self.update_encryption_schedule(batch_epoch + 1, schedule);
@ -380,10 +389,11 @@ where
/// If the winner of the vote has changed, restarts Key Generation for the set of nodes implied
/// by the current change.
pub(super) fn update_key_gen(
pub(super) fn update_key_gen<R: Rng>(
&mut self,
era: u64,
pub_keys: &BTreeMap<N, PublicKey>,
rng: &mut R,
) -> Result<Step<C, N>> {
if self.key_gen_state.as_ref().map(KeyGenState::public_keys) == Some(pub_keys) {
return Ok(Step::default()); // The change is the same as before. Continue DKG as is.
@ -394,9 +404,8 @@ where
let threshold = util::max_faulty(pub_keys.len());
let sk = self.netinfo.secret_key().clone();
let our_id = self.our_id().clone();
let (key_gen, part) =
SyncKeyGen::new(&mut self.rng, our_id, sk, pub_keys.clone(), threshold)
.map_err(Error::SyncKeyGen)?;
let (key_gen, part) = SyncKeyGen::new(our_id, sk, pub_keys.clone(), threshold, rng)
.map_err(Error::SyncKeyGen)?;
self.key_gen_state = Some(KeyGenState::new(key_gen));
if let Some(part) = part {
self.send_transaction(KeyGenMessage::Part(part))
@ -413,16 +422,20 @@ where
self.vote_counter = VoteCounter::new(netinfo.clone(), era);
self.honey_badger = HoneyBadger::builder(netinfo)
.session_id(era)
.rng(self.rng.sub_rng())
.params(params)
.build();
}
/// Handles a `Part` message that was output by Honey Badger.
fn handle_part(&mut self, sender_id: &N, part: Part) -> Result<Step<C, N>> {
fn handle_part<R: Rng>(
&mut self,
sender_id: &N,
part: Part,
rng: &mut R,
) -> Result<Step<C, N>> {
let outcome = if let Some(kgs) = self.key_gen_state.as_mut() {
kgs.key_gen
.handle_part(&mut self.rng, &sender_id, part)
.handle_part(&sender_id, part, rng)
.map_err(Error::SyncKeyGen)?
} else {
// No key generation ongoing.

View File

@ -201,7 +201,7 @@ mod tests {
/// the vote by node `i` for making `j` the only validator. Each node signed this for nodes
/// `0`, `1`, ... in order.
fn setup(node_num: usize, era: u64) -> (Vec<VoteCounter<usize>>, Vec<Vec<SignedVote<usize>>>) {
let mut rng = rand::thread_rng();
let mut rng = rand::OsRng::new().expect("could not initialize OsRng");
// Create keys for threshold cryptography.
let netinfos = NetworkInfo::generate_map(0..node_num, &mut rng)
.expect("Failed to generate `NetworkInfo` map");

View File

@ -2,11 +2,10 @@ use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::sync::Arc;
use rand::{self, Rand, Rng};
use rand::Rand;
use serde::{de::DeserializeOwned, Serialize};
use super::{EncryptionSchedule, HoneyBadger, Params, SubsetHandlingStrategy};
use crate::util::SubRng;
use crate::{Contribution, NetworkInfo, NodeIdT};
/// A Honey Badger builder, to configure the parameters and create new instances of `HoneyBadger`.
@ -18,8 +17,6 @@ pub struct HoneyBadgerBuilder<C, N> {
session_id: u64,
/// Start in this epoch.
epoch: u64,
/// Random number generator passed on to algorithm instance for signing and encrypting.
rng: Box<dyn Rng>,
/// Parameters controlling Honey Badger's behavior and performance.
params: Params,
_phantom: PhantomData<C>,
@ -37,18 +34,11 @@ where
netinfo,
session_id: 0,
epoch: 0,
rng: Box::new(rand::thread_rng()),
params: Params::default(),
_phantom: PhantomData,
}
}
/// Sets the random number generator for the public key cryptography.
pub fn rng<R: Rng + 'static>(&mut self, rng: R) -> &mut Self {
self.rng = Box::new(rng);
self
}
/// Sets the session identifier.
///
/// Different session IDs foil replay attacks in two instances with the same epoch numbers and
@ -100,7 +90,6 @@ where
has_input: false,
epochs: BTreeMap::new(),
params: self.params.clone(),
rng: Box::new(self.rng.sub_rng()),
}
}
}

View File

@ -17,7 +17,7 @@ use super::{Batch, Error, MessageContent, Result, Step};
use crate::fault_log::{Fault, FaultKind, FaultLog};
use crate::subset::{self as cs, Subset, SubsetOutput};
use crate::threshold_decrypt::{self as td, ThresholdDecrypt};
use crate::{Contribution, DistAlgorithm, NetworkInfo, NodeIdT};
use crate::{Contribution, NetworkInfo, NodeIdT};
type CsStep<N> = cs::Step<N>;
@ -75,7 +75,7 @@ where
/// Provides input to the Subset instance, unless it has already completed.
fn handle_input(&mut self, proposal: Vec<u8>) -> Result<CsStep<N>> {
match self {
SubsetState::Ongoing(ref mut cs) => cs.handle_input(proposal),
SubsetState::Ongoing(ref mut cs) => cs.propose(proposal),
SubsetState::Complete(_) => return Ok(cs::Step::default()),
}
.map_err(Error::InputSubset)

View File

@ -9,7 +9,7 @@ use serde_derive::{Deserialize, Serialize};
use super::epoch_state::EpochState;
use super::{Batch, Error, HoneyBadgerBuilder, Message, Result};
use crate::{util, Contribution, DistAlgorithm, Fault, FaultKind, NetworkInfo, NodeIdT};
use crate::{Contribution, DistAlgorithm, Fault, FaultKind, NetworkInfo, NodeIdT};
use super::Params;
@ -30,10 +30,6 @@ pub struct HoneyBadger<C, N: Rand> {
pub(super) epochs: BTreeMap<u64, EpochState<C, N>>,
/// Parameters controlling Honey Badger's behavior and performance.
pub(super) params: Params,
/// A random number generator used for secret key generation.
// Boxed to avoid overloading the algorithm's type with more generics.
#[derivative(Debug(format_with = "util::fmt_rng"))]
pub(super) rng: Box<dyn Rng + Send + Sync>,
}
/// A `HoneyBadger` step, possibly containing multiple outputs.
@ -50,11 +46,16 @@ where
type Message = Message<N>;
type Error = Error;
fn handle_input(&mut self, input: Self::Input) -> Result<Step<C, N>> {
self.propose(&input)
fn handle_input<R: Rng>(&mut self, input: Self::Input, rng: &mut R) -> Result<Step<C, N>> {
self.propose(&input, rng)
}
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<C, N>> {
fn handle_message<R: Rng>(
&mut self,
sender_id: &Self::NodeId,
message: Self::Message,
_rng: &mut R,
) -> Result<Step<C, N>> {
self.handle_message(sender_id, message)
}
@ -84,7 +85,7 @@ where
///
/// If we are the only validator, this will immediately output a batch, containing our
/// proposal.
pub fn propose(&mut self, proposal: &C) -> Result<Step<C, N>> {
pub fn propose<R: Rng>(&mut self, proposal: &C, rng: &mut R) -> Result<Step<C, N>> {
if !self.netinfo.is_validator() {
return Ok(Step::default());
}
@ -97,7 +98,6 @@ where
"We created the epoch_state in `self.epoch_state_mut(...)` just a moment ago.",
)
};
let rng = &mut self.rng;
epoch_state.propose(proposal, rng)?
};
Ok(step.join(self.try_output_batches()?))

View File

@ -33,7 +33,7 @@ use serde::{de::DeserializeOwned, Serialize};
use crate::dynamic_honey_badger::{self, Batch as DhbBatch, DynamicHoneyBadger, Message};
use crate::transaction_queue::TransactionQueue;
use crate::{util, Contribution, DistAlgorithm, NetworkInfo, NodeIdT};
use crate::{Contribution, DistAlgorithm, NetworkInfo, NodeIdT};
pub use crate::dynamic_honey_badger::{Change, ChangeState, Input};
@ -101,10 +101,7 @@ where
}
/// Creates a new Queueing Honey Badger instance with an empty buffer.
pub fn build<R>(self, rng: R) -> QueueingHoneyBadgerWithStep<T, N, Q>
where
R: 'static + Rng + Send + Sync,
{
pub fn build<R: Rng>(self, rng: &mut R) -> QueueingHoneyBadgerWithStep<T, N, Q> {
self.build_with_transactions(None, rng)
.expect("building without transactions cannot fail")
}
@ -114,20 +111,19 @@ where
pub fn build_with_transactions<TI, R>(
mut self,
txs: TI,
rng: R,
rng: &mut R,
) -> Result<QueueingHoneyBadgerWithStep<T, N, Q>>
where
TI: IntoIterator<Item = T>,
R: 'static + Rng + Send + Sync,
R: Rng,
{
self.queue.extend(txs);
let mut qhb = QueueingHoneyBadger {
dyn_hb: self.dyn_hb,
batch_size: self.batch_size,
queue: self.queue,
rng: Box::new(rng),
};
let step = qhb.propose()?;
let step = qhb.propose(rng)?;
Ok((qhb, step))
}
}
@ -143,9 +139,6 @@ pub struct QueueingHoneyBadger<T, N: Rand + Ord, Q> {
dyn_hb: DynamicHoneyBadger<Vec<T>, N>,
/// The queue of pending transactions that haven't been output in a batch yet.
queue: Q,
/// Random number generator used for choosing transactions from the queue.
#[derivative(Debug(format_with = "util::fmt_rng"))]
rng: Box<dyn Rng + Send + Sync>,
}
/// A `QueueingHoneyBadger` step, possibly containing multiple outputs.
@ -163,17 +156,23 @@ where
type Message = Message<N>;
type Error = Error;
fn handle_input(&mut self, input: Self::Input) -> Result<Step<T, N>> {
fn handle_input<R: Rng>(&mut self, input: Self::Input, rng: &mut R) -> Result<Step<T, N>> {
// User transactions are forwarded to `HoneyBadger` right away. Internal messages are
// in addition signed and broadcast.
match input {
Input::User(tx) => self.push_transaction(tx),
Input::Change(change) => self.vote_for(change),
Input::User(tx) => self.push_transaction(tx, rng),
Input::Change(change) => self.vote_for(change, rng),
}
}
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<T, N>> {
self.handle_message(sender_id, message)
fn handle_message<R: Rng>(
&mut self,
sender_id: &N,
message: Self::Message,
rng: &mut R,
) -> Result<Step<T, N>> {
self.handle_message(sender_id, message, rng)
}
fn terminated(&self) -> bool {
@ -205,40 +204,53 @@ where
/// If no proposal has yet been made for the current epoch, this may trigger one. In this case,
/// a nonempty step will returned, with the corresponding messages. (Or, if we are the only
/// validator, even with the completed batch as an output.)
pub fn push_transaction(&mut self, tx: T) -> Result<Step<T, N>> {
pub fn push_transaction<R: Rng>(&mut self, tx: T, rng: &mut R) -> Result<Step<T, N>> {
self.queue.extend(iter::once(tx));
self.propose()
self.propose(rng)
}
/// Casts a vote to change the set of validators.
///
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_for(&mut self, change: Change<N>) -> Result<Step<T, N>> {
self.apply(|dyn_hb| dyn_hb.vote_for(change))
pub fn vote_for<R: Rng>(&mut self, change: Change<N>, rng: &mut R) -> Result<Step<T, N>> {
self.apply(|dyn_hb, _| dyn_hb.vote_for(change), rng)
}
/// Casts a vote to add a node as a validator.
///
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_to_add(&mut self, node_id: N, pub_key: PublicKey) -> Result<Step<T, N>> {
self.apply(|dyn_hb| dyn_hb.vote_to_add(node_id, pub_key))
pub fn vote_to_add<R: Rng>(
&mut self,
node_id: N,
pub_key: PublicKey,
rng: &mut R,
) -> Result<Step<T, N>> {
self.apply(|dyn_hb, _| dyn_hb.vote_to_add(node_id, pub_key), rng)
}
/// Casts a vote to demote a validator to observer.
///
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_to_remove(&mut self, node_id: &N) -> Result<Step<T, N>> {
self.apply(|dyn_hb| dyn_hb.vote_to_remove(node_id))
pub fn vote_to_remove<R: Rng>(&mut self, node_id: &N, rng: &mut R) -> Result<Step<T, N>> {
self.apply(|dyn_hb, _| dyn_hb.vote_to_remove(node_id), rng)
}
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<T, N>> {
self.apply(|dyn_hb| dyn_hb.handle_message(sender_id, message))
pub fn handle_message<R: Rng>(
&mut self,
sender_id: &N,
message: Message<N>,
rng: &mut R,
) -> Result<Step<T, N>> {
self.apply(
|dyn_hb, rng| dyn_hb.handle_message(sender_id, message, rng),
rng,
)
}
/// Returns a reference to the internal managed `DynamicHoneyBadger` instance.
@ -252,14 +264,18 @@ where
}
/// Applies a function `f` to the `DynamicHoneyBadger` instance and processes the step.
fn apply<F>(&mut self, f: F) -> Result<Step<T, N>>
fn apply<R, F>(&mut self, f: F, rng: &mut R) -> Result<Step<T, N>>
where
F: FnOnce(&mut DynamicHoneyBadger<Vec<T>, N>) -> dynamic_honey_badger::Result<Step<T, N>>,
F: FnOnce(
&mut DynamicHoneyBadger<Vec<T>, N>,
&mut R,
) -> dynamic_honey_badger::Result<Step<T, N>>,
R: Rng,
{
let step = f(&mut self.dyn_hb).map_err(Error::Input)?;
let step = f(&mut self.dyn_hb, rng).map_err(Error::Input)?;
self.queue
.remove_multiple(step.output.iter().flat_map(Batch::iter));
Ok(step.join(self.propose()?))
Ok(step.join(self.propose(rng)?))
}
/// Returns the epoch of the next batch that will be output.
@ -278,14 +294,14 @@ where
}
/// Initiates the next epoch by proposing a batch from the queue.
fn propose(&mut self) -> Result<Step<T, N>> {
fn propose<R: Rng>(&mut self, rng: &mut R) -> Result<Step<T, N>> {
let mut step = Step::default();
while self.can_propose() {
let amount = cmp::max(1, self.batch_size / self.dyn_hb.netinfo().num_nodes());
let proposal = self.queue.choose(&mut self.rng, amount, self.batch_size);
let proposal = self.queue.choose(rng, amount, self.batch_size);
step.extend(
self.dyn_hb
.handle_input(Input::User(proposal))
.handle_input(Input::User(proposal), rng)
.map_err(Error::Propose)?,
);
}

View File

@ -3,7 +3,7 @@
use std::result;
use crate::crypto::PublicKey;
use rand::Rand;
use rand::{Rand, Rng};
use serde::{de::DeserializeOwned, Serialize};
use super::{
@ -88,8 +88,8 @@ where
///
/// If we are the only validator, this will immediately output a batch, containing our
/// proposal.
pub fn propose(&mut self, contrib: C) -> Result<C, N> {
self.apply(|algo| algo.propose(contrib))
pub fn propose<R: Rng>(&mut self, rng: &mut R, contrib: C) -> Result<C, N> {
self.apply(|algo| algo.propose(contrib, rng))
}
/// Casts a vote to change the set of validators or parameters.

View File

@ -10,6 +10,7 @@ mod honey_badger;
mod message;
mod queueing_honey_badger;
use rand::Rng;
use std::collections::BTreeMap;
use std::fmt::Debug;
@ -99,16 +100,21 @@ where
type Message = Message<D::Message>;
type Error = D::Error;
fn handle_input(&mut self, input: Self::Input) -> Result<DaStep<Self>, D::Error> {
self.handle_input(input)
fn handle_input<R: Rng>(
&mut self,
input: Self::Input,
rng: &mut R,
) -> Result<DaStep<Self>, D::Error> {
self.handle_input(input, rng)
}
fn handle_message(
fn handle_message<R: Rng>(
&mut self,
sender_id: &D::NodeId,
message: Self::Message,
rng: &mut R,
) -> Result<DaStep<Self>, D::Error> {
self.handle_message(sender_id, message)
self.handle_message(sender_id, message, rng)
}
fn terminated(&self) -> bool {
@ -137,21 +143,26 @@ where
}
/// Handles an input. This will call the wrapped algorithm's `handle_input`.
pub fn handle_input(&mut self, input: D::Input) -> Result<DaStep<Self>, D::Error> {
self.apply(|algo| algo.handle_input(input))
pub fn handle_input<R: Rng>(
&mut self,
input: D::Input,
rng: &mut R,
) -> Result<DaStep<Self>, D::Error> {
self.apply(|algo| algo.handle_input(input, rng))
}
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(
pub fn handle_message<R: Rng>(
&mut self,
sender_id: &D::NodeId,
message: Message<D::Message>,
rng: &mut R,
) -> Result<DaStep<Self>, D::Error> {
match message {
Message::EpochStarted(epoch) => Ok(self.handle_epoch_started(sender_id, epoch)),
Message::Algo(msg) => self.handle_message_content(sender_id, msg),
Message::Algo(msg) => self.handle_message_content(sender_id, msg, rng),
}
}
@ -207,12 +218,13 @@ where
}
/// Handles a Honey Badger algorithm message in a given epoch.
fn handle_message_content(
fn handle_message_content<R: Rng>(
&mut self,
sender_id: &D::NodeId,
content: D::Message,
rng: &mut R,
) -> Result<DaStep<Self>, D::Error> {
self.apply(|algo| algo.handle_message(sender_id, content))
self.apply(|algo| algo.handle_message(sender_id, content, rng))
}
/// Updates the current Honey Badger epoch.

View File

@ -3,7 +3,7 @@
use std::result;
use crate::crypto::PublicKey;
use rand::Rand;
use rand::{Rand, Rng};
use serde::{de::DeserializeOwned, Serialize};
use super::{SenderQueue, SenderQueueableDistAlgorithm};
@ -51,31 +51,36 @@ where
/// If no proposal has yet been made for the current epoch, this may trigger one. In this case,
/// a nonempty step will returned, with the corresponding messages. (Or, if we are the only
/// validator, even with the completed batch as an output.)
pub fn push_transaction(&mut self, tx: T) -> Result<T, N, Q> {
self.apply(|algo| algo.push_transaction(tx))
pub fn push_transaction<R: Rng>(&mut self, tx: T, rng: &mut R) -> Result<T, N, Q> {
self.apply(|algo| algo.push_transaction(tx, rng))
}
/// Casts a vote to change the set of validators or parameters.
///
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_for(&mut self, change: Change<N>) -> Result<T, N, Q> {
self.apply(|algo| algo.vote_for(change))
pub fn vote_for<R: Rng>(&mut self, change: Change<N>, rng: &mut R) -> Result<T, N, Q> {
self.apply(|algo| algo.vote_for(change, rng))
}
/// Casts a vote to add a node as a validator.
///
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_to_add(&mut self, node_id: N, pub_key: PublicKey) -> Result<T, N, Q> {
self.apply(|algo| algo.vote_to_add(node_id, pub_key))
pub fn vote_to_add<R: Rng>(
&mut self,
node_id: N,
pub_key: PublicKey,
rng: &mut R,
) -> Result<T, N, Q> {
self.apply(|algo| algo.vote_to_add(node_id, pub_key, rng))
}
/// Casts a vote to demote a validator to observer.
///
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_to_remove(&mut self, node_id: &N) -> Result<T, N, Q> {
self.apply(|algo| algo.vote_to_remove(node_id))
pub fn vote_to_remove<R: Rng>(&mut self, node_id: &N, rng: &mut R) -> Result<T, N, Q> {
self.apply(|algo| algo.vote_to_remove(node_id, rng))
}
}

View File

@ -10,7 +10,7 @@ use serde_derive::Serialize;
use super::proposal_state::{ProposalState, Step as ProposalStep};
use super::{Error, Message, MessageContent, Result};
use crate::{util, DistAlgorithm, NetworkInfo, NodeIdT, SessionIdT};
use rand::Rand;
use rand::{Rand, Rng};
/// A `Subset` step, possibly containing several outputs.
pub type Step<N> = crate::Step<Message<N>, SubsetOutput<N>, N>;
@ -48,11 +48,16 @@ impl<N: NodeIdT + Rand, S: SessionIdT> DistAlgorithm for Subset<N, S> {
type Message = Message<N>;
type Error = Error;
fn handle_input(&mut self, input: Self::Input) -> Result<Step<N>> {
fn handle_input<R: Rng>(&mut self, input: Self::Input, _rng: &mut R) -> Result<Step<N>> {
self.propose(input)
}
fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<N>> {
fn handle_message<R: Rng>(
&mut self,
sender_id: &N,
message: Message<N>,
_rng: &mut R,
) -> Result<Step<N>> {
self.handle_message(sender_id, message)
}

View File

@ -61,8 +61,8 @@
//! use threshold_crypto::{PublicKey, SecretKey, SignatureShare};
//! use hbbft::sync_key_gen::{AckOutcome, PartOutcome, SyncKeyGen};
//!
//! // Use a default random number generator for any randomness:
//! let mut rng = rand::thread_rng();
//! // Use the OS random number generator for any randomness:
//! let mut rng = rand::OsRng::new().expect("Could not open OS random number generator.");
//!
//! // Two out of four shares will suffice to sign or encrypt something.
//! let (threshold, node_num) = (1, 4);
@ -80,8 +80,13 @@
//! let mut nodes = BTreeMap::new();
//! let mut parts = Vec::new();
//! for (id, sk) in sec_keys.into_iter().enumerate() {
//! let (sync_key_gen, opt_part) = SyncKeyGen::new(&mut rng, id, sk, pub_keys.clone(), threshold)
//! .unwrap_or_else(|_| panic!("Failed to create `SyncKeyGen` instance for node #{}", id));
//! let (sync_key_gen, opt_part) = SyncKeyGen::new(
//! id,
//! sk,
//! pub_keys.clone(),
//! threshold,
//! &mut rng,
//! ).unwrap_or_else(|_| panic!("Failed to create `SyncKeyGen` instance for node #{}", id));
//! nodes.insert(id, sync_key_gen);
//! parts.push((id, opt_part.unwrap())); // Would be `None` for observer nodes.
//! }
@ -91,7 +96,7 @@
//! for (sender_id, part) in parts {
//! for (&id, node) in &mut nodes {
//! match node
//! .handle_part(&mut rng, &sender_id, part.clone())
//! .handle_part(&sender_id, part.clone(), &mut rng)
//! .expect("Failed to handle Part")
//! {
//! PartOutcome::Valid(Some(ack)) => acks.push((id, ack)),
@ -316,11 +321,11 @@ impl<N: NodeIdT> SyncKeyGen<N> {
/// If we are not a validator but only an observer, no `Part` message is produced and no
/// messages need to be sent.
pub fn new<R: rand::Rng>(
rng: &mut R,
our_id: N,
sec_key: SecretKey,
pub_keys: BTreeMap<N, PublicKey>,
threshold: usize,
rng: &mut R,
) -> Result<(SyncKeyGen<N>, Option<Part>), Error> {
let our_idx = pub_keys
.keys()
@ -366,9 +371,9 @@ impl<N: NodeIdT> SyncKeyGen<N> {
/// Note that `handle_part` also needs to explicitly be called with this instance's own `Part`.
pub fn handle_part<R: rand::Rng>(
&mut self,
rng: &mut R,
sender_id: &N,
part: Part,
rng: &mut R,
) -> Result<PartOutcome, Error> {
let sender_idx = self.node_index(sender_id).ok_or(Error::UnknownSender)?;
let row = match self.handle_part_or_fault(sender_idx, part) {

View File

@ -16,6 +16,7 @@ use std::sync::Arc;
use crate::crypto::{self, Ciphertext, DecryptionShare};
use failure::Fail;
use rand::Rng;
use rand_derive::Rand;
use serde_derive::{Deserialize, Serialize};
@ -74,11 +75,16 @@ impl<N: NodeIdT> DistAlgorithm for ThresholdDecrypt<N> {
type Message = Message;
type Error = Error;
fn handle_input(&mut self, _input: ()) -> Result<Step<N>> {
fn handle_input<R: Rng>(&mut self, _input: (), _rng: &mut R) -> Result<Step<N>> {
self.start_decryption()
}
fn handle_message(&mut self, sender_id: &N, message: Message) -> Result<Step<N>> {
fn handle_message<R: Rng>(
&mut self,
sender_id: &Self::NodeId,
message: Message,
_rng: &mut R,
) -> Result<Step<N>> {
self.handle_message(sender_id, message)
}

View File

@ -22,6 +22,7 @@ use std::{fmt, result};
use crate::crypto::{self, hash_g2, Signature, SignatureShare, G2};
use failure::Fail;
use log::debug;
use rand::Rng;
use rand_derive::Rand;
use serde_derive::{Deserialize, Serialize};
@ -82,12 +83,17 @@ impl<N: NodeIdT> DistAlgorithm for ThresholdSign<N> {
type Error = Error;
/// Sends our threshold signature share if not yet sent.
fn handle_input(&mut self, _input: ()) -> Result<Step<N>> {
fn handle_input<R: Rng>(&mut self, _input: (), _rng: &mut R) -> Result<Step<N>> {
self.sign()
}
/// Receives input from a remote node.
fn handle_message(&mut self, sender_id: &N, message: Message) -> Result<Step<N>> {
fn handle_message<R: Rng>(
&mut self,
sender_id: &Self::NodeId,
message: Message,
_rng: &mut R,
) -> Result<Step<N>> {
self.handle_message(sender_id, message)
}

View File

@ -6,6 +6,7 @@ use std::hash::Hash;
use std::iter::once;
use failure::Fail;
use rand::Rng;
use serde::{de::DeserializeOwned, Serialize};
use crate::fault_log::{Fault, FaultLog};
@ -249,6 +250,9 @@ where
}
/// A distributed algorithm that defines a message flow.
///
/// Many algorithms require an RNG which must be supplied on each call. It is up to the caller to
/// ensure that this random number generator is cryptographically secure.
pub trait DistAlgorithm: Send + Sync {
/// Unique node identifier.
type NodeId: NodeIdT;
@ -263,15 +267,20 @@ pub trait DistAlgorithm: Send + Sync {
type Error: Fail;
/// Handles an input provided by the user, and returns
fn handle_input(&mut self, input: Self::Input) -> Result<DaStep<Self>, Self::Error>
fn handle_input<R: Rng>(
&mut self,
input: Self::Input,
rng: &mut R,
) -> Result<DaStep<Self>, Self::Error>
where
Self: Sized;
/// Handles a message received from node `sender_id`.
fn handle_message(
fn handle_message<R: Rng>(
&mut self,
sender_id: &Self::NodeId,
message: Self::Message,
rng: &mut R,
) -> Result<DaStep<Self>, Self::Error>
where
Self: Sized;

View File

@ -6,31 +6,6 @@
use std::fmt;
use hex_fmt::HexFmt;
use rand;
/// Workaround trait for creating new random number generators
pub trait SubRng {
/// Returns a new random number generator in a `Box`.
fn sub_rng(&mut self) -> Box<dyn rand::Rng + Send + Sync>;
}
impl<R> SubRng for R
where
R: rand::Rng,
{
fn sub_rng(&mut self) -> Box<dyn rand::Rng + Send + Sync> {
// Currently hard-coded to be an `Isaac64Rng`, until better options emerge. This is either
// dependant on `rand` 0.5 support or an API re-design of parts of `threshold_crypto` and
// `hbbft`.
let rng = self.gen::<rand::isaac::Isaac64Rng>();
Box::new(rng)
}
}
/// Prints "`<RNG>`" as a placeholder for a random number generator in debug output.
pub fn fmt_rng<T>(_: T, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("<RNG>")
}
/// Prints a byte slice as shortened hexadecimal in debug output.
pub fn fmt_hex<T: AsRef<[u8]>>(bytes: T, f: &mut fmt::Formatter) -> fmt::Result {

View File

@ -26,7 +26,7 @@ use rand::{Rng, SeedableRng};
use hbbft::binary_agreement::BinaryAgreement;
use hbbft::DistAlgorithm;
use crate::net::adversary::ReorderingAdversary;
use crate::net::adversary::{Adversary, ReorderingAdversary};
use crate::net::proptest::{gen_seed, NetworkDimension, TestRng, TestRngSeed};
use crate::net::{NetBuilder, NewNodeInfo, VirtualNet};
@ -72,19 +72,22 @@ proptest! {
type NodeId = u16;
impl VirtualNet<BinaryAgreement<NodeId, u8>> {
impl<A> VirtualNet<BinaryAgreement<NodeId, u8>, A>
where
A: Adversary<BinaryAgreement<NodeId, u8>>,
{
fn test_binary_agreement<R>(&mut self, input: Option<bool>, mut rng: R)
where
R: Rng + 'static,
{
let ids: Vec<NodeId> = self.nodes().map(|n| *n.id()).collect();
for id in ids {
let _ = self.send_input(id, input.unwrap_or_else(|| rng.gen::<bool>()));
let _ = self.send_input(id, input.unwrap_or_else(|| rng.gen::<bool>()), &mut rng);
}
// Handle messages in random order until all nodes have output the proposed value.
while !self.nodes().all(|node| node.algorithm().terminated()) {
let _ = self.crank_expect();
let _ = self.crank_expect(&mut rng);
}
// Verify that all instances output the same value.
let mut expected = input;
@ -117,13 +120,12 @@ fn binary_agreement(cfg: TestConfig) {
.num_faulty(num_faulty_nodes as usize)
.message_limit(10_000 * size as usize)
.time_limit(time::Duration::from_secs(30 * size as u64))
.rng(rng.gen::<TestRng>())
.adversary(ReorderingAdversary::new(rng.gen::<TestRng>()))
.adversary(ReorderingAdversary::new())
.using(move |node_info: NewNodeInfo<_>| {
BinaryAgreement::new(Arc::new(node_info.netinfo), 0)
.expect("Failed to create a BinaryAgreement instance.")
})
.build()
.build(&mut rng)
.expect("Could not construct test network.");
net.test_binary_agreement(cfg.input, rng.gen::<TestRng>());
println!(

View File

@ -9,9 +9,12 @@ use std::sync::{Arc, Mutex};
use hbbft::binary_agreement::{BinaryAgreement, MessageContent, SbvMessage};
use hbbft::threshold_sign::ThresholdSign;
use hbbft::{DaStep, DistAlgorithm, NetworkInfo};
use proptest::{proptest, proptest_helper};
use rand::{Rng, SeedableRng};
use crate::net::adversary::{NetMutHandle, QueuePosition};
use crate::net::err::CrankError;
use crate::net::proptest::{gen_seed, TestRng, TestRngSeed};
use crate::net::{Adversary, NetBuilder, NetMessage};
type NodeId = usize;
@ -236,14 +239,18 @@ const NODES_PER_GROUP: usize = 2;
const NUM_NODES: usize = (NODES_PER_GROUP * 3 + 1);
impl AbaCommonCoinAdversary {
fn new(netinfo_mutex: Arc<Mutex<Option<Arc<NetworkInfo<NodeId>>>>>) -> Self {
Self::new_with_epoch(netinfo_mutex, 0, false)
fn new<R: Rng>(
netinfo_mutex: Arc<Mutex<Option<Arc<NetworkInfo<NodeId>>>>>,
rng: &mut R,
) -> Self {
Self::new_with_epoch(netinfo_mutex, 0, false, rng)
}
fn new_with_epoch(
fn new_with_epoch<R: Rng>(
netinfo_mutex: Arc<Mutex<Option<Arc<NetworkInfo<NodeId>>>>>,
epoch: u64,
a_estimated: bool,
rng: &mut R,
) -> Self {
AbaCommonCoinAdversary {
stage: 0,
@ -265,7 +272,7 @@ impl AbaCommonCoinAdversary {
let mut coin = ThresholdSign::new_with_document(netinfo, coin_id)
.expect("Failed to set the coin's ID");
let _ = coin
.handle_input(())
.handle_input((), rng)
.expect("Calling handle_input on Coin failed");
CoinState::InProgress(Box::new(coin))
}
@ -288,7 +295,7 @@ impl AbaCommonCoinAdversary {
}
}
fn inject_stage_messages(&mut self, net: &mut NetMutHandle<Algo>) {
fn inject_stage_messages(&mut self, net: &mut NetMutHandle<Algo, Self>) {
if self.sent_stage_messages {
return;
}
@ -371,7 +378,7 @@ impl AbaCommonCoinAdversary {
}
impl Adversary<Algo> for AbaCommonCoinAdversary {
fn pre_crank(&mut self, mut net: NetMutHandle<Algo>) {
fn pre_crank<R: Rng>(&mut self, mut net: NetMutHandle<Algo, Self>, rng: &mut R) {
self.inject_stage_messages(&mut net);
net.sort_messages_by(|a, b| {
a.payload()
@ -395,19 +402,21 @@ impl Adversary<Algo> for AbaCommonCoinAdversary {
self.coin_state
.value()
.expect("Coin value not known at end of epoch"),
rng,
);
redo_crank = true;
}
}
if redo_crank {
self.pre_crank(net);
self.pre_crank(net, rng);
}
}
fn tamper(
fn tamper<R: Rng>(
&mut self,
_: NetMutHandle<Algo>,
_: NetMutHandle<Algo, Self>,
msg: NetMessage<Algo>,
_rng: &mut R,
) -> Result<DaStep<Algo>, CrankError<Algo>> {
if let MessageContent::Coin(ref coin_msg) = msg.payload().content {
let mut new_coin_state = None;
@ -427,13 +436,24 @@ impl Adversary<Algo> for AbaCommonCoinAdversary {
}
}
#[test]
fn reordering_attack() {
proptest! {
#[test]
#[allow(clippy::unnecessary_operation)]
fn reordering_attack(seed in gen_seed()) {
do_reordering_attack(seed)
}
}
fn do_reordering_attack(seed: TestRngSeed) {
let _ = env_logger::try_init();
let mut rng: TestRng = TestRng::from_seed(seed);
let ids: Vec<NodeId> = (0..NUM_NODES).collect();
let adversary_netinfo: Arc<Mutex<Option<Arc<NetworkInfo<NodeId>>>>> = Default::default();
let (mut net, _) = NetBuilder::new(ids.iter().cloned())
.adversary(AbaCommonCoinAdversary::new(adversary_netinfo.clone()))
.adversary(AbaCommonCoinAdversary::new(
adversary_netinfo.clone(),
&mut rng,
))
.crank_limit(10000)
.using(move |info| {
let netinfo = Arc::new(info.netinfo);
@ -443,7 +463,7 @@ fn reordering_attack() {
BinaryAgreement::new(netinfo, 0).expect("failed to create BinaryAgreement instance")
})
.num_faulty(1)
.build()
.build(&mut rng)
.unwrap();
for id in ids {
@ -451,15 +471,15 @@ fn reordering_attack() {
// This is the faulty node.
} else if id < (1 + NODES_PER_GROUP * 2) {
// Group A
let _ = net.send_input(id, false).unwrap();
let _ = net.send_input(id, false, &mut rng).unwrap();
} else {
// Group B
let _ = net.send_input(id, true).unwrap();
let _ = net.send_input(id, true, &mut rng).unwrap();
}
}
while !net.nodes().skip(1).all(|n| n.algorithm().terminated()) {
net.crank_expect();
net.crank_expect(&mut rng);
}
// Verify that all instances output the same value.

View File

@ -48,6 +48,7 @@ impl Adversary<Broadcast<NodeId>> for ProposeAdversary {
}
fn step(&mut self) -> Vec<MessageWithSender<Broadcast<NodeId>>> {
let mut rng = rand::thread_rng();
if self.has_sent {
return vec![];
}
@ -57,7 +58,7 @@ impl Adversary<Broadcast<NodeId>> for ProposeAdversary {
.flat_map(|(&id, netinfo)| {
Broadcast::new(netinfo.clone(), id)
.expect("broadcast instance")
.handle_input(b"Fake news".to_vec())
.handle_input(b"Fake news".to_vec(), &mut rng)
.expect("propose")
.messages
.into_iter()

View File

@ -33,8 +33,8 @@
//! some cases be upgraded to actual references, if the underlying node is faulty (see
//! `NodeHandle::node()` and `NodeHandle::node_mut()`).
use std::cmp;
use std::collections::VecDeque;
use std::{cmp, fmt};
use rand::Rng;
@ -46,13 +46,19 @@ use crate::net::{CrankError, NetMessage, Node, VirtualNet};
///
/// Allows querying public information of the network or getting immutable handles to any node.
#[derive(Debug)]
pub struct NetHandle<'a, D: 'a>(&'a VirtualNet<D>)
where
D: DistAlgorithm;
impl<'a, D: 'a> NetHandle<'a, D>
pub struct NetHandle<'a, D: 'a, A>(&'a VirtualNet<D, A>)
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>;
impl<'a, D: 'a, A> NetHandle<'a, D, A>
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>,
{
/// Returns a node handle iterator over all nodes in the network.
#[inline]
@ -106,18 +112,22 @@ pub enum QueuePosition {
/// Allows reordering of messages, injecting new ones into the network queue and getting mutable
/// handles to nodes.
#[derive(Debug)]
pub struct NetMutHandle<'a, D: 'a>(&'a mut VirtualNet<D>)
where
D: DistAlgorithm;
impl<'a, D> NetMutHandle<'a, D>
pub struct NetMutHandle<'a, D: 'a, A>(&'a mut VirtualNet<D, A>)
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>;
impl<'a, D, A> NetMutHandle<'a, D, A>
where
D: DistAlgorithm,
A: Adversary<D>,
D::NodeId: Clone,
D::Message: Clone,
D::Output: Clone,
{
pub fn new(net: &'a mut VirtualNet<D>) -> Self {
pub fn new(net: &'a mut VirtualNet<D, A>) -> Self {
NetMutHandle(net)
}
@ -143,8 +153,12 @@ where
}
/// Normally dispatch a message
pub fn dispatch_message(&mut self, msg: NetMessage<D>) -> Result<DaStep<D>, CrankError<D>> {
self.0.dispatch_message(msg)
pub fn dispatch_message<R: Rng>(
&mut self,
msg: NetMessage<D>,
rng: &mut R,
) -> Result<DaStep<D>, CrankError<D>> {
self.0.dispatch_message(msg, rng)
}
/// Injects a message into the network.
@ -211,12 +225,15 @@ where
}
// Downgrade-conversion.
impl<'a, D> From<NetMutHandle<'a, D>> for NetHandle<'a, D>
impl<'a, D, A> From<NetMutHandle<'a, D, A>> for NetHandle<'a, D, A>
where
D: DistAlgorithm,
A: Adversary<D>,
D::Message: Clone,
D::Output: Clone,
{
#[inline]
fn from(n: NetMutHandle<D>) -> NetHandle<D> {
fn from(n: NetMutHandle<D, A>) -> NetHandle<D, A> {
NetHandle(n.0)
}
}
@ -305,6 +322,7 @@ where
/// Network adversary.
pub trait Adversary<D>
where
Self: Sized,
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
@ -316,7 +334,7 @@ where
///
/// The default implementation does not alter the passed network in any way.
#[inline]
fn pre_crank(&mut self, _net: NetMutHandle<D>) {}
fn pre_crank<R: Rng>(&mut self, _net: NetMutHandle<D, Self>, _rng: &mut R) {}
/// Tamper with a faulty node's operation.
///
@ -332,12 +350,13 @@ where
/// `VirtualNet::dispatch_message`, which results in the message being processed as if the node
/// was not faulty.
#[inline]
fn tamper(
fn tamper<R: Rng>(
&mut self,
mut net: NetMutHandle<D>,
mut net: NetMutHandle<D, Self>,
msg: NetMessage<D>,
rng: &mut R,
) -> Result<DaStep<D>, CrankError<D>> {
net.dispatch_message(msg)
net.dispatch_message(msg, rng)
}
}
@ -388,7 +407,7 @@ where
D::Output: Clone,
{
#[inline]
fn pre_crank(&mut self, mut net: NetMutHandle<D>) {
fn pre_crank<R: Rng>(&mut self, mut net: NetMutHandle<D, Self>, _rng: &mut R) {
// Message are sorted by NodeID on each step.
net.sort_messages_by(|a, b| a.to.cmp(&b.to))
}
@ -399,26 +418,12 @@ where
/// An adversary that swaps the message at the front of the message queue for a random message
/// within the queue before every `crank`. Thus the order in which messages are received by nodes is
/// random, which allows to test randomized message delivery.
pub struct ReorderingAdversary {
/// Random number generator to reorder messages.
rng: Box<dyn Rng>,
}
impl fmt::Debug for ReorderingAdversary {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ReorderingAdversary")
.field("rng", &"<RNG>")
.finish()
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct ReorderingAdversary {}
impl ReorderingAdversary {
#[inline]
pub fn new<R>(rng: R) -> Self
where
R: 'static + Rng,
{
ReorderingAdversary { rng: Box::new(rng) }
pub fn new() -> Self {
ReorderingAdversary {}
}
}
@ -429,10 +434,10 @@ where
D::Output: Clone,
{
#[inline]
fn pre_crank(&mut self, mut net: NetMutHandle<D>) {
fn pre_crank<R: Rng>(&mut self, mut net: NetMutHandle<D, Self>, rng: &mut R) {
let l = net.0.messages_len();
if l > 0 {
net.swap_messages(0, self.rng.gen_range(0, l));
net.swap_messages(0, rng.gen_range(0, l));
}
}
}

View File

@ -26,7 +26,6 @@ use rand;
use rand::{Rand, Rng};
use hbbft::dynamic_honey_badger::Batch;
use hbbft::util::SubRng;
use hbbft::{self, Contribution, DaStep, DistAlgorithm, Fault, NetworkInfo, NodeIdT, Step};
use crate::try_some;
@ -284,6 +283,7 @@ where
/// New network node construction information.
///
/// Helper structure passed to node constructors when building virtual networks.
#[derive(Debug)]
pub struct NewNodeInfo<D>
where
D: DistAlgorithm,
@ -294,28 +294,6 @@ where
pub netinfo: NetworkInfo<D::NodeId>,
/// Whether or not the node is marked faulty.
pub faulty: bool,
/// An initialized random number generated for exclusive use by the node.
///
/// Can be ignored, but usually comes in handy with algorithms that require additional
/// randomness for instantiation or operation.
///
/// Note that the random number generator type may differ from the one set for generation on
/// the `VirtualNet`, due to limitations of the `rand` crates API.
pub rng: Box<dyn rand::Rng>,
}
impl<D> fmt::Debug for NewNodeInfo<D>
where
D: DistAlgorithm,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("NewNodeInfo")
.field("id", &self.id)
.field("netinfo", &self.netinfo)
.field("faulty", &self.faulty)
.field("rng", &"<RNG>")
.finish()
}
}
/// Virtual network builder.
@ -325,7 +303,7 @@ where
///
/// Note that, in addition to the constructor `new`, either `using` or `using_step` must be called,
/// otherwise the construction will fail and panic.
pub struct NetBuilder<D, I>
pub struct NetBuilder<D, I, A>
where
D: DistAlgorithm,
{
@ -336,7 +314,7 @@ where
/// Dist-algorithm constructor function.
cons: Option<Box<Fn(NewNodeInfo<D>) -> (D, DaStep<D>)>>,
/// Network adversary.
adversary: Option<Box<dyn Adversary<D>>>,
adversary: Option<A>,
/// Trace-enabling flag. `None` means use environment.
trace: Option<bool>,
/// Optional crank limit.
@ -348,36 +326,35 @@ where
/// Property to cause an error if a `Fault` is output from a correct node. By default,
/// encountering a fault leads to an error.
error_on_fault: bool,
/// Random number generator used to generate keys.
rng: Option<Box<dyn Rng>>,
}
impl<D, I> fmt::Debug for NetBuilder<D, I>
impl<D, I, A> fmt::Debug for NetBuilder<D, I, A>
where
D: DistAlgorithm,
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("NetBuilder")
.field("node_ids", &())
.field("num_faulty", &self.num_faulty)
.field("cons", &self.cons.is_some())
.field("adversary", &self.cons.is_some())
.field("adversary", &self.adversary)
.field("trace", &self.trace)
.field("crank_limit", &self.crank_limit)
.field("message_limit", &self.message_limit)
.field("time_limit", &self.time_limit)
.field("error_on_fault", &self.error_on_fault)
.field("rng", &"<RNG>")
.finish()
}
}
impl<D, I> NetBuilder<D, I>
impl<D, I, A> NetBuilder<D, I, A>
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
I: IntoIterator<Item = D::NodeId>,
A: Adversary<D>,
{
/// Construct a new network builder.
///
@ -399,7 +376,6 @@ where
message_limit: None,
time_limit: DEFAULT_TIME_LIMIT,
error_on_fault: true,
rng: None,
}
}
@ -407,11 +383,8 @@ where
///
/// If not set, the virtual network is constructed with a `NullAdversary`.
#[inline]
pub fn adversary<A>(mut self, adversary: A) -> Self
where
A: Adversary<D> + 'static,
{
self.adversary = Some(Box::new(adversary));
pub fn adversary(mut self, adversary: A) -> Self {
self.adversary = Some(adversary);
self
}
@ -455,20 +428,6 @@ where
self
}
/// Random number generator.
///
/// Overrides the random number generator used. If not specified, a `thread_rng` will be
/// used on construction.
///
/// The passed in generator is used for key generation.
pub fn rng<R>(mut self, rng: R) -> Self
where
R: Rng + 'static,
{
self.rng = Some(Box::new(rng));
self
}
/// Time limit.
///
/// Sets the time limit; `crank` will fail if called after this much time as elapsed since
@ -531,9 +490,10 @@ where
///
/// If the total number of nodes is not `> 3 * num_faulty`, construction will panic.
#[inline]
pub fn build(self) -> Result<(VirtualNet<D>, Vec<(D::NodeId, DaStep<D>)>), CrankError<D>> {
let rng: Box<dyn Rng> = self.rng.unwrap_or_else(|| Box::new(rand::thread_rng()));
pub fn build<R: Rng>(
self,
rng: &mut R,
) -> Result<(VirtualNet<D, A>, Vec<(D::NodeId, DaStep<D>)>), CrankError<D>> {
// The time limit can be overriden through environment variables:
let override_time_limit = env::var("HBBFT_NO_TIME_LIMIT")
// We fail early, to avoid tricking the user into thinking that they have set the time
@ -587,20 +547,28 @@ where
}
}
/// Virtual network instance.
pub struct VirtualNet<D>
/// A virtual network
///
/// Virtual networks host a number of nodes that are marked either correct or faulty. Each time a
/// node emits a `Step`, the contained messages are queued for delivery, which happens whenever
/// `crank()` is called. Additionally, inputs (see `DistAlgorithm::Input`) can be sent to any node.
///
/// An adversary can be hooked into the network to affect the order of message delivery or the
/// behaviour of faulty nodes.
#[derive(Debug)]
pub struct VirtualNet<D, A>
where
D: DistAlgorithm,
A: Adversary<D>,
D::Message: Clone,
D::Output: Clone,
{
/// Maps node IDs to actual node instances.
nodes: NodeMap<D>,
/// A collection of all network messages queued up for delivery.
messages: collections::VecDeque<NetMessage<D>>,
/// An Adversary that controls the network delivery schedule and all faulty nodes.
/// Always present (initialized to `NullAdversary` by default), but an `Option` to be swappable
/// during execution, allowing a `&mut self` to be passed to the adversary without running afoul
/// of the borrow checker.
adversary: Option<Box<dyn Adversary<D>>>,
/// An optional `Adversary` that controls the network delivery schedule and all faulty nodes.
adversary: Option<A>,
/// Trace output; if active, writes out a log of all messages.
trace: Option<io::BufWriter<fs::File>>,
/// The number of times the network has been cranked.
@ -621,36 +589,12 @@ where
error_on_fault: bool,
}
impl<D> fmt::Debug for VirtualNet<D>
where
D: DistAlgorithm,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("VirtualNet")
.field("nodes", &self.nodes.len())
.field("messages", &self.messages)
.field("adversary", &self.adversary.is_some())
.field("trace", &self.trace.is_some())
.field("crank_count", &self.crank_count)
.field("crank_limit", &self.crank_limit)
.field("message_count", &self.message_count)
.field("message_limit", &self.message_limit)
.field("error_on_fault", &self.error_on_fault)
.finish()
}
}
/// A virtual network
///
/// Virtual networks host a number of nodes that are marked either correct or faulty. Each time a
/// node emits a `Step`, the contained messages are queued for delivery, which happens whenever
/// `crank()` is called. Additionally, inputs (see `DistAlgorithm::Input`) can be sent to any node.
///
/// An adversary can be hooked into the network to affect the order of message delivery or the
/// behaviour of faulty nodes.
impl<D> VirtualNet<D>
impl<D, A> VirtualNet<D, A>
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>,
{
/// Returns an iterator over *all* nodes in the network.
#[inline]
@ -753,11 +697,12 @@ where
}
}
impl<D> VirtualNet<D>
impl<D, A> VirtualNet<D, A>
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>,
{
/// Create new virtual network with step constructor.
///
@ -808,7 +753,6 @@ where
id: id.clone(),
netinfo,
faulty: is_faulty,
rng: rng.sub_rng(),
});
steps.insert(id.clone(), step);
(id, Node::new(algorithm, is_faulty))
@ -832,7 +776,7 @@ where
VirtualNet {
nodes,
messages,
adversary: Some(Box::new(adversary::NullAdversary::new())),
adversary: None,
trace: None,
crank_count: 0,
crank_limit: None,
@ -850,7 +794,11 @@ where
///
/// Retrieves the receiving node for a `msg` and hands over the payload.
#[inline]
pub fn dispatch_message(&mut self, msg: NetMessage<D>) -> Result<DaStep<D>, CrankError<D>> {
pub fn dispatch_message<R: Rng>(
&mut self,
msg: NetMessage<D>,
rng: &mut R,
) -> Result<DaStep<D>, CrankError<D>> {
let node = self
.nodes
.get_mut(&msg.to)
@ -862,7 +810,7 @@ where
let msg_copy = msg.clone();
let step = node
.algorithm
.handle_message(&msg.from, msg.payload)
.handle_message(&msg.from, msg.payload, rng)
.map_err(move |err| CrankError::HandleMessage { msg: msg_copy, err })?;
Ok(step)
@ -877,17 +825,18 @@ where
///
/// Panics if `id` does not name a valid node.
#[inline]
pub fn send_input(
pub fn send_input<R: Rng>(
&mut self,
id: D::NodeId,
input: D::Input,
rng: &mut R,
) -> Result<DaStep<D>, CrankError<D>> {
let step = self
.nodes
.get_mut(&id)
.expect("cannot handle input on non-existing node")
.algorithm
.handle_input(input)
.handle_input(input, rng)
.map_err(CrankError::HandleInput)?;
self.message_count = self.message_count.saturating_add(process_step(
@ -909,7 +858,10 @@ where
/// If a successful `Step` was generated, all of its messages are queued on the network and the
/// `Step` is returned.
#[inline]
pub fn crank(&mut self) -> Option<Result<(D::NodeId, DaStep<D>), CrankError<D>>> {
pub fn crank<R: Rng>(
&mut self,
rng: &mut R,
) -> Option<Result<(D::NodeId, DaStep<D>), CrankError<D>>> {
// Check limits.
if let Some(limit) = self.crank_limit {
if self.crank_count >= limit {
@ -935,7 +887,7 @@ where
let mut adv = self.adversary.take();
if let Some(ref mut adversary) = adv {
// If an adversary was set, we let it affect the network now.
adversary.pre_crank(adversary::NetMutHandle::new(self))
adversary.pre_crank(adversary::NetMutHandle::new(self), rng)
}
self.adversary = adv;
@ -966,7 +918,7 @@ where
let mut adv = self.adversary.take();
let opt_tamper_result = adv.as_mut().map(|adversary| {
// If an adversary was set, we let it affect the network now.
adversary.tamper(adversary::NetMutHandle::new(self), msg)
adversary.tamper(adversary::NetMutHandle::new(self), msg, rng)
});
self.adversary = adv;
@ -977,7 +929,7 @@ where
)
} else {
// A correct node simply handles the message.
try_some!(self.dispatch_message(msg))
try_some!(self.dispatch_message(msg, rng))
};
// All messages are expanded and added to the queue. We opt for copying them, so we can
@ -1005,19 +957,20 @@ where
///
/// Shortcut for cranking the network, expecting both progress to be made as well as processing
/// to proceed.
pub fn crank_expect(&mut self) -> (D::NodeId, DaStep<D>) {
self.crank()
pub fn crank_expect<R: Rng>(&mut self, rng: &mut R) -> (D::NodeId, DaStep<D>) {
self.crank(rng)
.expect("crank: network queue empty")
.expect("crank: node failed to process step")
}
}
impl<D> VirtualNet<D>
impl<D, A> VirtualNet<D, A>
where
D: DistAlgorithm,
D::Message: Clone,
D::Input: Clone,
D::Output: Clone,
A: Adversary<D>,
{
/// Send input to all nodes.
///
@ -1026,17 +979,11 @@ where
///
/// If an error occurs, the first error is returned and broadcasting aborted.
#[inline]
pub fn broadcast_input<'a>(
pub fn broadcast_input<'a, R: Rng>(
&'a mut self,
input: &'a D::Input,
rng: &mut R,
) -> Result<Vec<(D::NodeId, DaStep<D>)>, CrankError<D>> {
// Note: The tricky lifetime annotation basically says that the input value given must
// live as long as the iterator returned lives (because it is cloned on every step,
// with steps only evaluated each time `next()` is called. For the same reason the
// network should not go away ealier either.
// Note: It's unfortunately not possible to loop and call `send_input`,
let steps: Vec<_> = self
.nodes
.values_mut()
@ -1044,7 +991,7 @@ where
Ok((
node.id().clone(),
node.algorithm
.handle_input(input.clone())
.handle_input(input.clone(), rng)
.map_err(CrankError::HandleInputAll)?,
))
})
@ -1066,9 +1013,11 @@ where
}
}
impl<C, D, N> VirtualNet<D>
impl<C, D, N, A> VirtualNet<D, A>
where
D: DistAlgorithm<Output = Batch<C, N>>,
D::Message: Clone,
A: Adversary<D>,
C: Contribution + Clone,
N: NodeIdT,
{
@ -1089,9 +1038,12 @@ where
}
}
impl<D> ops::Index<D::NodeId> for VirtualNet<D>
impl<D, A> ops::Index<D::NodeId> for VirtualNet<D, A>
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>,
{
type Output = Node<D>;
@ -1101,37 +1053,15 @@ where
}
}
impl<D> ops::IndexMut<D::NodeId> for VirtualNet<D>
impl<D, A> ops::IndexMut<D::NodeId> for VirtualNet<D, A>
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>,
{
#[inline]
fn index_mut(&mut self, index: D::NodeId) -> &mut Self::Output {
self.get_mut(index).expect("indexed node not found")
}
}
/// Convenient iterator implementation, calls crank repeatedly until the message queue is empty.
///
/// Accessing the network during iterator would require
/// [streaming iterators](https://crates.io/crates/streaming-iterator), an alternative is using
/// a `while let` loop:
///
/// ```rust,no_run
/// while let Some(rstep) = net.crank() {
/// // `net` can still be mutable borrowed here.
/// }
/// ```
impl<D> Iterator for VirtualNet<D>
where
D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
{
type Item = Result<(D::NodeId, DaStep<D>), CrankError<D>>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.crank()
}
}

View File

@ -8,7 +8,7 @@ use crate::net::{NetBuilder, NewNodeInfo};
use hbbft::dynamic_honey_badger::{Change, ChangeState, DynamicHoneyBadger, Input};
use hbbft::sender_queue::SenderQueue;
use proptest::{prelude::ProptestConfig, prop_compose, proptest, proptest_helper};
use rand::{Rng, SeedableRng};
use rand::SeedableRng;
/// Choose a node's contribution for an epoch.
///
@ -92,22 +92,18 @@ fn do_drop_and_readd(cfg: TestConfig) {
.message_limit(15_000 * cfg.dimension.size() as usize)
// 30 secs per node.
.time_limit(time::Duration::from_secs(30 * cfg.dimension.size() as u64))
// Ensure runs are reproducible.
.rng(rng.gen::<TestRng>())
.adversary(ReorderingAdversary::new(rng.gen::<TestRng>()))
.adversary(ReorderingAdversary::new())
.using_step(move |node: NewNodeInfo<SenderQueue<_>>| {
let id = node.id;
println!("Constructing new dynamic honey badger node #{}", id);
let dhb = DynamicHoneyBadger::builder()
.rng(node.rng)
.build(node.netinfo.clone());
let dhb = DynamicHoneyBadger::builder().build(node.netinfo.clone());
SenderQueue::builder(
dhb,
node.netinfo.all_ids().filter(|&&them| them != id).cloned(),
)
.build(node.id)
})
.build()
.build(&mut rng)
.expect("could not construct test network");
// We will use the first correct node as the node we will remove from and re-add to the network.
@ -133,7 +129,7 @@ fn do_drop_and_readd(cfg: TestConfig) {
// The step will have its messages added to the queue automatically, we ignore the output.
let _ = net
.send_input(*id, Input::User(proposal))
.send_input(*id, Input::User(proposal), &mut rng)
.expect("could not send initial transaction");
}
@ -148,8 +144,11 @@ fn do_drop_and_readd(cfg: TestConfig) {
let pub_keys_add = netinfo.public_key_map().clone();
let mut pub_keys_rm = pub_keys_add.clone();
pub_keys_rm.remove(&pivot_node_id);
net.broadcast_input(&Input::Change(Change::NodeChange(pub_keys_rm.clone())))
.expect("broadcasting failed");
net.broadcast_input(
&Input::Change(Change::NodeChange(pub_keys_rm.clone())),
&mut rng,
)
.expect("broadcasting failed");
// We are tracking (correct) nodes' state through the process by ticking them off individually.
let mut awaiting_removal: collections::BTreeSet<_> =
@ -164,7 +163,7 @@ fn do_drop_and_readd(cfg: TestConfig) {
// Run the network:
loop {
let (node_id, step) = net.crank_expect();
let (node_id, step) = net.crank_expect(&mut rng);
if !net[node_id].is_faulty() {
for batch in &step.output {
// Check that correct nodes don't output different batches for the same epoch.
@ -219,6 +218,7 @@ fn do_drop_and_readd(cfg: TestConfig) {
.send_input(
node_id,
Input::Change(Change::NodeChange(pub_keys_add.clone())),
&mut rng,
)
.expect("failed to send `Add` input");
}
@ -291,7 +291,7 @@ fn do_drop_and_readd(cfg: TestConfig) {
choose_contribution(&mut rng, queue, cfg.batch_size, cfg.contribution_size);
let _ = net
.send_input(node_id, Input::User(proposal))
.send_input(node_id, Input::User(proposal), &mut rng)
.expect("could not send follow-up transaction");
}
}

View File

@ -46,8 +46,8 @@ impl<D: DistAlgorithm> TestNode<D> {
}
/// Inputs a value into the instance.
pub fn handle_input(&mut self, input: D::Input) {
let step = self.algo.handle_input(input).expect("input");
pub fn handle_input<R: Rng>(&mut self, input: D::Input, rng: &mut R) {
let step = self.algo.handle_input(input, rng).expect("input");
self.outputs.extend(step.output);
self.messages.extend(step.messages);
self.faults.extend(step.fault_log.0);
@ -73,11 +73,13 @@ impl<D: DistAlgorithm> TestNode<D> {
/// Handles the first message in the node's queue.
fn handle_message(&mut self) {
let mut rng = rand::thread_rng();
let (from_id, msg) = self.queue.pop_front().expect("message not found");
debug!("Handling {:?} -> {:?}: {:?}", from_id, self.id, msg);
let step = self
.algo
.handle_message(&from_id, msg)
.handle_message(&from_id, msg, &mut rng)
.expect("handling message");
self.outputs.extend(step.output);
self.messages.extend(step.messages);
@ -558,9 +560,11 @@ where
/// Inputs a value in node `id`.
pub fn input(&mut self, id: NodeId, value: D::Input) {
let mut rng = rand::thread_rng();
let (msgs, faults): (Vec<_>, Vec<_>) = {
let node = self.nodes.get_mut(&id).expect("input instance");
node.handle_input(value);
node.handle_input(value, &mut rng);
(
node.messages.drain(..).collect(),
node.faults.drain(..).collect(),

View File

@ -90,8 +90,10 @@ fn new_queueing_hb(
.cloned()
.chain(iter::once(observer));
let dhb = DynamicHoneyBadger::builder().build((*netinfo).clone());
let rng = rand::thread_rng().gen::<Isaac64Rng>();
let (qhb, qhb_step) = QueueingHoneyBadger::builder(dhb).batch_size(3).build(rng);
let mut rng = rand::thread_rng().gen::<Isaac64Rng>();
let (qhb, qhb_step) = QueueingHoneyBadger::builder(dhb)
.batch_size(3)
.build(&mut rng);
let (sq, mut step) = SenderQueue::builder(qhb, peer_ids).build(our_id);
step.extend_with(qhb_step, Message::from);
(sq, step)

View File

@ -23,7 +23,7 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) {
.enumerate()
.map(|(id, sk)| {
let (sync_key_gen, proposal) =
SyncKeyGen::new(&mut rand::thread_rng(), id, sk, pub_keys.clone(), threshold)
SyncKeyGen::new(id, sk, pub_keys.clone(), threshold, &mut rand::thread_rng())
.unwrap_or_else(|_err| {
panic!("Failed to create `SyncKeyGen` instance #{}", id)
});
@ -38,7 +38,7 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) {
for (node_id, node) in nodes.iter_mut().enumerate() {
let proposal = proposal.clone().expect("proposal");
let ack = match node
.handle_part(&mut rand::thread_rng(), &sender_id, proposal)
.handle_part(&sender_id, proposal, &mut rand::thread_rng())
.expect("failed to handle part")
{
PartOutcome::Valid(Some(ack)) => ack,

View File

@ -18,8 +18,11 @@ fn test_threshold_sign<A>(mut network: TestNetwork<A, ThresholdSign<NodeId>>) ->
where
A: Adversary<ThresholdSign<NodeId>>,
{
let mut rng = rand::thread_rng();
network.input_all(());
network.observer.handle_input(()); // Observer will only return after `input` was called.
network.observer.handle_input((), &mut rng); // Observer will only return after `input` was called.
// Handle messages until all good nodes have terminated.
while !network.nodes.values().all(TestNode::terminated) {