mirror of https://github.com/poanetwork/hbbft.git
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:
parent
1c7fc60db9
commit
eafa77d5fc
|
@ -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");
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 step = self
|
|
||||||
.honey_badger
|
let contrib = InternalContrib {
|
||||||
.propose(&InternalContrib {
|
|
||||||
contrib,
|
contrib,
|
||||||
key_gen_messages,
|
key_gen_messages,
|
||||||
votes: self.vote_counter.pending_votes().cloned().collect(),
|
votes: self.vote_counter.pending_votes().cloned().collect(),
|
||||||
})
|
};
|
||||||
|
|
||||||
|
let step = self
|
||||||
|
.honey_badger
|
||||||
|
.propose(&contrib, rng)
|
||||||
.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,8 +404,7 @@ 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 {
|
||||||
|
@ -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.
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()?))
|
||||||
|
|
|
@ -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)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
25
src/util.rs
25
src/util.rs
|
@ -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 {
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn reordering_attack() {
|
#[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.
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
210
tests/net/mod.rs
210
tests/net/mod.rs
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,7 +144,10 @@ 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(
|
||||||
|
&Input::Change(Change::NodeChange(pub_keys_rm.clone())),
|
||||||
|
&mut rng,
|
||||||
|
)
|
||||||
.expect("broadcasting failed");
|
.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.
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue