diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index e879392..29dbb7b 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -104,7 +104,7 @@ impl Signature { } /// A secret key, or a secret key share. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct SecretKey(Fr); impl Default for SecretKey { diff --git a/src/crypto/poly.rs b/src/crypto/poly.rs index b58ad01..f03dab6 100644 --- a/src/crypto/poly.rs +++ b/src/crypto/poly.rs @@ -390,8 +390,8 @@ impl BivarPoly { } } -/// A commitment to a bivariate polynomial. -#[derive(Debug, Clone, Serialize, Deserialize)] +/// A commitment to a symmetric bivariate polynomial. +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] pub struct BivarCommitment { /// The polynomial's degree in each of the two variables. degree: usize, diff --git a/src/honey_badger.rs b/src/honey_badger.rs index 0b0ee05..0ad23e8 100644 --- a/src/honey_badger.rs +++ b/src/honey_badger.rs @@ -3,12 +3,11 @@ use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque}; use std::fmt::Debug; use std::hash::Hash; use std::rc::Rc; -use std::{cmp, iter}; +use std::{cmp, iter, mem}; use bincode; use rand; -use serde::de::DeserializeOwned; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use common_subset::{self, CommonSubset}; use crypto::{Ciphertext, DecryptionShare}; @@ -63,7 +62,7 @@ pub struct HoneyBadger { impl DistAlgorithm for HoneyBadger where - Tx: Eq + Hash + Serialize + DeserializeOwned + Debug, + Tx: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq, NodeUid: Ord + Clone + Debug, { type NodeUid = NodeUid; @@ -119,7 +118,7 @@ where // TODO: Use a threshold encryption scheme to encrypt the proposed transactions. impl HoneyBadger where - Tx: Eq + Hash + Serialize + DeserializeOwned + Debug, + Tx: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq, NodeUid: Ord + Clone + Debug, { /// Returns a new Honey Badger instance with the given parameters, starting at epoch `0`. @@ -156,6 +155,11 @@ where Ok(()) } + /// Empties and returns the transaction buffer. + pub fn drain_buffer(&mut self) -> Vec { + mem::replace(&mut self.buffer, Vec::new()) + } + /// Proposes a new batch in the current epoch. fn propose(&mut self) -> HoneyBadgerResult<()> { let proposal = self.choose_transactions()?; diff --git a/src/messaging.rs b/src/messaging.rs index d10ac34..f243725 100644 --- a/src/messaging.rs +++ b/src/messaging.rs @@ -136,7 +136,7 @@ impl<'a, D: DistAlgorithm + 'a> Iterator for OutputIter<'a, D> { /// use this construction to zero out the section of heap memory that is /// allocated for `secret_key` when the corresponding instance of /// `NetworkInfo` goes out of scope. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct NetworkInfo { our_uid: NodeUid, all_uids: BTreeSet, diff --git a/src/sync_key_gen.rs b/src/sync_key_gen.rs index aac0c57..67dbc06 100644 --- a/src/sync_key_gen.rs +++ b/src/sync_key_gen.rs @@ -34,25 +34,27 @@ use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet}; +use std::fmt::Debug; + +use bincode; +use clear_on_drop::ClearOnDrop; +use pairing::bls12_381::{Fr, G1Affine}; +use pairing::{CurveAffine, Field}; +use rand::OsRng; use crypto::poly::{BivarCommitment, BivarPoly, Poly}; use crypto::serde_impl::field_vec::FieldWrap; use crypto::{Ciphertext, PublicKey, PublicKeySet, SecretKey}; -use bincode; -use pairing::bls12_381::{Fr, G1Affine}; -use pairing::{CurveAffine, Field}; -use rand::OsRng; - // TODO: No need to send our own row and value to ourselves. /// A commitment to a bivariate polynomial, and for each node, an encrypted row of values. -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone, Hash, Eq, PartialEq)] pub struct Propose(BivarCommitment, Vec); /// A confirmation that we have received a node's proposal and verified our row against the /// commitment. For each node, it contains one encrypted value of our row. -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone, Hash, Eq, PartialEq)] pub struct Accept(u64, Vec); /// The information needed to track a single proposer's secret sharing process. @@ -84,33 +86,37 @@ impl ProposalState { /// A synchronous algorithm for dealerless distributed key generation. /// /// It requires that all nodes handle all messages in the exact same order. -pub struct SyncKeyGen { +pub struct SyncKeyGen { /// Our node index. our_idx: u64, /// Our secret key. sec_key: SecretKey, /// The public keys of all nodes, by node index. - pub_keys: Vec, + pub_keys: BTreeMap, /// Proposed bivariate polynomial. proposals: BTreeMap, /// The degree of the generated polynomial. threshold: usize, } -impl SyncKeyGen { +impl SyncKeyGen { /// Creates a new `SyncKeyGen` instance, together with the `Propose` message that should be /// broadcast. pub fn new( - our_idx: u64, + our_uid: &NodeUid, sec_key: SecretKey, - pub_keys: Vec, + pub_keys: BTreeMap, threshold: usize, - ) -> (SyncKeyGen, Propose) { + ) -> (SyncKeyGen, Propose) { + let our_idx = pub_keys + .keys() + .position(|uid| uid == our_uid) + .expect("missing pub key for own ID") as u64; let mut rng = OsRng::new().expect("OS random number generator"); let our_proposal = BivarPoly::random(threshold, &mut rng); let commit = our_proposal.commitment(); let rows: Vec<_> = pub_keys - .iter() + .values() .enumerate() .map(|(i, pk)| { let row = our_proposal.row(i as u64 + 1); @@ -131,9 +137,16 @@ impl SyncKeyGen { /// Handles a `Propose` message. If it is valid, returns an `Accept` message to be broadcast. pub fn handle_propose( &mut self, - sender_idx: u64, + sender_id: &NodeUid, Propose(commit, rows): Propose, ) -> Option { + let sender_idx = + if let Some(sender_idx) = self.pub_keys.keys().position(|uid| uid == sender_id) { + sender_idx as u64 + } else { + debug!("Unknown sender {:?}", sender_id); + return None; + }; let commit_row = commit.row(self.our_idx + 1); match self.proposals.entry(sender_idx) { Entry::Occupied(_) => return None, // Ignore multiple proposals. @@ -150,7 +163,7 @@ impl SyncKeyGen { // The row is valid: now encrypt one value for each node. let values = self .pub_keys - .iter() + .values() .enumerate() .map(|(idx, pk)| { let val = row.evaluate(idx as u64 + 1); @@ -163,7 +176,14 @@ impl SyncKeyGen { } /// Handles an `Accept` message. - pub fn handle_accept(&mut self, sender_idx: u64, accept: Accept) { + pub fn handle_accept(&mut self, sender_id: &NodeUid, accept: Accept) { + let sender_idx = + if let Some(sender_idx) = self.pub_keys.keys().position(|uid| uid == sender_id) { + sender_idx as u64 + } else { + debug!("Unknown sender {:?}", sender_id); + return; + }; if let Err(err) = self.handle_accept_or_err(sender_idx, accept) { debug!("Invalid accept from node {}: {}", sender_idx, err); } @@ -194,7 +214,7 @@ impl SyncKeyGen { /// /// These are only secure if `is_ready` returned `true`. Otherwise it is not guaranteed that /// none of the nodes knows the secret master key. - pub fn generate(&self) -> (PublicKeySet, SecretKey) { + pub fn generate(&self) -> (PublicKeySet, ClearOnDrop>) { let mut pk_commit = Poly::zero().commitment(); let mut sk_val = Fr::zero(); for proposal in self @@ -206,7 +226,8 @@ impl SyncKeyGen { let row: Poly = Poly::interpolate(proposal.values.iter().take(self.threshold + 1)); sk_val.add_assign(&row.evaluate(0)); } - (pk_commit.into(), SecretKey::from_value(sk_val)) + let sk = ClearOnDrop::new(Box::new(SecretKey::from_value(sk_val))); + (pk_commit.into(), sk) } /// Handles an `Accept` message or returns an error string. diff --git a/tests/agreement.rs b/tests/agreement.rs index de4b047..198a8de 100644 --- a/tests/agreement.rs +++ b/tests/agreement.rs @@ -19,6 +19,8 @@ extern crate hbbft; extern crate log; extern crate pairing; extern crate rand; +#[macro_use] +extern crate serde_derive; mod network; diff --git a/tests/broadcast.rs b/tests/broadcast.rs index 597900a..a4c47cd 100644 --- a/tests/broadcast.rs +++ b/tests/broadcast.rs @@ -6,6 +6,8 @@ extern crate log; extern crate env_logger; extern crate pairing; extern crate rand; +#[macro_use] +extern crate serde_derive; mod network; diff --git a/tests/common_coin.rs b/tests/common_coin.rs index cd62c27..9e26daf 100644 --- a/tests/common_coin.rs +++ b/tests/common_coin.rs @@ -6,6 +6,8 @@ extern crate hbbft; extern crate log; extern crate pairing; extern crate rand; +#[macro_use] +extern crate serde_derive; mod network; diff --git a/tests/common_subset.rs b/tests/common_subset.rs index 84dbbcc..37be109 100644 --- a/tests/common_subset.rs +++ b/tests/common_subset.rs @@ -6,6 +6,8 @@ extern crate hbbft; extern crate log; extern crate pairing; extern crate rand; +#[macro_use] +extern crate serde_derive; mod network; diff --git a/tests/honey_badger.rs b/tests/honey_badger.rs index e22edd7..f2e883c 100644 --- a/tests/honey_badger.rs +++ b/tests/honey_badger.rs @@ -6,6 +6,8 @@ extern crate log; extern crate env_logger; extern crate pairing; extern crate rand; +#[macro_use] +extern crate serde_derive; mod network; diff --git a/tests/network/mod.rs b/tests/network/mod.rs index 71d5716..414cdfb 100644 --- a/tests/network/mod.rs +++ b/tests/network/mod.rs @@ -9,7 +9,7 @@ use hbbft::crypto::SecretKeySet; use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage}; /// A node identifier. In the tests, nodes are simply numbered. -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Clone, Copy, Serialize, Deserialize)] pub struct NodeUid(pub usize); /// A "node" running an instance of the algorithm `D`. diff --git a/tests/sync_key_gen.rs b/tests/sync_key_gen.rs index 7a1a2c6..6b6f46f 100644 --- a/tests/sync_key_gen.rs +++ b/tests/sync_key_gen.rs @@ -15,16 +15,19 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) { // Generate individual key pairs for encryption. These are not suitable for threshold schemes. let sec_keys: Vec = (0..node_num).map(|_| SecretKey::new(&mut rng)).collect(); - let pub_keys: Vec = sec_keys.iter().map(|sk| sk.public_key()).collect(); + let pub_keys: BTreeMap = sec_keys + .iter() + .map(|sk| sk.public_key()) + .enumerate() + .collect(); // Create the `SyncKeyGen` instances and initial proposals. let mut nodes = Vec::new(); let proposals: Vec<_> = sec_keys .into_iter() .enumerate() - .map(|(idx, sk)| { - let (sync_key_gen, proposal) = - SyncKeyGen::new(idx as u64, sk, pub_keys.clone(), threshold); + .map(|(id, sk)| { + let (sync_key_gen, proposal) = SyncKeyGen::new(&id, sk, pub_keys.clone(), threshold); nodes.push(sync_key_gen); proposal }) @@ -32,23 +35,23 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) { // Handle the first `threshold + 1` proposals. Those should suffice for key generation. let mut accepts = Vec::new(); - for (sender_idx, proposal) in proposals[..=threshold].iter().enumerate() { - for (node_idx, node) in nodes.iter_mut().enumerate() { + for (sender_id, proposal) in proposals[..=threshold].iter().enumerate() { + for (node_id, node) in nodes.iter_mut().enumerate() { let accept = node - .handle_propose(sender_idx as u64, proposal.clone()) + .handle_propose(&sender_id, proposal.clone()) .expect("valid proposal"); // Only the first `threshold + 1` manage to commit their `Accept`s. - if node_idx <= 2 * threshold { - accepts.push((node_idx, accept)); + if node_id <= 2 * threshold { + accepts.push((node_id, accept)); } } } // Handle the `Accept`s from `2 * threshold + 1` nodes. - for (sender_idx, accept) in accepts { + for (sender_id, accept) in accepts { for node in &mut nodes { assert!(!node.is_ready()); // Not enough `Accept`s yet. - node.handle_accept(sender_idx as u64, accept.clone()); + node.handle_accept(&sender_id, accept.clone()); } }