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 tx_from_algo = messaging.tx_from_algo();
let stop_tx = messaging.stop_tx(); 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. // All spawned threads will have exited by the end of the scope.
crossbeam::scope(|scope| { crossbeam::scope(|scope| {
// Start the centralised message delivery system. // 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 { if let Some(v) = value {
// FIXME: Use the output. // FIXME: Use the output.
let step = broadcast let step = broadcast
.handle_input(v.clone().into()) .handle_input(v.clone().into(), &mut rng)
.expect("propose value"); .expect("propose value");
for msg in step.messages { for msg in step.messages {
tx_from_algo.send(msg).expect("send from algo"); tx_from_algo.send(msg).expect("send from algo");

View File

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

View File

@ -5,6 +5,7 @@ use std::{fmt, result};
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use hex_fmt::{HexFmt, HexList}; use hex_fmt::{HexFmt, HexList};
use log::{debug, warn}; use log::{debug, warn};
use rand::Rng;
use reed_solomon_erasure as rse; use reed_solomon_erasure as rse;
use reed_solomon_erasure::ReedSolomon; use reed_solomon_erasure::ReedSolomon;
@ -47,11 +48,16 @@ impl<N: NodeIdT> DistAlgorithm for Broadcast<N> {
type Message = Message; type Message = Message;
type Error = Error; 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) 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) self.handle_message(sender_id, message)
} }

View File

@ -92,7 +92,7 @@
//! ``` //! ```
//! use hbbft::broadcast::{Broadcast, Error, Step}; //! use hbbft::broadcast::{Broadcast, Error, Step};
//! use hbbft::{NetworkInfo, SourcedMessage, Target, TargetedMessage}; //! use hbbft::{NetworkInfo, SourcedMessage, Target, TargetedMessage};
//! use rand::{thread_rng, Rng}; //! use rand::{OsRng, Rng};
//! use std::collections::{BTreeMap, BTreeSet, VecDeque}; //! use std::collections::{BTreeMap, BTreeSet, VecDeque};
//! use std::iter::once; //! use std::iter::once;
//! use std::sync::Arc; //! use std::sync::Arc;
@ -102,7 +102,7 @@
//! const NUM_NODES: u64 = 7; //! const NUM_NODES: u64 = 7;
//! const PROPOSER_ID: u64 = 3; //! 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. //! // Create a random set of keys for testing.
//! let netinfos = NetworkInfo::generate_map(0..NUM_NODES, &mut rng) //! 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 std::sync::Arc;
use crate::crypto::{SecretKey, SecretKeySet}; use crate::crypto::{SecretKey, SecretKeySet};
use rand::{self, Rand, Rng}; use rand::Rand;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use super::{DynamicHoneyBadger, EncryptionSchedule, JoinPlan, Result, Step, VoteCounter}; use super::{DynamicHoneyBadger, EncryptionSchedule, JoinPlan, Result, Step, VoteCounter};
use crate::honey_badger::{HoneyBadger, Params, SubsetHandlingStrategy}; use crate::honey_badger::{HoneyBadger, Params, SubsetHandlingStrategy};
use crate::util::SubRng;
use crate::{Contribution, NetworkInfo, NodeIdT}; use crate::{Contribution, NetworkInfo, NodeIdT};
/// A Dynamic Honey Badger builder, to configure the parameters and create new instances of /// 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> { pub struct DynamicHoneyBadgerBuilder<C, N> {
/// Start in this era. /// Start in this era.
era: u64, 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. /// Parameters controlling Honey Badger's behavior and performance.
params: Params, params: Params,
_phantom: PhantomData<(C, N)>, _phantom: PhantomData<(C, N)>,
@ -32,7 +28,6 @@ where
fn default() -> Self { fn default() -> Self {
DynamicHoneyBadgerBuilder { DynamicHoneyBadgerBuilder {
era: 0, era: 0,
rng: Box::new(rand::thread_rng()),
params: Params::default(), params: Params::default(),
_phantom: PhantomData, _phantom: PhantomData,
} }
@ -62,12 +57,6 @@ where
self 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. /// Sets the strategy to use when handling `Subset` output.
pub fn subset_handling_strategy( pub fn subset_handling_strategy(
&mut self, &mut self,
@ -93,16 +82,16 @@ where
pub fn build(&mut self, netinfo: NetworkInfo<N>) -> DynamicHoneyBadger<C, N> { pub fn build(&mut self, netinfo: NetworkInfo<N>) -> DynamicHoneyBadger<C, N> {
let DynamicHoneyBadgerBuilder { let DynamicHoneyBadgerBuilder {
era, era,
rng,
params, params,
_phantom, _phantom,
} = self; } = self;
let arc_netinfo = Arc::new(netinfo.clone()); let arc_netinfo = Arc::new(netinfo.clone());
let honey_badger = HoneyBadger::builder(arc_netinfo.clone()) let honey_badger = HoneyBadger::builder(arc_netinfo.clone())
.session_id(*era) .session_id(*era)
.params(params.clone()) .params(params.clone())
.rng(rng.sub_rng())
.build(); .build();
DynamicHoneyBadger { DynamicHoneyBadger {
netinfo, netinfo,
max_future_epochs: params.max_future_epochs, max_future_epochs: params.max_future_epochs,
@ -111,16 +100,19 @@ where
key_gen_msg_buffer: Vec::new(), key_gen_msg_buffer: Vec::new(),
honey_badger, honey_badger,
key_gen_state: None, key_gen_state: None,
rng: Box::new(rng.sub_rng()),
} }
} }
/// Creates a new `DynamicHoneyBadger` configured to start a new network as a single validator. /// 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>> { pub fn build_first_node<R: rand::Rng>(
let sk_set = SecretKeySet::random(0, &mut self.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 pk_set = sk_set.public_keys();
let sks = sk_set.secret_key_share(0); 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 pub_keys = once((our_id.clone(), sk.public_key())).collect();
let netinfo = NetworkInfo::new(our_id, sks, pk_set, sk, pub_keys); let netinfo = NetworkInfo::new(our_id, sks, pk_set, sk, pub_keys);
Ok(self.build(netinfo)) Ok(self.build(netinfo))
@ -131,12 +123,13 @@ where
/// ///
/// **Deprecated**: Please use `DynamicHoneyBadger::new_joining` instead. /// **Deprecated**: Please use `DynamicHoneyBadger::new_joining` instead.
#[deprecated] #[deprecated]
pub fn build_joining( pub fn build_joining<R: rand::Rng>(
&mut self, &mut self,
our_id: N, our_id: N,
secret_key: SecretKey, secret_key: SecretKey,
join_plan: JoinPlan<N>, join_plan: JoinPlan<N>,
rng: &mut R,
) -> Result<(DynamicHoneyBadger<C, N>, Step<C, N>)> { ) -> 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 bincode;
use derivative::Derivative; use derivative::Derivative;
use log::debug; use log::debug;
use rand::{self, Rand, Rng}; use rand::{Rand, Rng};
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use super::votes::{SignedVote, VoteCounter}; 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::honey_badger::{self, HoneyBadger, Message as HbMessage};
use crate::sync_key_gen::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen}; 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}; use crate::{Contribution, DistAlgorithm, Epoched, NetworkInfo, NodeIdT, Target};
/// A Honey Badger instance that can handle adding and removing nodes. /// 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>, pub(super) honey_badger: HoneyBadger<InternalContrib<C, N>, N>,
/// The current key generation process, and the change it applies to. /// The current key generation process, and the change it applies to.
pub(super) key_gen_state: Option<KeyGenState<N>>, 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> impl<C, N> DistAlgorithm for DynamicHoneyBadger<C, N>
@ -57,17 +53,22 @@ where
type Message = Message<N>; type Message = Message<N>;
type Error = Error; 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 // User contributions are forwarded to `HoneyBadger` right away. Votes are signed and
// broadcast. // broadcast.
match input { match input {
Input::User(contrib) => self.propose(contrib), Input::User(contrib) => self.propose(contrib, rng),
Input::Change(change) => self.vote_for(change), Input::Change(change) => self.vote_for(change),
} }
} }
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<C, N>> { fn handle_message<R: Rng>(
self.handle_message(sender_id, message) &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 { fn terminated(&self) -> bool {
@ -90,11 +91,11 @@ where
} }
/// Creates a new `DynamicHoneyBadger` ready to join the network specified in the `JoinPlan`. /// 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, our_id: N,
secret_key: SecretKey, secret_key: SecretKey,
join_plan: JoinPlan<N>, join_plan: JoinPlan<N>,
mut rng: R, rng: &mut R,
) -> Result<(Self, Step<C, N>)> { ) -> Result<(Self, Step<C, N>)> {
let netinfo = NetworkInfo::new( let netinfo = NetworkInfo::new(
our_id, our_id,
@ -108,7 +109,6 @@ where
let honey_badger = HoneyBadger::builder(arc_netinfo.clone()) let honey_badger = HoneyBadger::builder(arc_netinfo.clone())
.session_id(join_plan.era) .session_id(join_plan.era)
.params(join_plan.params) .params(join_plan.params)
.rng(rng.sub_rng())
.build(); .build();
let mut dhb = DynamicHoneyBadger { let mut dhb = DynamicHoneyBadger {
netinfo, netinfo,
@ -118,11 +118,10 @@ where
key_gen_msg_buffer: Vec::new(), key_gen_msg_buffer: Vec::new(),
honey_badger, honey_badger,
key_gen_state: None, key_gen_state: None,
rng: Box::new(rng),
}; };
let step = match join_plan.change { let step = match join_plan.change {
ChangeState::InProgress(ref change) => match 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(), _ => Step::default(),
}, },
ChangeState::None | ChangeState::Complete(..) => 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 /// If we are the only validator, this will immediately output a batch, containing our
/// proposal. /// 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 let key_gen_messages = self
.key_gen_msg_buffer .key_gen_msg_buffer
.iter() .iter()
.filter(|kg_msg| kg_msg.era() == self.era) .filter(|kg_msg| kg_msg.era() == self.era)
.cloned() .cloned()
.collect(); .collect();
let contrib = InternalContrib {
contrib,
key_gen_messages,
votes: self.vote_counter.pending_votes().cloned().collect(),
};
let step = self let step = self
.honey_badger .honey_badger
.propose(&InternalContrib { .propose(&contrib, rng)
contrib,
key_gen_messages,
votes: self.vote_counter.pending_votes().cloned().collect(),
})
.map_err(Error::ProposeHoneyBadger)?; .map_err(Error::ProposeHoneyBadger)?;
self.process_output(step) self.process_output(step, rng)
} }
/// Casts a vote to change the set of validators or parameters. /// Casts a vote to change the set of validators or parameters.
@ -195,11 +197,16 @@ where
/// Handles a message received from `sender_id`. /// Handles a message received from `sender_id`.
/// ///
/// This must be called with every message we receive from another node. /// 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 { if message.era() == self.era {
match message { match message {
Message::HoneyBadger(_, hb_msg) => { 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 Message::KeyGen(_, kg_msg, sig) => self
.handle_key_gen_message(sender_id, kg_msg, *sig) .handle_key_gen_message(sender_id, kg_msg, *sig)
@ -249,10 +256,11 @@ where
} }
/// Handles a message for the `HoneyBadger` instance. /// Handles a message for the `HoneyBadger` instance.
fn handle_honey_badger_message( fn handle_honey_badger_message<R: Rng>(
&mut self, &mut self,
sender_id: &N, sender_id: &N,
message: HbMessage<N>, message: HbMessage<N>,
rng: &mut R,
) -> Result<Step<C, N>> { ) -> Result<Step<C, N>> {
if !self.netinfo.is_node_validator(sender_id) { if !self.netinfo.is_node_validator(sender_id) {
return Err(Error::UnknownSender); return Err(Error::UnknownSender);
@ -262,7 +270,7 @@ where
.honey_badger .honey_badger
.handle_message(sender_id, message) .handle_message(sender_id, message)
.map_err(Error::HandleHoneyBadgerMessage)?; .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 /// 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. /// Processes all pending batches output by Honey Badger.
fn process_output( fn process_output<R: Rng>(
&mut self, &mut self,
hb_step: honey_badger::Step<InternalContrib<C, N>, N>, hb_step: honey_badger::Step<InternalContrib<C, N>, N>,
rng: &mut R,
) -> Result<Step<C, N>> { ) -> Result<Step<C, N>> {
let mut step: Step<C, N> = Step::default(); let mut step: Step<C, N> = Step::default();
let output = step.extend_with(hb_step, |hb_msg| Message::HoneyBadger(self.era, hb_msg)); 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); step.fault_log.append(id.clone(), fault_kind);
} else { } else {
step.extend(match kg_msg { 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)?, 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. // If there is a new change, restart DKG. Inform the user about the current change.
match change { match change {
Change::NodeChange(ref pub_keys) => { 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) => { Change::EncryptionSchedule(schedule) => {
self.update_encryption_schedule(batch_epoch + 1, 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 /// If the winner of the vote has changed, restarts Key Generation for the set of nodes implied
/// by the current change. /// by the current change.
pub(super) fn update_key_gen( pub(super) fn update_key_gen<R: Rng>(
&mut self, &mut self,
era: u64, era: u64,
pub_keys: &BTreeMap<N, PublicKey>, pub_keys: &BTreeMap<N, PublicKey>,
rng: &mut R,
) -> Result<Step<C, N>> { ) -> Result<Step<C, N>> {
if self.key_gen_state.as_ref().map(KeyGenState::public_keys) == Some(pub_keys) { 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. 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 threshold = util::max_faulty(pub_keys.len());
let sk = self.netinfo.secret_key().clone(); let sk = self.netinfo.secret_key().clone();
let our_id = self.our_id().clone(); let our_id = self.our_id().clone();
let (key_gen, part) = let (key_gen, part) = SyncKeyGen::new(our_id, sk, pub_keys.clone(), threshold, rng)
SyncKeyGen::new(&mut self.rng, our_id, sk, pub_keys.clone(), threshold) .map_err(Error::SyncKeyGen)?;
.map_err(Error::SyncKeyGen)?;
self.key_gen_state = Some(KeyGenState::new(key_gen)); self.key_gen_state = Some(KeyGenState::new(key_gen));
if let Some(part) = part { if let Some(part) = part {
self.send_transaction(KeyGenMessage::Part(part)) self.send_transaction(KeyGenMessage::Part(part))
@ -413,16 +422,20 @@ where
self.vote_counter = VoteCounter::new(netinfo.clone(), era); self.vote_counter = VoteCounter::new(netinfo.clone(), era);
self.honey_badger = HoneyBadger::builder(netinfo) self.honey_badger = HoneyBadger::builder(netinfo)
.session_id(era) .session_id(era)
.rng(self.rng.sub_rng())
.params(params) .params(params)
.build(); .build();
} }
/// Handles a `Part` message that was output by Honey Badger. /// 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() { let outcome = if let Some(kgs) = self.key_gen_state.as_mut() {
kgs.key_gen kgs.key_gen
.handle_part(&mut self.rng, &sender_id, part) .handle_part(&sender_id, part, rng)
.map_err(Error::SyncKeyGen)? .map_err(Error::SyncKeyGen)?
} else { } else {
// No key generation ongoing. // 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 /// the vote by node `i` for making `j` the only validator. Each node signed this for nodes
/// `0`, `1`, ... in order. /// `0`, `1`, ... in order.
fn setup(node_num: usize, era: u64) -> (Vec<VoteCounter<usize>>, Vec<Vec<SignedVote<usize>>>) { 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. // Create keys for threshold cryptography.
let netinfos = NetworkInfo::generate_map(0..node_num, &mut rng) let netinfos = NetworkInfo::generate_map(0..node_num, &mut rng)
.expect("Failed to generate `NetworkInfo` map"); .expect("Failed to generate `NetworkInfo` map");

View File

@ -2,11 +2,10 @@ use std::collections::BTreeMap;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
use rand::{self, Rand, Rng}; use rand::Rand;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use super::{EncryptionSchedule, HoneyBadger, Params, SubsetHandlingStrategy}; use super::{EncryptionSchedule, HoneyBadger, Params, SubsetHandlingStrategy};
use crate::util::SubRng;
use crate::{Contribution, NetworkInfo, NodeIdT}; use crate::{Contribution, NetworkInfo, NodeIdT};
/// A Honey Badger builder, to configure the parameters and create new instances of `HoneyBadger`. /// 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, session_id: u64,
/// Start in this epoch. /// Start in this epoch.
epoch: u64, 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. /// Parameters controlling Honey Badger's behavior and performance.
params: Params, params: Params,
_phantom: PhantomData<C>, _phantom: PhantomData<C>,
@ -37,18 +34,11 @@ where
netinfo, netinfo,
session_id: 0, session_id: 0,
epoch: 0, epoch: 0,
rng: Box::new(rand::thread_rng()),
params: Params::default(), params: Params::default(),
_phantom: PhantomData, _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. /// Sets the session identifier.
/// ///
/// Different session IDs foil replay attacks in two instances with the same epoch numbers and /// Different session IDs foil replay attacks in two instances with the same epoch numbers and
@ -100,7 +90,6 @@ where
has_input: false, has_input: false,
epochs: BTreeMap::new(), epochs: BTreeMap::new(),
params: self.params.clone(), 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::fault_log::{Fault, FaultKind, FaultLog};
use crate::subset::{self as cs, Subset, SubsetOutput}; use crate::subset::{self as cs, Subset, SubsetOutput};
use crate::threshold_decrypt::{self as td, ThresholdDecrypt}; 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>; type CsStep<N> = cs::Step<N>;
@ -75,7 +75,7 @@ where
/// Provides input to the Subset instance, unless it has already completed. /// Provides input to the Subset instance, unless it has already completed.
fn handle_input(&mut self, proposal: Vec<u8>) -> Result<CsStep<N>> { fn handle_input(&mut self, proposal: Vec<u8>) -> Result<CsStep<N>> {
match self { 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()), SubsetState::Complete(_) => return Ok(cs::Step::default()),
} }
.map_err(Error::InputSubset) .map_err(Error::InputSubset)

View File

@ -9,7 +9,7 @@ use serde_derive::{Deserialize, Serialize};
use super::epoch_state::EpochState; use super::epoch_state::EpochState;
use super::{Batch, Error, HoneyBadgerBuilder, Message, Result}; 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; use super::Params;
@ -30,10 +30,6 @@ pub struct HoneyBadger<C, N: Rand> {
pub(super) epochs: BTreeMap<u64, EpochState<C, N>>, pub(super) epochs: BTreeMap<u64, EpochState<C, N>>,
/// Parameters controlling Honey Badger's behavior and performance. /// Parameters controlling Honey Badger's behavior and performance.
pub(super) params: Params, 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. /// A `HoneyBadger` step, possibly containing multiple outputs.
@ -50,11 +46,16 @@ where
type Message = Message<N>; type Message = Message<N>;
type Error = Error; 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>> {
self.propose(&input) 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) 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 /// If we are the only validator, this will immediately output a batch, containing our
/// proposal. /// 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() { if !self.netinfo.is_validator() {
return Ok(Step::default()); return Ok(Step::default());
} }
@ -97,7 +98,6 @@ where
"We created the epoch_state in `self.epoch_state_mut(...)` just a moment ago.", "We created the epoch_state in `self.epoch_state_mut(...)` just a moment ago.",
) )
}; };
let rng = &mut self.rng;
epoch_state.propose(proposal, rng)? epoch_state.propose(proposal, rng)?
}; };
Ok(step.join(self.try_output_batches()?)) 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::dynamic_honey_badger::{self, Batch as DhbBatch, DynamicHoneyBadger, Message};
use crate::transaction_queue::TransactionQueue; 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}; 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. /// Creates a new Queueing Honey Badger instance with an empty buffer.
pub fn build<R>(self, rng: R) -> QueueingHoneyBadgerWithStep<T, N, Q> pub fn build<R: Rng>(self, rng: &mut R) -> QueueingHoneyBadgerWithStep<T, N, Q> {
where
R: 'static + Rng + Send + Sync,
{
self.build_with_transactions(None, rng) self.build_with_transactions(None, rng)
.expect("building without transactions cannot fail") .expect("building without transactions cannot fail")
} }
@ -114,20 +111,19 @@ where
pub fn build_with_transactions<TI, R>( pub fn build_with_transactions<TI, R>(
mut self, mut self,
txs: TI, txs: TI,
rng: R, rng: &mut R,
) -> Result<QueueingHoneyBadgerWithStep<T, N, Q>> ) -> Result<QueueingHoneyBadgerWithStep<T, N, Q>>
where where
TI: IntoIterator<Item = T>, TI: IntoIterator<Item = T>,
R: 'static + Rng + Send + Sync, R: Rng,
{ {
self.queue.extend(txs); self.queue.extend(txs);
let mut qhb = QueueingHoneyBadger { let mut qhb = QueueingHoneyBadger {
dyn_hb: self.dyn_hb, dyn_hb: self.dyn_hb,
batch_size: self.batch_size, batch_size: self.batch_size,
queue: self.queue, queue: self.queue,
rng: Box::new(rng),
}; };
let step = qhb.propose()?; let step = qhb.propose(rng)?;
Ok((qhb, step)) Ok((qhb, step))
} }
} }
@ -143,9 +139,6 @@ pub struct QueueingHoneyBadger<T, N: Rand + Ord, Q> {
dyn_hb: DynamicHoneyBadger<Vec<T>, N>, dyn_hb: DynamicHoneyBadger<Vec<T>, N>,
/// The queue of pending transactions that haven't been output in a batch yet. /// The queue of pending transactions that haven't been output in a batch yet.
queue: Q, 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. /// A `QueueingHoneyBadger` step, possibly containing multiple outputs.
@ -163,17 +156,23 @@ where
type Message = Message<N>; type Message = Message<N>;
type Error = Error; 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 // User transactions are forwarded to `HoneyBadger` right away. Internal messages are
// in addition signed and broadcast. // in addition signed and broadcast.
match input { match input {
Input::User(tx) => self.push_transaction(tx), Input::User(tx) => self.push_transaction(tx, rng),
Input::Change(change) => self.vote_for(change), Input::Change(change) => self.vote_for(change, rng),
} }
} }
fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result<Step<T, N>> { fn handle_message<R: Rng>(
self.handle_message(sender_id, message) &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 { 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, /// 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 /// a nonempty step will returned, with the corresponding messages. (Or, if we are the only
/// validator, even with the completed batch as an output.) /// 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.queue.extend(iter::once(tx));
self.propose() self.propose(rng)
} }
/// Casts a vote to change the set of validators. /// 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 /// 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. /// 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>> { 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)) self.apply(|dyn_hb, _| dyn_hb.vote_for(change), rng)
} }
/// Casts a vote to add a node as a validator. /// 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 /// 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. /// 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>> { pub fn vote_to_add<R: Rng>(
self.apply(|dyn_hb| dyn_hb.vote_to_add(node_id, pub_key)) &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. /// 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 /// 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. /// 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>> { 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)) self.apply(|dyn_hb, _| dyn_hb.vote_to_remove(node_id), rng)
} }
/// Handles a message received from `sender_id`. /// Handles a message received from `sender_id`.
/// ///
/// This must be called with every message we receive from another node. /// 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>> { pub fn handle_message<R: Rng>(
self.apply(|dyn_hb| dyn_hb.handle_message(sender_id, message)) &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. /// 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. /// 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 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 self.queue
.remove_multiple(step.output.iter().flat_map(Batch::iter)); .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. /// 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. /// 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(); let mut step = Step::default();
while self.can_propose() { while self.can_propose() {
let amount = cmp::max(1, self.batch_size / self.dyn_hb.netinfo().num_nodes()); 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( step.extend(
self.dyn_hb self.dyn_hb
.handle_input(Input::User(proposal)) .handle_input(Input::User(proposal), rng)
.map_err(Error::Propose)?, .map_err(Error::Propose)?,
); );
} }

View File

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

View File

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

View File

@ -3,7 +3,7 @@
use std::result; use std::result;
use crate::crypto::PublicKey; use crate::crypto::PublicKey;
use rand::Rand; use rand::{Rand, Rng};
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use super::{SenderQueue, SenderQueueableDistAlgorithm}; 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, /// 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 /// a nonempty step will returned, with the corresponding messages. (Or, if we are the only
/// validator, even with the completed batch as an output.) /// validator, even with the completed batch as an output.)
pub fn push_transaction(&mut self, tx: T) -> Result<T, N, Q> { pub fn push_transaction<R: Rng>(&mut self, tx: T, rng: &mut R) -> Result<T, N, Q> {
self.apply(|algo| algo.push_transaction(tx)) self.apply(|algo| algo.push_transaction(tx, rng))
} }
/// Casts a vote to change the set of validators or parameters. /// 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 /// 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. /// 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> { 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)) self.apply(|algo| algo.vote_for(change, rng))
} }
/// Casts a vote to add a node as a validator. /// 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 /// 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. /// 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> { pub fn vote_to_add<R: Rng>(
self.apply(|algo| algo.vote_to_add(node_id, pub_key)) &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. /// 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 /// 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. /// 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> { 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)) 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::proposal_state::{ProposalState, Step as ProposalStep};
use super::{Error, Message, MessageContent, Result}; use super::{Error, Message, MessageContent, Result};
use crate::{util, DistAlgorithm, NetworkInfo, NodeIdT, SessionIdT}; use crate::{util, DistAlgorithm, NetworkInfo, NodeIdT, SessionIdT};
use rand::Rand; use rand::{Rand, Rng};
/// A `Subset` step, possibly containing several outputs. /// A `Subset` step, possibly containing several outputs.
pub type Step<N> = crate::Step<Message<N>, SubsetOutput<N>, N>; 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 Message = Message<N>;
type Error = Error; 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) 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) self.handle_message(sender_id, message)
} }

View File

@ -61,8 +61,8 @@
//! use threshold_crypto::{PublicKey, SecretKey, SignatureShare}; //! use threshold_crypto::{PublicKey, SecretKey, SignatureShare};
//! use hbbft::sync_key_gen::{AckOutcome, PartOutcome, SyncKeyGen}; //! use hbbft::sync_key_gen::{AckOutcome, PartOutcome, SyncKeyGen};
//! //!
//! // Use a default random number generator for any randomness: //! // Use the OS random number generator for any randomness:
//! let mut rng = rand::thread_rng(); //! 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. //! // Two out of four shares will suffice to sign or encrypt something.
//! let (threshold, node_num) = (1, 4); //! let (threshold, node_num) = (1, 4);
@ -80,8 +80,13 @@
//! let mut nodes = BTreeMap::new(); //! let mut nodes = BTreeMap::new();
//! let mut parts = Vec::new(); //! let mut parts = Vec::new();
//! for (id, sk) in sec_keys.into_iter().enumerate() { //! 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) //! let (sync_key_gen, opt_part) = SyncKeyGen::new(
//! .unwrap_or_else(|_| panic!("Failed to create `SyncKeyGen` instance for node #{}", id)); //! 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); //! nodes.insert(id, sync_key_gen);
//! parts.push((id, opt_part.unwrap())); // Would be `None` for observer nodes. //! parts.push((id, opt_part.unwrap())); // Would be `None` for observer nodes.
//! } //! }
@ -91,7 +96,7 @@
//! for (sender_id, part) in parts { //! for (sender_id, part) in parts {
//! for (&id, node) in &mut nodes { //! for (&id, node) in &mut nodes {
//! match node //! match node
//! .handle_part(&mut rng, &sender_id, part.clone()) //! .handle_part(&sender_id, part.clone(), &mut rng)
//! .expect("Failed to handle Part") //! .expect("Failed to handle Part")
//! { //! {
//! PartOutcome::Valid(Some(ack)) => acks.push((id, ack)), //! 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 /// If we are not a validator but only an observer, no `Part` message is produced and no
/// messages need to be sent. /// messages need to be sent.
pub fn new<R: rand::Rng>( pub fn new<R: rand::Rng>(
rng: &mut R,
our_id: N, our_id: N,
sec_key: SecretKey, sec_key: SecretKey,
pub_keys: BTreeMap<N, PublicKey>, pub_keys: BTreeMap<N, PublicKey>,
threshold: usize, threshold: usize,
rng: &mut R,
) -> Result<(SyncKeyGen<N>, Option<Part>), Error> { ) -> Result<(SyncKeyGen<N>, Option<Part>), Error> {
let our_idx = pub_keys let our_idx = pub_keys
.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`. /// Note that `handle_part` also needs to explicitly be called with this instance's own `Part`.
pub fn handle_part<R: rand::Rng>( pub fn handle_part<R: rand::Rng>(
&mut self, &mut self,
rng: &mut R,
sender_id: &N, sender_id: &N,
part: Part, part: Part,
rng: &mut R,
) -> Result<PartOutcome, Error> { ) -> Result<PartOutcome, Error> {
let sender_idx = self.node_index(sender_id).ok_or(Error::UnknownSender)?; let sender_idx = self.node_index(sender_id).ok_or(Error::UnknownSender)?;
let row = match self.handle_part_or_fault(sender_idx, part) { 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 crate::crypto::{self, Ciphertext, DecryptionShare};
use failure::Fail; use failure::Fail;
use rand::Rng;
use rand_derive::Rand; use rand_derive::Rand;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -74,11 +75,16 @@ impl<N: NodeIdT> DistAlgorithm for ThresholdDecrypt<N> {
type Message = Message; type Message = Message;
type Error = Error; 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() 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) 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 crate::crypto::{self, hash_g2, Signature, SignatureShare, G2};
use failure::Fail; use failure::Fail;
use log::debug; use log::debug;
use rand::Rng;
use rand_derive::Rand; use rand_derive::Rand;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -82,12 +83,17 @@ impl<N: NodeIdT> DistAlgorithm for ThresholdSign<N> {
type Error = Error; type Error = Error;
/// Sends our threshold signature share if not yet sent. /// 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() self.sign()
} }
/// Receives input from a remote node. /// 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) self.handle_message(sender_id, message)
} }

View File

@ -6,6 +6,7 @@ use std::hash::Hash;
use std::iter::once; use std::iter::once;
use failure::Fail; use failure::Fail;
use rand::Rng;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use crate::fault_log::{Fault, FaultLog}; use crate::fault_log::{Fault, FaultLog};
@ -249,6 +250,9 @@ where
} }
/// A distributed algorithm that defines a message flow. /// 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 { pub trait DistAlgorithm: Send + Sync {
/// Unique node identifier. /// Unique node identifier.
type NodeId: NodeIdT; type NodeId: NodeIdT;
@ -263,15 +267,20 @@ pub trait DistAlgorithm: Send + Sync {
type Error: Fail; type Error: Fail;
/// Handles an input provided by the user, and returns /// 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 where
Self: Sized; Self: Sized;
/// Handles a message received from node `sender_id`. /// Handles a message received from node `sender_id`.
fn handle_message( fn handle_message<R: Rng>(
&mut self, &mut self,
sender_id: &Self::NodeId, sender_id: &Self::NodeId,
message: Self::Message, message: Self::Message,
rng: &mut R,
) -> Result<DaStep<Self>, Self::Error> ) -> Result<DaStep<Self>, Self::Error>
where where
Self: Sized; Self: Sized;

View File

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

View File

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

View File

@ -33,8 +33,8 @@
//! some cases be upgraded to actual references, if the underlying node is faulty (see //! some cases be upgraded to actual references, if the underlying node is faulty (see
//! `NodeHandle::node()` and `NodeHandle::node_mut()`). //! `NodeHandle::node()` and `NodeHandle::node_mut()`).
use std::cmp;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::{cmp, fmt};
use rand::Rng; 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. /// Allows querying public information of the network or getting immutable handles to any node.
#[derive(Debug)] #[derive(Debug)]
pub struct NetHandle<'a, D: 'a>(&'a VirtualNet<D>) pub struct NetHandle<'a, D: 'a, A>(&'a VirtualNet<D, A>)
where
D: DistAlgorithm;
impl<'a, D: 'a> NetHandle<'a, D>
where where
D: DistAlgorithm, 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. /// Returns a node handle iterator over all nodes in the network.
#[inline] #[inline]
@ -106,18 +112,22 @@ pub enum QueuePosition {
/// Allows reordering of messages, injecting new ones into the network queue and getting mutable /// Allows reordering of messages, injecting new ones into the network queue and getting mutable
/// handles to nodes. /// handles to nodes.
#[derive(Debug)] #[derive(Debug)]
pub struct NetMutHandle<'a, D: 'a>(&'a mut VirtualNet<D>) pub struct NetMutHandle<'a, D: 'a, A>(&'a mut VirtualNet<D, A>)
where
D: DistAlgorithm;
impl<'a, D> NetMutHandle<'a, D>
where where
D: DistAlgorithm, 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::NodeId: Clone,
D::Message: Clone, D::Message: Clone,
D::Output: 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) NetMutHandle(net)
} }
@ -143,8 +153,12 @@ where
} }
/// Normally dispatch a message /// Normally dispatch a message
pub fn dispatch_message(&mut self, msg: NetMessage<D>) -> Result<DaStep<D>, CrankError<D>> { pub fn dispatch_message<R: Rng>(
self.0.dispatch_message(msg) &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. /// Injects a message into the network.
@ -211,12 +225,15 @@ where
} }
// Downgrade-conversion. // 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 where
D: DistAlgorithm, D: DistAlgorithm,
A: Adversary<D>,
D::Message: Clone,
D::Output: Clone,
{ {
#[inline] #[inline]
fn from(n: NetMutHandle<D>) -> NetHandle<D> { fn from(n: NetMutHandle<D, A>) -> NetHandle<D, A> {
NetHandle(n.0) NetHandle(n.0)
} }
} }
@ -305,6 +322,7 @@ where
/// Network adversary. /// Network adversary.
pub trait Adversary<D> pub trait Adversary<D>
where where
Self: Sized,
D: DistAlgorithm, D: DistAlgorithm,
D::Message: Clone, D::Message: Clone,
D::Output: Clone, D::Output: Clone,
@ -316,7 +334,7 @@ where
/// ///
/// The default implementation does not alter the passed network in any way. /// The default implementation does not alter the passed network in any way.
#[inline] #[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. /// 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 /// `VirtualNet::dispatch_message`, which results in the message being processed as if the node
/// was not faulty. /// was not faulty.
#[inline] #[inline]
fn tamper( fn tamper<R: Rng>(
&mut self, &mut self,
mut net: NetMutHandle<D>, mut net: NetMutHandle<D, Self>,
msg: NetMessage<D>, msg: NetMessage<D>,
rng: &mut R,
) -> Result<DaStep<D>, CrankError<D>> { ) -> Result<DaStep<D>, CrankError<D>> {
net.dispatch_message(msg) net.dispatch_message(msg, rng)
} }
} }
@ -388,7 +407,7 @@ where
D::Output: Clone, D::Output: Clone,
{ {
#[inline] #[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. // Message are sorted by NodeID on each step.
net.sort_messages_by(|a, b| a.to.cmp(&b.to)) 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 /// 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 /// 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. /// random, which allows to test randomized message delivery.
pub struct ReorderingAdversary { #[derive(Copy, Clone, Debug, Default)]
/// Random number generator to reorder messages. pub struct ReorderingAdversary {}
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()
}
}
impl ReorderingAdversary { impl ReorderingAdversary {
#[inline] pub fn new() -> Self {
pub fn new<R>(rng: R) -> Self ReorderingAdversary {}
where
R: 'static + Rng,
{
ReorderingAdversary { rng: Box::new(rng) }
} }
} }
@ -429,10 +434,10 @@ where
D::Output: Clone, D::Output: Clone,
{ {
#[inline] #[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(); let l = net.0.messages_len();
if l > 0 { 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 rand::{Rand, Rng};
use hbbft::dynamic_honey_badger::Batch; use hbbft::dynamic_honey_badger::Batch;
use hbbft::util::SubRng;
use hbbft::{self, Contribution, DaStep, DistAlgorithm, Fault, NetworkInfo, NodeIdT, Step}; use hbbft::{self, Contribution, DaStep, DistAlgorithm, Fault, NetworkInfo, NodeIdT, Step};
use crate::try_some; use crate::try_some;
@ -284,6 +283,7 @@ where
/// New network node construction information. /// New network node construction information.
/// ///
/// Helper structure passed to node constructors when building virtual networks. /// Helper structure passed to node constructors when building virtual networks.
#[derive(Debug)]
pub struct NewNodeInfo<D> pub struct NewNodeInfo<D>
where where
D: DistAlgorithm, D: DistAlgorithm,
@ -294,28 +294,6 @@ where
pub netinfo: NetworkInfo<D::NodeId>, pub netinfo: NetworkInfo<D::NodeId>,
/// Whether or not the node is marked faulty. /// Whether or not the node is marked faulty.
pub faulty: bool, 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. /// Virtual network builder.
@ -325,7 +303,7 @@ where
/// ///
/// Note that, in addition to the constructor `new`, either `using` or `using_step` must be called, /// Note that, in addition to the constructor `new`, either `using` or `using_step` must be called,
/// otherwise the construction will fail and panic. /// otherwise the construction will fail and panic.
pub struct NetBuilder<D, I> pub struct NetBuilder<D, I, A>
where where
D: DistAlgorithm, D: DistAlgorithm,
{ {
@ -336,7 +314,7 @@ where
/// Dist-algorithm constructor function. /// Dist-algorithm constructor function.
cons: Option<Box<Fn(NewNodeInfo<D>) -> (D, DaStep<D>)>>, cons: Option<Box<Fn(NewNodeInfo<D>) -> (D, DaStep<D>)>>,
/// Network adversary. /// Network adversary.
adversary: Option<Box<dyn Adversary<D>>>, adversary: Option<A>,
/// Trace-enabling flag. `None` means use environment. /// Trace-enabling flag. `None` means use environment.
trace: Option<bool>, trace: Option<bool>,
/// Optional crank limit. /// Optional crank limit.
@ -348,36 +326,35 @@ where
/// Property to cause an error if a `Fault` is output from a correct node. By default, /// Property to cause an error if a `Fault` is output from a correct node. By default,
/// encountering a fault leads to an error. /// encountering a fault leads to an error.
error_on_fault: bool, 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 where
D: DistAlgorithm, D: DistAlgorithm,
A: fmt::Debug,
{ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("NetBuilder") f.debug_struct("NetBuilder")
.field("node_ids", &()) .field("node_ids", &())
.field("num_faulty", &self.num_faulty) .field("num_faulty", &self.num_faulty)
.field("cons", &self.cons.is_some()) .field("cons", &self.cons.is_some())
.field("adversary", &self.cons.is_some()) .field("adversary", &self.adversary)
.field("trace", &self.trace) .field("trace", &self.trace)
.field("crank_limit", &self.crank_limit) .field("crank_limit", &self.crank_limit)
.field("message_limit", &self.message_limit) .field("message_limit", &self.message_limit)
.field("time_limit", &self.time_limit) .field("time_limit", &self.time_limit)
.field("error_on_fault", &self.error_on_fault) .field("error_on_fault", &self.error_on_fault)
.field("rng", &"<RNG>")
.finish() .finish()
} }
} }
impl<D, I> NetBuilder<D, I> impl<D, I, A> NetBuilder<D, I, A>
where where
D: DistAlgorithm, D: DistAlgorithm,
D::Message: Clone, D::Message: Clone,
D::Output: Clone, D::Output: Clone,
I: IntoIterator<Item = D::NodeId>, I: IntoIterator<Item = D::NodeId>,
A: Adversary<D>,
{ {
/// Construct a new network builder. /// Construct a new network builder.
/// ///
@ -399,7 +376,6 @@ where
message_limit: None, message_limit: None,
time_limit: DEFAULT_TIME_LIMIT, time_limit: DEFAULT_TIME_LIMIT,
error_on_fault: true, error_on_fault: true,
rng: None,
} }
} }
@ -407,11 +383,8 @@ where
/// ///
/// If not set, the virtual network is constructed with a `NullAdversary`. /// If not set, the virtual network is constructed with a `NullAdversary`.
#[inline] #[inline]
pub fn adversary<A>(mut self, adversary: A) -> Self pub fn adversary(mut self, adversary: A) -> Self {
where self.adversary = Some(adversary);
A: Adversary<D> + 'static,
{
self.adversary = Some(Box::new(adversary));
self self
} }
@ -455,20 +428,6 @@ where
self 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. /// Time limit.
/// ///
/// Sets the time limit; `crank` will fail if called after this much time as elapsed since /// 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. /// If the total number of nodes is not `> 3 * num_faulty`, construction will panic.
#[inline] #[inline]
pub fn build(self) -> Result<(VirtualNet<D>, Vec<(D::NodeId, DaStep<D>)>), CrankError<D>> { pub fn build<R: Rng>(
let rng: Box<dyn Rng> = self.rng.unwrap_or_else(|| Box::new(rand::thread_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: // The time limit can be overriden through environment variables:
let override_time_limit = env::var("HBBFT_NO_TIME_LIMIT") 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 // We fail early, to avoid tricking the user into thinking that they have set the time
@ -587,20 +547,28 @@ where
} }
} }
/// Virtual network instance. /// A virtual network
pub struct VirtualNet<D> ///
/// 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 where
D: DistAlgorithm, D: DistAlgorithm,
A: Adversary<D>,
D::Message: Clone,
D::Output: Clone,
{ {
/// Maps node IDs to actual node instances. /// Maps node IDs to actual node instances.
nodes: NodeMap<D>, nodes: NodeMap<D>,
/// A collection of all network messages queued up for delivery. /// A collection of all network messages queued up for delivery.
messages: collections::VecDeque<NetMessage<D>>, messages: collections::VecDeque<NetMessage<D>>,
/// An Adversary that controls the network delivery schedule and all faulty nodes. /// An optional `Adversary` that controls the network delivery schedule and all faulty nodes.
/// Always present (initialized to `NullAdversary` by default), but an `Option` to be swappable adversary: Option<A>,
/// 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>>>,
/// Trace output; if active, writes out a log of all messages. /// Trace output; if active, writes out a log of all messages.
trace: Option<io::BufWriter<fs::File>>, trace: Option<io::BufWriter<fs::File>>,
/// The number of times the network has been cranked. /// The number of times the network has been cranked.
@ -621,36 +589,12 @@ where
error_on_fault: bool, error_on_fault: bool,
} }
impl<D> fmt::Debug for VirtualNet<D> impl<D, A> VirtualNet<D, A>
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>
where where
D: DistAlgorithm, D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>,
{ {
/// Returns an iterator over *all* nodes in the network. /// Returns an iterator over *all* nodes in the network.
#[inline] #[inline]
@ -753,11 +697,12 @@ where
} }
} }
impl<D> VirtualNet<D> impl<D, A> VirtualNet<D, A>
where where
D: DistAlgorithm, D: DistAlgorithm,
D::Message: Clone, D::Message: Clone,
D::Output: Clone, D::Output: Clone,
A: Adversary<D>,
{ {
/// Create new virtual network with step constructor. /// Create new virtual network with step constructor.
/// ///
@ -808,7 +753,6 @@ where
id: id.clone(), id: id.clone(),
netinfo, netinfo,
faulty: is_faulty, faulty: is_faulty,
rng: rng.sub_rng(),
}); });
steps.insert(id.clone(), step); steps.insert(id.clone(), step);
(id, Node::new(algorithm, is_faulty)) (id, Node::new(algorithm, is_faulty))
@ -832,7 +776,7 @@ where
VirtualNet { VirtualNet {
nodes, nodes,
messages, messages,
adversary: Some(Box::new(adversary::NullAdversary::new())), adversary: None,
trace: None, trace: None,
crank_count: 0, crank_count: 0,
crank_limit: None, crank_limit: None,
@ -850,7 +794,11 @@ where
/// ///
/// Retrieves the receiving node for a `msg` and hands over the payload. /// Retrieves the receiving node for a `msg` and hands over the payload.
#[inline] #[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 let node = self
.nodes .nodes
.get_mut(&msg.to) .get_mut(&msg.to)
@ -862,7 +810,7 @@ where
let msg_copy = msg.clone(); let msg_copy = msg.clone();
let step = node let step = node
.algorithm .algorithm
.handle_message(&msg.from, msg.payload) .handle_message(&msg.from, msg.payload, rng)
.map_err(move |err| CrankError::HandleMessage { msg: msg_copy, err })?; .map_err(move |err| CrankError::HandleMessage { msg: msg_copy, err })?;
Ok(step) Ok(step)
@ -877,17 +825,18 @@ where
/// ///
/// Panics if `id` does not name a valid node. /// Panics if `id` does not name a valid node.
#[inline] #[inline]
pub fn send_input( pub fn send_input<R: Rng>(
&mut self, &mut self,
id: D::NodeId, id: D::NodeId,
input: D::Input, input: D::Input,
rng: &mut R,
) -> Result<DaStep<D>, CrankError<D>> { ) -> Result<DaStep<D>, CrankError<D>> {
let step = self let step = self
.nodes .nodes
.get_mut(&id) .get_mut(&id)
.expect("cannot handle input on non-existing node") .expect("cannot handle input on non-existing node")
.algorithm .algorithm
.handle_input(input) .handle_input(input, rng)
.map_err(CrankError::HandleInput)?; .map_err(CrankError::HandleInput)?;
self.message_count = self.message_count.saturating_add(process_step( 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 /// If a successful `Step` was generated, all of its messages are queued on the network and the
/// `Step` is returned. /// `Step` is returned.
#[inline] #[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. // Check limits.
if let Some(limit) = self.crank_limit { if let Some(limit) = self.crank_limit {
if self.crank_count >= limit { if self.crank_count >= limit {
@ -935,7 +887,7 @@ where
let mut adv = self.adversary.take(); let mut adv = self.adversary.take();
if let Some(ref mut adversary) = adv { if let Some(ref mut adversary) = adv {
// If an adversary was set, we let it affect the network now. // 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; self.adversary = adv;
@ -966,7 +918,7 @@ where
let mut adv = self.adversary.take(); let mut adv = self.adversary.take();
let opt_tamper_result = adv.as_mut().map(|adversary| { let opt_tamper_result = adv.as_mut().map(|adversary| {
// If an adversary was set, we let it affect the network now. // 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; self.adversary = adv;
@ -977,7 +929,7 @@ where
) )
} else { } else {
// A correct node simply handles the message. // 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 // 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 /// Shortcut for cranking the network, expecting both progress to be made as well as processing
/// to proceed. /// to proceed.
pub fn crank_expect(&mut self) -> (D::NodeId, DaStep<D>) { pub fn crank_expect<R: Rng>(&mut self, rng: &mut R) -> (D::NodeId, DaStep<D>) {
self.crank() self.crank(rng)
.expect("crank: network queue empty") .expect("crank: network queue empty")
.expect("crank: node failed to process step") .expect("crank: node failed to process step")
} }
} }
impl<D> VirtualNet<D> impl<D, A> VirtualNet<D, A>
where where
D: DistAlgorithm, D: DistAlgorithm,
D::Message: Clone, D::Message: Clone,
D::Input: Clone, D::Input: Clone,
D::Output: Clone, D::Output: Clone,
A: Adversary<D>,
{ {
/// Send input to all nodes. /// Send input to all nodes.
/// ///
@ -1026,17 +979,11 @@ where
/// ///
/// If an error occurs, the first error is returned and broadcasting aborted. /// If an error occurs, the first error is returned and broadcasting aborted.
#[inline] #[inline]
pub fn broadcast_input<'a>( pub fn broadcast_input<'a, R: Rng>(
&'a mut self, &'a mut self,
input: &'a D::Input, input: &'a D::Input,
rng: &mut R,
) -> Result<Vec<(D::NodeId, DaStep<D>)>, CrankError<D>> { ) -> 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 let steps: Vec<_> = self
.nodes .nodes
.values_mut() .values_mut()
@ -1044,7 +991,7 @@ where
Ok(( Ok((
node.id().clone(), node.id().clone(),
node.algorithm node.algorithm
.handle_input(input.clone()) .handle_input(input.clone(), rng)
.map_err(CrankError::HandleInputAll)?, .map_err(CrankError::HandleInputAll)?,
)) ))
}) })
@ -1066,9 +1013,11 @@ where
} }
} }
impl<C, D, N> VirtualNet<D> impl<C, D, N, A> VirtualNet<D, A>
where where
D: DistAlgorithm<Output = Batch<C, N>>, D: DistAlgorithm<Output = Batch<C, N>>,
D::Message: Clone,
A: Adversary<D>,
C: Contribution + Clone, C: Contribution + Clone,
N: NodeIdT, 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 where
D: DistAlgorithm, D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>,
{ {
type Output = Node<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 where
D: DistAlgorithm, D: DistAlgorithm,
D::Message: Clone,
D::Output: Clone,
A: Adversary<D>,
{ {
#[inline] #[inline]
fn index_mut(&mut self, index: D::NodeId) -> &mut Self::Output { fn index_mut(&mut self, index: D::NodeId) -> &mut Self::Output {
self.get_mut(index).expect("indexed node not found") 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::dynamic_honey_badger::{Change, ChangeState, DynamicHoneyBadger, Input};
use hbbft::sender_queue::SenderQueue; use hbbft::sender_queue::SenderQueue;
use proptest::{prelude::ProptestConfig, prop_compose, proptest, proptest_helper}; use proptest::{prelude::ProptestConfig, prop_compose, proptest, proptest_helper};
use rand::{Rng, SeedableRng}; use rand::SeedableRng;
/// Choose a node's contribution for an epoch. /// 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) .message_limit(15_000 * cfg.dimension.size() as usize)
// 30 secs per node. // 30 secs per node.
.time_limit(time::Duration::from_secs(30 * cfg.dimension.size() as u64)) .time_limit(time::Duration::from_secs(30 * cfg.dimension.size() as u64))
// Ensure runs are reproducible. .adversary(ReorderingAdversary::new())
.rng(rng.gen::<TestRng>())
.adversary(ReorderingAdversary::new(rng.gen::<TestRng>()))
.using_step(move |node: NewNodeInfo<SenderQueue<_>>| { .using_step(move |node: NewNodeInfo<SenderQueue<_>>| {
let id = node.id; let id = node.id;
println!("Constructing new dynamic honey badger node #{}", id); println!("Constructing new dynamic honey badger node #{}", id);
let dhb = DynamicHoneyBadger::builder() let dhb = DynamicHoneyBadger::builder().build(node.netinfo.clone());
.rng(node.rng)
.build(node.netinfo.clone());
SenderQueue::builder( SenderQueue::builder(
dhb, dhb,
node.netinfo.all_ids().filter(|&&them| them != id).cloned(), node.netinfo.all_ids().filter(|&&them| them != id).cloned(),
) )
.build(node.id) .build(node.id)
}) })
.build() .build(&mut rng)
.expect("could not construct test network"); .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. // 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. // The step will have its messages added to the queue automatically, we ignore the output.
let _ = net let _ = net
.send_input(*id, Input::User(proposal)) .send_input(*id, Input::User(proposal), &mut rng)
.expect("could not send initial transaction"); .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 pub_keys_add = netinfo.public_key_map().clone();
let mut pub_keys_rm = pub_keys_add.clone(); let mut pub_keys_rm = pub_keys_add.clone();
pub_keys_rm.remove(&pivot_node_id); pub_keys_rm.remove(&pivot_node_id);
net.broadcast_input(&Input::Change(Change::NodeChange(pub_keys_rm.clone()))) net.broadcast_input(
.expect("broadcasting failed"); &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. // We are tracking (correct) nodes' state through the process by ticking them off individually.
let mut awaiting_removal: collections::BTreeSet<_> = let mut awaiting_removal: collections::BTreeSet<_> =
@ -164,7 +163,7 @@ fn do_drop_and_readd(cfg: TestConfig) {
// Run the network: // Run the network:
loop { loop {
let (node_id, step) = net.crank_expect(); let (node_id, step) = net.crank_expect(&mut rng);
if !net[node_id].is_faulty() { if !net[node_id].is_faulty() {
for batch in &step.output { for batch in &step.output {
// Check that correct nodes don't output different batches for the same epoch. // 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( .send_input(
node_id, node_id,
Input::Change(Change::NodeChange(pub_keys_add.clone())), Input::Change(Change::NodeChange(pub_keys_add.clone())),
&mut rng,
) )
.expect("failed to send `Add` input"); .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); choose_contribution(&mut rng, queue, cfg.batch_size, cfg.contribution_size);
let _ = net 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"); .expect("could not send follow-up transaction");
} }
} }

View File

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

View File

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

View File

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

View File

@ -18,8 +18,11 @@ fn test_threshold_sign<A>(mut network: TestNetwork<A, ThresholdSign<NodeId>>) ->
where where
A: Adversary<ThresholdSign<NodeId>>, A: Adversary<ThresholdSign<NodeId>>,
{ {
let mut rng = rand::thread_rng();
network.input_all(()); 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. // Handle messages until all good nodes have terminated.
while !network.nodes.values().all(TestNode::terminated) { while !network.nodes.values().all(TestNode::terminated) {