mirror of https://github.com/poanetwork/hbbft.git
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:
parent
326616e3b8
commit
4cc35587c7
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue