2018-03-16 11:12:14 -07:00
|
|
|
//! Networking controls of the consensus node.
|
2018-04-06 09:01:14 -07:00
|
|
|
use std::io;
|
2018-04-01 14:29:12 -07:00
|
|
|
use std::collections::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-03-20 09:32:19 -07:00
|
|
|
use crossbeam;
|
2018-04-11 09:57:30 -07:00
|
|
|
use merkle::Hashable;
|
2018-03-20 09:32:19 -07:00
|
|
|
|
2018-04-01 14:29:12 -07:00
|
|
|
use connection;
|
2018-03-19 10:12:20 -07:00
|
|
|
use broadcast;
|
|
|
|
use commst;
|
2018-04-05 09:10:08 -07:00
|
|
|
use messaging::Messaging;
|
2018-03-16 11:12:14 -07:00
|
|
|
|
2018-04-06 09:01:14 -07:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
IoError(io::Error),
|
|
|
|
CommsError(commst::Error),
|
|
|
|
NotImplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<io::Error> for Error {
|
|
|
|
fn from(err: io::Error) -> Error { Error::IoError(err) }
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<commst::Error> for Error {
|
|
|
|
fn from(err: commst::Error) -> Error { Error::CommsError(err) }
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
value: Option<T>
|
2018-03-16 11:12:14 -07:00
|
|
|
}
|
|
|
|
|
2018-04-11 09:57:30 -07:00
|
|
|
impl<T: Clone + Debug + Hashable + 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-01 14:29:12 -07:00
|
|
|
pub fn new(addr: SocketAddr,
|
|
|
|
remotes: HashSet<SocketAddr>,
|
|
|
|
value: Option<T>) -> Self
|
2018-03-27 13:59:38 -07:00
|
|
|
{
|
|
|
|
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-04-06 09:01:14 -07:00
|
|
|
pub fn run(&self) -> Result<T, Error>
|
2018-03-19 10:12:20 -07:00
|
|
|
{
|
2018-04-03 04:53:59 -07:00
|
|
|
let value = &self.value;
|
2018-04-01 14:29:12 -07:00
|
|
|
let connections = connection::make(&self.addr, &self.remotes);
|
2018-04-05 09:10:08 -07:00
|
|
|
let num_nodes = connections.len() + 1;
|
2018-03-16 11:12:14 -07:00
|
|
|
|
2018-04-05 09:10:08 -07:00
|
|
|
// Initialise the message delivery system and obtain TX and RX handles.
|
|
|
|
let messaging: Messaging<T> = Messaging::new(num_nodes);
|
|
|
|
let to_comms_rxs = messaging.to_comms_rxs();
|
|
|
|
let from_comms_tx = messaging.from_comms_tx();
|
|
|
|
let to_algo_rxs = messaging.to_algo_rxs();
|
|
|
|
let from_algo_tx = messaging.from_algo_tx();
|
2018-04-03 04:53:59 -07: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-04-05 05:09:46 -07:00
|
|
|
messaging.spawn(scope);
|
|
|
|
|
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-04-05 05:09:46 -07:00
|
|
|
let ref to_algo_rx0 = to_algo_rxs[0];
|
2018-04-03 15:08:26 -07:00
|
|
|
scope.spawn(move || {
|
2018-04-05 05:09:46 -07:00
|
|
|
match broadcast::Instance::new(from_algo_tx,
|
|
|
|
to_algo_rx0,
|
2018-04-03 15:08:26 -07:00
|
|
|
value.to_owned(),
|
2018-04-05 05:09:46 -07:00
|
|
|
num_nodes,
|
2018-04-03 15:08:26 -07:00
|
|
|
0)
|
|
|
|
.run()
|
|
|
|
{
|
2018-04-06 05:57:48 -07:00
|
|
|
Ok(t) => {
|
|
|
|
debug!("Broadcast instance 0 succeeded: {}",
|
2018-04-11 07:44:59 -07:00
|
|
|
String::from_utf8(T::into(t)).unwrap());
|
2018-04-06 05:57:48 -07:00
|
|
|
},
|
2018-04-06 09:01:14 -07:00
|
|
|
Err(e) => error!("Broadcast instance 0: {:?}", e)
|
2018-04-03 15:08:26 -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-04-05 05:09:46 -07:00
|
|
|
let ref to_comms_rx = to_comms_rxs[i];
|
2018-04-04 04:18:57 -07:00
|
|
|
let node_index = i + 1;
|
2018-04-02 13:26:40 -07:00
|
|
|
|
2018-04-01 14:29:12 -07:00
|
|
|
scope.spawn(move || {
|
2018-04-06 09:01:14 -07:00
|
|
|
match commst::CommsTask::new(from_comms_tx,
|
|
|
|
to_comms_rx,
|
|
|
|
// FIXME: handle error
|
|
|
|
c.stream.try_clone().unwrap(),
|
|
|
|
node_index)
|
|
|
|
.run()
|
|
|
|
{
|
|
|
|
Ok(_) => debug!("Comms task {} succeeded", node_index),
|
|
|
|
Err(e) => error!("Comms task {}: {:?}", node_index, e)
|
|
|
|
}
|
2018-04-01 14:29:12 -07:00
|
|
|
});
|
2018-03-19 10:12:20 -07:00
|
|
|
|
2018-04-05 05:09:46 -07:00
|
|
|
|
2018-04-02 13:26:40 -07:00
|
|
|
// Associate a broadcast instance to the above comms task.
|
2018-04-05 05:09:46 -07:00
|
|
|
let ref to_algo_rx = to_algo_rxs[node_index];
|
2018-04-02 13:26:40 -07:00
|
|
|
scope.spawn(move || {
|
2018-04-05 05:09:46 -07:00
|
|
|
match broadcast::Instance::new(from_algo_tx,
|
|
|
|
to_algo_rx,
|
2018-04-03 15:08:26 -07:00
|
|
|
None,
|
2018-04-05 05:09:46 -07:00
|
|
|
num_nodes,
|
2018-04-04 04:18:57 -07:00
|
|
|
node_index)
|
2018-04-02 13:26:40 -07:00
|
|
|
.run()
|
|
|
|
{
|
2018-04-06 05:57:48 -07:00
|
|
|
Ok(t) => {
|
|
|
|
debug!("Broadcast instance {} succeeded: {}",
|
|
|
|
node_index,
|
2018-04-11 07:44:59 -07:00
|
|
|
String::from_utf8(T::into(t)).unwrap());
|
2018-04-06 05:57:48 -07:00
|
|
|
},
|
2018-04-06 09:01:14 -07:00
|
|
|
Err(e) => error!("Broadcast instance {}: {:?}",
|
|
|
|
node_index, e)
|
2018-04-02 13:26:40 -07:00
|
|
|
}
|
|
|
|
});
|
2018-03-20 09:32:19 -07:00
|
|
|
}
|
|
|
|
|
2018-04-02 13:26:40 -07:00
|
|
|
// TODO: continue the implementation of the asynchronous common
|
|
|
|
// subset algorithm.
|
2018-04-06 09:01:14 -07:00
|
|
|
Err(Error::NotImplemented)
|
|
|
|
}) // end of thread scope
|
2018-03-16 11:12:14 -07:00
|
|
|
}
|
|
|
|
}
|
2018-04-11 09:57:30 -07:00
|
|
|
|
|
|
|
// #[cfg(test)]
|
|
|
|
// mod tests {
|
|
|
|
// use std::collections::HashSet;
|
|
|
|
// use node;
|
|
|
|
|
|
|
|
// /// Test that the node works to completion.
|
|
|
|
// #[test]
|
|
|
|
// fn test_node_0() {
|
|
|
|
// let node = node::Node::new("127.0.0.1:10000".parse().unwrap(),
|
|
|
|
// HashSet::new(),
|
|
|
|
// Some("abc".as_bytes().to_vec()));
|
|
|
|
// let result = node.run();
|
|
|
|
// assert!(match result { Err(node::Error::NotImplemented) => true,
|
|
|
|
// _ => false });
|
|
|
|
// }
|
|
|
|
// }
|