Add `NetworkInfo` to `Batch`.

Instead of just the public key, the batches returned from
`DynamicHoneyBadger` and `QueueingHoneyBadger` now contain the full
`NetworkInfo`, so the user can use the validators' keys for signing and
encryption.
This commit is contained in:
Andreas Fackler 2018-10-10 15:05:04 +02:00 committed by Andreas Fackler
parent 326616e3b8
commit 4cc35587c7
7 changed files with 106 additions and 85 deletions

View File

@ -1,15 +1,14 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use rand::Rand;
use serde::{Deserialize, Serialize};
use super::{ChangeState, JoinPlan};
use crypto::{PublicKey, PublicKeySet};
use messaging::NetworkInfo;
use traits::NodeIdT;
/// A batch of transactions the algorithm has output.
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug)]
pub struct Batch<C, N> {
/// The sequence number: there is exactly one batch in each epoch.
pub(super) epoch: u64,
@ -17,22 +16,12 @@ pub struct Batch<C, N> {
pub(super) contributions: BTreeMap<N, C>,
/// The current state of adding or removing a node: whether any is in progress, or completed
/// this epoch.
change: ChangeState<N>,
/// The public network info, if `change` is not `None`.
pub_netinfo: Option<(PublicKeySet, BTreeMap<N, PublicKey>)>,
pub(super) change: ChangeState<N>,
/// The network info that applies to the _next_ epoch.
pub(super) netinfo: Arc<NetworkInfo<N>>,
}
impl<C, N: NodeIdT + Rand> Batch<C, N> {
/// Returns a new, empty batch with the given epoch.
pub fn new(epoch: u64) -> Self {
Batch {
epoch,
contributions: BTreeMap::new(),
change: ChangeState::None,
pub_netinfo: None,
}
}
impl<C, N: NodeIdT> Batch<C, N> {
pub fn epoch(&self) -> u64 {
self.epoch
}
@ -43,6 +32,12 @@ impl<C, N: NodeIdT + Rand> Batch<C, N> {
&self.change
}
/// Returns the `NetworkInfo` containing the information about the validators that will produce
/// the _next_ epoch after this one.
pub fn network_info(&self) -> &Arc<NetworkInfo<N>> {
&self.netinfo
}
/// Returns an iterator over references to all transactions included in the batch.
pub fn iter<'a>(&'a self) -> impl Iterator<Item = <&'a C as IntoIterator>::Item>
where
@ -88,25 +83,27 @@ impl<C, N: NodeIdT + Rand> Batch<C, N> {
where
N: Serialize + for<'r> Deserialize<'r>,
{
self.pub_netinfo
.as_ref()
.map(|&(ref pub_key_set, ref pub_keys)| JoinPlan {
epoch: self.epoch + 1,
change: self.change.clone(),
pub_key_set: pub_key_set.clone(),
pub_keys: pub_keys.clone(),
})
if self.change == ChangeState::None {
return None;
}
Some(JoinPlan {
epoch: self.epoch + 1,
change: self.change.clone(),
pub_key_set: self.netinfo.public_key_set().clone(),
pub_keys: self.netinfo.public_key_map().clone(),
})
}
/// Sets the current change state, and if it is not `None`, inserts the network information so
/// that a `JoinPlan` can be generated for the next epoch.
pub(super) fn set_change(&mut self, change: ChangeState<N>, netinfo: &NetworkInfo<N>) {
self.change = change;
if self.change != ChangeState::None {
self.pub_netinfo = Some((
netinfo.public_key_set().clone(),
netinfo.public_key_map().clone(),
));
}
/// Returns `true` if all public parts of the batch are equal to `other`. Secret keys and our
/// own node ID are ignored.
pub fn public_eq(&self, other: &Self) -> bool
where
C: PartialEq,
{
self.epoch == other.epoch
&& self.contributions == other.contributions
&& self.change == other.change
&& self.netinfo.public_key_set() == other.netinfo.public_key_set()
&& self.netinfo.public_key_map() == other.netinfo.public_key_map()
}
}

View File

@ -1,10 +1,10 @@
use rand::Rand;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::{fmt, mem};
use bincode;
use crypto::Signature;
use rand;
use rand::{self, Rand};
use serde::{Deserialize, Serialize};
use super::votes::{SignedVote, VoteCounter};
@ -258,9 +258,8 @@ where
let start_epoch = self.start_epoch;
let output = step.extend_with(hb_step, |hb_msg| Message::HoneyBadger(start_epoch, hb_msg));
for hb_batch in output {
// Create the batch we output ourselves. It will contain the _user_ transactions of
// `hb_batch`, and the current change state.
let mut batch = Batch::new(hb_batch.epoch + self.start_epoch);
let batch_epoch = hb_batch.epoch + self.start_epoch;
let mut batch_contributions = BTreeMap::new();
// Add the user transactions to `batch` and handle votes and DKG messages.
for (id, int_contrib) in hb_batch.contributions {
@ -271,7 +270,7 @@ where
} = int_contrib;
step.fault_log
.extend(self.vote_counter.add_committed_votes(&id, votes)?);
batch.contributions.insert(id.clone(), contrib);
batch_contributions.insert(id.clone(), 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 {
@ -295,18 +294,25 @@ where
}
}
if let Some(kgs) = self.take_ready_key_gen() {
let change = if let Some(kgs) = self.take_ready_key_gen() {
// If DKG completed, apply the change, restart Honey Badger, and inform the user.
debug!("{:?} DKG for {:?} complete!", self.our_id(), kgs.change);
self.netinfo = kgs.key_gen.into_network_info()?;
self.restart_honey_badger(batch.epoch + 1);
batch.set_change(ChangeState::Complete(kgs.change), &self.netinfo);
self.restart_honey_badger(batch_epoch + 1);
ChangeState::Complete(kgs.change)
} else if let Some(change) = self.vote_counter.compute_winner().cloned() {
// If there is a new change, restart DKG. Inform the user about the current change.
step.extend(self.update_key_gen(batch.epoch + 1, &change)?);
batch.set_change(ChangeState::InProgress(change), &self.netinfo);
}
step.output.push_back(batch);
step.extend(self.update_key_gen(batch_epoch + 1, &change)?);
ChangeState::InProgress(change)
} else {
ChangeState::None
};
step.output.push_back(Batch {
epoch: batch_epoch,
change,
netinfo: Arc::new(self.netinfo.clone()),
contributions: batch_contributions,
});
}
// If `start_epoch` changed, we can now handle some queued messages.
if start_epoch < self.start_epoch {

View File

@ -15,7 +15,6 @@ extern crate threshold_crypto as crypto;
mod network;
use std::cmp;
use std::collections::BTreeMap;
use std::sync::Arc;
@ -101,22 +100,7 @@ where
input_add = true;
}
}
verify_output_sequence(&network);
}
/// Verifies that all instances output the same sequence of batches. We already know that all of
/// them have output all transactions and events, but some may have advanced a few empty batches
/// more than others, so we ignore those.
fn verify_output_sequence<A>(network: &TestNetwork<A, UsizeDhb>)
where
A: Adversary<UsizeDhb>,
{
let expected = network.nodes[&NodeId(0)].outputs().to_vec();
assert!(!expected.is_empty());
for node in network.nodes.values() {
let len = cmp::min(expected.len(), node.outputs().len());
assert_eq!(&expected[..len], &node.outputs()[..len]);
}
network.verify_batches();
}
// Allow passing `netinfo` by value. `TestNetwork` expects this function signature.

View File

@ -25,7 +25,9 @@ use rand;
use rand::{Rand, Rng};
use threshold_crypto as crypto;
use hbbft::dynamic_honey_badger::Batch;
use hbbft::messaging::{self, DistAlgorithm, NetworkInfo, Step};
use hbbft::traits::{Contribution, NodeIdT};
use hbbft::util::SubRng;
pub use self::adversary::Adversary;
@ -958,6 +960,29 @@ where
}
}
impl<C, D, N> VirtualNet<D>
where
D: DistAlgorithm<Output = Batch<C, N>>,
C: Contribution + Clone,
N: NodeIdT,
{
/// Verifies that all nodes' outputs agree, and returns the output.
pub fn verify_batches(&self) -> &[Batch<C, N>] {
let first = self.correct_nodes().nth(0).unwrap().outputs();
let pub_eq = |(b0, b1): (&Batch<C, _>, &Batch<C, _>)| b0.public_eq(b1);
for (i, node) in self.correct_nodes().enumerate().skip(0) {
assert!(
first.iter().zip(node.outputs()).all(pub_eq),
"Outputs of nodes 0 and {} differ: {:?} != {:?}",
i,
first,
node.outputs()
);
}
first
}
}
impl<D> ops::Index<D::NodeId> for VirtualNet<D>
where
D: DistAlgorithm,

View File

@ -245,8 +245,7 @@ fn do_drop_and_readd(cfg: TestConfig) {
}
// As a final step, we verify that all nodes have arrived at the same conclusion.
let first = net.correct_nodes().nth(0).unwrap().outputs();
assert!(net.nodes().all(|node| node.outputs() == first));
let out = net.verify_batches();
println!("End result: {:?}", first);
println!("End result: {:?}", out);
}

View File

@ -6,7 +6,9 @@ use std::sync::Arc;
use crypto::SecretKeyShare;
use rand::{self, Rng};
use hbbft::dynamic_honey_badger::Batch;
use hbbft::messaging::{DistAlgorithm, NetworkInfo, Step, Target, TargetedMessage};
use hbbft::traits::Contribution;
/// A node identifier. In the tests, nodes are simply numbered.
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, Serialize, Deserialize, Rand)]
@ -541,3 +543,27 @@ where
}
}
}
impl<A: Adversary<D>, C, D> TestNetwork<A, D>
where
D: DistAlgorithm<Output = Batch<C, NodeId>, NodeId = NodeId>,
C: Contribution + Clone,
{
/// Verifies that all nodes' outputs agree.
#[allow(unused)] // Not used in all tests.
pub fn verify_batches(&self) {
let expected = self.nodes[&NodeId(0)].outputs().to_vec();
assert!(!expected.is_empty());
let pub_eq = |(b0, b1): (&Batch<C, _>, &Batch<C, _>)| b0.public_eq(b1);
for node in self.nodes.values() {
assert_eq!(expected.len(), node.outputs().len());
assert!(
expected.iter().zip(node.outputs()).all(pub_eq),
"Outputs of nodes 0 and {} differ: {:?} != {:?}",
node.instance().our_id().0,
expected,
node.outputs()
);
}
}
}

View File

@ -15,7 +15,6 @@ extern crate threshold_crypto as crypto;
mod network;
use std::cmp;
use std::collections::BTreeMap;
use std::sync::Arc;
@ -83,22 +82,7 @@ fn test_queueing_honey_badger<A>(
input_add = true;
}
}
verify_output_sequence(&network);
}
/// Verifies that all instances output the same sequence of batches. We already know that all of
/// them have output all transactions and events, but some may have advanced a few empty batches
/// more than others, so we ignore those.
fn verify_output_sequence<A>(network: &TestNetwork<A, QueueingHoneyBadger<usize, NodeId>>)
where
A: Adversary<QueueingHoneyBadger<usize, NodeId>>,
{
let expected = network.nodes[&NodeId(0)].outputs().to_vec();
assert!(!expected.is_empty());
for node in network.nodes.values() {
let len = cmp::min(expected.len(), node.outputs().len());
assert_eq!(&expected[..len], &node.outputs()[..len]);
}
network.verify_batches();
}
// Allow passing `netinfo` by value. `TestNetwork` expects this function signature.