mirror of https://github.com/poanetwork/hbbft.git
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:
parent
7b3ff717e0
commit
062b35ab3a
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()?;
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -19,6 +19,8 @@ extern crate hbbft;
|
|||
extern crate log;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod network;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ extern crate hbbft;
|
|||
extern crate log;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod network;
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ extern crate hbbft;
|
|||
extern crate log;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod network;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue