From eafa77d5fcbdaf549e09f101d618923d408b3468 Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Fri, 14 Dec 2018 13:51:09 +0100 Subject: [PATCH] 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. --- examples/network/node.rs | 4 +- examples/simulation.rs | 60 ++--- src/binary_agreement/binary_agreement.rs | 12 +- src/broadcast/broadcast.rs | 10 +- src/broadcast/mod.rs | 4 +- src/dynamic_honey_badger/builder.rs | 33 ++- .../dynamic_honey_badger.rs | 85 ++++--- src/dynamic_honey_badger/votes.rs | 2 +- src/honey_badger/builder.rs | 13 +- src/honey_badger/epoch_state.rs | 4 +- src/honey_badger/honey_badger.rs | 20 +- src/queueing_honey_badger/mod.rs | 84 ++++--- src/sender_queue/dynamic_honey_badger.rs | 6 +- src/sender_queue/mod.rs | 32 ++- src/sender_queue/queueing_honey_badger.rs | 23 +- src/subset/subset.rs | 11 +- src/sync_key_gen.rs | 19 +- src/threshold_decrypt.rs | 10 +- src/threshold_sign.rs | 10 +- src/traits.rs | 13 +- src/util.rs | 25 --- tests/binary_agreement.rs | 16 +- tests/binary_agreement_mitm.rs | 52 +++-- tests/broadcast.rs | 3 +- tests/net/adversary.rs | 87 ++++---- tests/net/mod.rs | 210 ++++++------------ tests/net_dynamic_hb.rs | 26 +-- tests/network/mod.rs | 12 +- tests/queueing_honey_badger.rs | 6 +- tests/sync_key_gen.rs | 4 +- tests/threshold_sign.rs | 5 +- 31 files changed, 462 insertions(+), 439 deletions(-) diff --git a/examples/network/node.rs b/examples/network/node.rs index 33e4996..2915cd4 100644 --- a/examples/network/node.rs +++ b/examples/network/node.rs @@ -108,6 +108,8 @@ impl + PartialEq + Send + Sync + From> + let tx_from_algo = messaging.tx_from_algo(); let stop_tx = messaging.stop_tx(); + let mut rng = rand::OsRng::new().unwrap(); + // All spawned threads will have exited by the end of the scope. crossbeam::scope(|scope| { // Start the centralised message delivery system. @@ -124,7 +126,7 @@ impl + PartialEq + Send + Sync + From> + if let Some(v) = value { // FIXME: Use the output. let step = broadcast - .handle_input(v.clone().into()) + .handle_input(v.clone().into(), &mut rng) .expect("propose value"); for msg in step.messages { tx_from_algo.send(msg).expect("send from algo"); diff --git a/examples/simulation.rs b/examples/simulation.rs index 486505f..874ef29 100644 --- a/examples/simulation.rs +++ b/examples/simulation.rs @@ -5,7 +5,7 @@ use std::{cmp, u64}; use colored::*; use docopt::Docopt; use itertools::Itertools; -use rand::{Isaac64Rng, Rng}; +use rand::Rng; use rand_derive::Rand; use serde::de::DeserializeOwned; use serde::Serialize; @@ -56,14 +56,7 @@ struct Args { pub struct NodeId(pub usize); /// A transaction. -#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Clone)] -pub struct Transaction(pub Vec); - -impl Transaction { - fn new(len: usize) -> Transaction { - Transaction(rand::thread_rng().gen_iter().take(len).collect()) - } -} +type Transaction = Vec; /// A serialized message with a sender and the timestamp of arrival. #[derive(Eq, PartialEq, Debug)] @@ -168,7 +161,7 @@ where } /// Handles the first message in the node's queue. - fn handle_message(&mut self) { + fn handle_message(&mut self, rng: &mut R) { let ts_msg = self.in_queue.pop_front().expect("message not found"); self.time = cmp::max(self.time, ts_msg.time); self.message_count += 1; @@ -177,7 +170,7 @@ where let msg = bincode::deserialize::(&ts_msg.message).expect("deserialize"); let step = self .algo - .handle_message(&ts_msg.sender_id, msg) + .handle_message(&ts_msg.sender_id, msg, rng) .expect("handling message"); self.time += start.elapsed() * self.hw_quality.cpu_factor / 100; self.send_output_and_msgs(step) @@ -247,20 +240,21 @@ where D::Message: Serialize + DeserializeOwned + Clone, { /// Creates a new network with `good_num` good nodes, and `dead_num` dead nodes. - pub fn new( + pub fn new( good_num: usize, adv_num: usize, new_algo: F, hw_quality: HwQuality, + rng: &mut R, ) -> TestNetwork where - F: Fn(NetworkInfo) -> (D, DaStep), + F: Fn(NetworkInfo, &mut R) -> (D, DaStep), { let node_ids = (0..(good_num + adv_num)).map(NodeId); - let netinfos = NetworkInfo::generate_map(node_ids, &mut rand::thread_rng()) - .expect("Failed to create `NetworkInfo` map"); + let netinfos = + NetworkInfo::generate_map(node_ids, rng).expect("Failed to create `NetworkInfo` map"); let new_node = |(id, netinfo): (NodeId, NetworkInfo<_>)| { - (id, TestNode::new(new_algo(netinfo), hw_quality)) + (id, TestNode::new(new_algo(netinfo, rng), hw_quality)) }; let mut network = TestNetwork { nodes: netinfos.into_iter().map(new_node).collect(), @@ -299,7 +293,7 @@ where /// Handles a queued message in one of the nodes with the earliest timestamp, if any. Returns /// the recipient's ID. - pub fn step(&mut self) -> Option { + pub fn step(&mut self, rng: &mut R) -> Option { let min_time = self .nodes .values() @@ -311,10 +305,10 @@ where .filter(|(_, node)| node.next_event_time() == Some(min_time)) .map(|(id, _)| *id) .collect(); - let next_id = *rand::thread_rng().choose(&min_ids).unwrap(); + let next_id = *rng.choose(&min_ids).unwrap(); let msgs: Vec<_> = { let node = self.nodes.get_mut(&next_id).unwrap(); - node.handle_message(); + node.handle_message(rng); node.out_queue.drain(..).collect() }; self.dispatch_messages(msgs); @@ -378,14 +372,14 @@ impl EpochInfo { } /// Proposes `num_txs` values and expects nodes to output and order them. -fn simulate_honey_badger(mut network: TestNetwork) { +fn simulate_honey_badger(mut network: TestNetwork, rng: &mut R) { // Handle messages until all nodes have output all transactions. println!( "{}", "Epoch Min/Max Time Txs Msgs/Node Size/Node".bold() ); let mut epochs = Vec::new(); - while let Some(id) = network.step() { + while let Some(id) = network.step(rng) { for &(time, ref batch) in &network.nodes[&id].outputs { let epoch = batch.epoch() as usize; if epochs.len() <= epoch { @@ -406,11 +400,14 @@ fn parse_args() -> Result { fn main() { env_logger::init(); + let mut rng = rand::OsRng::new().expect("Could not initialize OS random number generator."); + let args = parse_args().unwrap_or_else(|e| e.exit()); if args.flag_n <= 3 * args.flag_f { let msg = "Honey Badger only works if less than one third of the nodes are faulty."; println!("{}", msg.red().bold()); } + println!("Simulating Honey Badger with:"); println!("{} nodes, {} faulty", args.flag_n, args.flag_f); println!( @@ -422,11 +419,14 @@ fn main() { args.flag_lag, args.flag_bw, args.flag_cpu ); println!(); + let num_good_nodes = args.flag_n - args.flag_f; + let txs: Vec<_> = (0..args.flag_txs) - .map(|_| Transaction::new(args.flag_tx_size)) + .map(|_| rng.gen_iter().take(args.flag_tx_size).collect()) .collect(); - let new_honey_badger = |netinfo: NetworkInfo| { + + let new_honey_badger = |netinfo: NetworkInfo, rng: &mut rand::OsRng| { let our_id = *netinfo.our_id(); let peer_ids: Vec<_> = netinfo .all_ids() @@ -436,17 +436,25 @@ fn main() { let dhb = DynamicHoneyBadger::builder().build(netinfo); let (qhb, qhb_step) = QueueingHoneyBadger::builder(dhb) .batch_size(args.flag_b) - .build_with_transactions(txs.clone(), rand::thread_rng().gen::()) + .build_with_transactions(txs.clone(), rng) .expect("instantiate QueueingHoneyBadger"); let (sq, mut step) = SenderQueue::builder(qhb, peer_ids.into_iter()).build(our_id); step.extend_with(qhb_step, Message::from); (sq, step) }; + let hw_quality = HwQuality { latency: Duration::from_millis(args.flag_lag), inv_bw: Duration::new(0, 8_000_000 / args.flag_bw), cpu_factor: (10_000f32 / args.flag_cpu) as u32, }; - let network = TestNetwork::new(num_good_nodes, args.flag_f, new_honey_badger, hw_quality); - simulate_honey_badger(network); + + let network = TestNetwork::new( + num_good_nodes, + args.flag_f, + new_honey_badger, + hw_quality, + &mut rng, + ); + simulate_honey_badger(network, &mut rng); } diff --git a/src/binary_agreement/binary_agreement.rs b/src/binary_agreement/binary_agreement.rs index 13772b5..3327c05 100644 --- a/src/binary_agreement/binary_agreement.rs +++ b/src/binary_agreement/binary_agreement.rs @@ -5,6 +5,7 @@ use std::{fmt, result}; use crate::crypto::SignatureShare; use bincode; use log::debug; +use rand::Rng; use super::bool_multimap::BoolMultimap; use super::bool_set::{self, BoolSet}; @@ -177,13 +178,18 @@ impl DistAlgorithm for BinaryAgreement { type Message = Message; type Error = Error; - fn handle_input(&mut self, input: Self::Input) -> Result> { + fn handle_input(&mut self, input: Self::Input, _rng: &mut R) -> Result> { self.propose(input) } /// Receive input from a remote node. - fn handle_message(&mut self, sender_id: &Self::NodeId, msg: Message) -> Result> { - self.handle_message(sender_id, msg) + fn handle_message( + &mut self, + sender_id: &Self::NodeId, + message: Message, + _rng: &mut R, + ) -> Result> { + self.handle_message(sender_id, message) } /// Whether the algorithm has terminated. diff --git a/src/broadcast/broadcast.rs b/src/broadcast/broadcast.rs index ad4797c..a8ce4ae 100644 --- a/src/broadcast/broadcast.rs +++ b/src/broadcast/broadcast.rs @@ -5,6 +5,7 @@ use std::{fmt, result}; use byteorder::{BigEndian, ByteOrder}; use hex_fmt::{HexFmt, HexList}; use log::{debug, warn}; +use rand::Rng; use reed_solomon_erasure as rse; use reed_solomon_erasure::ReedSolomon; @@ -47,11 +48,16 @@ impl DistAlgorithm for Broadcast { type Message = Message; type Error = Error; - fn handle_input(&mut self, input: Self::Input) -> Result> { + fn handle_input(&mut self, input: Self::Input, _rng: &mut R) -> Result> { self.broadcast(input) } - fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result> { + fn handle_message( + &mut self, + sender_id: &Self::NodeId, + message: Message, + _rng: &mut R, + ) -> Result> { self.handle_message(sender_id, message) } diff --git a/src/broadcast/mod.rs b/src/broadcast/mod.rs index f4d0390..82a340d 100644 --- a/src/broadcast/mod.rs +++ b/src/broadcast/mod.rs @@ -92,7 +92,7 @@ //! ``` //! use hbbft::broadcast::{Broadcast, Error, Step}; //! use hbbft::{NetworkInfo, SourcedMessage, Target, TargetedMessage}; -//! use rand::{thread_rng, Rng}; +//! use rand::{OsRng, Rng}; //! use std::collections::{BTreeMap, BTreeSet, VecDeque}; //! use std::iter::once; //! use std::sync::Arc; @@ -102,7 +102,7 @@ //! const NUM_NODES: u64 = 7; //! const PROPOSER_ID: u64 = 3; //! -//! let mut rng = thread_rng(); +//! let mut rng = OsRng::new().expect("Could not initialize OS random number generator."); //! //! // Create a random set of keys for testing. //! let netinfos = NetworkInfo::generate_map(0..NUM_NODES, &mut rng) diff --git a/src/dynamic_honey_badger/builder.rs b/src/dynamic_honey_badger/builder.rs index d5774a9..4d88ca9 100644 --- a/src/dynamic_honey_badger/builder.rs +++ b/src/dynamic_honey_badger/builder.rs @@ -4,12 +4,11 @@ use std::marker::PhantomData; use std::sync::Arc; use crate::crypto::{SecretKey, SecretKeySet}; -use rand::{self, Rand, Rng}; +use rand::Rand; use serde::{de::DeserializeOwned, Serialize}; use super::{DynamicHoneyBadger, EncryptionSchedule, JoinPlan, Result, Step, VoteCounter}; use crate::honey_badger::{HoneyBadger, Params, SubsetHandlingStrategy}; -use crate::util::SubRng; use crate::{Contribution, NetworkInfo, NodeIdT}; /// A Dynamic Honey Badger builder, to configure the parameters and create new instances of @@ -17,9 +16,6 @@ use crate::{Contribution, NetworkInfo, NodeIdT}; pub struct DynamicHoneyBadgerBuilder { /// Start in this era. era: u64, - /// Random number generator passed on to algorithm instance for key generation. Also used to - /// instantiate `HoneyBadger`. - rng: Box, /// Parameters controlling Honey Badger's behavior and performance. params: Params, _phantom: PhantomData<(C, N)>, @@ -32,7 +28,6 @@ where fn default() -> Self { DynamicHoneyBadgerBuilder { era: 0, - rng: Box::new(rand::thread_rng()), params: Params::default(), _phantom: PhantomData, } @@ -62,12 +57,6 @@ where self } - /// Sets the random number generator to be used to instantiate cryptographic structures. - pub fn rng(&mut self, rng: R) -> &mut Self { - self.rng = Box::new(rng); - self - } - /// Sets the strategy to use when handling `Subset` output. pub fn subset_handling_strategy( &mut self, @@ -93,16 +82,16 @@ where pub fn build(&mut self, netinfo: NetworkInfo) -> DynamicHoneyBadger { let DynamicHoneyBadgerBuilder { era, - rng, params, _phantom, } = self; let arc_netinfo = Arc::new(netinfo.clone()); + let honey_badger = HoneyBadger::builder(arc_netinfo.clone()) .session_id(*era) .params(params.clone()) - .rng(rng.sub_rng()) .build(); + DynamicHoneyBadger { netinfo, max_future_epochs: params.max_future_epochs, @@ -111,16 +100,19 @@ where key_gen_msg_buffer: Vec::new(), honey_badger, key_gen_state: None, - rng: Box::new(rng.sub_rng()), } } /// Creates a new `DynamicHoneyBadger` configured to start a new network as a single validator. - pub fn build_first_node(&mut self, our_id: N) -> Result> { - let sk_set = SecretKeySet::random(0, &mut self.rng); + pub fn build_first_node( + &mut self, + our_id: N, + rng: &mut R, + ) -> Result> { + let sk_set = SecretKeySet::random(0, rng); let pk_set = sk_set.public_keys(); let sks = sk_set.secret_key_share(0); - let sk: SecretKey = self.rng.gen(); + let sk: SecretKey = rng.gen(); let pub_keys = once((our_id.clone(), sk.public_key())).collect(); let netinfo = NetworkInfo::new(our_id, sks, pk_set, sk, pub_keys); Ok(self.build(netinfo)) @@ -131,12 +123,13 @@ where /// /// **Deprecated**: Please use `DynamicHoneyBadger::new_joining` instead. #[deprecated] - pub fn build_joining( + pub fn build_joining( &mut self, our_id: N, secret_key: SecretKey, join_plan: JoinPlan, + rng: &mut R, ) -> Result<(DynamicHoneyBadger, Step)> { - DynamicHoneyBadger::new_joining(our_id, secret_key, join_plan, self.rng.sub_rng()) + DynamicHoneyBadger::new_joining(our_id, secret_key, join_plan, rng) } } diff --git a/src/dynamic_honey_badger/dynamic_honey_badger.rs b/src/dynamic_honey_badger/dynamic_honey_badger.rs index 425d0d8..1baf79e 100644 --- a/src/dynamic_honey_badger/dynamic_honey_badger.rs +++ b/src/dynamic_honey_badger/dynamic_honey_badger.rs @@ -6,7 +6,7 @@ use crate::crypto::{PublicKey, SecretKey, Signature}; use bincode; use derivative::Derivative; use log::debug; -use rand::{self, Rand, Rng}; +use rand::{Rand, Rng}; use serde::{de::DeserializeOwned, Serialize}; use super::votes::{SignedVote, VoteCounter}; @@ -19,7 +19,7 @@ use crate::fault_log::{Fault, FaultKind, FaultLog}; use crate::honey_badger::{self, HoneyBadger, Message as HbMessage}; use crate::sync_key_gen::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen}; -use crate::util::{self, SubRng}; +use crate::util; use crate::{Contribution, DistAlgorithm, Epoched, NetworkInfo, NodeIdT, Target}; /// A Honey Badger instance that can handle adding and removing nodes. @@ -40,10 +40,6 @@ pub struct DynamicHoneyBadger { pub(super) honey_badger: HoneyBadger, N>, /// The current key generation process, and the change it applies to. pub(super) key_gen_state: Option>, - /// 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, } impl DistAlgorithm for DynamicHoneyBadger @@ -57,17 +53,22 @@ where type Message = Message; type Error = Error; - fn handle_input(&mut self, input: Self::Input) -> Result> { + fn handle_input(&mut self, input: Self::Input, rng: &mut R) -> Result> { // User contributions are forwarded to `HoneyBadger` right away. Votes are signed and // broadcast. match input { - Input::User(contrib) => self.propose(contrib), + Input::User(contrib) => self.propose(contrib, rng), Input::Change(change) => self.vote_for(change), } } - fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result> { - self.handle_message(sender_id, message) + fn handle_message( + &mut self, + sender_id: &Self::NodeId, + msg: Self::Message, + rng: &mut R, + ) -> Result> { + self.handle_message(sender_id, msg, rng) } fn terminated(&self) -> bool { @@ -90,11 +91,11 @@ where } /// Creates a new `DynamicHoneyBadger` ready to join the network specified in the `JoinPlan`. - pub fn new_joining( + pub fn new_joining( our_id: N, secret_key: SecretKey, join_plan: JoinPlan, - mut rng: R, + rng: &mut R, ) -> Result<(Self, Step)> { let netinfo = NetworkInfo::new( our_id, @@ -108,7 +109,6 @@ where let honey_badger = HoneyBadger::builder(arc_netinfo.clone()) .session_id(join_plan.era) .params(join_plan.params) - .rng(rng.sub_rng()) .build(); let mut dhb = DynamicHoneyBadger { netinfo, @@ -118,11 +118,10 @@ where key_gen_msg_buffer: Vec::new(), honey_badger, key_gen_state: None, - rng: Box::new(rng), }; let step = match join_plan.change { ChangeState::InProgress(ref change) => match change { - Change::NodeChange(change) => dhb.update_key_gen(join_plan.era, change)?, + Change::NodeChange(change) => dhb.update_key_gen(join_plan.era, change, rng)?, _ => Step::default(), }, ChangeState::None | ChangeState::Complete(..) => Step::default(), @@ -141,22 +140,25 @@ where /// /// If we are the only validator, this will immediately output a batch, containing our /// proposal. - pub fn propose(&mut self, contrib: C) -> Result> { + pub fn propose(&mut self, contrib: C, rng: &mut R) -> Result> { let key_gen_messages = self .key_gen_msg_buffer .iter() .filter(|kg_msg| kg_msg.era() == self.era) .cloned() .collect(); + + let contrib = InternalContrib { + contrib, + key_gen_messages, + votes: self.vote_counter.pending_votes().cloned().collect(), + }; + let step = self .honey_badger - .propose(&InternalContrib { - contrib, - key_gen_messages, - votes: self.vote_counter.pending_votes().cloned().collect(), - }) + .propose(&contrib, rng) .map_err(Error::ProposeHoneyBadger)?; - self.process_output(step) + self.process_output(step, rng) } /// Casts a vote to change the set of validators or parameters. @@ -195,11 +197,16 @@ where /// Handles a message received from `sender_id`. /// /// This must be called with every message we receive from another node. - pub fn handle_message(&mut self, sender_id: &N, message: Message) -> Result> { + pub fn handle_message( + &mut self, + sender_id: &N, + message: Message, + rng: &mut R, + ) -> Result> { if message.era() == self.era { match message { Message::HoneyBadger(_, hb_msg) => { - self.handle_honey_badger_message(sender_id, hb_msg) + self.handle_honey_badger_message(sender_id, hb_msg, rng) } Message::KeyGen(_, kg_msg, sig) => self .handle_key_gen_message(sender_id, kg_msg, *sig) @@ -249,10 +256,11 @@ where } /// Handles a message for the `HoneyBadger` instance. - fn handle_honey_badger_message( + fn handle_honey_badger_message( &mut self, sender_id: &N, message: HbMessage, + rng: &mut R, ) -> Result> { if !self.netinfo.is_node_validator(sender_id) { return Err(Error::UnknownSender); @@ -262,7 +270,7 @@ where .honey_badger .handle_message(sender_id, message) .map_err(Error::HandleHoneyBadgerMessage)?; - self.process_output(step) + self.process_output(step, rng) } /// Handles a vote or key generation message and tries to commit it as a transaction. These @@ -297,9 +305,10 @@ where } /// Processes all pending batches output by Honey Badger. - fn process_output( + fn process_output( &mut self, hb_step: honey_badger::Step, N>, + rng: &mut R, ) -> Result> { let mut step: Step = Step::default(); let output = step.extend_with(hb_step, |hb_msg| Message::HoneyBadger(self.era, hb_msg)); @@ -329,7 +338,7 @@ where step.fault_log.append(id.clone(), fault_kind); } else { step.extend(match kg_msg { - KeyGenMessage::Part(part) => self.handle_part(&s_id, part)?, + KeyGenMessage::Part(part) => self.handle_part(&s_id, part, rng)?, KeyGenMessage::Ack(ack) => self.handle_ack(&s_id, ack)?, }); } @@ -346,7 +355,7 @@ where // If there is a new change, restart DKG. Inform the user about the current change. match change { Change::NodeChange(ref pub_keys) => { - step.extend(self.update_key_gen(batch_epoch + 1, pub_keys)?); + step.extend(self.update_key_gen(batch_epoch + 1, pub_keys, rng)?); } Change::EncryptionSchedule(schedule) => { self.update_encryption_schedule(batch_epoch + 1, schedule); @@ -380,10 +389,11 @@ where /// If the winner of the vote has changed, restarts Key Generation for the set of nodes implied /// by the current change. - pub(super) fn update_key_gen( + pub(super) fn update_key_gen( &mut self, era: u64, pub_keys: &BTreeMap, + rng: &mut R, ) -> Result> { if self.key_gen_state.as_ref().map(KeyGenState::public_keys) == Some(pub_keys) { return Ok(Step::default()); // The change is the same as before. Continue DKG as is. @@ -394,9 +404,8 @@ where let threshold = util::max_faulty(pub_keys.len()); let sk = self.netinfo.secret_key().clone(); let our_id = self.our_id().clone(); - let (key_gen, part) = - SyncKeyGen::new(&mut self.rng, our_id, sk, pub_keys.clone(), threshold) - .map_err(Error::SyncKeyGen)?; + let (key_gen, part) = SyncKeyGen::new(our_id, sk, pub_keys.clone(), threshold, rng) + .map_err(Error::SyncKeyGen)?; self.key_gen_state = Some(KeyGenState::new(key_gen)); if let Some(part) = part { self.send_transaction(KeyGenMessage::Part(part)) @@ -413,16 +422,20 @@ where self.vote_counter = VoteCounter::new(netinfo.clone(), era); self.honey_badger = HoneyBadger::builder(netinfo) .session_id(era) - .rng(self.rng.sub_rng()) .params(params) .build(); } /// Handles a `Part` message that was output by Honey Badger. - fn handle_part(&mut self, sender_id: &N, part: Part) -> Result> { + fn handle_part( + &mut self, + sender_id: &N, + part: Part, + rng: &mut R, + ) -> Result> { let outcome = if let Some(kgs) = self.key_gen_state.as_mut() { kgs.key_gen - .handle_part(&mut self.rng, &sender_id, part) + .handle_part(&sender_id, part, rng) .map_err(Error::SyncKeyGen)? } else { // No key generation ongoing. diff --git a/src/dynamic_honey_badger/votes.rs b/src/dynamic_honey_badger/votes.rs index 3723955..e3444d4 100644 --- a/src/dynamic_honey_badger/votes.rs +++ b/src/dynamic_honey_badger/votes.rs @@ -201,7 +201,7 @@ mod tests { /// the vote by node `i` for making `j` the only validator. Each node signed this for nodes /// `0`, `1`, ... in order. fn setup(node_num: usize, era: u64) -> (Vec>, Vec>>) { - let mut rng = rand::thread_rng(); + let mut rng = rand::OsRng::new().expect("could not initialize OsRng"); // Create keys for threshold cryptography. let netinfos = NetworkInfo::generate_map(0..node_num, &mut rng) .expect("Failed to generate `NetworkInfo` map"); diff --git a/src/honey_badger/builder.rs b/src/honey_badger/builder.rs index b57c6e9..1611dd9 100644 --- a/src/honey_badger/builder.rs +++ b/src/honey_badger/builder.rs @@ -2,11 +2,10 @@ use std::collections::BTreeMap; use std::marker::PhantomData; use std::sync::Arc; -use rand::{self, Rand, Rng}; +use rand::Rand; use serde::{de::DeserializeOwned, Serialize}; use super::{EncryptionSchedule, HoneyBadger, Params, SubsetHandlingStrategy}; -use crate::util::SubRng; use crate::{Contribution, NetworkInfo, NodeIdT}; /// A Honey Badger builder, to configure the parameters and create new instances of `HoneyBadger`. @@ -18,8 +17,6 @@ pub struct HoneyBadgerBuilder { session_id: u64, /// Start in this epoch. epoch: u64, - /// Random number generator passed on to algorithm instance for signing and encrypting. - rng: Box, /// Parameters controlling Honey Badger's behavior and performance. params: Params, _phantom: PhantomData, @@ -37,18 +34,11 @@ where netinfo, session_id: 0, epoch: 0, - rng: Box::new(rand::thread_rng()), params: Params::default(), _phantom: PhantomData, } } - /// Sets the random number generator for the public key cryptography. - pub fn rng(&mut self, rng: R) -> &mut Self { - self.rng = Box::new(rng); - self - } - /// Sets the session identifier. /// /// Different session IDs foil replay attacks in two instances with the same epoch numbers and @@ -100,7 +90,6 @@ where has_input: false, epochs: BTreeMap::new(), params: self.params.clone(), - rng: Box::new(self.rng.sub_rng()), } } } diff --git a/src/honey_badger/epoch_state.rs b/src/honey_badger/epoch_state.rs index b17fa67..e934ef0 100644 --- a/src/honey_badger/epoch_state.rs +++ b/src/honey_badger/epoch_state.rs @@ -17,7 +17,7 @@ use super::{Batch, Error, MessageContent, Result, Step}; use crate::fault_log::{Fault, FaultKind, FaultLog}; use crate::subset::{self as cs, Subset, SubsetOutput}; use crate::threshold_decrypt::{self as td, ThresholdDecrypt}; -use crate::{Contribution, DistAlgorithm, NetworkInfo, NodeIdT}; +use crate::{Contribution, NetworkInfo, NodeIdT}; type CsStep = cs::Step; @@ -75,7 +75,7 @@ where /// Provides input to the Subset instance, unless it has already completed. fn handle_input(&mut self, proposal: Vec) -> Result> { match self { - SubsetState::Ongoing(ref mut cs) => cs.handle_input(proposal), + SubsetState::Ongoing(ref mut cs) => cs.propose(proposal), SubsetState::Complete(_) => return Ok(cs::Step::default()), } .map_err(Error::InputSubset) diff --git a/src/honey_badger/honey_badger.rs b/src/honey_badger/honey_badger.rs index 561bd04..f44652c 100644 --- a/src/honey_badger/honey_badger.rs +++ b/src/honey_badger/honey_badger.rs @@ -9,7 +9,7 @@ use serde_derive::{Deserialize, Serialize}; use super::epoch_state::EpochState; use super::{Batch, Error, HoneyBadgerBuilder, Message, Result}; -use crate::{util, Contribution, DistAlgorithm, Fault, FaultKind, NetworkInfo, NodeIdT}; +use crate::{Contribution, DistAlgorithm, Fault, FaultKind, NetworkInfo, NodeIdT}; use super::Params; @@ -30,10 +30,6 @@ pub struct HoneyBadger { pub(super) epochs: BTreeMap>, /// Parameters controlling Honey Badger's behavior and performance. pub(super) params: Params, - /// A random number generator used for secret key generation. - // Boxed to avoid overloading the algorithm's type with more generics. - #[derivative(Debug(format_with = "util::fmt_rng"))] - pub(super) rng: Box, } /// A `HoneyBadger` step, possibly containing multiple outputs. @@ -50,11 +46,16 @@ where type Message = Message; type Error = Error; - fn handle_input(&mut self, input: Self::Input) -> Result> { - self.propose(&input) + fn handle_input(&mut self, input: Self::Input, rng: &mut R) -> Result> { + self.propose(&input, rng) } - fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result> { + fn handle_message( + &mut self, + sender_id: &Self::NodeId, + message: Self::Message, + _rng: &mut R, + ) -> Result> { self.handle_message(sender_id, message) } @@ -84,7 +85,7 @@ where /// /// If we are the only validator, this will immediately output a batch, containing our /// proposal. - pub fn propose(&mut self, proposal: &C) -> Result> { + pub fn propose(&mut self, proposal: &C, rng: &mut R) -> Result> { if !self.netinfo.is_validator() { return Ok(Step::default()); } @@ -97,7 +98,6 @@ where "We created the epoch_state in `self.epoch_state_mut(...)` just a moment ago.", ) }; - let rng = &mut self.rng; epoch_state.propose(proposal, rng)? }; Ok(step.join(self.try_output_batches()?)) diff --git a/src/queueing_honey_badger/mod.rs b/src/queueing_honey_badger/mod.rs index a87b08b..c90cbb7 100644 --- a/src/queueing_honey_badger/mod.rs +++ b/src/queueing_honey_badger/mod.rs @@ -33,7 +33,7 @@ use serde::{de::DeserializeOwned, Serialize}; use crate::dynamic_honey_badger::{self, Batch as DhbBatch, DynamicHoneyBadger, Message}; use crate::transaction_queue::TransactionQueue; -use crate::{util, Contribution, DistAlgorithm, NetworkInfo, NodeIdT}; +use crate::{Contribution, DistAlgorithm, NetworkInfo, NodeIdT}; pub use crate::dynamic_honey_badger::{Change, ChangeState, Input}; @@ -101,10 +101,7 @@ where } /// Creates a new Queueing Honey Badger instance with an empty buffer. - pub fn build(self, rng: R) -> QueueingHoneyBadgerWithStep - where - R: 'static + Rng + Send + Sync, - { + pub fn build(self, rng: &mut R) -> QueueingHoneyBadgerWithStep { self.build_with_transactions(None, rng) .expect("building without transactions cannot fail") } @@ -114,20 +111,19 @@ where pub fn build_with_transactions( mut self, txs: TI, - rng: R, + rng: &mut R, ) -> Result> where TI: IntoIterator, - R: 'static + Rng + Send + Sync, + R: Rng, { self.queue.extend(txs); let mut qhb = QueueingHoneyBadger { dyn_hb: self.dyn_hb, batch_size: self.batch_size, queue: self.queue, - rng: Box::new(rng), }; - let step = qhb.propose()?; + let step = qhb.propose(rng)?; Ok((qhb, step)) } } @@ -143,9 +139,6 @@ pub struct QueueingHoneyBadger { dyn_hb: DynamicHoneyBadger, N>, /// The queue of pending transactions that haven't been output in a batch yet. queue: Q, - /// Random number generator used for choosing transactions from the queue. - #[derivative(Debug(format_with = "util::fmt_rng"))] - rng: Box, } /// A `QueueingHoneyBadger` step, possibly containing multiple outputs. @@ -163,17 +156,23 @@ where type Message = Message; type Error = Error; - fn handle_input(&mut self, input: Self::Input) -> Result> { + fn handle_input(&mut self, input: Self::Input, rng: &mut R) -> Result> { // User transactions are forwarded to `HoneyBadger` right away. Internal messages are // in addition signed and broadcast. + match input { - Input::User(tx) => self.push_transaction(tx), - Input::Change(change) => self.vote_for(change), + Input::User(tx) => self.push_transaction(tx, rng), + Input::Change(change) => self.vote_for(change, rng), } } - fn handle_message(&mut self, sender_id: &N, message: Self::Message) -> Result> { - self.handle_message(sender_id, message) + fn handle_message( + &mut self, + sender_id: &N, + message: Self::Message, + rng: &mut R, + ) -> Result> { + self.handle_message(sender_id, message, rng) } fn terminated(&self) -> bool { @@ -205,40 +204,53 @@ where /// If no proposal has yet been made for the current epoch, this may trigger one. In this case, /// a nonempty step will returned, with the corresponding messages. (Or, if we are the only /// validator, even with the completed batch as an output.) - pub fn push_transaction(&mut self, tx: T) -> Result> { + pub fn push_transaction(&mut self, tx: T, rng: &mut R) -> Result> { self.queue.extend(iter::once(tx)); - self.propose() + self.propose(rng) } /// Casts a vote to change the set of validators. /// /// This stores a pending vote for the change. It will be included in some future batch, and /// once enough validators have been voted for the same change, it will take effect. - pub fn vote_for(&mut self, change: Change) -> Result> { - self.apply(|dyn_hb| dyn_hb.vote_for(change)) + pub fn vote_for(&mut self, change: Change, rng: &mut R) -> Result> { + self.apply(|dyn_hb, _| dyn_hb.vote_for(change), rng) } /// Casts a vote to add a node as a validator. /// /// This stores a pending vote for the change. It will be included in some future batch, and /// once enough validators have been voted for the same change, it will take effect. - pub fn vote_to_add(&mut self, node_id: N, pub_key: PublicKey) -> Result> { - self.apply(|dyn_hb| dyn_hb.vote_to_add(node_id, pub_key)) + pub fn vote_to_add( + &mut self, + node_id: N, + pub_key: PublicKey, + rng: &mut R, + ) -> Result> { + self.apply(|dyn_hb, _| dyn_hb.vote_to_add(node_id, pub_key), rng) } /// Casts a vote to demote a validator to observer. /// /// This stores a pending vote for the change. It will be included in some future batch, and /// once enough validators have been voted for the same change, it will take effect. - pub fn vote_to_remove(&mut self, node_id: &N) -> Result> { - self.apply(|dyn_hb| dyn_hb.vote_to_remove(node_id)) + pub fn vote_to_remove(&mut self, node_id: &N, rng: &mut R) -> Result> { + self.apply(|dyn_hb, _| dyn_hb.vote_to_remove(node_id), rng) } /// Handles a message received from `sender_id`. /// /// This must be called with every message we receive from another node. - pub fn handle_message(&mut self, sender_id: &N, message: Message) -> Result> { - self.apply(|dyn_hb| dyn_hb.handle_message(sender_id, message)) + pub fn handle_message( + &mut self, + sender_id: &N, + message: Message, + rng: &mut R, + ) -> Result> { + self.apply( + |dyn_hb, rng| dyn_hb.handle_message(sender_id, message, rng), + rng, + ) } /// Returns a reference to the internal managed `DynamicHoneyBadger` instance. @@ -252,14 +264,18 @@ where } /// Applies a function `f` to the `DynamicHoneyBadger` instance and processes the step. - fn apply(&mut self, f: F) -> Result> + fn apply(&mut self, f: F, rng: &mut R) -> Result> where - F: FnOnce(&mut DynamicHoneyBadger, N>) -> dynamic_honey_badger::Result>, + F: FnOnce( + &mut DynamicHoneyBadger, N>, + &mut R, + ) -> dynamic_honey_badger::Result>, + R: Rng, { - let step = f(&mut self.dyn_hb).map_err(Error::Input)?; + let step = f(&mut self.dyn_hb, rng).map_err(Error::Input)?; self.queue .remove_multiple(step.output.iter().flat_map(Batch::iter)); - Ok(step.join(self.propose()?)) + Ok(step.join(self.propose(rng)?)) } /// Returns the epoch of the next batch that will be output. @@ -278,14 +294,14 @@ where } /// Initiates the next epoch by proposing a batch from the queue. - fn propose(&mut self) -> Result> { + fn propose(&mut self, rng: &mut R) -> Result> { let mut step = Step::default(); while self.can_propose() { let amount = cmp::max(1, self.batch_size / self.dyn_hb.netinfo().num_nodes()); - let proposal = self.queue.choose(&mut self.rng, amount, self.batch_size); + let proposal = self.queue.choose(rng, amount, self.batch_size); step.extend( self.dyn_hb - .handle_input(Input::User(proposal)) + .handle_input(Input::User(proposal), rng) .map_err(Error::Propose)?, ); } diff --git a/src/sender_queue/dynamic_honey_badger.rs b/src/sender_queue/dynamic_honey_badger.rs index 926751f..4d3ba9a 100644 --- a/src/sender_queue/dynamic_honey_badger.rs +++ b/src/sender_queue/dynamic_honey_badger.rs @@ -3,7 +3,7 @@ use std::result; use crate::crypto::PublicKey; -use rand::Rand; +use rand::{Rand, Rng}; use serde::{de::DeserializeOwned, Serialize}; use super::{ @@ -88,8 +88,8 @@ where /// /// If we are the only validator, this will immediately output a batch, containing our /// proposal. - pub fn propose(&mut self, contrib: C) -> Result { - self.apply(|algo| algo.propose(contrib)) + pub fn propose(&mut self, rng: &mut R, contrib: C) -> Result { + self.apply(|algo| algo.propose(contrib, rng)) } /// Casts a vote to change the set of validators or parameters. diff --git a/src/sender_queue/mod.rs b/src/sender_queue/mod.rs index 438653d..52ba402 100644 --- a/src/sender_queue/mod.rs +++ b/src/sender_queue/mod.rs @@ -10,6 +10,7 @@ mod honey_badger; mod message; mod queueing_honey_badger; +use rand::Rng; use std::collections::BTreeMap; use std::fmt::Debug; @@ -99,16 +100,21 @@ where type Message = Message; type Error = D::Error; - fn handle_input(&mut self, input: Self::Input) -> Result, D::Error> { - self.handle_input(input) + fn handle_input( + &mut self, + input: Self::Input, + rng: &mut R, + ) -> Result, D::Error> { + self.handle_input(input, rng) } - fn handle_message( + fn handle_message( &mut self, sender_id: &D::NodeId, message: Self::Message, + rng: &mut R, ) -> Result, D::Error> { - self.handle_message(sender_id, message) + self.handle_message(sender_id, message, rng) } fn terminated(&self) -> bool { @@ -137,21 +143,26 @@ where } /// Handles an input. This will call the wrapped algorithm's `handle_input`. - pub fn handle_input(&mut self, input: D::Input) -> Result, D::Error> { - self.apply(|algo| algo.handle_input(input)) + pub fn handle_input( + &mut self, + input: D::Input, + rng: &mut R, + ) -> Result, D::Error> { + self.apply(|algo| algo.handle_input(input, rng)) } /// Handles a message received from `sender_id`. /// /// This must be called with every message we receive from another node. - pub fn handle_message( + pub fn handle_message( &mut self, sender_id: &D::NodeId, message: Message, + rng: &mut R, ) -> Result, D::Error> { match message { Message::EpochStarted(epoch) => Ok(self.handle_epoch_started(sender_id, epoch)), - Message::Algo(msg) => self.handle_message_content(sender_id, msg), + Message::Algo(msg) => self.handle_message_content(sender_id, msg, rng), } } @@ -207,12 +218,13 @@ where } /// Handles a Honey Badger algorithm message in a given epoch. - fn handle_message_content( + fn handle_message_content( &mut self, sender_id: &D::NodeId, content: D::Message, + rng: &mut R, ) -> Result, 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. diff --git a/src/sender_queue/queueing_honey_badger.rs b/src/sender_queue/queueing_honey_badger.rs index 0ef2350..2923312 100644 --- a/src/sender_queue/queueing_honey_badger.rs +++ b/src/sender_queue/queueing_honey_badger.rs @@ -3,7 +3,7 @@ use std::result; use crate::crypto::PublicKey; -use rand::Rand; +use rand::{Rand, Rng}; use serde::{de::DeserializeOwned, Serialize}; use super::{SenderQueue, SenderQueueableDistAlgorithm}; @@ -51,31 +51,36 @@ where /// If no proposal has yet been made for the current epoch, this may trigger one. In this case, /// a nonempty step will returned, with the corresponding messages. (Or, if we are the only /// validator, even with the completed batch as an output.) - pub fn push_transaction(&mut self, tx: T) -> Result { - self.apply(|algo| algo.push_transaction(tx)) + pub fn push_transaction(&mut self, tx: T, rng: &mut R) -> Result { + self.apply(|algo| algo.push_transaction(tx, rng)) } /// Casts a vote to change the set of validators or parameters. /// /// This stores a pending vote for the change. It will be included in some future batch, and /// once enough validators have been voted for the same change, it will take effect. - pub fn vote_for(&mut self, change: Change) -> Result { - self.apply(|algo| algo.vote_for(change)) + pub fn vote_for(&mut self, change: Change, rng: &mut R) -> Result { + self.apply(|algo| algo.vote_for(change, rng)) } /// Casts a vote to add a node as a validator. /// /// This stores a pending vote for the change. It will be included in some future batch, and /// once enough validators have been voted for the same change, it will take effect. - pub fn vote_to_add(&mut self, node_id: N, pub_key: PublicKey) -> Result { - self.apply(|algo| algo.vote_to_add(node_id, pub_key)) + pub fn vote_to_add( + &mut self, + node_id: N, + pub_key: PublicKey, + rng: &mut R, + ) -> Result { + self.apply(|algo| algo.vote_to_add(node_id, pub_key, rng)) } /// Casts a vote to demote a validator to observer. /// /// This stores a pending vote for the change. It will be included in some future batch, and /// once enough validators have been voted for the same change, it will take effect. - pub fn vote_to_remove(&mut self, node_id: &N) -> Result { - self.apply(|algo| algo.vote_to_remove(node_id)) + pub fn vote_to_remove(&mut self, node_id: &N, rng: &mut R) -> Result { + self.apply(|algo| algo.vote_to_remove(node_id, rng)) } } diff --git a/src/subset/subset.rs b/src/subset/subset.rs index f8dafb6..294e5fc 100644 --- a/src/subset/subset.rs +++ b/src/subset/subset.rs @@ -10,7 +10,7 @@ use serde_derive::Serialize; use super::proposal_state::{ProposalState, Step as ProposalStep}; use super::{Error, Message, MessageContent, Result}; use crate::{util, DistAlgorithm, NetworkInfo, NodeIdT, SessionIdT}; -use rand::Rand; +use rand::{Rand, Rng}; /// A `Subset` step, possibly containing several outputs. pub type Step = crate::Step, SubsetOutput, N>; @@ -48,11 +48,16 @@ impl DistAlgorithm for Subset { type Message = Message; type Error = Error; - fn handle_input(&mut self, input: Self::Input) -> Result> { + fn handle_input(&mut self, input: Self::Input, _rng: &mut R) -> Result> { self.propose(input) } - fn handle_message(&mut self, sender_id: &N, message: Message) -> Result> { + fn handle_message( + &mut self, + sender_id: &N, + message: Message, + _rng: &mut R, + ) -> Result> { self.handle_message(sender_id, message) } diff --git a/src/sync_key_gen.rs b/src/sync_key_gen.rs index 24db484..5e2d9dd 100644 --- a/src/sync_key_gen.rs +++ b/src/sync_key_gen.rs @@ -61,8 +61,8 @@ //! use threshold_crypto::{PublicKey, SecretKey, SignatureShare}; //! use hbbft::sync_key_gen::{AckOutcome, PartOutcome, SyncKeyGen}; //! -//! // Use a default random number generator for any randomness: -//! let mut rng = rand::thread_rng(); +//! // Use the OS random number generator for any randomness: +//! let mut rng = rand::OsRng::new().expect("Could not open OS random number generator."); //! //! // Two out of four shares will suffice to sign or encrypt something. //! let (threshold, node_num) = (1, 4); @@ -80,8 +80,13 @@ //! let mut nodes = BTreeMap::new(); //! let mut parts = Vec::new(); //! for (id, sk) in sec_keys.into_iter().enumerate() { -//! let (sync_key_gen, opt_part) = SyncKeyGen::new(&mut rng, id, sk, pub_keys.clone(), threshold) -//! .unwrap_or_else(|_| panic!("Failed to create `SyncKeyGen` instance for node #{}", id)); +//! let (sync_key_gen, opt_part) = SyncKeyGen::new( +//! id, +//! sk, +//! pub_keys.clone(), +//! threshold, +//! &mut rng, +//! ).unwrap_or_else(|_| panic!("Failed to create `SyncKeyGen` instance for node #{}", id)); //! nodes.insert(id, sync_key_gen); //! parts.push((id, opt_part.unwrap())); // Would be `None` for observer nodes. //! } @@ -91,7 +96,7 @@ //! for (sender_id, part) in parts { //! for (&id, node) in &mut nodes { //! match node -//! .handle_part(&mut rng, &sender_id, part.clone()) +//! .handle_part(&sender_id, part.clone(), &mut rng) //! .expect("Failed to handle Part") //! { //! PartOutcome::Valid(Some(ack)) => acks.push((id, ack)), @@ -316,11 +321,11 @@ impl SyncKeyGen { /// If we are not a validator but only an observer, no `Part` message is produced and no /// messages need to be sent. pub fn new( - rng: &mut R, our_id: N, sec_key: SecretKey, pub_keys: BTreeMap, threshold: usize, + rng: &mut R, ) -> Result<(SyncKeyGen, Option), Error> { let our_idx = pub_keys .keys() @@ -366,9 +371,9 @@ impl SyncKeyGen { /// Note that `handle_part` also needs to explicitly be called with this instance's own `Part`. pub fn handle_part( &mut self, - rng: &mut R, sender_id: &N, part: Part, + rng: &mut R, ) -> Result { let sender_idx = self.node_index(sender_id).ok_or(Error::UnknownSender)?; let row = match self.handle_part_or_fault(sender_idx, part) { diff --git a/src/threshold_decrypt.rs b/src/threshold_decrypt.rs index fc0e662..39cdf0b 100644 --- a/src/threshold_decrypt.rs +++ b/src/threshold_decrypt.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use crate::crypto::{self, Ciphertext, DecryptionShare}; use failure::Fail; +use rand::Rng; use rand_derive::Rand; use serde_derive::{Deserialize, Serialize}; @@ -74,11 +75,16 @@ impl DistAlgorithm for ThresholdDecrypt { type Message = Message; type Error = Error; - fn handle_input(&mut self, _input: ()) -> Result> { + fn handle_input(&mut self, _input: (), _rng: &mut R) -> Result> { self.start_decryption() } - fn handle_message(&mut self, sender_id: &N, message: Message) -> Result> { + fn handle_message( + &mut self, + sender_id: &Self::NodeId, + message: Message, + _rng: &mut R, + ) -> Result> { self.handle_message(sender_id, message) } diff --git a/src/threshold_sign.rs b/src/threshold_sign.rs index 402d0e8..e3f6b30 100644 --- a/src/threshold_sign.rs +++ b/src/threshold_sign.rs @@ -22,6 +22,7 @@ use std::{fmt, result}; use crate::crypto::{self, hash_g2, Signature, SignatureShare, G2}; use failure::Fail; use log::debug; +use rand::Rng; use rand_derive::Rand; use serde_derive::{Deserialize, Serialize}; @@ -82,12 +83,17 @@ impl DistAlgorithm for ThresholdSign { type Error = Error; /// Sends our threshold signature share if not yet sent. - fn handle_input(&mut self, _input: ()) -> Result> { + fn handle_input(&mut self, _input: (), _rng: &mut R) -> Result> { self.sign() } /// Receives input from a remote node. - fn handle_message(&mut self, sender_id: &N, message: Message) -> Result> { + fn handle_message( + &mut self, + sender_id: &Self::NodeId, + message: Message, + _rng: &mut R, + ) -> Result> { self.handle_message(sender_id, message) } diff --git a/src/traits.rs b/src/traits.rs index 0bd62b8..030780d 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -6,6 +6,7 @@ use std::hash::Hash; use std::iter::once; use failure::Fail; +use rand::Rng; use serde::{de::DeserializeOwned, Serialize}; use crate::fault_log::{Fault, FaultLog}; @@ -249,6 +250,9 @@ where } /// A distributed algorithm that defines a message flow. +/// +/// Many algorithms require an RNG which must be supplied on each call. It is up to the caller to +/// ensure that this random number generator is cryptographically secure. pub trait DistAlgorithm: Send + Sync { /// Unique node identifier. type NodeId: NodeIdT; @@ -263,15 +267,20 @@ pub trait DistAlgorithm: Send + Sync { type Error: Fail; /// Handles an input provided by the user, and returns - fn handle_input(&mut self, input: Self::Input) -> Result, Self::Error> + fn handle_input( + &mut self, + input: Self::Input, + rng: &mut R, + ) -> Result, Self::Error> where Self: Sized; /// Handles a message received from node `sender_id`. - fn handle_message( + fn handle_message( &mut self, sender_id: &Self::NodeId, message: Self::Message, + rng: &mut R, ) -> Result, Self::Error> where Self: Sized; diff --git a/src/util.rs b/src/util.rs index 31721ed..6bd155b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -6,31 +6,6 @@ use std::fmt; use hex_fmt::HexFmt; -use rand; - -/// Workaround trait for creating new random number generators -pub trait SubRng { - /// Returns a new random number generator in a `Box`. - fn sub_rng(&mut self) -> Box; -} - -impl SubRng for R -where - R: rand::Rng, -{ - fn sub_rng(&mut self) -> Box { - // 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::(); - Box::new(rng) - } -} - -/// Prints "``" as a placeholder for a random number generator in debug output. -pub fn fmt_rng(_: T, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("") -} /// Prints a byte slice as shortened hexadecimal in debug output. pub fn fmt_hex>(bytes: T, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/tests/binary_agreement.rs b/tests/binary_agreement.rs index 7f352b7..e6b32fa 100644 --- a/tests/binary_agreement.rs +++ b/tests/binary_agreement.rs @@ -26,7 +26,7 @@ use rand::{Rng, SeedableRng}; use hbbft::binary_agreement::BinaryAgreement; use hbbft::DistAlgorithm; -use crate::net::adversary::ReorderingAdversary; +use crate::net::adversary::{Adversary, ReorderingAdversary}; use crate::net::proptest::{gen_seed, NetworkDimension, TestRng, TestRngSeed}; use crate::net::{NetBuilder, NewNodeInfo, VirtualNet}; @@ -72,19 +72,22 @@ proptest! { type NodeId = u16; -impl VirtualNet> { +impl VirtualNet, A> +where + A: Adversary>, +{ fn test_binary_agreement(&mut self, input: Option, mut rng: R) where R: Rng + 'static, { let ids: Vec = self.nodes().map(|n| *n.id()).collect(); for id in ids { - let _ = self.send_input(id, input.unwrap_or_else(|| rng.gen::())); + let _ = self.send_input(id, input.unwrap_or_else(|| rng.gen::()), &mut rng); } // Handle messages in random order until all nodes have output the proposed value. while !self.nodes().all(|node| node.algorithm().terminated()) { - let _ = self.crank_expect(); + let _ = self.crank_expect(&mut rng); } // Verify that all instances output the same value. let mut expected = input; @@ -117,13 +120,12 @@ fn binary_agreement(cfg: TestConfig) { .num_faulty(num_faulty_nodes as usize) .message_limit(10_000 * size as usize) .time_limit(time::Duration::from_secs(30 * size as u64)) - .rng(rng.gen::()) - .adversary(ReorderingAdversary::new(rng.gen::())) + .adversary(ReorderingAdversary::new()) .using(move |node_info: NewNodeInfo<_>| { BinaryAgreement::new(Arc::new(node_info.netinfo), 0) .expect("Failed to create a BinaryAgreement instance.") }) - .build() + .build(&mut rng) .expect("Could not construct test network."); net.test_binary_agreement(cfg.input, rng.gen::()); println!( diff --git a/tests/binary_agreement_mitm.rs b/tests/binary_agreement_mitm.rs index e2d7a00..c0f7009 100644 --- a/tests/binary_agreement_mitm.rs +++ b/tests/binary_agreement_mitm.rs @@ -9,9 +9,12 @@ use std::sync::{Arc, Mutex}; use hbbft::binary_agreement::{BinaryAgreement, MessageContent, SbvMessage}; use hbbft::threshold_sign::ThresholdSign; use hbbft::{DaStep, DistAlgorithm, NetworkInfo}; +use proptest::{proptest, proptest_helper}; +use rand::{Rng, SeedableRng}; use crate::net::adversary::{NetMutHandle, QueuePosition}; use crate::net::err::CrankError; +use crate::net::proptest::{gen_seed, TestRng, TestRngSeed}; use crate::net::{Adversary, NetBuilder, NetMessage}; type NodeId = usize; @@ -236,14 +239,18 @@ const NODES_PER_GROUP: usize = 2; const NUM_NODES: usize = (NODES_PER_GROUP * 3 + 1); impl AbaCommonCoinAdversary { - fn new(netinfo_mutex: Arc>>>>) -> Self { - Self::new_with_epoch(netinfo_mutex, 0, false) + fn new( + netinfo_mutex: Arc>>>>, + rng: &mut R, + ) -> Self { + Self::new_with_epoch(netinfo_mutex, 0, false, rng) } - fn new_with_epoch( + fn new_with_epoch( netinfo_mutex: Arc>>>>, epoch: u64, a_estimated: bool, + rng: &mut R, ) -> Self { AbaCommonCoinAdversary { stage: 0, @@ -265,7 +272,7 @@ impl AbaCommonCoinAdversary { let mut coin = ThresholdSign::new_with_document(netinfo, coin_id) .expect("Failed to set the coin's ID"); let _ = coin - .handle_input(()) + .handle_input((), rng) .expect("Calling handle_input on Coin failed"); CoinState::InProgress(Box::new(coin)) } @@ -288,7 +295,7 @@ impl AbaCommonCoinAdversary { } } - fn inject_stage_messages(&mut self, net: &mut NetMutHandle) { + fn inject_stage_messages(&mut self, net: &mut NetMutHandle) { if self.sent_stage_messages { return; } @@ -371,7 +378,7 @@ impl AbaCommonCoinAdversary { } impl Adversary for AbaCommonCoinAdversary { - fn pre_crank(&mut self, mut net: NetMutHandle) { + fn pre_crank(&mut self, mut net: NetMutHandle, rng: &mut R) { self.inject_stage_messages(&mut net); net.sort_messages_by(|a, b| { a.payload() @@ -395,19 +402,21 @@ impl Adversary for AbaCommonCoinAdversary { self.coin_state .value() .expect("Coin value not known at end of epoch"), + rng, ); redo_crank = true; } } if redo_crank { - self.pre_crank(net); + self.pre_crank(net, rng); } } - fn tamper( + fn tamper( &mut self, - _: NetMutHandle, + _: NetMutHandle, msg: NetMessage, + _rng: &mut R, ) -> Result, CrankError> { if let MessageContent::Coin(ref coin_msg) = msg.payload().content { let mut new_coin_state = None; @@ -427,13 +436,24 @@ impl Adversary for AbaCommonCoinAdversary { } } -#[test] -fn reordering_attack() { +proptest! { + #[test] + #[allow(clippy::unnecessary_operation)] + fn reordering_attack(seed in gen_seed()) { + do_reordering_attack(seed) + } +} + +fn do_reordering_attack(seed: TestRngSeed) { let _ = env_logger::try_init(); + let mut rng: TestRng = TestRng::from_seed(seed); let ids: Vec = (0..NUM_NODES).collect(); let adversary_netinfo: Arc>>>> = Default::default(); let (mut net, _) = NetBuilder::new(ids.iter().cloned()) - .adversary(AbaCommonCoinAdversary::new(adversary_netinfo.clone())) + .adversary(AbaCommonCoinAdversary::new( + adversary_netinfo.clone(), + &mut rng, + )) .crank_limit(10000) .using(move |info| { let netinfo = Arc::new(info.netinfo); @@ -443,7 +463,7 @@ fn reordering_attack() { BinaryAgreement::new(netinfo, 0).expect("failed to create BinaryAgreement instance") }) .num_faulty(1) - .build() + .build(&mut rng) .unwrap(); for id in ids { @@ -451,15 +471,15 @@ fn reordering_attack() { // This is the faulty node. } else if id < (1 + NODES_PER_GROUP * 2) { // Group A - let _ = net.send_input(id, false).unwrap(); + let _ = net.send_input(id, false, &mut rng).unwrap(); } else { // Group B - let _ = net.send_input(id, true).unwrap(); + let _ = net.send_input(id, true, &mut rng).unwrap(); } } while !net.nodes().skip(1).all(|n| n.algorithm().terminated()) { - net.crank_expect(); + net.crank_expect(&mut rng); } // Verify that all instances output the same value. diff --git a/tests/broadcast.rs b/tests/broadcast.rs index f603a54..8d11ed6 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -48,6 +48,7 @@ impl Adversary> for ProposeAdversary { } fn step(&mut self) -> Vec>> { + let mut rng = rand::thread_rng(); if self.has_sent { return vec![]; } @@ -57,7 +58,7 @@ impl Adversary> for ProposeAdversary { .flat_map(|(&id, netinfo)| { Broadcast::new(netinfo.clone(), id) .expect("broadcast instance") - .handle_input(b"Fake news".to_vec()) + .handle_input(b"Fake news".to_vec(), &mut rng) .expect("propose") .messages .into_iter() diff --git a/tests/net/adversary.rs b/tests/net/adversary.rs index b22e2e3..f380494 100644 --- a/tests/net/adversary.rs +++ b/tests/net/adversary.rs @@ -33,8 +33,8 @@ //! some cases be upgraded to actual references, if the underlying node is faulty (see //! `NodeHandle::node()` and `NodeHandle::node_mut()`). +use std::cmp; use std::collections::VecDeque; -use std::{cmp, fmt}; use rand::Rng; @@ -46,13 +46,19 @@ use crate::net::{CrankError, NetMessage, Node, VirtualNet}; /// /// Allows querying public information of the network or getting immutable handles to any node. #[derive(Debug)] -pub struct NetHandle<'a, D: 'a>(&'a VirtualNet) -where - D: DistAlgorithm; - -impl<'a, D: 'a> NetHandle<'a, D> +pub struct NetHandle<'a, D: 'a, A>(&'a VirtualNet) where D: DistAlgorithm, + D::Message: Clone, + D::Output: Clone, + A: Adversary; + +impl<'a, D: 'a, A> NetHandle<'a, D, A> +where + D: DistAlgorithm, + D::Message: Clone, + D::Output: Clone, + A: Adversary, { /// Returns a node handle iterator over all nodes in the network. #[inline] @@ -106,18 +112,22 @@ pub enum QueuePosition { /// Allows reordering of messages, injecting new ones into the network queue and getting mutable /// handles to nodes. #[derive(Debug)] -pub struct NetMutHandle<'a, D: 'a>(&'a mut VirtualNet) -where - D: DistAlgorithm; - -impl<'a, D> NetMutHandle<'a, D> +pub struct NetMutHandle<'a, D: 'a, A>(&'a mut VirtualNet) where D: DistAlgorithm, + D::Message: Clone, + D::Output: Clone, + A: Adversary; + +impl<'a, D, A> NetMutHandle<'a, D, A> +where + D: DistAlgorithm, + A: Adversary, D::NodeId: Clone, D::Message: Clone, D::Output: Clone, { - pub fn new(net: &'a mut VirtualNet) -> Self { + pub fn new(net: &'a mut VirtualNet) -> Self { NetMutHandle(net) } @@ -143,8 +153,12 @@ where } /// Normally dispatch a message - pub fn dispatch_message(&mut self, msg: NetMessage) -> Result, CrankError> { - self.0.dispatch_message(msg) + pub fn dispatch_message( + &mut self, + msg: NetMessage, + rng: &mut R, + ) -> Result, CrankError> { + self.0.dispatch_message(msg, rng) } /// Injects a message into the network. @@ -211,12 +225,15 @@ where } // Downgrade-conversion. -impl<'a, D> From> for NetHandle<'a, D> +impl<'a, D, A> From> for NetHandle<'a, D, A> where D: DistAlgorithm, + A: Adversary, + D::Message: Clone, + D::Output: Clone, { #[inline] - fn from(n: NetMutHandle) -> NetHandle { + fn from(n: NetMutHandle) -> NetHandle { NetHandle(n.0) } } @@ -305,6 +322,7 @@ where /// Network adversary. pub trait Adversary where + Self: Sized, D: DistAlgorithm, D::Message: Clone, D::Output: Clone, @@ -316,7 +334,7 @@ where /// /// The default implementation does not alter the passed network in any way. #[inline] - fn pre_crank(&mut self, _net: NetMutHandle) {} + fn pre_crank(&mut self, _net: NetMutHandle, _rng: &mut R) {} /// Tamper with a faulty node's operation. /// @@ -332,12 +350,13 @@ where /// `VirtualNet::dispatch_message`, which results in the message being processed as if the node /// was not faulty. #[inline] - fn tamper( + fn tamper( &mut self, - mut net: NetMutHandle, + mut net: NetMutHandle, msg: NetMessage, + rng: &mut R, ) -> Result, CrankError> { - net.dispatch_message(msg) + net.dispatch_message(msg, rng) } } @@ -388,7 +407,7 @@ where D::Output: Clone, { #[inline] - fn pre_crank(&mut self, mut net: NetMutHandle) { + fn pre_crank(&mut self, mut net: NetMutHandle, _rng: &mut R) { // Message are sorted by NodeID on each step. net.sort_messages_by(|a, b| a.to.cmp(&b.to)) } @@ -399,26 +418,12 @@ where /// An adversary that swaps the message at the front of the message queue for a random message /// within the queue before every `crank`. Thus the order in which messages are received by nodes is /// random, which allows to test randomized message delivery. -pub struct ReorderingAdversary { - /// Random number generator to reorder messages. - rng: Box, -} - -impl fmt::Debug for ReorderingAdversary { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ReorderingAdversary") - .field("rng", &"") - .finish() - } -} +#[derive(Copy, Clone, Debug, Default)] +pub struct ReorderingAdversary {} impl ReorderingAdversary { - #[inline] - pub fn new(rng: R) -> Self - where - R: 'static + Rng, - { - ReorderingAdversary { rng: Box::new(rng) } + pub fn new() -> Self { + ReorderingAdversary {} } } @@ -429,10 +434,10 @@ where D::Output: Clone, { #[inline] - fn pre_crank(&mut self, mut net: NetMutHandle) { + fn pre_crank(&mut self, mut net: NetMutHandle, rng: &mut R) { let l = net.0.messages_len(); if l > 0 { - net.swap_messages(0, self.rng.gen_range(0, l)); + net.swap_messages(0, rng.gen_range(0, l)); } } } diff --git a/tests/net/mod.rs b/tests/net/mod.rs index 385fce8..e29f44d 100644 --- a/tests/net/mod.rs +++ b/tests/net/mod.rs @@ -26,7 +26,6 @@ use rand; use rand::{Rand, Rng}; use hbbft::dynamic_honey_badger::Batch; -use hbbft::util::SubRng; use hbbft::{self, Contribution, DaStep, DistAlgorithm, Fault, NetworkInfo, NodeIdT, Step}; use crate::try_some; @@ -284,6 +283,7 @@ where /// New network node construction information. /// /// Helper structure passed to node constructors when building virtual networks. +#[derive(Debug)] pub struct NewNodeInfo where D: DistAlgorithm, @@ -294,28 +294,6 @@ where pub netinfo: NetworkInfo, /// Whether or not the node is marked faulty. pub faulty: bool, - /// An initialized random number generated for exclusive use by the node. - /// - /// Can be ignored, but usually comes in handy with algorithms that require additional - /// randomness for instantiation or operation. - /// - /// Note that the random number generator type may differ from the one set for generation on - /// the `VirtualNet`, due to limitations of the `rand` crates API. - pub rng: Box, -} - -impl fmt::Debug for NewNodeInfo -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", &"") - .finish() - } } /// Virtual network builder. @@ -325,7 +303,7 @@ where /// /// Note that, in addition to the constructor `new`, either `using` or `using_step` must be called, /// otherwise the construction will fail and panic. -pub struct NetBuilder +pub struct NetBuilder where D: DistAlgorithm, { @@ -336,7 +314,7 @@ where /// Dist-algorithm constructor function. cons: Option) -> (D, DaStep)>>, /// Network adversary. - adversary: Option>>, + adversary: Option, /// Trace-enabling flag. `None` means use environment. trace: Option, /// Optional crank limit. @@ -348,36 +326,35 @@ where /// Property to cause an error if a `Fault` is output from a correct node. By default, /// encountering a fault leads to an error. error_on_fault: bool, - /// Random number generator used to generate keys. - rng: Option>, } -impl fmt::Debug for NetBuilder +impl fmt::Debug for NetBuilder where D: DistAlgorithm, + A: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("NetBuilder") .field("node_ids", &()) .field("num_faulty", &self.num_faulty) .field("cons", &self.cons.is_some()) - .field("adversary", &self.cons.is_some()) + .field("adversary", &self.adversary) .field("trace", &self.trace) .field("crank_limit", &self.crank_limit) .field("message_limit", &self.message_limit) .field("time_limit", &self.time_limit) .field("error_on_fault", &self.error_on_fault) - .field("rng", &"") .finish() } } -impl NetBuilder +impl NetBuilder where D: DistAlgorithm, D::Message: Clone, D::Output: Clone, I: IntoIterator, + A: Adversary, { /// Construct a new network builder. /// @@ -399,7 +376,6 @@ where message_limit: None, time_limit: DEFAULT_TIME_LIMIT, error_on_fault: true, - rng: None, } } @@ -407,11 +383,8 @@ where /// /// If not set, the virtual network is constructed with a `NullAdversary`. #[inline] - pub fn adversary(mut self, adversary: A) -> Self - where - A: Adversary + 'static, - { - self.adversary = Some(Box::new(adversary)); + pub fn adversary(mut self, adversary: A) -> Self { + self.adversary = Some(adversary); self } @@ -455,20 +428,6 @@ where self } - /// Random number generator. - /// - /// Overrides the random number generator used. If not specified, a `thread_rng` will be - /// used on construction. - /// - /// The passed in generator is used for key generation. - pub fn rng(mut self, rng: R) -> Self - where - R: Rng + 'static, - { - self.rng = Some(Box::new(rng)); - self - } - /// Time limit. /// /// Sets the time limit; `crank` will fail if called after this much time as elapsed since @@ -531,9 +490,10 @@ where /// /// If the total number of nodes is not `> 3 * num_faulty`, construction will panic. #[inline] - pub fn build(self) -> Result<(VirtualNet, Vec<(D::NodeId, DaStep)>), CrankError> { - let rng: Box = self.rng.unwrap_or_else(|| Box::new(rand::thread_rng())); - + pub fn build( + self, + rng: &mut R, + ) -> Result<(VirtualNet, Vec<(D::NodeId, DaStep)>), CrankError> { // The time limit can be overriden through environment variables: let override_time_limit = env::var("HBBFT_NO_TIME_LIMIT") // We fail early, to avoid tricking the user into thinking that they have set the time @@ -587,20 +547,28 @@ where } } -/// Virtual network instance. -pub struct VirtualNet +/// A virtual network +/// +/// Virtual networks host a number of nodes that are marked either correct or faulty. Each time a +/// node emits a `Step`, the contained messages are queued for delivery, which happens whenever +/// `crank()` is called. Additionally, inputs (see `DistAlgorithm::Input`) can be sent to any node. +/// +/// An adversary can be hooked into the network to affect the order of message delivery or the +/// behaviour of faulty nodes. +#[derive(Debug)] +pub struct VirtualNet where D: DistAlgorithm, + A: Adversary, + D::Message: Clone, + D::Output: Clone, { /// Maps node IDs to actual node instances. nodes: NodeMap, /// A collection of all network messages queued up for delivery. messages: collections::VecDeque>, - /// An Adversary that controls the network delivery schedule and all faulty nodes. - /// Always present (initialized to `NullAdversary` by default), but an `Option` to be swappable - /// during execution, allowing a `&mut self` to be passed to the adversary without running afoul - /// of the borrow checker. - adversary: Option>>, + /// An optional `Adversary` that controls the network delivery schedule and all faulty nodes. + adversary: Option, /// Trace output; if active, writes out a log of all messages. trace: Option>, /// The number of times the network has been cranked. @@ -621,36 +589,12 @@ where error_on_fault: bool, } -impl fmt::Debug for VirtualNet -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 VirtualNet +impl VirtualNet where D: DistAlgorithm, + D::Message: Clone, + D::Output: Clone, + A: Adversary, { /// Returns an iterator over *all* nodes in the network. #[inline] @@ -753,11 +697,12 @@ where } } -impl VirtualNet +impl VirtualNet where D: DistAlgorithm, D::Message: Clone, D::Output: Clone, + A: Adversary, { /// Create new virtual network with step constructor. /// @@ -808,7 +753,6 @@ where id: id.clone(), netinfo, faulty: is_faulty, - rng: rng.sub_rng(), }); steps.insert(id.clone(), step); (id, Node::new(algorithm, is_faulty)) @@ -832,7 +776,7 @@ where VirtualNet { nodes, messages, - adversary: Some(Box::new(adversary::NullAdversary::new())), + adversary: None, trace: None, crank_count: 0, crank_limit: None, @@ -850,7 +794,11 @@ where /// /// Retrieves the receiving node for a `msg` and hands over the payload. #[inline] - pub fn dispatch_message(&mut self, msg: NetMessage) -> Result, CrankError> { + pub fn dispatch_message( + &mut self, + msg: NetMessage, + rng: &mut R, + ) -> Result, CrankError> { let node = self .nodes .get_mut(&msg.to) @@ -862,7 +810,7 @@ where let msg_copy = msg.clone(); let step = node .algorithm - .handle_message(&msg.from, msg.payload) + .handle_message(&msg.from, msg.payload, rng) .map_err(move |err| CrankError::HandleMessage { msg: msg_copy, err })?; Ok(step) @@ -877,17 +825,18 @@ where /// /// Panics if `id` does not name a valid node. #[inline] - pub fn send_input( + pub fn send_input( &mut self, id: D::NodeId, input: D::Input, + rng: &mut R, ) -> Result, CrankError> { let step = self .nodes .get_mut(&id) .expect("cannot handle input on non-existing node") .algorithm - .handle_input(input) + .handle_input(input, rng) .map_err(CrankError::HandleInput)?; self.message_count = self.message_count.saturating_add(process_step( @@ -909,7 +858,10 @@ where /// If a successful `Step` was generated, all of its messages are queued on the network and the /// `Step` is returned. #[inline] - pub fn crank(&mut self) -> Option), CrankError>> { + pub fn crank( + &mut self, + rng: &mut R, + ) -> Option), CrankError>> { // Check limits. if let Some(limit) = self.crank_limit { if self.crank_count >= limit { @@ -935,7 +887,7 @@ where let mut adv = self.adversary.take(); if let Some(ref mut adversary) = adv { // If an adversary was set, we let it affect the network now. - adversary.pre_crank(adversary::NetMutHandle::new(self)) + adversary.pre_crank(adversary::NetMutHandle::new(self), rng) } self.adversary = adv; @@ -966,7 +918,7 @@ where let mut adv = self.adversary.take(); let opt_tamper_result = adv.as_mut().map(|adversary| { // If an adversary was set, we let it affect the network now. - adversary.tamper(adversary::NetMutHandle::new(self), msg) + adversary.tamper(adversary::NetMutHandle::new(self), msg, rng) }); self.adversary = adv; @@ -977,7 +929,7 @@ where ) } else { // A correct node simply handles the message. - try_some!(self.dispatch_message(msg)) + try_some!(self.dispatch_message(msg, rng)) }; // All messages are expanded and added to the queue. We opt for copying them, so we can @@ -1005,19 +957,20 @@ where /// /// Shortcut for cranking the network, expecting both progress to be made as well as processing /// to proceed. - pub fn crank_expect(&mut self) -> (D::NodeId, DaStep) { - self.crank() + pub fn crank_expect(&mut self, rng: &mut R) -> (D::NodeId, DaStep) { + self.crank(rng) .expect("crank: network queue empty") .expect("crank: node failed to process step") } } -impl VirtualNet +impl VirtualNet where D: DistAlgorithm, D::Message: Clone, D::Input: Clone, D::Output: Clone, + A: Adversary, { /// Send input to all nodes. /// @@ -1026,17 +979,11 @@ where /// /// If an error occurs, the first error is returned and broadcasting aborted. #[inline] - pub fn broadcast_input<'a>( + pub fn broadcast_input<'a, R: Rng>( &'a mut self, input: &'a D::Input, + rng: &mut R, ) -> Result)>, CrankError> { - // Note: The tricky lifetime annotation basically says that the input value given must - // live as long as the iterator returned lives (because it is cloned on every step, - // with steps only evaluated each time `next()` is called. For the same reason the - // network should not go away ealier either. - - // Note: It's unfortunately not possible to loop and call `send_input`, - let steps: Vec<_> = self .nodes .values_mut() @@ -1044,7 +991,7 @@ where Ok(( node.id().clone(), node.algorithm - .handle_input(input.clone()) + .handle_input(input.clone(), rng) .map_err(CrankError::HandleInputAll)?, )) }) @@ -1066,9 +1013,11 @@ where } } -impl VirtualNet +impl VirtualNet where D: DistAlgorithm>, + D::Message: Clone, + A: Adversary, C: Contribution + Clone, N: NodeIdT, { @@ -1089,9 +1038,12 @@ where } } -impl ops::Index for VirtualNet +impl ops::Index for VirtualNet where D: DistAlgorithm, + D::Message: Clone, + D::Output: Clone, + A: Adversary, { type Output = Node; @@ -1101,37 +1053,15 @@ where } } -impl ops::IndexMut for VirtualNet +impl ops::IndexMut for VirtualNet where D: DistAlgorithm, + D::Message: Clone, + D::Output: Clone, + A: Adversary, { #[inline] fn index_mut(&mut self, index: D::NodeId) -> &mut Self::Output { self.get_mut(index).expect("indexed node not found") } } - -/// Convenient iterator implementation, calls crank repeatedly until the message queue is empty. -/// -/// Accessing the network during iterator would require -/// [streaming iterators](https://crates.io/crates/streaming-iterator), an alternative is using -/// a `while let` loop: -/// -/// ```rust,no_run -/// while let Some(rstep) = net.crank() { -/// // `net` can still be mutable borrowed here. -/// } -/// ``` -impl Iterator for VirtualNet -where - D: DistAlgorithm, - D::Message: Clone, - D::Output: Clone, -{ - type Item = Result<(D::NodeId, DaStep), CrankError>; - - #[inline] - fn next(&mut self) -> Option { - self.crank() - } -} diff --git a/tests/net_dynamic_hb.rs b/tests/net_dynamic_hb.rs index dbc33af..d3cfcc4 100644 --- a/tests/net_dynamic_hb.rs +++ b/tests/net_dynamic_hb.rs @@ -8,7 +8,7 @@ use crate::net::{NetBuilder, NewNodeInfo}; use hbbft::dynamic_honey_badger::{Change, ChangeState, DynamicHoneyBadger, Input}; use hbbft::sender_queue::SenderQueue; use proptest::{prelude::ProptestConfig, prop_compose, proptest, proptest_helper}; -use rand::{Rng, SeedableRng}; +use rand::SeedableRng; /// Choose a node's contribution for an epoch. /// @@ -92,22 +92,18 @@ fn do_drop_and_readd(cfg: TestConfig) { .message_limit(15_000 * cfg.dimension.size() as usize) // 30 secs per node. .time_limit(time::Duration::from_secs(30 * cfg.dimension.size() as u64)) - // Ensure runs are reproducible. - .rng(rng.gen::()) - .adversary(ReorderingAdversary::new(rng.gen::())) + .adversary(ReorderingAdversary::new()) .using_step(move |node: NewNodeInfo>| { let id = node.id; println!("Constructing new dynamic honey badger node #{}", id); - let dhb = DynamicHoneyBadger::builder() - .rng(node.rng) - .build(node.netinfo.clone()); + let dhb = DynamicHoneyBadger::builder().build(node.netinfo.clone()); SenderQueue::builder( dhb, node.netinfo.all_ids().filter(|&&them| them != id).cloned(), ) .build(node.id) }) - .build() + .build(&mut rng) .expect("could not construct test network"); // We will use the first correct node as the node we will remove from and re-add to the network. @@ -133,7 +129,7 @@ fn do_drop_and_readd(cfg: TestConfig) { // The step will have its messages added to the queue automatically, we ignore the output. let _ = net - .send_input(*id, Input::User(proposal)) + .send_input(*id, Input::User(proposal), &mut rng) .expect("could not send initial transaction"); } @@ -148,8 +144,11 @@ fn do_drop_and_readd(cfg: TestConfig) { let pub_keys_add = netinfo.public_key_map().clone(); let mut pub_keys_rm = pub_keys_add.clone(); pub_keys_rm.remove(&pivot_node_id); - net.broadcast_input(&Input::Change(Change::NodeChange(pub_keys_rm.clone()))) - .expect("broadcasting failed"); + net.broadcast_input( + &Input::Change(Change::NodeChange(pub_keys_rm.clone())), + &mut rng, + ) + .expect("broadcasting failed"); // We are tracking (correct) nodes' state through the process by ticking them off individually. let mut awaiting_removal: collections::BTreeSet<_> = @@ -164,7 +163,7 @@ fn do_drop_and_readd(cfg: TestConfig) { // Run the network: loop { - let (node_id, step) = net.crank_expect(); + let (node_id, step) = net.crank_expect(&mut rng); if !net[node_id].is_faulty() { for batch in &step.output { // Check that correct nodes don't output different batches for the same epoch. @@ -219,6 +218,7 @@ fn do_drop_and_readd(cfg: TestConfig) { .send_input( node_id, Input::Change(Change::NodeChange(pub_keys_add.clone())), + &mut rng, ) .expect("failed to send `Add` input"); } @@ -291,7 +291,7 @@ fn do_drop_and_readd(cfg: TestConfig) { choose_contribution(&mut rng, queue, cfg.batch_size, cfg.contribution_size); let _ = net - .send_input(node_id, Input::User(proposal)) + .send_input(node_id, Input::User(proposal), &mut rng) .expect("could not send follow-up transaction"); } } diff --git a/tests/network/mod.rs b/tests/network/mod.rs index dae5414..fd333c7 100644 --- a/tests/network/mod.rs +++ b/tests/network/mod.rs @@ -46,8 +46,8 @@ impl TestNode { } /// Inputs a value into the instance. - pub fn handle_input(&mut self, input: D::Input) { - let step = self.algo.handle_input(input).expect("input"); + pub fn handle_input(&mut self, input: D::Input, rng: &mut R) { + let step = self.algo.handle_input(input, rng).expect("input"); self.outputs.extend(step.output); self.messages.extend(step.messages); self.faults.extend(step.fault_log.0); @@ -73,11 +73,13 @@ impl TestNode { /// Handles the first message in the node's queue. fn handle_message(&mut self) { + let mut rng = rand::thread_rng(); + let (from_id, msg) = self.queue.pop_front().expect("message not found"); debug!("Handling {:?} -> {:?}: {:?}", from_id, self.id, msg); let step = self .algo - .handle_message(&from_id, msg) + .handle_message(&from_id, msg, &mut rng) .expect("handling message"); self.outputs.extend(step.output); self.messages.extend(step.messages); @@ -558,9 +560,11 @@ where /// Inputs a value in node `id`. pub fn input(&mut self, id: NodeId, value: D::Input) { + let mut rng = rand::thread_rng(); + let (msgs, faults): (Vec<_>, Vec<_>) = { let node = self.nodes.get_mut(&id).expect("input instance"); - node.handle_input(value); + node.handle_input(value, &mut rng); ( node.messages.drain(..).collect(), node.faults.drain(..).collect(), diff --git a/tests/queueing_honey_badger.rs b/tests/queueing_honey_badger.rs index 2b061f8..7cfff9e 100644 --- a/tests/queueing_honey_badger.rs +++ b/tests/queueing_honey_badger.rs @@ -90,8 +90,10 @@ fn new_queueing_hb( .cloned() .chain(iter::once(observer)); let dhb = DynamicHoneyBadger::builder().build((*netinfo).clone()); - let rng = rand::thread_rng().gen::(); - let (qhb, qhb_step) = QueueingHoneyBadger::builder(dhb).batch_size(3).build(rng); + let mut rng = rand::thread_rng().gen::(); + let (qhb, qhb_step) = QueueingHoneyBadger::builder(dhb) + .batch_size(3) + .build(&mut rng); let (sq, mut step) = SenderQueue::builder(qhb, peer_ids).build(our_id); step.extend_with(qhb_step, Message::from); (sq, step) diff --git a/tests/sync_key_gen.rs b/tests/sync_key_gen.rs index a0e4a1e..d3c6fcd 100644 --- a/tests/sync_key_gen.rs +++ b/tests/sync_key_gen.rs @@ -23,7 +23,7 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) { .enumerate() .map(|(id, sk)| { let (sync_key_gen, proposal) = - SyncKeyGen::new(&mut rand::thread_rng(), id, sk, pub_keys.clone(), threshold) + SyncKeyGen::new(id, sk, pub_keys.clone(), threshold, &mut rand::thread_rng()) .unwrap_or_else(|_err| { panic!("Failed to create `SyncKeyGen` instance #{}", id) }); @@ -38,7 +38,7 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) { for (node_id, node) in nodes.iter_mut().enumerate() { let proposal = proposal.clone().expect("proposal"); let ack = match node - .handle_part(&mut rand::thread_rng(), &sender_id, proposal) + .handle_part(&sender_id, proposal, &mut rand::thread_rng()) .expect("failed to handle part") { PartOutcome::Valid(Some(ack)) => ack, diff --git a/tests/threshold_sign.rs b/tests/threshold_sign.rs index 4df0d35..fd0b9fd 100644 --- a/tests/threshold_sign.rs +++ b/tests/threshold_sign.rs @@ -18,8 +18,11 @@ fn test_threshold_sign(mut network: TestNetwork>) -> where A: Adversary>, { + let mut rng = rand::thread_rng(); + network.input_all(()); - network.observer.handle_input(()); // Observer will only return after `input` was called. + + network.observer.handle_input((), &mut rng); // Observer will only return after `input` was called. // Handle messages until all good nodes have terminated. while !network.nodes.values().all(TestNode::terminated) {