review comment coverage

This commit is contained in:
Vladimir Komendantskiy 2018-06-14 12:28:38 +01:00
parent 5008d11ada
commit 3393052b4b
9 changed files with 84 additions and 112 deletions

View File

@ -64,10 +64,6 @@ pub fn main() {
let args: Args = parse_args();
println!("{:?}", args);
let node = Node::new(
args.bind_address,
args.remote_addresses,
args.value,
);
let node = Node::new(args.bind_address, args.remote_addresses, args.value);
node.run().expect("Node failed");
}

View File

@ -428,7 +428,7 @@ fn main() {
println!();
let num_good_nodes = args.flag_n - args.flag_f;
let txs = (0..args.flag_txs).map(|_| Transaction::new(args.flag_tx_size));
let sk_set = SecretKeySet::<Bls12>::new(args.flag_f, &mut rand::thread_rng());
let sk_set = SecretKeySet::<Bls12>::random(args.flag_f, &mut rand::thread_rng());
let pk_set = sk_set.public_keys();
let new_honey_badger = |id: NodeUid, all_ids: BTreeSet<NodeUid>| {
let netinfo = Rc::new(NetworkInfo::new(

View File

@ -65,12 +65,12 @@ pub struct AgreementMessage {
/// Binary Agreement instance
pub struct Agreement<NodeUid>
where
NodeUid: Clone + Debug,
NodeUid: Clone + Debug + Eq + Hash,
{
/// Shared network information.
netinfo: Rc<NetworkInfo<NodeUid>>,
/// Honey Badger algorithm epoch.
hb_epoch: u64,
/// Session ID, e.g, the Honey Badger algorithm epoch.
session_id: u64,
/// Agreement algorithm epoch.
epoch: u32,
/// Bin values. Reset on every epoch update.
@ -110,9 +110,6 @@ where
messages: VecDeque<AgreementMessage>,
/// Whether the `Conf` message round has started in the current epoch.
conf_round: bool,
/// The subset of `bin_values` contained in received `Conf` messages before invoking the Common
/// Coin instance.
conf_vals: BinValues,
/// A common coin instance. It is reset on epoch update.
common_coin: CommonCoin<NodeUid, Nonce>,
}
@ -174,11 +171,11 @@ impl<NodeUid: Clone + Debug + Eq + Hash + Ord> DistAlgorithm for Agreement<NodeU
}
impl<NodeUid: Clone + Debug + Eq + Hash + Ord> Agreement<NodeUid> {
pub fn new(netinfo: Rc<NetworkInfo<NodeUid>>, hb_epoch: u64) -> Self {
pub fn new(netinfo: Rc<NetworkInfo<NodeUid>>, session_id: u64) -> Self {
let invocation_id = netinfo.invocation_id();
Agreement {
netinfo: netinfo.clone(),
hb_epoch,
session_id,
epoch: 0,
bin_values: BinValues::new(),
received_bval: BTreeMap::new(),
@ -193,8 +190,7 @@ impl<NodeUid: Clone + Debug + Eq + Hash + Ord> Agreement<NodeUid> {
terminated: false,
messages: VecDeque::new(),
conf_round: false,
conf_vals: BinValues::None,
common_coin: CommonCoin::new(netinfo, Nonce::new(invocation_id.as_ref(), hb_epoch, 0)),
common_coin: CommonCoin::new(netinfo, Nonce::new(invocation_id.as_ref(), session_id, 0)),
}
}
@ -335,15 +331,7 @@ impl<NodeUid: Clone + Debug + Eq + Hash + Ord> Agreement<NodeUid> {
self.extend_common_coin();
if let Some(coin) = self.common_coin.next_output() {
// Check the termination condition: "continue looping until both a value b is output in some
// round r, and the value Coin_r' = b for some round r' > r."
self.terminated = self.terminated || self.decision == Some(coin);
if self.terminated {
debug!("Node {:?} Agreement terminated", self.netinfo.our_uid());
return Ok(());
}
let b = if let Some(b) = self.conf_vals.definite() {
let b = if let Some(b) = self.count_conf().1.definite() {
// Outputting a value is allowed only once.
if self.decision.is_none() && b == coin {
self.decide(b);
@ -394,12 +382,11 @@ impl<NodeUid: Clone + Debug + Eq + Hash + Ord> Agreement<NodeUid> {
fn try_finish_conf_round(&mut self) -> AgreementResult<()> {
if self.conf_round {
let (count_vals, vals) = self.count_conf();
let (count_vals, _) = self.count_conf();
if count_vals < self.netinfo.num_nodes() - self.netinfo.num_faulty() {
// Continue waiting for (N - f) `Conf` messages
return Ok(());
}
self.conf_vals = vals;
// Invoke the comon coin.
self.common_coin.input(())?;
self.extend_common_coin();
@ -459,11 +446,10 @@ impl<NodeUid: Clone + Debug + Eq + Hash + Ord> Agreement<NodeUid> {
self.received_aux.clear();
self.received_conf.clear();
self.conf_round = false;
self.conf_vals = BinValues::None;
self.epoch += 1;
let nonce = Nonce::new(
self.netinfo.invocation_id().as_ref(),
self.hb_epoch,
self.session_id,
self.epoch,
);
self.common_coin = CommonCoin::new(self.netinfo.clone(), nonce);
@ -479,10 +465,10 @@ impl<NodeUid: Clone + Debug + Eq + Hash + Ord> Agreement<NodeUid> {
struct Nonce(Vec<u8>);
impl Nonce {
pub fn new(invocation_id: &[u8], hb_epoch: u64, agreement_epoch: u32) -> Self {
pub fn new(invocation_id: &[u8], session_id: u64, agreement_epoch: u32) -> Self {
Nonce(Vec::from(format!(
"Nonce for Honey Badger {:?} @ epoch {}:{}",
invocation_id, hb_epoch, agreement_epoch
"Nonce for Honey Badger {:?}@{}:{}",
invocation_id, session_id, agreement_epoch
)))
}
}

View File

@ -1,5 +1,6 @@
use std::collections::{BTreeMap, VecDeque};
use std::fmt::{self, Debug};
use std::hash::Hash;
use std::iter::once;
use std::rc::Rc;
@ -91,7 +92,7 @@ impl Debug for BroadcastMessage {
/// eventually be able to decode (i.e. receive at least `f + 1` `Echo` messages).
/// * So a node with `2 * f + 1` `Ready`s and `f + 1` `Echos` will decode and _output_ the value,
/// knowing that every other good node will eventually do the same.
pub struct Broadcast<N> {
pub struct Broadcast<N: Clone + Eq + Hash> {
/// Shared network data.
netinfo: Rc<NetworkInfo<N>>,
/// The UID of the sending node.
@ -114,7 +115,7 @@ pub struct Broadcast<N> {
output: Option<Vec<u8>>,
}
impl<N: Eq + Debug + Clone + Ord> DistAlgorithm for Broadcast<N> {
impl<N: Eq + Debug + Clone + Hash + Ord> DistAlgorithm for Broadcast<N> {
type NodeUid = N;
// TODO: Allow anything serializable and deserializable, i.e. make this a type parameter
// T: Serialize + DeserializeOwned
@ -163,7 +164,7 @@ impl<N: Eq + Debug + Clone + Ord> DistAlgorithm for Broadcast<N> {
}
}
impl<N: Eq + Debug + Clone + Ord> Broadcast<N> {
impl<N: Eq + Debug + Clone + Hash + Ord> Broadcast<N> {
/// Creates a new broadcast instance to be used by node `our_id` which expects a value proposal
/// from node `proposer_id`.
pub fn new(netinfo: Rc<NetworkInfo<N>>, proposer_id: N) -> BroadcastResult<Self> {

View File

@ -2,7 +2,7 @@
use std::collections::{BTreeMap, VecDeque};
use std::fmt::Debug;
use std::mem::replace;
use std::hash::Hash;
use std::rc::Rc;
use pairing::bls12_381::Bls12;
@ -46,7 +46,7 @@ impl CommonCoinMessage {
#[derive(Debug)]
pub struct CommonCoin<N, T>
where
N: Debug,
N: Clone + Debug + Eq + Hash,
{
netinfo: Rc<NetworkInfo<N>>,
/// The name of this common coin. It is required to be unique for each common coin round.
@ -55,8 +55,6 @@ where
output: Option<bool>,
/// Outgoing message queue.
messages: VecDeque<CommonCoinMessage>,
/// Incoming messages buffered before we provide input to the common coin.
incoming_queue: VecDeque<(N, Signature<Bls12>)>,
/// All received threshold signature shares.
received_shares: BTreeMap<N, Signature<Bls12>>,
/// Whether we provided input to the common coin.
@ -67,7 +65,7 @@ where
impl<N, T> DistAlgorithm for CommonCoin<N, T>
where
N: Clone + Debug + Ord,
N: Clone + Debug + Hash + Ord,
T: Clone + AsRef<[u8]>,
{
type NodeUid = N;
@ -91,19 +89,7 @@ where
if self.terminated {
return Ok(());
}
let CommonCoinMessage(share) = message;
if !self.had_input {
self.incoming_queue.push_back((sender_id.clone(), share));
return Ok(());
} else {
let queued_msgs = replace(&mut self.incoming_queue, VecDeque::new());
for (sender_id, msg) in queued_msgs {
self.handle_share(&sender_id, msg)?;
}
}
self.handle_share(sender_id, share)
}
@ -131,7 +117,7 @@ where
impl<N, T> CommonCoin<N, T>
where
N: Clone + Debug + Ord,
N: Clone + Debug + Hash + Ord,
T: Clone + AsRef<[u8]>,
{
pub fn new(netinfo: Rc<NetworkInfo<N>>, nonce: T) -> Self {
@ -140,7 +126,6 @@ where
nonce,
output: None,
messages: VecDeque::new(),
incoming_queue: VecDeque::new(),
received_shares: BTreeMap::new(),
had_input: false,
terminated: false,
@ -155,63 +140,54 @@ where
}
fn handle_share(&mut self, sender_id: &N, share: Signature<Bls12>) -> Result<()> {
let node_indices = self.netinfo.node_indices();
if let Some(i) = node_indices.get(sender_id) {
let pk_i = self.netinfo.public_key_set().public_key_share(*i);
if let Some(i) = self.netinfo.node_index(sender_id) {
let pk_i = self.netinfo.public_key_set().public_key_share(*i as u64);
if !pk_i.verify(&share, &self.nonce) {
// Silently ignore the invalid share.
debug!(
"{:?} received invalid share from {:?}",
self.netinfo.our_uid(),
sender_id
);
return Ok(());
}
debug!(
"{:?} received a valid share from {:?}",
self.netinfo.our_uid(),
sender_id
);
self.received_shares.insert(sender_id.clone(), share);
let received_shares = &self.received_shares;
if received_shares.len() > self.netinfo.num_faulty() {
// Pass the indices of sender nodes to `combine_signatures`.
let shares: BTreeMap<&u64, &Signature<Bls12>> = self
.netinfo
.all_uids()
.iter()
.map(|id| (&node_indices[id], received_shares.get(id)))
.filter(|(_, share)| share.is_some())
.map(|(n, share)| (n, share.unwrap()))
.collect();
let sig = self.netinfo.public_key_set().combine_signatures(shares)?;
// Verify the successfully combined signature with the main public key.
if !self
.netinfo
.public_key_set()
.public_key()
.verify(&sig, &self.nonce)
{
// Abort
error!(
"{:?} main public key verification failed",
self.netinfo.our_uid()
);
self.terminated = true;
return Err(ErrorKind::VerificationFailed.into());
}
if self.had_input && received_shares.len() > self.netinfo.num_faulty() {
let sig = self.combine_and_verify_sig()?;
// Output the parity of the verified signature.
let parity = sig.parity();
self.output = Some(parity);
self.terminated = true;
debug!("{:?} coin is {}", self.netinfo.our_uid(), parity);
}
Ok(())
} else {
Err(ErrorKind::UnknownSender.into())
}
}
fn combine_and_verify_sig(&self) -> Result<Signature<Bls12>> {
// Pass the indices of sender nodes to `combine_signatures`.
let ids_shares: BTreeMap<&N, &Signature<Bls12>> = self.received_shares.iter().collect();
let ids_u64: BTreeMap<&N, u64> = ids_shares
.keys()
.map(|&id| (id, *self.netinfo.node_index(id).unwrap() as u64))
.collect();
// Convert indices to `u64` which is an interface type for `pairing`.
let shares: BTreeMap<&u64, &Signature<Bls12>> = ids_shares
.iter()
.map(|(id, &share)| (&ids_u64[id], share))
.collect();
let sig = self.netinfo.public_key_set().combine_signatures(shares)?;
if !self
.netinfo
.public_key_set()
.public_key()
.verify(&sig, &self.nonce)
{
// Abort
error!(
"{:?} main public key verification failed",
self.netinfo.our_uid()
);
Err(ErrorKind::VerificationFailed.into())
} else {
Ok(sig)
}
}
}

View File

@ -70,7 +70,7 @@ impl<E: Engine> PublicKey<E> {
}
/// A signature, or a signature share.
#[derive(Clone, PartialOrd)]
#[derive(Clone)]
pub struct Signature<E: Engine>(E::G2);
impl<E: Engine> fmt::Debug for Signature<E> {

View File

@ -1,5 +1,6 @@
use std::collections::{BTreeMap, BTreeSet};
use std::collections::{BTreeSet, HashMap};
use std::fmt::Debug;
use std::hash::Hash;
use pairing::bls12_381::Bls12;
@ -131,16 +132,17 @@ impl<'a, D: DistAlgorithm + 'a> Iterator for OutputIter<'a, D> {
/// Common data shared between algorithms.
#[derive(Debug)]
pub struct NetworkInfo<NodeUid> {
pub struct NetworkInfo<NodeUid: Clone + Eq + Hash> {
our_uid: NodeUid,
all_uids: BTreeSet<NodeUid>,
num_nodes: usize,
num_faulty: usize,
secret_key: SecretKey<Bls12>,
public_key_set: PublicKeySet<Bls12>,
node_indices: HashMap<NodeUid, usize>,
}
impl<NodeUid: Ord> NetworkInfo<NodeUid> {
impl<NodeUid: Clone + Hash + Ord> NetworkInfo<NodeUid> {
pub fn new(
our_uid: NodeUid,
all_uids: BTreeSet<NodeUid>,
@ -151,6 +153,12 @@ impl<NodeUid: Ord> NetworkInfo<NodeUid> {
panic!("Missing own ID");
}
let num_nodes = all_uids.len();
let node_indices = all_uids
.iter()
.cloned()
.enumerate()
.map(|(n, id)| (id, n))
.collect();
NetworkInfo {
our_uid,
all_uids,
@ -158,6 +166,7 @@ impl<NodeUid: Ord> NetworkInfo<NodeUid> {
num_faulty: (num_nodes - 1) / 3,
secret_key,
public_key_set,
node_indices,
}
}
@ -190,18 +199,17 @@ impl<NodeUid: Ord> NetworkInfo<NodeUid> {
&self.public_key_set
}
/// The canonical numbering of all nodes.
///
/// FIXME: To avoid multiple computations of the same result, caching should be introduced.
pub fn node_indices(&self) -> BTreeMap<&NodeUid, u64> {
self.all_uids
.iter()
.enumerate()
.map(|(n, id)| (id, n as u64))
.collect()
/// The index of a node in a canonical numbering of all nodes.
pub fn node_index(&self, id: &NodeUid) -> Option<&usize> {
self.node_indices.get(id)
}
/// Returns the unique ID of the Honey Badger invocation.
///
/// FIXME: Using the public key as the invocation ID either requires agreeing on the keys on
/// each invocation, or makes it unsafe to reuse keys for different invocations. A better
/// invocation ID would be one that is distributed to all nodes on each invocation and would be
/// independent from the public key, so that reusing keys would be safer.
pub fn invocation_id(&self) -> Vec<u8> {
self.public_key_set.public_key().to_bytes()
}

View File

@ -55,7 +55,8 @@ const GOOD_SAMPLE_SET: f64 = 400.0;
/// sample set. This check assumes logarithmic growth of the expected number of throws of one coin
/// size.
fn check_coin_distribution(num_samples: usize, count_true: usize, count_false: usize) {
const EXPECTED_SHARE: f64 = 0.48;
// Maximum 40% expectation in case of 400 samples or more.
const EXPECTED_SHARE: f64 = 0.4;
let max_gain = GOOD_SAMPLE_SET.log2();
let num_samples_f64 = num_samples as f64;
let gain = num_samples_f64.log2().min(max_gain);

View File

@ -1,5 +1,6 @@
use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;
use pairing::bls12_381::Bls12;
@ -143,7 +144,10 @@ impl<D: DistAlgorithm> Adversary<D> for SilentAdversary {
}
/// A collection of `TestNode`s representing a network.
pub struct TestNetwork<A: Adversary<D>, D: DistAlgorithm> {
pub struct TestNetwork<A: Adversary<D>, D: DistAlgorithm>
where
<D as DistAlgorithm>::NodeUid: Hash,
{
pub nodes: BTreeMap<D::NodeUid, TestNode<D>>,
pub adv_nodes: BTreeMap<D::NodeUid, Rc<NetworkInfo<D::NodeUid>>>,
adversary: A,