mirror of https://github.com/poanetwork/hbbft.git
Separate keys and key shares.
This commit is contained in:
parent
2d9b872484
commit
15cc6ffddd
|
@ -29,7 +29,7 @@ env:
|
|||
- RUST_NEXT=nightly-2018-07-13
|
||||
script:
|
||||
- cargo +${RUST_NEXT} clippy -- --deny clippy
|
||||
- cargo +${RUST_NEXT} clippy --tests -- --deny clippy
|
||||
- cargo +${RUST_NEXT} clippy --tests --examples -- --deny clippy
|
||||
- cargo +${RUST_NEXT} clippy --all-features -- --deny clippy
|
||||
- cargo +${RUST_NEXT} clippy --all-features --tests -- --deny clippy
|
||||
- cargo +${RUST_NEXT} fmt -- --check
|
||||
|
|
|
@ -44,7 +44,7 @@ use std::{io, iter, process, thread, time};
|
|||
|
||||
use hbbft::broadcast::{Broadcast, BroadcastMessage};
|
||||
use hbbft::crypto::poly::Poly;
|
||||
use hbbft::crypto::SecretKeySet;
|
||||
use hbbft::crypto::{SecretKey, SecretKeySet};
|
||||
use hbbft::messaging::{DistAlgorithm, NetworkInfo, SourcedMessage};
|
||||
use hbbft::proto::message::BroadcastProto;
|
||||
use network::commst;
|
||||
|
@ -107,10 +107,15 @@ impl<T: Clone + Debug + AsRef<[u8]> + PartialEq + Send + Sync + From<Vec<u8>> +
|
|||
// keys here. A fully-featured application would need to take appropriately initialized keys
|
||||
// from elsewhere.
|
||||
let secret_key_set = SecretKeySet::from(Poly::zero());
|
||||
let secret_key = secret_key_set.secret_key_share(our_id as u64);
|
||||
let public_key_set = secret_key_set.public_keys();
|
||||
let sk_share = secret_key_set.secret_key_share(our_id as u64);
|
||||
let pub_key_set = secret_key_set.public_keys();
|
||||
let sk = SecretKey::default();
|
||||
let pub_keys = all_ids
|
||||
.iter()
|
||||
.map(|id| (*id, SecretKey::default().public_key()))
|
||||
.collect();
|
||||
|
||||
let netinfo = NetworkInfo::new(our_id, all_ids.clone(), secret_key, public_key_set);
|
||||
let netinfo = NetworkInfo::new(our_id, sk_share, pub_key_set, sk, pub_keys);
|
||||
|
||||
if value.is_some() != (our_id == 0) {
|
||||
panic!("Exactly the first node must propose a value.");
|
||||
|
|
|
@ -13,7 +13,7 @@ extern crate serde;
|
|||
extern crate serde_derive;
|
||||
extern crate signifix;
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{cmp, u64};
|
||||
|
||||
|
@ -25,7 +25,7 @@ use serde::de::DeserializeOwned;
|
|||
use serde::Serialize;
|
||||
use signifix::{metric, TryFrom};
|
||||
|
||||
use hbbft::crypto::SecretKeySet;
|
||||
use hbbft::dynamic_honey_badger::DynamicHoneyBadger;
|
||||
use hbbft::messaging::{DistAlgorithm, NetworkInfo, Step, Target};
|
||||
use hbbft::queueing_honey_badger::{Batch, QueueingHoneyBadger};
|
||||
|
||||
|
@ -246,22 +246,19 @@ where
|
|||
/// Creates a new network with `good_num` good nodes, and `dead_num` dead nodes.
|
||||
pub fn new<F>(
|
||||
good_num: usize,
|
||||
dead_num: usize,
|
||||
adv_num: usize,
|
||||
new_algo: F,
|
||||
hw_quality: HwQuality,
|
||||
) -> TestNetwork<D>
|
||||
where
|
||||
F: Fn(NodeUid, BTreeSet<NodeUid>) -> D,
|
||||
F: Fn(NetworkInfo<NodeUid>) -> D,
|
||||
{
|
||||
let node_ids: BTreeSet<NodeUid> = (0..(good_num + dead_num)).map(NodeUid).collect();
|
||||
let new_node_by_id = |id: NodeUid| {
|
||||
(
|
||||
id,
|
||||
TestNode::new(new_algo(id, node_ids.clone()), hw_quality),
|
||||
)
|
||||
let netinfos = NetworkInfo::generate_map((0..(good_num + adv_num)).map(NodeUid));
|
||||
let new_node = |(uid, netinfo): (NodeUid, NetworkInfo<_>)| {
|
||||
(uid, TestNode::new(new_algo(netinfo), hw_quality))
|
||||
};
|
||||
let mut network = TestNetwork {
|
||||
nodes: (0..good_num).map(NodeUid).map(new_node_by_id).collect(),
|
||||
nodes: netinfos.into_iter().map(new_node).collect(),
|
||||
};
|
||||
let initial_msgs: Vec<_> = network
|
||||
.nodes
|
||||
|
@ -363,8 +360,8 @@ impl EpochInfo {
|
|||
println!(
|
||||
"{:>5} {:6} {:6} {:5} {:9} {:>9}B",
|
||||
batch.epoch().to_string().cyan(),
|
||||
min_t.as_secs() * 1000 + max_t.subsec_nanos() as u64 / 1_000_000,
|
||||
max_t.as_secs() * 1000 + max_t.subsec_nanos() as u64 / 1_000_000,
|
||||
min_t.as_secs() * 1000 + u64::from(max_t.subsec_nanos()) / 1_000_000,
|
||||
max_t.as_secs() * 1000 + u64::from(max_t.subsec_nanos()) / 1_000_000,
|
||||
txs,
|
||||
network.message_count() / network.nodes.len(),
|
||||
metric::Signifix::try_from(network.message_size() / network.nodes.len() as u64)
|
||||
|
@ -433,19 +430,14 @@ 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::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 = NetworkInfo::new(
|
||||
id,
|
||||
all_ids,
|
||||
sk_set.secret_key_share(id.0 as u64),
|
||||
pk_set.clone(),
|
||||
);
|
||||
QueueingHoneyBadger::builder(netinfo)
|
||||
let new_honey_badger = |netinfo: NetworkInfo<NodeUid>| {
|
||||
let dyn_hb = DynamicHoneyBadger::builder(netinfo)
|
||||
.build()
|
||||
.expect("instantiate DynamicHoneyBadger");
|
||||
QueueingHoneyBadger::builder(dyn_hb)
|
||||
.batch_size(args.flag_b)
|
||||
.build_with_transactions(txs.clone())
|
||||
.expect("Instantiate honey_badger")
|
||||
.expect("instantiate QueueingHoneyBadger")
|
||||
};
|
||||
let hw_quality = HwQuality {
|
||||
latency: Duration::from_millis(args.flag_lag),
|
||||
|
|
|
@ -271,7 +271,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
proposer_id: NodeUid,
|
||||
) -> AgreementResult<Self> {
|
||||
let invocation_id = netinfo.invocation_id();
|
||||
if let Some(&proposer_i) = netinfo.node_index(&proposer_id) {
|
||||
if let Some(proposer_i) = netinfo.node_index(&proposer_id) {
|
||||
Ok(Agreement {
|
||||
netinfo: netinfo.clone(),
|
||||
session_id,
|
||||
|
@ -670,7 +670,7 @@ impl<NodeUid: Clone + Debug + Ord> Agreement<NodeUid> {
|
|||
let nonce = Nonce::new(
|
||||
self.netinfo.invocation_id().as_ref(),
|
||||
self.session_id,
|
||||
*self.netinfo.node_index(&self.proposer_id).unwrap(),
|
||||
self.netinfo.node_index(&self.proposer_id).unwrap(),
|
||||
self.epoch,
|
||||
);
|
||||
// TODO: Don't spend time creating a `CommonCoin` instance in epochs where the common coin
|
||||
|
|
|
@ -65,23 +65,13 @@
|
|||
//! const NUM_NODES: u64 = 7;
|
||||
//! const PROPOSER_ID: u64 = 3;
|
||||
//!
|
||||
//! // Create set of node ids.
|
||||
//! let all_uids: BTreeSet<_> = (0..NUM_NODES).collect();
|
||||
//!
|
||||
//! // Secret keys are required to complete the NetworkInfo structure, but not used in the
|
||||
//! // broadcast algorithm.
|
||||
//! let mut rng = thread_rng();
|
||||
//! let secret_keys = SecretKeySet::random(4, &mut rng);
|
||||
//!
|
||||
//! // Create initial nodes by instantiating a `NetworkInfo` for each:
|
||||
//! let mut nodes: BTreeMap<_, _> = all_uids.iter().cloned().map(|i| {
|
||||
//! let netinfo = NetworkInfo::new(
|
||||
//! i,
|
||||
//! all_uids.clone(),
|
||||
//! secret_keys.secret_key_share(i),
|
||||
//! secret_keys.public_keys(),
|
||||
//! );
|
||||
//! // Create a random set of keys for testing.
|
||||
//! let netinfos = NetworkInfo::generate_map(0..NUM_NODES);
|
||||
//!
|
||||
//! // Create initial nodes by instantiating a `Broadcast` for each:
|
||||
//! let mut nodes: BTreeMap<_, _> = netinfos.into_iter().map(|(i, netinfo)| {
|
||||
//! let bc = Broadcast::new(Arc::new(netinfo), PROPOSER_ID)
|
||||
//! .expect("could not instantiate Broadcast");
|
||||
//!
|
||||
|
@ -267,7 +257,7 @@ impl<NodeUid: Debug + Clone + Ord> DistAlgorithm for Broadcast<NodeUid> {
|
|||
sender_id: &NodeUid,
|
||||
message: Self::Message,
|
||||
) -> BroadcastResult<BroadcastStep<NodeUid>> {
|
||||
if !self.netinfo.all_uids().contains(sender_id) {
|
||||
if !self.netinfo.is_node_validator(sender_id) {
|
||||
return Err(ErrorKind::UnknownSender.into());
|
||||
}
|
||||
let fault_log = match message {
|
||||
|
@ -538,7 +528,6 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
let mut leaf_values: Vec<Option<Box<[u8]>>> = self
|
||||
.netinfo
|
||||
.all_uids()
|
||||
.iter()
|
||||
.map(|id| {
|
||||
self.echos.get(id).and_then(|p| {
|
||||
if p.root_hash.as_slice() == hash {
|
||||
|
@ -555,11 +544,6 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `i` if `node_id` is the `i`-th ID among all participating nodes.
|
||||
fn index_of_node(&self, node_id: &NodeUid) -> Option<usize> {
|
||||
self.netinfo.all_uids().iter().position(|id| id == node_id)
|
||||
}
|
||||
|
||||
/// Returns `true` if the proof is valid and has the same index as the node ID. Otherwise
|
||||
/// logs an info message.
|
||||
fn validate_proof(&self, p: &Proof<Vec<u8>>, id: &NodeUid) -> bool {
|
||||
|
@ -570,7 +554,7 @@ impl<NodeUid: Debug + Clone + Ord> Broadcast<NodeUid> {
|
|||
HexProof(&p)
|
||||
);
|
||||
false
|
||||
} else if self.index_of_node(id) != Some(p.value[0] as usize)
|
||||
} else if self.netinfo.node_index(id) != Some(p.value[0] as usize)
|
||||
|| p.index(self.netinfo.num_nodes()) != p.value[0] as usize
|
||||
{
|
||||
info!(
|
||||
|
|
|
@ -26,7 +26,7 @@ use std::fmt::Debug;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crypto::error as cerror;
|
||||
use crypto::Signature;
|
||||
use crypto::{Signature, SignatureShare};
|
||||
use fault_log::{FaultKind, FaultLog};
|
||||
use messaging::{DistAlgorithm, NetworkInfo, Step, Target, TargetedMessage};
|
||||
|
||||
|
@ -46,14 +46,14 @@ error_chain! {
|
|||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
|
||||
pub struct CommonCoinMessage(Signature);
|
||||
pub struct CommonCoinMessage(SignatureShare);
|
||||
|
||||
impl CommonCoinMessage {
|
||||
pub fn new(sig: Signature) -> Self {
|
||||
pub fn new(sig: SignatureShare) -> Self {
|
||||
CommonCoinMessage(sig)
|
||||
}
|
||||
|
||||
pub fn to_sig(&self) -> &Signature {
|
||||
pub fn to_sig(&self) -> &SignatureShare {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ pub struct CommonCoin<NodeUid, T> {
|
|||
/// Outgoing message queue.
|
||||
messages: VecDeque<CommonCoinMessage>,
|
||||
/// All received threshold signature shares.
|
||||
received_shares: BTreeMap<NodeUid, Signature>,
|
||||
received_shares: BTreeMap<NodeUid, SignatureShare>,
|
||||
/// Whether we provided input to the common coin.
|
||||
had_input: bool,
|
||||
/// Termination flag.
|
||||
|
@ -163,13 +163,17 @@ where
|
|||
self.try_output()?;
|
||||
return Ok(FaultLog::new());
|
||||
}
|
||||
let share = self.netinfo.secret_key().sign(&self.nonce);
|
||||
let share = self.netinfo.secret_key_share().sign(&self.nonce);
|
||||
self.messages.push_back(CommonCoinMessage(share.clone()));
|
||||
let id = self.netinfo.our_uid().clone();
|
||||
self.handle_share(&id, share)
|
||||
}
|
||||
|
||||
fn handle_share(&mut self, sender_id: &NodeUid, share: Signature) -> Result<FaultLog<NodeUid>> {
|
||||
fn handle_share(
|
||||
&mut self,
|
||||
sender_id: &NodeUid,
|
||||
share: SignatureShare,
|
||||
) -> Result<FaultLog<NodeUid>> {
|
||||
if let Some(pk_i) = self.netinfo.public_key_share(sender_id) {
|
||||
if !pk_i.verify(&share, &self.nonce) {
|
||||
// Log the faulty node and ignore the invalid share.
|
||||
|
@ -206,13 +210,13 @@ where
|
|||
|
||||
fn combine_and_verify_sig(&self) -> Result<Signature> {
|
||||
// Pass the indices of sender nodes to `combine_signatures`.
|
||||
let ids_shares: BTreeMap<&NodeUid, &Signature> = self.received_shares.iter().collect();
|
||||
let ids_shares: BTreeMap<&NodeUid, &SignatureShare> = self.received_shares.iter().collect();
|
||||
let ids_u64: BTreeMap<&NodeUid, u64> = ids_shares
|
||||
.keys()
|
||||
.map(|&id| (id, *self.netinfo.node_index(id).unwrap() as u64))
|
||||
.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> = ids_shares
|
||||
let shares: BTreeMap<&u64, &SignatureShare> = ids_shares
|
||||
.iter()
|
||||
.map(|(id, &share)| (&ids_u64[id], share))
|
||||
.collect();
|
||||
|
|
|
@ -159,10 +159,10 @@ impl<NodeUid: Clone + Debug + Ord + Rand> CommonSubset<NodeUid> {
|
|||
|
||||
// Create all agreement instances.
|
||||
let mut agreement_instances: BTreeMap<NodeUid, Agreement<NodeUid>> = BTreeMap::new();
|
||||
for proposer_id in netinfo.all_uids().iter().cloned() {
|
||||
for proposer_id in netinfo.all_uids() {
|
||||
agreement_instances.insert(
|
||||
proposer_id.clone(),
|
||||
Agreement::new(netinfo.clone(), session_id, proposer_id)?,
|
||||
Agreement::new(netinfo.clone(), session_id, proposer_id.clone())?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ const CHACHA_RNG_SEED_SIZE: usize = 8;
|
|||
|
||||
const ERR_OS_RNG: &str = "could not initialize the OS random number generator";
|
||||
|
||||
/// A public key, or a public key share.
|
||||
/// A public key.
|
||||
#[derive(Deserialize, Serialize, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct PublicKey(#[serde(with = "serde_impl::projective")] G1);
|
||||
|
||||
|
@ -57,13 +57,6 @@ impl PublicKey {
|
|||
self.verify_g2(sig, hash_g2(msg))
|
||||
}
|
||||
|
||||
/// Returns `true` if the decryption share matches the ciphertext.
|
||||
pub fn verify_decryption_share(&self, share: &DecryptionShare, ct: &Ciphertext) -> bool {
|
||||
let Ciphertext(ref u, ref v, ref w) = *ct;
|
||||
let hash = hash_g1_g2(*u, v);
|
||||
Bls12::pairing(share.0, hash) == Bls12::pairing(self.0, *w)
|
||||
}
|
||||
|
||||
/// Encrypts the message.
|
||||
pub fn encrypt<M: AsRef<[u8]>>(&self, msg: M) -> Ciphertext {
|
||||
let r: Fr = OsRng::new().expect(ERR_OS_RNG).gen();
|
||||
|
@ -82,8 +75,44 @@ impl PublicKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// A signature, or a signature share.
|
||||
// note: random signatures can be generated for testing
|
||||
/// A public key share.
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PublicKeyShare(PublicKey);
|
||||
|
||||
impl fmt::Debug for PublicKeyShare {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let uncomp = (self.0).0.into_affine().into_uncompressed();
|
||||
let bytes = uncomp.as_ref();
|
||||
write!(f, "PublicKeyShare({:?})", HexBytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKeyShare {
|
||||
/// Returns `true` if the signature matches the element of `G2`.
|
||||
pub fn verify_g2<H: Into<G2Affine>>(&self, sig: &SignatureShare, hash: H) -> bool {
|
||||
self.0.verify_g2(&sig.0, hash)
|
||||
}
|
||||
|
||||
/// Returns `true` if the signature matches the message.
|
||||
pub fn verify<M: AsRef<[u8]>>(&self, sig: &SignatureShare, msg: M) -> bool {
|
||||
self.verify_g2(sig, hash_g2(msg))
|
||||
}
|
||||
|
||||
/// Returns `true` if the decryption share matches the ciphertext.
|
||||
pub fn verify_decryption_share(&self, share: &DecryptionShare, ct: &Ciphertext) -> bool {
|
||||
let Ciphertext(ref u, ref v, ref w) = *ct;
|
||||
let hash = hash_g1_g2(*u, v);
|
||||
Bls12::pairing(share.0, hash) == Bls12::pairing((self.0).0, *w)
|
||||
}
|
||||
|
||||
/// Returns a byte string representation of the public key share.
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// A signature.
|
||||
// Note: Random signatures can be generated for testing.
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Rand)]
|
||||
pub struct Signature(#[serde(with = "serde_impl::projective")] G2);
|
||||
|
||||
|
@ -112,7 +141,20 @@ impl Signature {
|
|||
}
|
||||
}
|
||||
|
||||
/// A secret key, or a secret key share.
|
||||
/// A signature share.
|
||||
// Note: Random signature shares can be generated for testing.
|
||||
#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Rand, Hash)]
|
||||
pub struct SignatureShare(pub(crate) Signature);
|
||||
|
||||
impl fmt::Debug for SignatureShare {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let uncomp = (self.0).0.into_affine().into_uncompressed();
|
||||
let bytes = uncomp.as_ref();
|
||||
write!(f, "SignatureShare({:?})", HexBytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
/// A secret key.
|
||||
#[derive(Clone, PartialEq, Eq, Rand)]
|
||||
pub struct SecretKey(Fr);
|
||||
|
||||
|
@ -169,13 +211,47 @@ impl SecretKey {
|
|||
let g = u.into_affine().mul(self.0);
|
||||
Some(xor_vec(&hash_bytes(g, v.len()), v))
|
||||
}
|
||||
}
|
||||
|
||||
/// A secret key share.
|
||||
#[derive(Clone, PartialEq, Eq, Rand, Default)]
|
||||
pub struct SecretKeyShare(SecretKey);
|
||||
|
||||
impl fmt::Debug for SecretKeyShare {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let uncomp = self.0.public_key().0.into_affine().into_uncompressed();
|
||||
let bytes = uncomp.as_ref();
|
||||
write!(f, "SecretKeyShare({:?})", HexBytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretKeyShare {
|
||||
/// Creates a secret key share from an existing value
|
||||
pub fn from_value(f: Fr) -> Self {
|
||||
SecretKeyShare(SecretKey::from_value(f))
|
||||
}
|
||||
|
||||
/// Returns the matching public key share.
|
||||
pub fn public_key_share(&self) -> PublicKeyShare {
|
||||
PublicKeyShare(self.0.public_key())
|
||||
}
|
||||
|
||||
/// Signs the given element of `G2`.
|
||||
pub fn sign_g2<H: Into<G2Affine>>(&self, hash: H) -> SignatureShare {
|
||||
SignatureShare(self.0.sign_g2(hash))
|
||||
}
|
||||
|
||||
/// Signs the given message.
|
||||
pub fn sign<M: AsRef<[u8]>>(&self, msg: M) -> SignatureShare {
|
||||
SignatureShare(self.0.sign(msg))
|
||||
}
|
||||
|
||||
/// Returns a decryption share, or `None`, if the ciphertext isn't valid.
|
||||
pub fn decrypt_share(&self, ct: &Ciphertext) -> Option<DecryptionShare> {
|
||||
if !ct.verify() {
|
||||
return None;
|
||||
}
|
||||
Some(DecryptionShare(ct.0.into_affine().mul(self.0)))
|
||||
Some(DecryptionShare(ct.0.into_affine().mul((self.0).0)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,17 +325,18 @@ impl PublicKeySet {
|
|||
}
|
||||
|
||||
/// Returns the `i`-th public key share.
|
||||
pub fn public_key_share<T: Into<FrRepr>>(&self, i: T) -> PublicKey {
|
||||
PublicKey(self.commit.evaluate(from_repr_plus_1::<Fr>(i.into())))
|
||||
pub fn public_key_share<T: Into<FrRepr>>(&self, i: T) -> PublicKeyShare {
|
||||
let value = self.commit.evaluate(from_repr_plus_1::<Fr>(i.into()));
|
||||
PublicKeyShare(PublicKey(value))
|
||||
}
|
||||
|
||||
/// Combines the shares into a signature that can be verified with the main public key.
|
||||
pub fn combine_signatures<'a, ITR, IND>(&self, shares: ITR) -> Result<Signature>
|
||||
where
|
||||
ITR: IntoIterator<Item = (&'a IND, &'a Signature)>,
|
||||
ITR: IntoIterator<Item = (&'a IND, &'a SignatureShare)>,
|
||||
IND: Into<FrRepr> + Clone + 'a,
|
||||
{
|
||||
let samples = shares.into_iter().map(|(i, share)| (i, &share.0));
|
||||
let samples = shares.into_iter().map(|(i, share)| (i, &(share.0).0));
|
||||
Ok(Signature(interpolate(self.commit.degree() + 1, samples)?))
|
||||
}
|
||||
|
||||
|
@ -304,8 +381,9 @@ impl SecretKeySet {
|
|||
}
|
||||
|
||||
/// Returns the `i`-th secret key share.
|
||||
pub fn secret_key_share<T: Into<FrRepr>>(&self, i: T) -> SecretKey {
|
||||
SecretKey(self.poly.evaluate(from_repr_plus_1::<Fr>(i.into())))
|
||||
pub fn secret_key_share<T: Into<FrRepr>>(&self, i: T) -> SecretKeyShare {
|
||||
let value = self.poly.evaluate(from_repr_plus_1::<Fr>(i.into()));
|
||||
SecretKeyShare(SecretKey(value))
|
||||
}
|
||||
|
||||
/// Returns the corresponding public key set. That information can be shared publicly.
|
||||
|
@ -430,14 +508,14 @@ mod tests {
|
|||
let pk_set = sk_set.public_keys();
|
||||
|
||||
// Make sure the keys are different, and the first coefficient is the main key.
|
||||
assert_ne!(pk_set.public_key(), pk_set.public_key_share(0));
|
||||
assert_ne!(pk_set.public_key(), pk_set.public_key_share(1));
|
||||
assert_ne!(pk_set.public_key(), pk_set.public_key_share(2));
|
||||
assert_ne!(pk_set.public_key(), pk_set.public_key_share(0).0);
|
||||
assert_ne!(pk_set.public_key(), pk_set.public_key_share(1).0);
|
||||
assert_ne!(pk_set.public_key(), pk_set.public_key_share(2).0);
|
||||
|
||||
// Make sure we don't hand out the main secret key to anyone.
|
||||
assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(0));
|
||||
assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(1));
|
||||
assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(2));
|
||||
assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(0).0);
|
||||
assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(1).0);
|
||||
assert_ne!(sk_set.secret_key(), sk_set.secret_key_share(2).0);
|
||||
|
||||
let msg = "Totally real news";
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use rand::Rand;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ChangeState, JoinPlan};
|
||||
use crypto::PublicKeySet;
|
||||
use crypto::{PublicKey, PublicKeySet};
|
||||
use messaging::NetworkInfo;
|
||||
|
||||
/// A batch of transactions the algorithm has output.
|
||||
|
@ -19,7 +19,7 @@ pub struct Batch<C, NodeUid> {
|
|||
/// this epoch.
|
||||
change: ChangeState<NodeUid>,
|
||||
/// The public network info, if `change` is not `None`.
|
||||
pub_netinfo: Option<(BTreeSet<NodeUid>, PublicKeySet)>,
|
||||
pub_netinfo: Option<(PublicKeySet, BTreeMap<NodeUid, PublicKey>)>,
|
||||
}
|
||||
|
||||
impl<C, NodeUid: Ord + Rand + Clone + Debug> Batch<C, NodeUid> {
|
||||
|
@ -90,11 +90,11 @@ impl<C, NodeUid: Ord + Rand + Clone + Debug> Batch<C, NodeUid> {
|
|||
{
|
||||
self.pub_netinfo
|
||||
.as_ref()
|
||||
.map(|&(ref all_uids, ref pub_key_set)| JoinPlan {
|
||||
.map(|&(ref pub_key_set, ref pub_keys)| JoinPlan {
|
||||
epoch: self.epoch + 1,
|
||||
change: self.change.clone(),
|
||||
all_uids: all_uids.clone(),
|
||||
pub_key_set: pub_key_set.clone(),
|
||||
pub_keys: pub_keys.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,10 @@ impl<C, NodeUid: Ord + Rand + Clone + Debug> Batch<C, NodeUid> {
|
|||
) {
|
||||
self.change = change;
|
||||
if self.change != ChangeState::None {
|
||||
self.pub_netinfo = Some((netinfo.all_uids().clone(), netinfo.public_key_set().clone()));
|
||||
self.pub_netinfo = Some((
|
||||
netinfo.public_key_set().clone(),
|
||||
netinfo.public_key_map().clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ use std::iter::once;
|
|||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rand::{self, Rand};
|
||||
use rand::{self, Rand, Rng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ChangeState, DynamicHoneyBadger, JoinPlan, MessageQueue, Result, VoteCounter};
|
||||
use crypto::{SecretKey, SecretKeySet};
|
||||
use crypto::{SecretKey, SecretKeySet, SecretKeyShare};
|
||||
use honey_badger::HoneyBadger;
|
||||
use messaging::NetworkInfo;
|
||||
|
||||
|
@ -51,8 +51,10 @@ where
|
|||
let mut rng = rand::thread_rng();
|
||||
let sk_set = SecretKeySet::random(0, &mut rng);
|
||||
let pk_set = sk_set.public_keys();
|
||||
let sk = sk_set.secret_key_share(0);
|
||||
let netinfo = NetworkInfo::new(our_uid.clone(), once(our_uid).collect(), sk, pk_set);
|
||||
let sks = sk_set.secret_key_share(0);
|
||||
let sk: SecretKey = rng.gen();
|
||||
let pub_keys = once((our_uid.clone(), sk.public_key())).collect();
|
||||
let netinfo = NetworkInfo::new(our_uid, sks, pk_set, sk, pub_keys);
|
||||
DynamicHoneyBadgerBuilder::new(netinfo)
|
||||
}
|
||||
|
||||
|
@ -65,9 +67,10 @@ where
|
|||
) -> Self {
|
||||
let netinfo = NetworkInfo::new(
|
||||
our_uid,
|
||||
join_plan.all_uids,
|
||||
secret_key,
|
||||
SecretKeyShare::default(), // TODO: Should be an option?
|
||||
join_plan.pub_key_set,
|
||||
secret_key,
|
||||
join_plan.pub_keys,
|
||||
);
|
||||
DynamicHoneyBadgerBuilder {
|
||||
netinfo,
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
//! pending change.
|
||||
|
||||
use rand::Rand;
|
||||
use std::collections::{BTreeSet, VecDeque};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::mem;
|
||||
|
@ -55,7 +55,7 @@ use bincode;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use self::votes::{SignedVote, VoteCounter};
|
||||
use crypto::{PublicKeySet, SecretKey, Signature};
|
||||
use crypto::{PublicKey, PublicKeySet, SecretKey, SecretKeyShare, Signature};
|
||||
use fault_log::{FaultKind, FaultLog};
|
||||
use honey_badger::{HoneyBadger, HoneyBadgerStep, Message as HbMessage};
|
||||
use messaging::{DistAlgorithm, NetworkInfo, Step, Target, TargetedMessage};
|
||||
|
@ -72,7 +72,7 @@ mod change;
|
|||
mod error;
|
||||
mod votes;
|
||||
|
||||
type KeyGenOutput = (PublicKeySet, Option<SecretKey>);
|
||||
type KeyGenOutput = (PublicKeySet, Option<SecretKeyShare>);
|
||||
|
||||
/// The user input for `DynamicHoneyBadger`.
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -84,11 +84,7 @@ pub enum Input<C, NodeUid> {
|
|||
}
|
||||
|
||||
/// A Honey Badger instance that can handle adding and removing nodes.
|
||||
pub struct DynamicHoneyBadger<C, NodeUid: Rand>
|
||||
where
|
||||
C: Eq + Serialize + for<'r> Deserialize<'r> + Debug + Hash,
|
||||
NodeUid: Ord + Clone + Serialize + for<'r> Deserialize<'r> + Debug,
|
||||
{
|
||||
pub struct DynamicHoneyBadger<C, NodeUid: Rand> {
|
||||
/// Shared network data.
|
||||
netinfo: NetworkInfo<NodeUid>,
|
||||
/// The maximum number of future epochs for which we handle messages simultaneously.
|
||||
|
@ -245,7 +241,7 @@ where
|
|||
sender_id: &NodeUid,
|
||||
message: HbMessage<NodeUid>,
|
||||
) -> Result<FaultLog<NodeUid>> {
|
||||
if !self.netinfo.all_uids().contains(sender_id) {
|
||||
if !self.netinfo.is_node_validator(sender_id) {
|
||||
info!("Unknown sender {:?} of message {:?}", sender_id, message);
|
||||
return Err(ErrorKind::UnknownSender.into());
|
||||
}
|
||||
|
@ -312,7 +308,7 @@ where
|
|||
// If DKG completed, apply the change.
|
||||
debug!("{:?} DKG for {:?} complete!", self.our_id(), change);
|
||||
// If we are a validator, we received a new secret key. Otherwise keep the old one.
|
||||
let sk = sk.unwrap_or_else(|| self.netinfo.secret_key().clone());
|
||||
let sk = sk.unwrap_or_else(|| self.netinfo.secret_key_share().clone());
|
||||
// Restart Honey Badger in the next epoch, and inform the user about the change.
|
||||
self.apply_change(&change, pub_key_set, sk, batch.epoch + 1)?;
|
||||
batch.set_change(ChangeState::Complete(change), &self.netinfo);
|
||||
|
@ -344,19 +340,22 @@ where
|
|||
&mut self,
|
||||
change: &Change<NodeUid>,
|
||||
pub_key_set: PublicKeySet,
|
||||
sk: SecretKey,
|
||||
sk_share: SecretKeyShare,
|
||||
epoch: u64,
|
||||
) -> Result<()> {
|
||||
self.key_gen = None;
|
||||
let mut all_uids = self.netinfo.all_uids().clone();
|
||||
if !match *change {
|
||||
Change::Remove(ref id) => all_uids.remove(id),
|
||||
Change::Add(ref id, _) => all_uids.insert(id.clone()),
|
||||
let mut pub_keys = self.netinfo.public_key_map().clone();
|
||||
if match *change {
|
||||
Change::Remove(ref id) => pub_keys.remove(id).is_none(),
|
||||
Change::Add(ref id, ref pub_key) => {
|
||||
pub_keys.insert(id.clone(), pub_key.clone()).is_some()
|
||||
}
|
||||
} {
|
||||
info!("No-op change: {:?}", change);
|
||||
}
|
||||
let netinfo = NetworkInfo::new(self.our_id().clone(), all_uids, sk, pub_key_set);
|
||||
self.netinfo = netinfo;
|
||||
let sk = self.netinfo.secret_key().clone();
|
||||
let our_id = self.our_id().clone();
|
||||
self.netinfo = NetworkInfo::new(our_id, sk_share, pub_key_set, sk, pub_keys);
|
||||
self.restart_honey_badger(epoch)
|
||||
}
|
||||
|
||||
|
@ -475,7 +474,7 @@ where
|
|||
kg_msg: &KeyGenMessage,
|
||||
) -> Result<bool> {
|
||||
let ser = bincode::serialize(kg_msg)?;
|
||||
let pk_opt = (self.netinfo.public_key_share(node_id)).or_else(|| {
|
||||
let pk_opt = (self.netinfo.public_key(node_id)).or_else(|| {
|
||||
self.key_gen
|
||||
.iter()
|
||||
.filter_map(|&(_, ref change): &(_, Change<_>)| match *change {
|
||||
|
@ -573,8 +572,8 @@ pub struct JoinPlan<NodeUid: Ord> {
|
|||
epoch: u64,
|
||||
/// The current change. If `InProgress`, key generation for it is beginning at `epoch`.
|
||||
change: ChangeState<NodeUid>,
|
||||
/// The set of all validators' node IDs.
|
||||
all_uids: BTreeSet<NodeUid>,
|
||||
/// The current public key set for threshold cryptography.
|
||||
pub_key_set: PublicKeySet,
|
||||
/// The public keys of the nodes taking part in key generation.
|
||||
pub_keys: BTreeMap<NodeUid, PublicKey>,
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ where
|
|||
/// Returns `true` if the signature is valid.
|
||||
fn validate(&self, signed_vote: &SignedVote<NodeUid>) -> Result<bool> {
|
||||
let ser_vote = bincode::serialize(&signed_vote.vote)?;
|
||||
let pk_opt = self.netinfo.public_key_share(&signed_vote.voter);
|
||||
let pk_opt = self.netinfo.public_key(&signed_vote.voter);
|
||||
Ok(pk_opt.map_or(false, |pk| pk.verify(&signed_vote.sig, ser_vote)))
|
||||
}
|
||||
}
|
||||
|
@ -181,13 +181,9 @@ impl<NodeUid> SignedVote<NodeUid> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rand;
|
||||
|
||||
use super::{Change, SignedVote, VoteCounter};
|
||||
use crypto::SecretKeySet;
|
||||
use fault_log::{FaultKind, FaultLog};
|
||||
use messaging::NetworkInfo;
|
||||
|
||||
|
@ -197,16 +193,15 @@ mod tests {
|
|||
/// the vote for `Remove(j)` by node `i`. Each node signed `Remove(0)`, `Remove(1)`, ... in
|
||||
/// order.
|
||||
fn setup(node_num: usize, era: u64) -> (Vec<VoteCounter<usize>>, Vec<Vec<SignedVote<usize>>>) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let sk_set = SecretKeySet::random(3, &mut rng);
|
||||
let ids: BTreeSet<usize> = (0..node_num).collect();
|
||||
let pk_set = sk_set.public_keys();
|
||||
let create_counter = |id: usize| {
|
||||
let sk = sk_set.secret_key_share(id as u64);
|
||||
let netinfo = NetworkInfo::new(id, ids.clone(), sk, pk_set.clone());
|
||||
VoteCounter::new(Arc::new(netinfo), era)
|
||||
};
|
||||
let mut counters: Vec<_> = (0..node_num).map(create_counter).collect();
|
||||
// Create keys for threshold cryptography.
|
||||
let netinfos = NetworkInfo::generate_map(0..node_num);
|
||||
|
||||
// Create a `VoteCounter` instance for each node.
|
||||
let create_counter =
|
||||
|(_, netinfo): (_, NetworkInfo<_>)| VoteCounter::new(Arc::new(netinfo), era);
|
||||
let mut counters: Vec<_> = netinfos.into_iter().map(create_counter).collect();
|
||||
|
||||
// Sign a few votes.
|
||||
let sign_votes = |counter: &mut VoteCounter<usize>| {
|
||||
(0..node_num)
|
||||
.map(Change::Remove)
|
||||
|
|
|
@ -133,7 +133,7 @@ where
|
|||
sender_id: &NodeUid,
|
||||
message: Self::Message,
|
||||
) -> HoneyBadgerResult<HoneyBadgerStep<C, NodeUid>> {
|
||||
if !self.netinfo.all_uids().contains(sender_id) {
|
||||
if !self.netinfo.is_node_validator(sender_id) {
|
||||
return Err(ErrorKind::UnknownSender.into());
|
||||
}
|
||||
let Message { epoch, content } = message;
|
||||
|
@ -428,7 +428,7 @@ where
|
|||
{
|
||||
let ids_u64: BTreeMap<&NodeUid, u64> = shares
|
||||
.keys()
|
||||
.map(|id| (id, *self.netinfo.node_index(id).unwrap() as u64))
|
||||
.map(|id| (id, self.netinfo.node_index(id).unwrap() as u64))
|
||||
.collect();
|
||||
let indexed_shares: BTreeMap<&u64, _> = shares
|
||||
.into_iter()
|
||||
|
@ -492,7 +492,7 @@ where
|
|||
if !self.netinfo.is_validator() {
|
||||
return Ok((ciphertext.verify(), FaultLog::new()));
|
||||
}
|
||||
let share = match self.netinfo.secret_key().decrypt_share(&ciphertext) {
|
||||
let share = match self.netinfo.secret_key_share().decrypt_share(&ciphertext) {
|
||||
None => return Ok((false, FaultLog::new())),
|
||||
Some(share) => share,
|
||||
};
|
||||
|
|
107
src/messaging.rs
107
src/messaging.rs
|
@ -1,7 +1,7 @@
|
|||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crypto::{PublicKey, PublicKeySet, SecretKey};
|
||||
use crypto::{PublicKey, PublicKeySet, PublicKeyShare, SecretKey, SecretKeyShare};
|
||||
use fault_log::FaultLog;
|
||||
|
||||
/// Message sent by a given source.
|
||||
|
@ -139,16 +139,18 @@ impl<'a, D: DistAlgorithm + 'a> Iterator for MessageIter<'a, D> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Common data shared between algorithms.
|
||||
/// Common data shared between algorithms: the nodes' IDs and key shares.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkInfo<NodeUid> {
|
||||
our_uid: NodeUid,
|
||||
all_uids: BTreeSet<NodeUid>,
|
||||
num_nodes: usize,
|
||||
num_faulty: usize,
|
||||
is_validator: bool,
|
||||
// TODO: Should this be an option? It only makes sense for validators.
|
||||
secret_key_share: SecretKeyShare,
|
||||
secret_key: SecretKey,
|
||||
public_key_set: PublicKeySet,
|
||||
public_key_shares: BTreeMap<NodeUid, PublicKeyShare>,
|
||||
public_keys: BTreeMap<NodeUid, PublicKey>,
|
||||
node_indices: BTreeMap<NodeUid, usize>,
|
||||
}
|
||||
|
@ -156,31 +158,33 @@ pub struct NetworkInfo<NodeUid> {
|
|||
impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
||||
pub fn new(
|
||||
our_uid: NodeUid,
|
||||
all_uids: BTreeSet<NodeUid>,
|
||||
secret_key: SecretKey,
|
||||
secret_key_share: SecretKeyShare,
|
||||
public_key_set: PublicKeySet,
|
||||
secret_key: SecretKey,
|
||||
public_keys: BTreeMap<NodeUid, PublicKey>,
|
||||
) -> Self {
|
||||
let num_nodes = all_uids.len();
|
||||
let is_validator = all_uids.contains(&our_uid);
|
||||
let node_indices: BTreeMap<NodeUid, usize> = all_uids
|
||||
.iter()
|
||||
let num_nodes = public_keys.len();
|
||||
let is_validator = public_keys.contains_key(&our_uid);
|
||||
let node_indices: BTreeMap<NodeUid, usize> = public_keys
|
||||
.keys()
|
||||
.enumerate()
|
||||
.map(|(n, id)| (id.clone(), n))
|
||||
.collect();
|
||||
let public_keys = node_indices
|
||||
let public_key_shares = node_indices
|
||||
.iter()
|
||||
.map(|(id, idx)| (id.clone(), public_key_set.public_key_share(*idx as u64)))
|
||||
.collect();
|
||||
NetworkInfo {
|
||||
our_uid,
|
||||
all_uids,
|
||||
num_nodes,
|
||||
num_faulty: (num_nodes - 1) / 3,
|
||||
is_validator,
|
||||
secret_key_share,
|
||||
secret_key,
|
||||
public_key_set,
|
||||
public_keys,
|
||||
public_key_shares,
|
||||
node_indices,
|
||||
public_keys,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,8 +194,8 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
|||
}
|
||||
|
||||
/// ID of all nodes in the network.
|
||||
pub fn all_uids(&self) -> &BTreeSet<NodeUid> {
|
||||
&self.all_uids
|
||||
pub fn all_uids(&self) -> impl Iterator<Item = &NodeUid> {
|
||||
self.public_keys.keys()
|
||||
}
|
||||
|
||||
/// The total number of nodes.
|
||||
|
@ -205,27 +209,44 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
|||
self.num_faulty
|
||||
}
|
||||
|
||||
/// Returns our secret key share for threshold cryptography.
|
||||
pub fn secret_key_share(&self) -> &SecretKeyShare {
|
||||
&self.secret_key_share
|
||||
}
|
||||
|
||||
/// Returns our secret key for encryption and signing.
|
||||
pub fn secret_key(&self) -> &SecretKey {
|
||||
&self.secret_key
|
||||
}
|
||||
|
||||
/// Returns the public key set for threshold cryptography.
|
||||
pub fn public_key_set(&self) -> &PublicKeySet {
|
||||
&self.public_key_set
|
||||
}
|
||||
|
||||
/// Returns the public key share if a node with that ID exists, otherwise `None`.
|
||||
pub fn public_key_share(&self, id: &NodeUid) -> Option<&PublicKey> {
|
||||
self.public_keys.get(id)
|
||||
pub fn public_key_share(&self, id: &NodeUid) -> Option<&PublicKeyShare> {
|
||||
self.public_key_shares.get(id)
|
||||
}
|
||||
|
||||
/// Returns a map of all node IDs to their public key shares.
|
||||
pub fn public_key_share_map(&self) -> &BTreeMap<NodeUid, PublicKeyShare> {
|
||||
&self.public_key_shares
|
||||
}
|
||||
|
||||
/// Returns a map of all node IDs to their public keys.
|
||||
pub fn public_key(&self, id: &NodeUid) -> Option<&PublicKey> {
|
||||
self.public_keys.get(id)
|
||||
}
|
||||
|
||||
/// Returns a map of all node IDs to their public keys.
|
||||
pub fn public_key_map(&self) -> &BTreeMap<NodeUid, PublicKey> {
|
||||
&self.public_keys
|
||||
}
|
||||
|
||||
/// 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)
|
||||
pub fn node_index(&self, id: &NodeUid) -> Option<usize> {
|
||||
self.node_indices.get(id).cloned()
|
||||
}
|
||||
|
||||
/// Returns the unique ID of the Honey Badger invocation.
|
||||
|
@ -243,4 +264,54 @@ impl<NodeUid: Clone + Ord> NetworkInfo<NodeUid> {
|
|||
pub fn is_validator(&self) -> bool {
|
||||
self.is_validator
|
||||
}
|
||||
|
||||
/// Returns `true` if the given node takes part in the consensus itself. If not, it is only an
|
||||
/// observer.
|
||||
pub fn is_node_validator(&self, uid: &NodeUid) -> bool {
|
||||
self.public_keys.contains_key(uid)
|
||||
}
|
||||
|
||||
/// Generates a map of matching `NetworkInfo`s for testing.
|
||||
pub fn generate_map<I>(uids: I) -> BTreeMap<NodeUid, NetworkInfo<NodeUid>>
|
||||
where
|
||||
I: IntoIterator<Item = NodeUid>,
|
||||
{
|
||||
use rand::{self, Rng};
|
||||
|
||||
use crypto::SecretKeySet;
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let all_uids: BTreeSet<NodeUid> = uids.into_iter().collect();
|
||||
let num_faulty = (all_uids.len() - 1) / 3;
|
||||
|
||||
// Generate the keys for threshold cryptography.
|
||||
let sk_set = SecretKeySet::random(num_faulty, &mut rng);
|
||||
let pk_set = sk_set.public_keys();
|
||||
|
||||
// Generate keys for individually signing and encrypting messages.
|
||||
let sec_keys: BTreeMap<_, SecretKey> =
|
||||
all_uids.iter().map(|id| (id.clone(), rng.gen())).collect();
|
||||
let pub_keys: BTreeMap<_, PublicKey> = sec_keys
|
||||
.iter()
|
||||
.map(|(id, sk)| (id.clone(), sk.public_key()))
|
||||
.collect();
|
||||
|
||||
// Create the corresponding `NetworkInfo` for each node.
|
||||
let create_netinfo = |(i, uid): (usize, NodeUid)| {
|
||||
let netinfo = NetworkInfo::new(
|
||||
uid.clone(),
|
||||
sk_set.secret_key_share(i as u64),
|
||||
pk_set.clone(),
|
||||
sec_keys[&uid].clone(),
|
||||
pub_keys.clone(),
|
||||
);
|
||||
(uid, netinfo)
|
||||
};
|
||||
all_uids
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(create_netinfo)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use agreement::bin_values::BinValues;
|
|||
use agreement::{AgreementContent, AgreementMessage};
|
||||
use broadcast::BroadcastMessage;
|
||||
use common_coin::CommonCoinMessage;
|
||||
use crypto::Signature;
|
||||
use crypto::{Signature, SignatureShare};
|
||||
use proto::message::*;
|
||||
|
||||
impl From<message::BroadcastProto> for BroadcastMessage {
|
||||
|
@ -91,7 +91,7 @@ impl AgreementMessage {
|
|||
p.set_term(b);
|
||||
}
|
||||
AgreementContent::Coin(ccm) => {
|
||||
let v = ccm.to_sig().to_vec();
|
||||
let v = ccm.to_sig().0.to_vec();
|
||||
p.set_coin(v);
|
||||
}
|
||||
}
|
||||
|
@ -117,9 +117,11 @@ impl AgreementMessage {
|
|||
} else if mp.has_term() {
|
||||
Some(AgreementContent::Term(mp.get_term()).with_epoch(epoch))
|
||||
} else if mp.has_coin() {
|
||||
Signature::from_bytes(mp.get_coin()).map(|sig| {
|
||||
AgreementContent::Coin(Box::new(CommonCoinMessage::new(sig))).with_epoch(epoch)
|
||||
})
|
||||
Signature::from_bytes(mp.get_coin())
|
||||
.map(SignatureShare)
|
||||
.map(|sig| {
|
||||
AgreementContent::Coin(Box::new(CommonCoinMessage::new(sig))).with_epoch(epoch)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use dynamic_honey_badger::{self, Batch as DhbBatch, DynamicHoneyBadger, Message};
|
||||
use fault_log::FaultLog;
|
||||
use messaging::{DistAlgorithm, NetworkInfo, Step, TargetedMessage};
|
||||
use messaging::{DistAlgorithm, Step, TargetedMessage};
|
||||
use transaction_queue::TransactionQueue;
|
||||
|
||||
pub use dynamic_honey_badger::{Change, ChangeState, Input};
|
||||
|
@ -26,15 +26,11 @@ error_chain!{
|
|||
|
||||
/// A Queueing Honey Badger builder, to configure the parameters and create new instances of
|
||||
/// `QueueingHoneyBadger`.
|
||||
pub struct QueueingHoneyBadgerBuilder<Tx, NodeUid> {
|
||||
pub struct QueueingHoneyBadgerBuilder<Tx, NodeUid: Rand> {
|
||||
/// Shared network data.
|
||||
netinfo: NetworkInfo<NodeUid>,
|
||||
dyn_hb: DynamicHoneyBadger<Vec<Tx>, NodeUid>,
|
||||
/// The target number of transactions to be included in each batch.
|
||||
batch_size: usize,
|
||||
/// The epoch at which to join the network.
|
||||
start_epoch: u64,
|
||||
/// The maximum number of future epochs for which we handle messages simultaneously.
|
||||
max_future_epochs: usize,
|
||||
_phantom: PhantomData<Tx>,
|
||||
}
|
||||
|
||||
|
@ -45,38 +41,23 @@ where
|
|||
{
|
||||
/// Returns a new `QueueingHoneyBadgerBuilder` configured to use the node IDs and cryptographic
|
||||
/// keys specified by `netinfo`.
|
||||
pub fn new(netinfo: NetworkInfo<NodeUid>) -> Self {
|
||||
pub fn new(dyn_hb: DynamicHoneyBadger<Vec<Tx>, NodeUid>) -> Self {
|
||||
// TODO: Use the defaults from `HoneyBadgerBuilder`.
|
||||
QueueingHoneyBadgerBuilder {
|
||||
netinfo,
|
||||
dyn_hb,
|
||||
batch_size: 100,
|
||||
start_epoch: 0,
|
||||
max_future_epochs: 3,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the target number of transactions per batch.
|
||||
pub fn batch_size(&mut self, batch_size: usize) -> &mut Self {
|
||||
pub fn batch_size(mut self, batch_size: usize) -> Self {
|
||||
self.batch_size = batch_size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the maximum number of future epochs for which we handle messages simultaneously.
|
||||
pub fn max_future_epochs(&mut self, max_future_epochs: usize) -> &mut Self {
|
||||
self.max_future_epochs = max_future_epochs;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the epoch at which to join the network as an observer. This requires the node to
|
||||
/// receive all broadcast messages for `start_epoch` and later.
|
||||
pub fn start_epoch(&mut self, start_epoch: u64) -> &mut Self {
|
||||
self.start_epoch = start_epoch;
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates a new Queueing Honey Badger instance with an empty buffer.
|
||||
pub fn build(&self) -> QueueingHoneyBadger<Tx, NodeUid>
|
||||
pub fn build(self) -> QueueingHoneyBadger<Tx, NodeUid>
|
||||
where
|
||||
Tx: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq,
|
||||
{
|
||||
|
@ -86,17 +67,14 @@ where
|
|||
|
||||
/// Returns a new Queueing Honey Badger instance that starts with the given transactions in its
|
||||
/// buffer.
|
||||
pub fn build_with_transactions<TI>(&self, txs: TI) -> Result<QueueingHoneyBadger<Tx, NodeUid>>
|
||||
pub fn build_with_transactions<TI>(self, txs: TI) -> Result<QueueingHoneyBadger<Tx, NodeUid>>
|
||||
where
|
||||
TI: IntoIterator<Item = Tx>,
|
||||
Tx: Serialize + for<'r> Deserialize<'r> + Debug + Hash + Eq,
|
||||
{
|
||||
let dyn_hb = DynamicHoneyBadger::builder(self.netinfo.clone())
|
||||
.max_future_epochs(self.max_future_epochs)
|
||||
.build()?;
|
||||
let queue = TransactionQueue(txs.into_iter().collect());
|
||||
let mut qhb = QueueingHoneyBadger {
|
||||
dyn_hb,
|
||||
dyn_hb: self.dyn_hb,
|
||||
queue,
|
||||
batch_size: self.batch_size,
|
||||
output: VecDeque::new(),
|
||||
|
@ -190,8 +168,10 @@ where
|
|||
{
|
||||
/// Returns a new `QueueingHoneyBadgerBuilder` configured to use the node IDs and cryptographic
|
||||
/// keys specified by `netinfo`.
|
||||
pub fn builder(netinfo: NetworkInfo<NodeUid>) -> QueueingHoneyBadgerBuilder<Tx, NodeUid> {
|
||||
QueueingHoneyBadgerBuilder::new(netinfo)
|
||||
pub fn builder(
|
||||
dyn_hb: DynamicHoneyBadger<Vec<Tx>, NodeUid>,
|
||||
) -> QueueingHoneyBadgerBuilder<Tx, NodeUid> {
|
||||
QueueingHoneyBadgerBuilder::new(dyn_hb)
|
||||
}
|
||||
|
||||
fn step(
|
||||
|
|
|
@ -43,7 +43,7 @@ use rand::OsRng;
|
|||
|
||||
use crypto::poly::{BivarCommitment, BivarPoly, Poly};
|
||||
use crypto::serde_impl::field_vec::FieldWrap;
|
||||
use crypto::{Ciphertext, PublicKey, PublicKeySet, SecretKey};
|
||||
use crypto::{Ciphertext, PublicKey, PublicKeySet, SecretKey, SecretKeyShare};
|
||||
use fault_log::{FaultKind, FaultLog};
|
||||
|
||||
// TODO: No need to send our own row and value to ourselves.
|
||||
|
@ -237,7 +237,7 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
|
|||
///
|
||||
/// 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, Option<SecretKey>) {
|
||||
pub fn generate(&self) -> (PublicKeySet, Option<SecretKeyShare>) {
|
||||
let mut pk_commit = Poly::zero().commitment();
|
||||
let mut opt_sk_val = self.our_idx.map(|_| Fr::zero());
|
||||
let is_complete = |proposal: &&ProposalState| proposal.is_complete(self.threshold);
|
||||
|
@ -248,7 +248,7 @@ impl<NodeUid: Ord + Clone + Debug> SyncKeyGen<NodeUid> {
|
|||
sk_val.add_assign(&row.evaluate(0));
|
||||
}
|
||||
}
|
||||
let opt_sk = opt_sk_val.map(SecretKey::from_value);
|
||||
let opt_sk = opt_sk_val.map(SecretKeyShare::from_value);
|
||||
(pk_commit.into(), opt_sk)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ use std::sync::Arc;
|
|||
use rand::Rng;
|
||||
|
||||
use hbbft::broadcast::{Broadcast, BroadcastMessage};
|
||||
use hbbft::crypto::SecretKeySet;
|
||||
use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage};
|
||||
use network::{
|
||||
Adversary, MessageScheduler, MessageWithSender, NodeUid, RandomAdversary, SilentAdversary,
|
||||
|
@ -78,16 +77,7 @@ impl Adversary<Broadcast<NodeUid>> for ProposeAdversary {
|
|||
};
|
||||
|
||||
// FIXME: Take the correct, known keys from the network.
|
||||
let mut rng = rand::thread_rng();
|
||||
let sk_set = SecretKeySet::random(self.adv_nodes.len(), &mut rng);
|
||||
let pk_set = sk_set.public_keys();
|
||||
|
||||
let netinfo = Arc::new(NetworkInfo::new(
|
||||
id,
|
||||
node_ids,
|
||||
sk_set.secret_key_share(0),
|
||||
pk_set,
|
||||
));
|
||||
let netinfo = Arc::new(NetworkInfo::generate_map(node_ids).remove(&id).unwrap());
|
||||
let mut bc = Broadcast::new(netinfo, id).expect("broadcast instance");
|
||||
// FIXME: Use the output.
|
||||
let _ = bc.input(b"Fake news".to_vec()).expect("propose");
|
||||
|
|
|
@ -97,7 +97,11 @@ where
|
|||
network.step();
|
||||
// Once all nodes have processed the removal of node 0, add it again.
|
||||
if !input_add && network.nodes.values().all(has_remove) {
|
||||
let pk = network.pk_set.public_key_share(0);
|
||||
let pk = network.nodes[&NodeUid(0)]
|
||||
.instance()
|
||||
.netinfo()
|
||||
.secret_key()
|
||||
.public_key();
|
||||
network.input_all(Input::Change(Change::Add(NodeUid(0), pk)));
|
||||
input_add = true;
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ impl Adversary<UsizeHoneyBadger> for FaultyShareAdversary {
|
|||
.public_key()
|
||||
.encrypt(fake_proposal);
|
||||
let share = adv_node
|
||||
.secret_key()
|
||||
.secret_key_share()
|
||||
.decrypt_share(&fake_ciphertext)
|
||||
.expect("decryption share");
|
||||
// Send the share to remote nodes.
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::fmt::{self, Debug};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rand::{self, Rng};
|
||||
|
||||
use hbbft::crypto::{PublicKeySet, SecretKeySet};
|
||||
use hbbft::crypto::SecretKeyShare;
|
||||
use hbbft::messaging::{DistAlgorithm, NetworkInfo, Target, TargetedMessage};
|
||||
|
||||
/// A node identifier. In the tests, nodes are simply numbered.
|
||||
|
@ -355,7 +355,6 @@ pub struct TestNetwork<A: Adversary<D>, D: DistAlgorithm> {
|
|||
pub nodes: BTreeMap<D::NodeUid, TestNode<D>>,
|
||||
pub observer: TestNode<D>,
|
||||
pub adv_nodes: BTreeMap<D::NodeUid, Arc<NetworkInfo<D::NodeUid>>>,
|
||||
pub pk_set: PublicKeySet,
|
||||
adversary: A,
|
||||
}
|
||||
|
||||
|
@ -376,43 +375,31 @@ where
|
|||
G: Fn(BTreeMap<D::NodeUid, Arc<NetworkInfo<D::NodeUid>>>) -> A,
|
||||
{
|
||||
let mut rng = rand::thread_rng();
|
||||
let sk_set = SecretKeySet::random(adv_num, &mut rng);
|
||||
let pk_set = sk_set.public_keys();
|
||||
let mut netinfos = NetworkInfo::generate_map((0..(good_num + adv_num)).map(NodeUid));
|
||||
let obs_netinfo = {
|
||||
let node_ni = netinfos.values().next().unwrap();
|
||||
NetworkInfo::new(
|
||||
NodeUid(good_num + adv_num),
|
||||
SecretKeyShare::default(),
|
||||
node_ni.public_key_set().clone(),
|
||||
rng.gen(),
|
||||
node_ni.public_key_map().clone(),
|
||||
)
|
||||
};
|
||||
let adv_netinfos = netinfos.split_off(&NodeUid(good_num));
|
||||
|
||||
let node_ids: BTreeSet<NodeUid> = (0..(good_num + adv_num)).map(NodeUid).collect();
|
||||
let new_node_by_id = |NodeUid(i): NodeUid| {
|
||||
(
|
||||
NodeUid(i),
|
||||
TestNode::new(new_algo(Arc::new(NetworkInfo::new(
|
||||
NodeUid(i),
|
||||
node_ids.clone(),
|
||||
sk_set.secret_key_share(i as u64),
|
||||
pk_set.clone(),
|
||||
)))),
|
||||
)
|
||||
let new_node = |(uid, netinfo): (NodeUid, NetworkInfo<_>)| {
|
||||
(uid, TestNode::new(new_algo(Arc::new(netinfo))))
|
||||
};
|
||||
let new_adv_node_by_id = |NodeUid(i): NodeUid| {
|
||||
(
|
||||
NodeUid(i),
|
||||
Arc::new(NetworkInfo::new(
|
||||
NodeUid(i),
|
||||
node_ids.clone(),
|
||||
sk_set.secret_key_share(i as u64),
|
||||
pk_set.clone(),
|
||||
)),
|
||||
)
|
||||
};
|
||||
let adv_nodes: BTreeMap<D::NodeUid, Arc<NetworkInfo<D::NodeUid>>> = (good_num
|
||||
..(good_num + adv_num))
|
||||
.map(NodeUid)
|
||||
.map(new_adv_node_by_id)
|
||||
.collect();
|
||||
let new_adv_node = |(uid, netinfo): (NodeUid, NetworkInfo<_>)| (uid, Arc::new(netinfo));
|
||||
let adv_nodes: BTreeMap<_, _> = adv_netinfos.into_iter().map(new_adv_node).collect();
|
||||
|
||||
let observer = TestNode::new(new_algo(Arc::new(obs_netinfo)));
|
||||
|
||||
let mut network = TestNetwork {
|
||||
nodes: (0..good_num).map(NodeUid).map(new_node_by_id).collect(),
|
||||
observer: new_node_by_id(NodeUid(good_num + adv_num)).1,
|
||||
nodes: netinfos.into_iter().map(new_node).collect(),
|
||||
observer,
|
||||
adversary: adversary(adv_nodes.clone()),
|
||||
pk_set: pk_set.clone(),
|
||||
adv_nodes,
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ use std::cmp;
|
|||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hbbft::dynamic_honey_badger::DynamicHoneyBadger;
|
||||
use hbbft::messaging::NetworkInfo;
|
||||
use hbbft::queueing_honey_badger::{Batch, Change, ChangeState, Input, QueueingHoneyBadger};
|
||||
use itertools::Itertools;
|
||||
|
@ -76,7 +77,12 @@ fn test_queueing_honey_badger<A>(
|
|||
for tx in (num_txs / 2)..num_txs {
|
||||
network.input_all(Input::User(tx));
|
||||
}
|
||||
let pk = network.pk_set.public_key_share(0);
|
||||
let pk = network.nodes[&NodeUid(0)]
|
||||
.instance()
|
||||
.dyn_hb()
|
||||
.netinfo()
|
||||
.secret_key()
|
||||
.public_key();
|
||||
network.input_all(Input::Change(Change::Add(NodeUid(0), pk)));
|
||||
input_add = true;
|
||||
}
|
||||
|
@ -102,9 +108,10 @@ where
|
|||
// Allow passing `netinfo` by value. `TestNetwork` expects this function signature.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
|
||||
fn new_queueing_hb(netinfo: Arc<NetworkInfo<NodeUid>>) -> QueueingHoneyBadger<usize, NodeUid> {
|
||||
QueueingHoneyBadger::builder((*netinfo).clone())
|
||||
.batch_size(3)
|
||||
let dyn_hb = DynamicHoneyBadger::builder((*netinfo).clone())
|
||||
.build()
|
||||
.expect("instantiate DHB");
|
||||
QueueingHoneyBadger::builder(dyn_hb).batch_size(3).build()
|
||||
}
|
||||
|
||||
fn test_queueing_honey_badger_different_sizes<A, F>(new_adversary: F, num_txs: usize)
|
||||
|
|
Loading…
Reference in New Issue