Fix DHB test with 1 validator; purge key gen msgs.

This adds a bit of special handling to make sure messages don't stay in
the queue forever in the `dynamic_honey_badger` tests, even if there is
only one validator: the problem was that the single validator is always
ready for input, so it never processed incoming messages. However, to
add the new validator, it needs to process the joining node's key
generation messages.

`DynamicHoneyBadger` now also removes committed key generation messages
from the queue, to avoid committing duplicates.
This commit is contained in:
Andreas Fackler 2018-07-15 12:12:27 +02:00
parent 01ad256363
commit 55ad2eae44
4 changed files with 35 additions and 42 deletions

View File

@ -258,10 +258,16 @@ where
let mut batch = Batch::new(hb_batch.epoch + self.start_epoch);
// Add the user transactions to `batch` and handle votes and DKG messages.
for (id, int_contrib) in hb_batch.contributions {
let votes = int_contrib.votes;
let InternalContrib {
votes,
key_gen_messages,
contrib,
} = int_contrib;
fault_log.extend(self.vote_counter.add_committed_votes(&id, votes)?);
batch.contributions.insert(id, int_contrib.contrib);
for SignedKeyGenMsg(epoch, s_id, kg_msg, sig) in int_contrib.key_gen_messages {
batch.contributions.insert(id, contrib);
self.key_gen_msg_buffer
.retain(|skgm| !key_gen_messages.contains(skgm));
for SignedKeyGenMsg(epoch, s_id, kg_msg, sig) in key_gen_messages {
if epoch < self.start_epoch {
info!("Obsolete key generation message: {:?}.", kg_msg);
continue;

View File

@ -1,6 +1,7 @@
//! Network tests for Dynamic Honey Badger.
extern crate hbbft;
extern crate itertools;
#[macro_use]
extern crate log;
extern crate env_logger;
@ -17,6 +18,7 @@ use std::cmp;
use std::collections::BTreeMap;
use std::sync::Arc;
use itertools::Itertools;
use rand::Rng;
use hbbft::dynamic_honey_badger::{Batch, Change, ChangeState, DynamicHoneyBadger, Input};
@ -54,38 +56,35 @@ where
}
// 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.
let node_busy = |node: &mut TestNode<UsizeDhb>| {
let node_busy = |node: &TestNode<UsizeDhb>| {
if !has_remove(node) || !has_add(node) {
return true;
}
let mut min_missing = 0;
for tx in node.outputs().iter().flat_map(Batch::iter) {
if *tx >= min_missing {
min_missing = tx + 1;
}
}
if min_missing < num_txs {
return true;
}
if node.outputs().last().unwrap().is_empty() {
let last = node.outputs().last().unwrap().epoch;
node.queue.retain(|(_, ref msg)| msg.epoch() < last);
}
false
node.outputs().iter().flat_map(Batch::iter).unique().count() < num_txs
};
let mut rng = rand::thread_rng();
let mut input_add = false; // Whether the vote to add node 0 has already been input.
let mut input_add = false;
// Handle messages in random order until all nodes have output all transactions.
while network.nodes.values_mut().any(node_busy) {
while network.nodes.values().any(node_busy) {
// Remove all messages belonging to epochs after all expected outputs.
for node in network.nodes.values_mut().filter(|node| !node_busy(node)) {
if let Some(last) = node.outputs().last().map(|out| out.epoch) {
node.queue.retain(|(_, ref msg)| msg.epoch() < last);
}
}
// 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() && node.instance().netinfo().is_validator()
node_busy(*node)
&& !node.instance().has_input()
&& node.instance().netinfo().is_validator()
// If there's only one node, it will immediately output on input. Make sure we
// first process all incoming messages before providing input again.
&& (network.nodes.len() > 2 || node.queue.is_empty())
})
.map(|(id, _)| *id)
.collect();
@ -93,14 +92,12 @@ where
let queue = queues.get_mut(id).unwrap();
queue.remove_all(network.nodes[id].outputs().iter().flat_map(Batch::iter));
network.input(*id, Input::User(queue.choose(3, 10)));
} else {
network.step();
}
network.step();
// Once all nodes have processed the removal of node 0, add it again.
if !input_add && network.nodes.values().all(has_remove) {
let pk = network.pk_set.public_key_share(0);
network.input_all(Input::Change(Change::Add(NodeUid(0), pk)));
info!("Input!");
input_add = true;
}
}
@ -137,8 +134,7 @@ where
let _ = env_logger::try_init();
let mut rng = rand::thread_rng();
// TODO: This should also work with two nodes.
let sizes = vec![3, 5, rng.gen_range(6, 10)];
let sizes = vec![2, 3, 5, rng.gen_range(6, 10)];
for size in sizes {
// The test is removing one correct node, so we allow fewer faulty ones.
let num_adv_nodes = (size - 2) / 3;

View File

@ -2,6 +2,7 @@
extern crate bincode;
extern crate hbbft;
extern crate itertools;
#[macro_use]
extern crate log;
extern crate env_logger;
@ -17,6 +18,7 @@ mod network;
use std::collections::BTreeMap;
use std::sync::Arc;
use itertools::Itertools;
use rand::Rng;
use hbbft::honey_badger::{self, Batch, HoneyBadger, MessageContent};
@ -130,13 +132,7 @@ where
// 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.
let node_busy = |node: &mut TestNode<UsizeHoneyBadger>| {
let mut min_missing = 0;
for tx in node.outputs().iter().flat_map(Batch::iter) {
if *tx >= min_missing {
min_missing = tx + 1;
}
}
if min_missing < num_txs {
if node.outputs().iter().flat_map(Batch::iter).unique().count() < num_txs {
return true;
}
if node.outputs().last().unwrap().is_empty() {

View File

@ -2,6 +2,7 @@
extern crate env_logger;
extern crate hbbft;
extern crate itertools;
#[macro_use]
extern crate log;
extern crate pairing;
@ -19,6 +20,7 @@ use std::sync::Arc;
use hbbft::messaging::NetworkInfo;
use hbbft::queueing_honey_badger::{Batch, Change, ChangeState, Input, QueueingHoneyBadger};
use itertools::Itertools;
use rand::Rng;
use network::{Adversary, MessageScheduler, NodeUid, SilentAdversary, TestNetwork, TestNode};
@ -55,13 +57,7 @@ fn test_queueing_honey_badger<A>(
if !has_remove(node) || !has_add(node) {
return true;
}
let mut min_missing = 0;
for tx in node.outputs().iter().flat_map(Batch::iter) {
if *tx >= min_missing {
min_missing = tx + 1;
}
}
if min_missing < num_txs {
if node.outputs().iter().flat_map(Batch::iter).unique().count() < num_txs {
return true;
}
if node.outputs().last().unwrap().is_empty() {
@ -81,7 +77,6 @@ fn test_queueing_honey_badger<A>(
}
let pk = network.pk_set.public_key_share(0);
network.input_all(Input::Change(Change::Add(NodeUid(0), pk)));
info!("Input!");
input_add = true;
}
}