Make SyncKeyGen NodeUid-aware.

This allows the caller to address nodes by ID instead of by index.

Also contains a few other minor changes that will be needed for
`DynamicHoneyBadger`.
This commit is contained in:
Andreas Fackler 2018-06-25 13:07:31 +02:00
parent 7b3ff717e0
commit 062b35ab3a
12 changed files with 78 additions and 40 deletions

View File

@ -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 {

View File

@ -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,

View File

@ -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<Tx, NodeUid> {
impl<Tx, NodeUid> DistAlgorithm for HoneyBadger<Tx, NodeUid>
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<Tx, NodeUid> HoneyBadger<Tx, NodeUid>
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<Tx> {
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()?;

View File

@ -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<NodeUid> {
our_uid: NodeUid,
all_uids: BTreeSet<NodeUid>,

View File

@ -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<Ciphertext>);
/// 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<Ciphertext>);
/// 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<NodeUid> {
/// Our node index.
our_idx: u64,
/// Our secret key.
sec_key: SecretKey,
/// The public keys of all nodes, by node index.
pub_keys: Vec<PublicKey>,
pub_keys: BTreeMap<NodeUid, PublicKey>,
/// Proposed bivariate polynomial.
proposals: BTreeMap<u64, ProposalState>,
/// The degree of the generated polynomial.
threshold: usize,
}
impl SyncKeyGen {
impl<NodeUid: Ord + Debug> SyncKeyGen<NodeUid> {
/// 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<PublicKey>,
pub_keys: BTreeMap<NodeUid, PublicKey>,
threshold: usize,
) -> (SyncKeyGen, Propose) {
) -> (SyncKeyGen<NodeUid>, 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<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 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<Box<SecretKey>>) {
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.

View File

@ -19,6 +19,8 @@ extern crate hbbft;
extern crate log;
extern crate pairing;
extern crate rand;
#[macro_use]
extern crate serde_derive;
mod network;

View File

@ -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;

View File

@ -6,6 +6,8 @@ extern crate hbbft;
extern crate log;
extern crate pairing;
extern crate rand;
#[macro_use]
extern crate serde_derive;
mod network;

View File

@ -6,6 +6,8 @@ extern crate hbbft;
extern crate log;
extern crate pairing;
extern crate rand;
#[macro_use]
extern crate serde_derive;
mod network;

View File

@ -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;

View File

@ -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`.

View File

@ -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<SecretKey> = (0..node_num).map(|_| SecretKey::new(&mut rng)).collect();
let pub_keys: Vec<PublicKey> = sec_keys.iter().map(|sk| sk.public_key()).collect();
let pub_keys: BTreeMap<usize, PublicKey> = 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());
}
}