2018-03-16 11:12:14 -07:00
|
|
|
//! Networking controls of the consensus node.
|
2018-05-05 06:39:32 -07:00
|
|
|
//!
|
|
|
|
//! ## Example
|
|
|
|
//!
|
|
|
|
//! The following code could be run on host 192.168.1.1:
|
|
|
|
//!
|
|
|
|
//! ```ignore
|
|
|
|
//! use hbbft::node::Node;
|
|
|
|
//! use std::net::SocketAddr;
|
|
|
|
//! use std::vec::Vec;
|
|
|
|
//!
|
|
|
|
//! fn main() {
|
|
|
|
//! let bind_address = "127.0.0.1:10001".parse().unwrap();
|
|
|
|
//! let remote_addresses = vec!["192.168.1.2:10002",
|
|
|
|
//! "192.168.1.3:10003",
|
|
|
|
//! "192.168.1.4:10004"]
|
|
|
|
//! .iter()
|
|
|
|
//! .map(|s| s.parse().unwrap())
|
|
|
|
//! .collect();
|
|
|
|
//!
|
|
|
|
//! let value = "Value #1".as_bytes().to_vec();
|
|
|
|
//!
|
|
|
|
//! let result = Node::new(bind_address, remote_addresses, Some(value))
|
|
|
|
//! .run();
|
|
|
|
//! println!("Consensus result {:?}", result);
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Similar code shall then run on hosts 192.168.1.2, 192.168.1.3 and
|
|
|
|
//! 192.168.1.4 with appropriate changes in `bind_address` and
|
|
|
|
//! `remote_addresses`. Each host has it's own optional broadcast `value`. If
|
|
|
|
//! the consensus `result` is not an error then every successfully terminated
|
|
|
|
//! consensus node will be the same `result`.
|
|
|
|
|
2018-06-13 10:24:35 -07:00
|
|
|
use std::collections::{BTreeSet, HashSet};
|
2018-03-16 11:12:14 -07:00
|
|
|
use std::fmt::Debug;
|
2018-03-16 14:04:06 -07:00
|
|
|
use std::marker::{Send, Sync};
|
2018-04-02 13:26:40 -07:00
|
|
|
use std::net::SocketAddr;
|
2018-07-11 12:15:08 -07:00
|
|
|
use std::sync::Arc;
|
2018-12-10 09:10:13 -08:00
|
|
|
use std::{iter, process, thread, time};
|
2018-03-20 09:32:19 -07:00
|
|
|
|
2018-07-30 05:39:27 -07:00
|
|
|
use crossbeam;
|
2018-10-29 07:36:56 -07:00
|
|
|
use log::{debug, error};
|
2018-07-30 05:39:27 -07:00
|
|
|
|
2018-12-11 05:44:36 -08:00
|
|
|
use crate::network::messaging::Messaging;
|
|
|
|
use crate::network::{commst, connection};
|
2018-08-08 06:46:43 -07:00
|
|
|
use hbbft::broadcast::{Broadcast, Message};
|
2018-12-11 05:44:36 -08:00
|
|
|
use hbbft::crypto::{poly::Poly, SecretKey, SecretKeySet};
|
2019-01-08 00:30:37 -08:00
|
|
|
use hbbft::{ConsensusProtocol, NetworkInfo, SourcedMessage};
|
2018-03-16 11:12:14 -07:00
|
|
|
|
|
|
|
/// This is a structure to start a consensus node.
|
2018-03-27 13:59:38 -07:00
|
|
|
pub struct Node<T> {
|
2018-03-16 11:12:14 -07:00
|
|
|
/// Incoming connection socket.
|
|
|
|
addr: SocketAddr,
|
2018-04-05 05:09:46 -07:00
|
|
|
/// Sockets of remote nodes.
|
2018-04-01 14:29:12 -07:00
|
|
|
remotes: HashSet<SocketAddr>,
|
2018-03-27 13:59:38 -07:00
|
|
|
/// Optionally, a value to be broadcast by this node.
|
2018-04-30 08:55:51 -07:00
|
|
|
value: Option<T>,
|
2018-03-16 11:12:14 -07:00
|
|
|
}
|
|
|
|
|
2018-05-05 06:39:32 -07:00
|
|
|
impl<T: Clone + Debug + AsRef<[u8]> + PartialEq + Send + Sync + From<Vec<u8>> + Into<Vec<u8>>>
|
2018-03-27 13:59:38 -07:00
|
|
|
Node<T>
|
|
|
|
{
|
|
|
|
/// Consensus node constructor. It only initialises initial parameters.
|
2018-04-30 08:55:51 -07:00
|
|
|
pub fn new(addr: SocketAddr, remotes: HashSet<SocketAddr>, value: Option<T>) -> Self {
|
|
|
|
Node {
|
|
|
|
addr,
|
|
|
|
remotes,
|
|
|
|
value,
|
|
|
|
}
|
2018-03-16 11:12:14 -07:00
|
|
|
}
|
|
|
|
|
2018-03-27 13:59:38 -07:00
|
|
|
/// Consensus node procedure implementing HoneyBadgerBFT.
|
2018-12-10 09:10:13 -08:00
|
|
|
pub fn run(&self) -> Result<T, Box<(dyn std::any::Any + Send + 'static)>> {
|
2018-04-03 04:53:59 -07:00
|
|
|
let value = &self.value;
|
2018-05-08 07:20:32 -07:00
|
|
|
let (our_str, connections) = connection::make(&self.addr, &self.remotes);
|
|
|
|
let mut node_strs: Vec<String> = iter::once(our_str.clone())
|
|
|
|
.chain(connections.iter().map(|c| c.node_str.clone()))
|
|
|
|
.collect();
|
|
|
|
node_strs.sort();
|
|
|
|
let our_id = node_strs.binary_search(&our_str).unwrap();
|
2018-06-13 10:24:35 -07:00
|
|
|
let all_ids: BTreeSet<_> = (0..node_strs.len()).collect();
|
|
|
|
|
|
|
|
// FIXME: This example doesn't call algorithms that use cryptography. However the keys are
|
|
|
|
// required by the interface to all algorithms in Honey Badger. Therefore we set placeholder
|
|
|
|
// keys here. A fully-featured application would need to take appropriately initialized keys
|
|
|
|
// from elsewhere.
|
2018-10-06 03:33:07 -07:00
|
|
|
let secret_key_set = SecretKeySet::from(Poly::zero());
|
|
|
|
let sk_share = secret_key_set.secret_key_share(our_id);
|
2018-07-17 06:54:12 -07:00
|
|
|
let pub_key_set = secret_key_set.public_keys();
|
|
|
|
let sk = SecretKey::default();
|
|
|
|
let pub_keys = all_ids
|
|
|
|
.iter()
|
|
|
|
.map(|id| (*id, SecretKey::default().public_key()))
|
|
|
|
.collect();
|
2018-03-16 11:12:14 -07:00
|
|
|
|
2018-07-17 06:54:12 -07:00
|
|
|
let netinfo = NetworkInfo::new(our_id, sk_share, pub_key_set, sk, pub_keys);
|
2018-06-13 10:24:35 -07:00
|
|
|
|
|
|
|
if value.is_some() != (our_id == 0) {
|
2018-05-08 07:20:32 -07:00
|
|
|
panic!("Exactly the first node must propose a value.");
|
|
|
|
}
|
|
|
|
|
2018-04-05 09:10:08 -07:00
|
|
|
// Initialise the message delivery system and obtain TX and RX handles.
|
2018-08-08 06:46:43 -07:00
|
|
|
let messaging: Messaging<Message> = Messaging::new(all_ids.len());
|
2018-04-29 06:27:40 -07:00
|
|
|
let rxs_to_comms = messaging.rxs_to_comms();
|
|
|
|
let tx_from_comms = messaging.tx_from_comms();
|
2018-05-08 07:20:32 -07:00
|
|
|
let rx_to_algo = messaging.rx_to_algo();
|
2018-04-29 06:27:40 -07:00
|
|
|
let tx_from_algo = messaging.tx_from_algo();
|
2018-04-13 10:28:41 -07:00
|
|
|
let stop_tx = messaging.stop_tx();
|
2018-04-03 04:53:59 -07:00
|
|
|
|
2018-12-27 01:34:34 -08:00
|
|
|
let mut rng = rand::rngs::OsRng::new().unwrap();
|
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.
2018-12-14 04:51:09 -08:00
|
|
|
|
2018-03-20 09:32:19 -07:00
|
|
|
// All spawned threads will have exited by the end of the scope.
|
|
|
|
crossbeam::scope(|scope| {
|
2018-04-05 09:10:08 -07:00
|
|
|
// Start the centralised message delivery system.
|
2018-05-08 07:20:32 -07:00
|
|
|
let _msg_handle = messaging.spawn(scope);
|
2018-04-05 05:09:46 -07:00
|
|
|
|
2018-04-03 15:08:26 -07:00
|
|
|
// Associate a broadcast instance with this node. This instance will
|
|
|
|
// broadcast the proposed value. There is no remote node
|
|
|
|
// corresponding to this instance, and no dedicated comms task. The
|
|
|
|
// node index is 0.
|
2018-12-10 09:10:13 -08:00
|
|
|
let broadcast_handle = scope.spawn(move |_| {
|
2018-05-30 01:08:43 -07:00
|
|
|
let mut broadcast =
|
2018-07-11 12:15:08 -07:00
|
|
|
Broadcast::new(Arc::new(netinfo), 0).expect("failed to instantiate broadcast");
|
2018-05-10 08:50:07 -07:00
|
|
|
|
|
|
|
if let Some(v) = value {
|
2018-07-16 04:33:00 -07:00
|
|
|
// FIXME: Use the output.
|
2018-08-29 08:28:02 -07:00
|
|
|
let step = broadcast
|
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.
2018-12-14 04:51:09 -08:00
|
|
|
.handle_input(v.clone().into(), &mut rng)
|
2018-08-29 08:28:02 -07:00
|
|
|
.expect("propose value");
|
2018-07-18 05:15:47 -07:00
|
|
|
for msg in step.messages {
|
2018-05-10 08:50:07 -07:00
|
|
|
tx_from_algo.send(msg).expect("send from algo");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
loop {
|
|
|
|
// Receive a message from the socket IO task.
|
|
|
|
let message = rx_to_algo.recv().expect("receive from algo");
|
|
|
|
let SourcedMessage { source: i, message } = message;
|
|
|
|
debug!("{} received from {}: {:?}", our_id, i, message);
|
2018-07-10 14:27:18 -07:00
|
|
|
let step = broadcast
|
2018-05-14 05:35:06 -07:00
|
|
|
.handle_message(&i, message)
|
2018-05-10 08:50:07 -07:00
|
|
|
.expect("handle broadcast message");
|
2018-07-18 05:15:47 -07:00
|
|
|
for msg in step.messages {
|
2018-05-10 08:50:07 -07:00
|
|
|
debug!("{} sending to {:?}: {:?}", our_id, msg.target, msg.message);
|
|
|
|
tx_from_algo.send(msg).expect("send from algo");
|
|
|
|
}
|
2018-07-10 14:27:18 -07:00
|
|
|
if let Some(output) = step.output.into_iter().next() {
|
2018-05-09 04:38:41 -07:00
|
|
|
println!(
|
|
|
|
"Broadcast succeeded! Node {} output: {}",
|
|
|
|
our_id,
|
2018-05-10 08:50:07 -07:00
|
|
|
String::from_utf8(output).unwrap()
|
2018-04-30 08:55:51 -07:00
|
|
|
);
|
2018-05-10 08:50:07 -07:00
|
|
|
break;
|
2018-04-30 08:55:51 -07:00
|
|
|
}
|
2018-04-03 15:08:26 -07:00
|
|
|
}
|
2018-05-08 07:20:32 -07:00
|
|
|
});
|
2018-03-16 11:12:14 -07:00
|
|
|
|
2018-04-04 04:18:57 -07:00
|
|
|
// Start a comms task for each connection. Node indices of those
|
|
|
|
// tasks are 1 through N where N is the number of connections.
|
2018-04-02 13:26:40 -07:00
|
|
|
for (i, c) in connections.iter().enumerate() {
|
2018-04-03 04:53:59 -07:00
|
|
|
// Receive side of a single-consumer channel from algorithm
|
|
|
|
// actor tasks to the comms task.
|
2018-05-08 07:20:32 -07:00
|
|
|
let node_index = if c.node_str < our_str { i } else { i + 1 };
|
|
|
|
let rx_to_comms = &rxs_to_comms[node_index];
|
2018-04-02 13:26:40 -07:00
|
|
|
|
2018-12-10 09:10:13 -08:00
|
|
|
scope.spawn(move |_| {
|
2018-08-08 06:46:43 -07:00
|
|
|
match commst::CommsTask::<Message>::new(
|
2018-04-30 08:55:51 -07:00
|
|
|
tx_from_comms,
|
|
|
|
rx_to_comms,
|
|
|
|
// FIXME: handle error
|
|
|
|
c.stream.try_clone().unwrap(),
|
|
|
|
node_index,
|
2018-12-11 05:44:36 -08:00
|
|
|
)
|
|
|
|
.run()
|
2018-04-06 09:01:14 -07:00
|
|
|
{
|
|
|
|
Ok(_) => debug!("Comms task {} succeeded", node_index),
|
2018-04-30 08:55:51 -07:00
|
|
|
Err(e) => error!("Comms task {}: {:?}", node_index, e),
|
2018-04-06 09:01:14 -07:00
|
|
|
}
|
2018-04-01 14:29:12 -07:00
|
|
|
});
|
2018-04-20 01:42:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for the broadcast instances to finish before stopping the
|
|
|
|
// messaging task.
|
2018-12-10 09:10:13 -08:00
|
|
|
let _ = broadcast_handle.join();
|
2018-03-20 09:32:19 -07:00
|
|
|
|
2018-05-09 04:38:41 -07:00
|
|
|
// Wait another second so that pending messages get sent out.
|
|
|
|
thread::sleep(time::Duration::from_secs(1));
|
|
|
|
|
2018-04-13 10:28:41 -07:00
|
|
|
// Stop the messaging task.
|
2018-04-30 08:55:51 -07:00
|
|
|
stop_tx
|
|
|
|
.send(())
|
|
|
|
.map_err(|e| {
|
|
|
|
error!("{}", e);
|
2018-12-11 05:44:36 -08:00
|
|
|
})
|
|
|
|
.unwrap();
|
2018-04-13 10:28:41 -07:00
|
|
|
|
2018-05-08 07:20:32 -07:00
|
|
|
process::exit(0);
|
2018-04-06 09:01:14 -07:00
|
|
|
}) // end of thread scope
|
2018-03-16 11:12:14 -07:00
|
|
|
}
|
|
|
|
}
|