2018-04-12 09:17:33 -07:00
|
|
|
//! Integration test of the reliable broadcast protocol.
|
|
|
|
|
|
|
|
extern crate hbbft;
|
2018-04-13 10:28:41 -07:00
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
|
|
|
extern crate simple_logger;
|
2018-04-12 09:17:33 -07:00
|
|
|
extern crate crossbeam;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate crossbeam_channel;
|
|
|
|
extern crate merkle;
|
|
|
|
|
|
|
|
mod netsim;
|
|
|
|
|
2018-04-20 17:29:54 -07:00
|
|
|
use std::collections::{BTreeMap, HashSet, HashMap, VecDeque};
|
2018-04-16 06:03:32 -07:00
|
|
|
use std::fmt;
|
2018-04-12 09:17:33 -07:00
|
|
|
use std::fmt::Debug;
|
|
|
|
use std::io;
|
2018-04-20 17:29:54 -07:00
|
|
|
use std::net::SocketAddr;
|
2018-04-24 03:29:13 -07:00
|
|
|
use std::sync::RwLock;
|
2018-04-13 10:28:41 -07:00
|
|
|
use crossbeam::{Scope, ScopedJoinHandle};
|
|
|
|
use crossbeam_channel::{bounded, Sender, Receiver};
|
2018-04-12 09:17:33 -07:00
|
|
|
|
|
|
|
use hbbft::proto::*;
|
2018-04-18 01:15:12 -07:00
|
|
|
use hbbft::messaging;
|
2018-04-24 03:29:13 -07:00
|
|
|
use hbbft::messaging::{QMessage, NodeUid, AlgoError, Algorithm, ProposedValue,
|
|
|
|
AlgoMessage, Handler,
|
|
|
|
MessageLoopState, MessageLoop, RemoteMessage};
|
2018-04-13 10:28:41 -07:00
|
|
|
use hbbft::broadcast;
|
2018-04-12 09:17:33 -07:00
|
|
|
|
|
|
|
use netsim::NetSim;
|
|
|
|
|
|
|
|
/// This is a structure to start a consensus node.
|
2018-04-24 03:29:13 -07:00
|
|
|
pub struct TestNode<'a> {
|
2018-04-13 10:28:41 -07:00
|
|
|
/// Node identifier.
|
|
|
|
node_index: usize,
|
|
|
|
/// Total number of nodes.
|
|
|
|
num_nodes: usize,
|
2018-04-20 17:29:54 -07:00
|
|
|
/// TX handles indexed with the receiving node address. One handle for each
|
|
|
|
/// other node.
|
2018-04-24 03:29:13 -07:00
|
|
|
txs: HashMap<NodeUid, Sender<Message<ProposedValue>>>,
|
2018-04-20 17:29:54 -07:00
|
|
|
/// RX handle indexed with the transmitting node address. One handle for
|
|
|
|
/// each other node.
|
2018-04-24 03:29:13 -07:00
|
|
|
rxs: HashMap<NodeUid, Receiver<Message<ProposedValue>>>,
|
2018-04-12 09:17:33 -07:00
|
|
|
/// Optionally, a value to be broadcast by this node.
|
2018-04-24 03:29:13 -07:00
|
|
|
value: Option<ProposedValue>,
|
|
|
|
/// Messaging system.
|
|
|
|
message_loop: MessageLoop<'a, TestAlgoError>
|
2018-04-12 09:17:33 -07:00
|
|
|
}
|
|
|
|
|
2018-04-24 03:29:13 -07:00
|
|
|
impl<'a> TestNode<'a>
|
2018-04-12 09:17:33 -07:00
|
|
|
{
|
|
|
|
/// Consensus node constructor. It only initialises initial parameters.
|
2018-04-13 10:28:41 -07:00
|
|
|
pub fn new(node_index: usize,
|
|
|
|
num_nodes: usize,
|
2018-04-24 03:29:13 -07:00
|
|
|
txs: HashMap<NodeUid, Sender<Message<ProposedValue>>>,
|
|
|
|
rxs: HashMap<NodeUid, Receiver<Message<ProposedValue>>>,
|
2018-04-20 17:29:54 -07:00
|
|
|
value: Option<ProposedValue>) -> Self
|
2018-04-12 09:17:33 -07:00
|
|
|
{
|
|
|
|
TestNode {
|
2018-04-20 17:29:54 -07:00
|
|
|
node_index,
|
|
|
|
num_nodes,
|
2018-04-24 03:29:13 -07:00
|
|
|
txs: txs.clone(),
|
2018-04-20 17:29:54 -07:00
|
|
|
rxs,
|
2018-04-24 03:29:13 -07:00
|
|
|
value,
|
|
|
|
message_loop: MessageLoop::new(txs)
|
2018-04-12 09:17:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-24 03:29:13 -07:00
|
|
|
pub fn run(&'a self) -> Result<HashSet<ProposedValue>, Error>
|
2018-04-13 10:28:41 -07:00
|
|
|
{
|
2018-04-24 03:39:40 -07:00
|
|
|
let node0_uid = node_addr(0);
|
2018-04-24 03:29:13 -07:00
|
|
|
self.message_loop.insert_algo(Algorithm::Broadcast(node0_uid), self);
|
2018-04-13 10:28:41 -07:00
|
|
|
|
2018-04-20 17:29:54 -07:00
|
|
|
Err(Error::NotImplemented)
|
2018-04-12 09:17:33 -07:00
|
|
|
}
|
2018-04-18 01:15:12 -07:00
|
|
|
|
2018-04-24 03:29:13 -07:00
|
|
|
pub fn handler(&self, m: QMessage, tx: Sender<QMessage>) ->
|
|
|
|
Result<MessageLoopState, TestAlgoError>
|
|
|
|
{
|
|
|
|
Err(TestAlgoError::TestError)
|
2018-04-18 01:15:12 -07:00
|
|
|
}
|
2018-04-12 09:17:33 -07:00
|
|
|
}
|
|
|
|
|
2018-04-24 03:29:13 -07:00
|
|
|
impl<'a> Handler<TestAlgoError> for TestNode<'a> {
|
|
|
|
fn handle(&self, m: QMessage, tx: Sender<QMessage>) ->
|
|
|
|
Result<MessageLoopState, TestAlgoError>
|
|
|
|
{
|
|
|
|
self.handler(m, tx)
|
|
|
|
}
|
|
|
|
}
|
2018-04-22 04:40:40 -07:00
|
|
|
|
2018-04-24 03:29:13 -07:00
|
|
|
pub fn broadcast_handler(txs: RwLock<HashMap<NodeUid,
|
|
|
|
Sender<Message<ProposedValue
|
|
|
|
>
|
|
|
|
>
|
|
|
|
>
|
|
|
|
>,
|
|
|
|
m: &QMessage, tx: Sender<QMessage>) ->
|
|
|
|
Result<MessageLoopState, TestAlgoError>
|
|
|
|
{
|
|
|
|
Err(TestAlgoError::TestError)
|
2018-04-20 17:29:54 -07:00
|
|
|
}
|
|
|
|
|
2018-04-13 10:28:41 -07:00
|
|
|
#[derive(Clone, Debug)]
|
2018-04-14 03:27:17 -07:00
|
|
|
pub enum Error {
|
|
|
|
Broadcast(broadcast::Error),
|
2018-04-12 09:17:33 -07:00
|
|
|
NotImplemented
|
|
|
|
}
|
|
|
|
|
2018-04-14 03:27:17 -07:00
|
|
|
impl From<broadcast::Error> for Error {
|
|
|
|
fn from(e: broadcast::Error) -> Error { Error::Broadcast(e) }
|
2018-04-13 10:28:41 -07:00
|
|
|
}
|
|
|
|
|
2018-04-20 17:29:54 -07:00
|
|
|
fn proposed_value(n: usize) -> ProposedValue {
|
|
|
|
let b: u8 = (n & 0xff) as u8;
|
|
|
|
vec![b; 10]
|
2018-04-16 06:03:32 -07:00
|
|
|
}
|
|
|
|
|
2018-04-22 04:40:40 -07:00
|
|
|
fn node_addr(node_index: usize) -> SocketAddr {
|
|
|
|
format!("127.0.0.1:{}", node_index).parse().unwrap()
|
|
|
|
}
|
|
|
|
|
2018-04-13 10:28:41 -07:00
|
|
|
/// Creates a vector of test nodes but does not run them.
|
2018-04-20 17:29:54 -07:00
|
|
|
fn create_test_nodes(num_nodes: usize,
|
2018-04-24 03:29:13 -07:00
|
|
|
net: &NetSim<Message<Vec<u8>>>) ->
|
2018-04-20 17:29:54 -07:00
|
|
|
Vec<TestNode>
|
2018-04-12 09:17:33 -07:00
|
|
|
{
|
|
|
|
let mut nodes = Vec::new();
|
|
|
|
for n in 0..num_nodes {
|
2018-04-20 17:29:54 -07:00
|
|
|
let value = proposed_value(n);
|
|
|
|
let mut txs = HashMap::new();
|
|
|
|
let mut rxs = HashMap::new();
|
2018-04-13 10:28:41 -07:00
|
|
|
// Set up comms channels to other nodes.
|
2018-04-12 09:17:33 -07:00
|
|
|
for m in 0..num_nodes {
|
|
|
|
if n == m {
|
|
|
|
// Skip the channel back to the node itself.
|
|
|
|
continue;
|
|
|
|
}
|
2018-04-22 04:40:40 -07:00
|
|
|
let addr = node_addr(m);
|
2018-04-20 17:29:54 -07:00
|
|
|
txs.insert(addr, net.tx(n, m));
|
|
|
|
rxs.insert(addr, net.rx(m, n));
|
2018-04-12 09:17:33 -07:00
|
|
|
}
|
2018-04-13 10:28:41 -07:00
|
|
|
nodes.push(TestNode::new(n, num_nodes, txs, rxs, Some(value)));
|
2018-04-12 09:17:33 -07:00
|
|
|
}
|
|
|
|
nodes
|
|
|
|
}
|
|
|
|
|
2018-04-18 13:06:54 -07:00
|
|
|
#[derive(Debug)]
|
2018-04-19 10:27:26 -07:00
|
|
|
pub enum TestAlgoError {
|
2018-04-18 13:06:54 -07:00
|
|
|
TestError
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AlgoError for TestAlgoError {
|
|
|
|
fn to_str(&self) -> &'static str {
|
|
|
|
"TestError"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-12 09:17:33 -07:00
|
|
|
#[test]
|
|
|
|
fn test_4_broadcast_nodes() {
|
2018-04-13 10:28:41 -07:00
|
|
|
simple_logger::init_with_level(log::Level::Debug).unwrap();
|
|
|
|
|
2018-04-12 09:17:33 -07:00
|
|
|
const NUM_NODES: usize = 4;
|
2018-04-14 03:27:17 -07:00
|
|
|
let net: NetSim<Message<Vec<u8>>> = NetSim::new(NUM_NODES);
|
2018-04-12 09:17:33 -07:00
|
|
|
let nodes = create_test_nodes(NUM_NODES, &net);
|
|
|
|
|
|
|
|
crossbeam::scope(|scope| {
|
2018-04-24 03:29:13 -07:00
|
|
|
for node in nodes.iter() {
|
2018-04-20 17:29:54 -07:00
|
|
|
scope.spawn(move || {
|
2018-04-24 03:29:13 -07:00
|
|
|
debug!("Running {:?}", node.node_index);
|
2018-04-20 17:29:54 -07:00
|
|
|
node.run().unwrap();
|
|
|
|
});
|
2018-04-12 09:17:33 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|