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;
|
2018-05-08 07:20:32 -07:00
|
|
|
extern crate env_logger;
|
2018-06-09 02:03:38 -07:00
|
|
|
extern crate pairing;
|
2018-05-01 09:32:01 -07:00
|
|
|
extern crate rand;
|
2018-06-25 04:07:31 -07:00
|
|
|
#[macro_use]
|
|
|
|
extern crate serde_derive;
|
2018-04-12 09:17:33 -07:00
|
|
|
|
2018-05-16 05:23:57 -07:00
|
|
|
mod network;
|
2018-05-02 06:34:30 -07:00
|
|
|
|
2018-05-16 05:23:57 -07:00
|
|
|
use std::collections::{BTreeMap, BTreeSet};
|
2018-05-29 05:17:30 -07:00
|
|
|
use std::iter::once;
|
2018-07-11 12:15:08 -07:00
|
|
|
use std::sync::Arc;
|
2018-05-02 06:34:30 -07:00
|
|
|
|
2018-05-16 05:23:57 -07:00
|
|
|
use rand::Rng;
|
2018-05-02 06:34:30 -07:00
|
|
|
|
2018-05-16 05:23:57 -07:00
|
|
|
use hbbft::broadcast::{Broadcast, BroadcastMessage};
|
2018-06-09 02:03:38 -07:00
|
|
|
use hbbft::crypto::SecretKeySet;
|
2018-05-29 05:17:30 -07:00
|
|
|
use hbbft::messaging::{DistAlgorithm, NetworkInfo, TargetedMessage};
|
2018-05-16 05:23:57 -07:00
|
|
|
use network::{Adversary, MessageScheduler, NodeUid, SilentAdversary, TestNetwork, TestNode};
|
2018-05-02 06:34:30 -07:00
|
|
|
|
2018-05-14 05:35:06 -07:00
|
|
|
/// An adversary that inputs an alternate value.
|
2018-05-03 01:07:37 -07:00
|
|
|
struct ProposeAdversary {
|
|
|
|
scheduler: MessageScheduler,
|
2018-05-14 05:35:06 -07:00
|
|
|
good_nodes: BTreeSet<NodeUid>,
|
|
|
|
adv_nodes: BTreeSet<NodeUid>,
|
2018-05-03 01:07:37 -07:00
|
|
|
has_sent: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ProposeAdversary {
|
|
|
|
/// Creates a new replay adversary with the given message scheduler.
|
|
|
|
fn new(
|
|
|
|
scheduler: MessageScheduler,
|
2018-05-14 05:35:06 -07:00
|
|
|
good_nodes: BTreeSet<NodeUid>,
|
|
|
|
adv_nodes: BTreeSet<NodeUid>,
|
2018-05-03 01:07:37 -07:00
|
|
|
) -> ProposeAdversary {
|
|
|
|
ProposeAdversary {
|
|
|
|
scheduler,
|
|
|
|
good_nodes,
|
|
|
|
adv_nodes,
|
|
|
|
has_sent: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 05:35:06 -07:00
|
|
|
impl Adversary<Broadcast<NodeUid>> for ProposeAdversary {
|
2018-07-05 07:47:45 -07:00
|
|
|
fn pick_node(&mut self, nodes: &BTreeMap<NodeUid, TestNode<Broadcast<NodeUid>>>) -> NodeUid {
|
2018-05-03 01:07:37 -07:00
|
|
|
self.scheduler.pick_node(nodes)
|
|
|
|
}
|
|
|
|
|
2018-05-14 05:35:06 -07:00
|
|
|
fn push_message(&mut self, _: NodeUid, _: TargetedMessage<BroadcastMessage, NodeUid>) {
|
2018-05-03 01:07:37 -07:00
|
|
|
// All messages are ignored.
|
|
|
|
}
|
|
|
|
|
2018-05-14 05:35:06 -07:00
|
|
|
fn step(&mut self) -> Vec<(NodeUid, TargetedMessage<BroadcastMessage, NodeUid>)> {
|
2018-05-03 01:07:37 -07:00
|
|
|
if self.has_sent {
|
|
|
|
return vec![];
|
|
|
|
}
|
|
|
|
self.has_sent = true;
|
2018-05-21 02:01:49 -07:00
|
|
|
let node_ids: BTreeSet<NodeUid> = self
|
|
|
|
.adv_nodes
|
2018-05-03 01:07:37 -07:00
|
|
|
.iter()
|
2018-05-14 05:35:06 -07:00
|
|
|
.chain(self.good_nodes.iter())
|
2018-05-03 01:07:37 -07:00
|
|
|
.cloned()
|
|
|
|
.collect();
|
2018-05-14 07:16:57 -07:00
|
|
|
let id = match self.adv_nodes.iter().next() {
|
|
|
|
Some(id) => *id,
|
|
|
|
None => return vec![],
|
|
|
|
};
|
2018-06-09 02:03:38 -07:00
|
|
|
|
|
|
|
// FIXME: Take the correct, known keys from the network.
|
|
|
|
let mut rng = rand::thread_rng();
|
2018-06-21 08:31:15 -07:00
|
|
|
let sk_set = SecretKeySet::random(self.adv_nodes.len(), &mut rng);
|
2018-06-09 02:03:38 -07:00
|
|
|
let pk_set = sk_set.public_keys();
|
|
|
|
|
2018-07-11 12:15:08 -07:00
|
|
|
let netinfo = Arc::new(NetworkInfo::new(
|
2018-06-09 02:03:38 -07:00
|
|
|
id,
|
|
|
|
node_ids,
|
|
|
|
sk_set.secret_key_share(0),
|
|
|
|
pk_set,
|
|
|
|
));
|
2018-05-29 05:17:30 -07:00
|
|
|
let mut bc = Broadcast::new(netinfo, id).expect("broadcast instance");
|
2018-05-14 05:35:06 -07:00
|
|
|
bc.input(b"Fake news".to_vec()).expect("propose");
|
|
|
|
bc.message_iter().map(|msg| (id, msg)).collect()
|
2018-05-03 01:07:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-02 06:34:30 -07:00
|
|
|
/// Broadcasts a value from node 0 and expects all good nodes to receive it.
|
2018-05-14 05:35:06 -07:00
|
|
|
fn test_broadcast<A: Adversary<Broadcast<NodeUid>>>(
|
|
|
|
mut network: TestNetwork<A, Broadcast<NodeUid>>,
|
|
|
|
proposed_value: &[u8],
|
|
|
|
) {
|
2018-05-08 07:20:32 -07:00
|
|
|
// This returns an error in all but the first test.
|
|
|
|
let _ = env_logger::try_init();
|
2018-05-01 09:32:01 -07:00
|
|
|
|
2018-05-04 02:14:19 -07:00
|
|
|
// Make node 0 propose the value.
|
2018-05-14 05:35:06 -07:00
|
|
|
network.input(NodeUid(0), proposed_value.to_vec());
|
2018-05-01 09:32:01 -07:00
|
|
|
|
|
|
|
// Handle messages in random order until all nodes have output the proposed value.
|
2018-05-19 05:29:31 -07:00
|
|
|
while !network.nodes.values().all(TestNode::terminated) {
|
|
|
|
network.step();
|
|
|
|
}
|
|
|
|
// Verify that all instances output the proposed value.
|
|
|
|
for node in network.nodes.values() {
|
2018-05-29 05:17:30 -07:00
|
|
|
assert!(once(&proposed_value.to_vec()).eq(node.outputs()));
|
2018-05-01 09:32:01 -07:00
|
|
|
}
|
2018-06-25 12:09:45 -07:00
|
|
|
assert!(once(&proposed_value.to_vec()).eq(network.observer.outputs()));
|
2018-04-12 09:17:33 -07:00
|
|
|
}
|
2018-05-02 06:34:30 -07:00
|
|
|
|
2018-07-11 12:15:08 -07:00
|
|
|
fn new_broadcast(netinfo: Arc<NetworkInfo<NodeUid>>) -> Broadcast<NodeUid> {
|
2018-05-29 05:17:30 -07:00
|
|
|
Broadcast::new(netinfo, NodeUid(0)).expect("Instantiate broadcast")
|
2018-05-16 05:23:57 -07:00
|
|
|
}
|
|
|
|
|
2018-05-14 07:16:57 -07:00
|
|
|
fn test_broadcast_different_sizes<A, F>(new_adversary: F, proposed_value: &[u8])
|
|
|
|
where
|
|
|
|
A: Adversary<Broadcast<NodeUid>>,
|
|
|
|
F: Fn(usize, usize) -> A,
|
|
|
|
{
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let sizes = (1..6)
|
2018-05-29 05:17:30 -07:00
|
|
|
.chain(once(rng.gen_range(6, 20)))
|
|
|
|
.chain(once(rng.gen_range(30, 50)));
|
2018-05-14 07:16:57 -07:00
|
|
|
for size in sizes {
|
|
|
|
let num_faulty_nodes = (size - 1) / 3;
|
|
|
|
let num_good_nodes = size - num_faulty_nodes;
|
2018-05-17 08:38:45 -07:00
|
|
|
info!(
|
2018-05-14 07:16:57 -07:00
|
|
|
"Network size: {} good nodes, {} faulty nodes",
|
|
|
|
num_good_nodes, num_faulty_nodes
|
|
|
|
);
|
2018-06-25 11:22:08 -07:00
|
|
|
let adversary = |_| new_adversary(num_good_nodes, num_faulty_nodes);
|
2018-05-16 05:23:57 -07:00
|
|
|
let network = TestNetwork::new(num_good_nodes, num_faulty_nodes, adversary, new_broadcast);
|
2018-05-14 07:16:57 -07:00
|
|
|
test_broadcast(network, proposed_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-04 02:14:19 -07:00
|
|
|
#[test]
|
2018-05-08 07:20:32 -07:00
|
|
|
fn test_8_broadcast_equal_leaves_silent() {
|
2018-06-25 11:22:08 -07:00
|
|
|
let adversary = |_| SilentAdversary::new(MessageScheduler::Random);
|
2018-05-04 02:14:19 -07:00
|
|
|
// Space is ASCII character 32. So 32 spaces will create shards that are all equal, even if the
|
|
|
|
// length of the value is inserted.
|
2018-05-16 05:23:57 -07:00
|
|
|
test_broadcast(
|
|
|
|
TestNetwork::new(8, 0, adversary, new_broadcast),
|
|
|
|
&[b' '; 32],
|
|
|
|
);
|
2018-05-04 02:14:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2018-05-14 07:16:57 -07:00
|
|
|
fn test_broadcast_random_delivery_silent() {
|
|
|
|
let new_adversary = |_: usize, _: usize| SilentAdversary::new(MessageScheduler::Random);
|
|
|
|
test_broadcast_different_sizes(new_adversary, b"Foo");
|
2018-05-04 02:14:19 -07:00
|
|
|
}
|
|
|
|
|
2018-05-02 06:34:30 -07:00
|
|
|
#[test]
|
2018-05-16 05:23:57 -07:00
|
|
|
fn test_broadcast_first_delivery_silent() {
|
2018-05-14 07:16:57 -07:00
|
|
|
let new_adversary = |_: usize, _: usize| SilentAdversary::new(MessageScheduler::First);
|
|
|
|
test_broadcast_different_sizes(new_adversary, b"Foo");
|
2018-05-08 07:20:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2018-05-16 05:23:57 -07:00
|
|
|
fn test_broadcast_random_delivery_adv_propose() {
|
2018-05-14 07:16:57 -07:00
|
|
|
let new_adversary = |num_good_nodes: usize, num_faulty_nodes: usize| {
|
|
|
|
let good_nodes: BTreeSet<NodeUid> = (0..num_good_nodes).map(NodeUid).collect();
|
|
|
|
let adv_nodes: BTreeSet<NodeUid> = (num_good_nodes..(num_good_nodes + num_faulty_nodes))
|
|
|
|
.map(NodeUid)
|
|
|
|
.collect();
|
|
|
|
ProposeAdversary::new(MessageScheduler::Random, good_nodes, adv_nodes)
|
|
|
|
};
|
|
|
|
test_broadcast_different_sizes(new_adversary, b"Foo");
|
2018-05-03 01:07:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2018-05-16 05:23:57 -07:00
|
|
|
fn test_broadcast_first_delivery_adv_propose() {
|
2018-05-14 07:16:57 -07:00
|
|
|
let new_adversary = |num_good_nodes: usize, num_faulty_nodes: usize| {
|
|
|
|
let good_nodes: BTreeSet<NodeUid> = (0..num_good_nodes).map(NodeUid).collect();
|
|
|
|
let adv_nodes: BTreeSet<NodeUid> = (num_good_nodes..(num_good_nodes + num_faulty_nodes))
|
|
|
|
.map(NodeUid)
|
|
|
|
.collect();
|
|
|
|
ProposeAdversary::new(MessageScheduler::First, good_nodes, adv_nodes)
|
|
|
|
};
|
|
|
|
test_broadcast_different_sizes(new_adversary, b"Foo");
|
2018-05-03 01:07:37 -07:00
|
|
|
}
|