2018-07-16 06:31:21 -07:00
|
|
|
#![deny(unused_must_use)]
|
2018-05-16 05:23:57 -07:00
|
|
|
//! Network tests for Honey Badger.
|
|
|
|
|
2018-06-25 11:22:08 -07:00
|
|
|
extern crate bincode;
|
2018-05-16 05:23:57 -07:00
|
|
|
extern crate hbbft;
|
2018-07-15 03:12:27 -07:00
|
|
|
extern crate itertools;
|
2018-05-16 05:23:57 -07:00
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
|
|
|
extern crate env_logger;
|
2018-06-09 02:03:38 -07:00
|
|
|
extern crate pairing;
|
2018-05-16 05:23:57 -07:00
|
|
|
extern crate rand;
|
2018-06-25 04:07:31 -07:00
|
|
|
#[macro_use]
|
2018-07-05 09:20:53 -07:00
|
|
|
extern crate rand_derive;
|
|
|
|
#[macro_use]
|
2018-06-25 04:07:31 -07:00
|
|
|
extern crate serde_derive;
|
2018-05-16 05:23:57 -07:00
|
|
|
|
|
|
|
mod network;
|
|
|
|
|
2018-06-25 11:22:08 -07:00
|
|
|
use std::collections::BTreeMap;
|
2018-07-11 12:15:08 -07:00
|
|
|
use std::sync::Arc;
|
2018-05-16 05:23:57 -07:00
|
|
|
|
2018-07-15 03:12:27 -07:00
|
|
|
use itertools::Itertools;
|
2018-05-16 05:23:57 -07:00
|
|
|
use rand::Rng;
|
|
|
|
|
2018-07-02 07:21:21 -07:00
|
|
|
use hbbft::honey_badger::{self, Batch, HoneyBadger, MessageContent};
|
2018-06-25 11:22:08 -07:00
|
|
|
use hbbft::messaging::{NetworkInfo, Target, TargetedMessage};
|
2018-07-09 05:29:01 -07:00
|
|
|
use hbbft::transaction_queue::TransactionQueue;
|
2018-05-29 05:17:30 -07:00
|
|
|
|
2018-06-25 11:22:08 -07:00
|
|
|
use network::{
|
2018-07-05 09:20:53 -07:00
|
|
|
Adversary, MessageScheduler, MessageWithSender, NodeUid, RandomAdversary, SilentAdversary,
|
|
|
|
TestNetwork, TestNode,
|
2018-06-25 11:22:08 -07:00
|
|
|
};
|
|
|
|
|
2018-07-09 05:29:01 -07:00
|
|
|
type UsizeHoneyBadger = HoneyBadger<Vec<usize>, NodeUid>;
|
|
|
|
|
2018-06-25 13:18:25 -07:00
|
|
|
/// An adversary whose nodes only send messages with incorrect decryption shares.
|
2018-06-25 11:22:08 -07:00
|
|
|
pub struct FaultyShareAdversary {
|
|
|
|
num_good: usize,
|
|
|
|
num_adv: usize,
|
2018-07-11 12:15:08 -07:00
|
|
|
adv_nodes: BTreeMap<NodeUid, Arc<NetworkInfo<NodeUid>>>,
|
2018-06-25 11:22:08 -07:00
|
|
|
scheduler: MessageScheduler,
|
|
|
|
share_triggers: BTreeMap<u64, bool>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FaultyShareAdversary {
|
|
|
|
/// Creates a new silent adversary with the given message scheduler.
|
|
|
|
pub fn new(
|
|
|
|
num_good: usize,
|
|
|
|
num_adv: usize,
|
2018-07-11 12:15:08 -07:00
|
|
|
adv_nodes: BTreeMap<NodeUid, Arc<NetworkInfo<NodeUid>>>,
|
2018-06-25 11:22:08 -07:00
|
|
|
scheduler: MessageScheduler,
|
|
|
|
) -> FaultyShareAdversary {
|
|
|
|
FaultyShareAdversary {
|
|
|
|
num_good,
|
|
|
|
num_adv,
|
|
|
|
scheduler,
|
|
|
|
share_triggers: BTreeMap::new(),
|
|
|
|
adv_nodes,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-09 05:29:01 -07:00
|
|
|
impl Adversary<UsizeHoneyBadger> for FaultyShareAdversary {
|
|
|
|
fn pick_node(&self, nodes: &BTreeMap<NodeUid, TestNode<UsizeHoneyBadger>>) -> NodeUid {
|
2018-06-25 11:22:08 -07:00
|
|
|
self.scheduler.pick_node(nodes)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push_message(
|
|
|
|
&mut self,
|
|
|
|
sender_id: NodeUid,
|
|
|
|
msg: TargetedMessage<honey_badger::Message<NodeUid>, NodeUid>,
|
|
|
|
) {
|
|
|
|
let NodeUid(sender_id) = sender_id;
|
|
|
|
if sender_id < self.num_good {
|
|
|
|
if let TargetedMessage {
|
|
|
|
target: Target::All,
|
|
|
|
message,
|
|
|
|
} = msg
|
|
|
|
{
|
|
|
|
let epoch = message.epoch();
|
|
|
|
// Set the trigger to simulate decryption share messages.
|
|
|
|
self.share_triggers.entry(epoch).or_insert(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-09 05:29:01 -07:00
|
|
|
fn step(&mut self) -> Vec<MessageWithSender<UsizeHoneyBadger>> {
|
2018-06-25 11:22:08 -07:00
|
|
|
let mut outgoing = vec![];
|
|
|
|
let fake_proposal = &Vec::from("X marks the spot");
|
|
|
|
|
2018-06-25 13:18:25 -07:00
|
|
|
for (epoch, trigger_set) in &mut self.share_triggers {
|
2018-06-25 11:22:08 -07:00
|
|
|
if *trigger_set {
|
|
|
|
// Unset the trigger.
|
|
|
|
*trigger_set = false;
|
|
|
|
// Broadcast fake decryption shares from all adversarial nodes.
|
|
|
|
for sender_id in self.num_good..self.num_adv {
|
|
|
|
let adv_node = &self.adv_nodes[&NodeUid(sender_id)];
|
|
|
|
let fake_ciphertext = (*adv_node)
|
|
|
|
.public_key_set()
|
|
|
|
.public_key()
|
|
|
|
.encrypt(fake_proposal);
|
|
|
|
let share = adv_node
|
2018-07-17 06:54:12 -07:00
|
|
|
.secret_key_share()
|
2018-06-25 11:22:08 -07:00
|
|
|
.decrypt_share(&fake_ciphertext)
|
|
|
|
.expect("decryption share");
|
|
|
|
// Send the share to remote nodes.
|
|
|
|
for proposer_id in 0..self.num_good + self.num_adv {
|
2018-07-05 09:20:53 -07:00
|
|
|
outgoing.push(MessageWithSender::new(
|
2018-06-25 11:22:08 -07:00
|
|
|
NodeUid(sender_id),
|
|
|
|
Target::All.message(
|
|
|
|
MessageContent::DecryptionShare {
|
|
|
|
proposer_id: NodeUid(proposer_id),
|
|
|
|
share: share.clone(),
|
|
|
|
}.with_epoch(*epoch),
|
|
|
|
),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
outgoing
|
|
|
|
}
|
|
|
|
}
|
2018-05-16 05:23:57 -07:00
|
|
|
|
|
|
|
/// Proposes `num_txs` values and expects nodes to output and order them.
|
2018-07-09 05:29:01 -07:00
|
|
|
fn test_honey_badger<A>(mut network: TestNetwork<A, UsizeHoneyBadger>, num_txs: usize)
|
2018-05-16 05:23:57 -07:00
|
|
|
where
|
2018-07-09 05:29:01 -07:00
|
|
|
A: Adversary<UsizeHoneyBadger>,
|
2018-05-16 05:23:57 -07:00
|
|
|
{
|
2018-07-09 05:29:01 -07:00
|
|
|
let new_queue = |id: &NodeUid| (*id, TransactionQueue((0..num_txs).collect()));
|
|
|
|
let mut queues: BTreeMap<_, _> = network.nodes.keys().map(new_queue).collect();
|
2018-05-16 05:23:57 -07:00
|
|
|
|
|
|
|
// Returns `true` if the node has not output all transactions yet.
|
|
|
|
// If it has, and has advanced another epoch, it clears all messages for later epochs.
|
2018-07-09 05:29:01 -07:00
|
|
|
let node_busy = |node: &mut TestNode<UsizeHoneyBadger>| {
|
2018-07-15 03:12:27 -07:00
|
|
|
if node.outputs().iter().flat_map(Batch::iter).unique().count() < num_txs {
|
2018-05-16 05:23:57 -07:00
|
|
|
return true;
|
|
|
|
}
|
2018-06-18 07:14:17 -07:00
|
|
|
if node.outputs().last().unwrap().is_empty() {
|
2018-05-16 05:23:57 -07:00
|
|
|
let last = node.outputs().last().unwrap().epoch;
|
2018-06-20 08:47:52 -07:00
|
|
|
node.queue.retain(|(_, ref msg)| msg.epoch() < last);
|
2018-05-16 05:23:57 -07:00
|
|
|
}
|
|
|
|
false
|
|
|
|
};
|
|
|
|
|
2018-07-12 08:53:12 -07:00
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
2018-05-16 05:23:57 -07:00
|
|
|
// Handle messages in random order until all nodes have output all transactions.
|
|
|
|
while network.nodes.values_mut().any(node_busy) {
|
2018-07-12 08:53:12 -07:00
|
|
|
// If a node is expecting input, take it from the queue. Otherwise handle a message.
|
|
|
|
let input_ids: Vec<_> = network
|
|
|
|
.nodes
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, node)| !node.instance().has_input())
|
|
|
|
.map(|(id, _)| *id)
|
|
|
|
.collect();
|
|
|
|
if let Some(id) = rng.choose(&input_ids) {
|
|
|
|
let queue = queues.get_mut(id).unwrap();
|
|
|
|
queue.remove_all(network.nodes[id].outputs().iter().flat_map(Batch::iter));
|
|
|
|
network.input(*id, queue.choose(3, 10));
|
|
|
|
} else {
|
|
|
|
network.step();
|
2018-07-09 05:29:01 -07:00
|
|
|
}
|
2018-05-16 05:23:57 -07:00
|
|
|
}
|
2018-06-26 04:15:30 -07:00
|
|
|
verify_output_sequence(&network);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Verifies that all instances output the same sequence of batches.
|
2018-07-09 05:29:01 -07:00
|
|
|
fn verify_output_sequence<A>(network: &TestNetwork<A, UsizeHoneyBadger>)
|
2018-06-26 04:15:30 -07:00
|
|
|
where
|
2018-07-09 05:29:01 -07:00
|
|
|
A: Adversary<UsizeHoneyBadger>,
|
2018-06-26 04:15:30 -07:00
|
|
|
{
|
|
|
|
let mut expected: Option<BTreeMap<&_, &_>> = None;
|
|
|
|
for node in network.nodes.values() {
|
|
|
|
assert!(!node.outputs().is_empty());
|
|
|
|
let outputs: BTreeMap<&u64, &BTreeMap<NodeUid, Vec<usize>>> = node
|
|
|
|
.outputs()
|
|
|
|
.iter()
|
|
|
|
.map(
|
|
|
|
|Batch {
|
|
|
|
epoch,
|
2018-07-09 05:29:01 -07:00
|
|
|
contributions,
|
|
|
|
}| (epoch, contributions),
|
2018-06-26 04:15:30 -07:00
|
|
|
)
|
|
|
|
.collect();
|
|
|
|
if expected.is_none() {
|
|
|
|
expected = Some(outputs);
|
|
|
|
} else if let Some(expected) = &expected {
|
|
|
|
assert_eq!(expected, &outputs);
|
|
|
|
}
|
|
|
|
}
|
2018-05-16 05:23:57 -07:00
|
|
|
}
|
|
|
|
|
2018-07-11 12:15:08 -07:00
|
|
|
fn new_honey_badger(netinfo: Arc<NetworkInfo<NodeUid>>) -> UsizeHoneyBadger {
|
2018-07-09 05:29:01 -07:00
|
|
|
HoneyBadger::builder(netinfo).build()
|
2018-05-16 05:23:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn test_honey_badger_different_sizes<A, F>(new_adversary: F, num_txs: usize)
|
|
|
|
where
|
2018-07-09 05:29:01 -07:00
|
|
|
A: Adversary<UsizeHoneyBadger>,
|
2018-07-11 12:15:08 -07:00
|
|
|
F: Fn(usize, usize, BTreeMap<NodeUid, Arc<NetworkInfo<NodeUid>>>) -> A,
|
2018-05-16 05:23:57 -07:00
|
|
|
{
|
|
|
|
// This returns an error in all but the first test.
|
|
|
|
let _ = env_logger::try_init();
|
|
|
|
|
|
|
|
let mut rng = rand::thread_rng();
|
2018-07-12 08:53:12 -07:00
|
|
|
let sizes = vec![1, 2, 3, 5, rng.gen_range(6, 10)];
|
2018-05-16 05:23:57 -07:00
|
|
|
for size in sizes {
|
2018-06-25 11:22:08 -07:00
|
|
|
let num_adv_nodes = (size - 1) / 3;
|
|
|
|
let num_good_nodes = size - num_adv_nodes;
|
2018-05-16 05:23:57 -07:00
|
|
|
info!(
|
|
|
|
"Network size: {} good nodes, {} faulty nodes",
|
2018-06-25 11:22:08 -07:00
|
|
|
num_good_nodes, num_adv_nodes
|
2018-05-16 05:23:57 -07:00
|
|
|
);
|
2018-06-25 11:22:08 -07:00
|
|
|
let adversary = |adv_nodes| new_adversary(num_good_nodes, num_adv_nodes, adv_nodes);
|
|
|
|
let network = TestNetwork::new(num_good_nodes, num_adv_nodes, adversary, new_honey_badger);
|
2018-05-16 05:23:57 -07:00
|
|
|
test_honey_badger(network, num_txs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_honey_badger_random_delivery_silent() {
|
2018-06-25 11:22:08 -07:00
|
|
|
let new_adversary = |_: usize, _: usize, _| SilentAdversary::new(MessageScheduler::Random);
|
2018-07-12 08:53:12 -07:00
|
|
|
test_honey_badger_different_sizes(new_adversary, 30);
|
2018-05-16 05:23:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_honey_badger_first_delivery_silent() {
|
2018-06-25 11:22:08 -07:00
|
|
|
let new_adversary = |_: usize, _: usize, _| SilentAdversary::new(MessageScheduler::First);
|
2018-07-12 08:53:12 -07:00
|
|
|
test_honey_badger_different_sizes(new_adversary, 30);
|
2018-05-16 05:23:57 -07:00
|
|
|
}
|
2018-06-25 11:22:08 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_honey_badger_faulty_share() {
|
|
|
|
let new_adversary = |num_good: usize, num_adv: usize, adv_nodes| {
|
|
|
|
FaultyShareAdversary::new(num_good, num_adv, adv_nodes, MessageScheduler::Random)
|
|
|
|
};
|
|
|
|
test_honey_badger_different_sizes(new_adversary, 8);
|
|
|
|
}
|
2018-07-05 09:20:53 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_honey_badger_random_adversary() {
|
|
|
|
let new_adversary = |_, _, _| {
|
|
|
|
// A 10% injection chance is roughly ~13k extra messages added.
|
|
|
|
RandomAdversary::new(0.1, 0.1, || TargetedMessage {
|
|
|
|
target: Target::All,
|
|
|
|
message: rand::random(),
|
|
|
|
})
|
|
|
|
};
|
|
|
|
test_honey_badger_different_sizes(new_adversary, 8);
|
|
|
|
}
|