Merge branch 'master' into optional_threshold_encryption_209

This commit is contained in:
Logan Collins 2018-10-25 13:41:56 -05:00 committed by GitHub
commit 00985edc46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 289 additions and 228 deletions

View File

@ -142,12 +142,12 @@ Our implementation modifies the protocols described in "[The Honey Badger of BFT
We have simplified algorithm naming conventions from the original paper.
| Algorithm Name | Original Name |
| ---------------- | -------------------------------- |
| Honey Badger | HoneyBadgerBFT |
| Subset | Asynchronous Common Subset (ACS) |
| Broadcast | Reliable Broadcast (RBC) |
| Binary Agreement | Binary Byzantine Agreement (BBA) |
| Algorithm Name | Original Name |
| ---------------- | --------------------------------------------- |
| Honey Badger | HoneyBadgerBFT |
| Subset | Asynchronous Common Subset (ACS) |
| Broadcast | Reliable Broadcast (RBC) |
| Binary Agreement | Asynchronous Binary Byzantine Agreement (ABA) |
## Contributing

View File

@ -232,9 +232,8 @@ impl<N: NodeIdT> BinaryAgreement<N> {
// Otherwise handle the `Term` as a `BVal`, `Aux` and `Conf`.
let mut sbvb_step = self.sbv_broadcast.handle_bval(sender_id, b)?;
sbvb_step.extend(self.sbv_broadcast.handle_aux(sender_id, b)?);
let mut step = self.handle_sbvb_step(sbvb_step)?;
step.extend(self.handle_conf(sender_id, BoolSet::from(b))?);
Ok(step)
let step = self.handle_sbvb_step(sbvb_step)?;
Ok(step.join(self.handle_conf(sender_id, BoolSet::from(b))?))
}
}
@ -272,12 +271,11 @@ impl<N: NodeIdT> BinaryAgreement<N> {
if !self.netinfo.is_validator() {
return Ok(Step::default());
}
let mut step: Step<_> = Target::All
let step: Step<_> = Target::All
.message(content.clone().with_epoch(self.epoch))
.into();
let our_id = &self.netinfo.our_id().clone();
step.extend(self.handle_message_content(our_id, content)?);
Ok(step)
let our_id = &self.our_id().clone();
Ok(step.join(self.handle_message_content(our_id, content)?))
}
/// Handles a step returned from the `ThresholdSign`.
@ -346,19 +344,19 @@ impl<N: NodeIdT> BinaryAgreement<N> {
}
// Output the Binary Agreement value.
let mut step = Step::default();
step.output.push_back(b);
step.output.push(b);
// Latch the decided state.
self.decision = Some(b);
debug!(
"{:?}/{:?} (is_validator: {}) decision: {}",
self.netinfo.our_id(),
self.our_id(),
self.proposer_id,
self.netinfo.is_validator(),
b
);
if self.netinfo.is_validator() {
let msg = MessageContent::Term(b).with_epoch(self.epoch + 1);
step.messages.push_back(Target::All.message(msg));
step.messages.push(Target::All.message(msg));
}
step
}
@ -374,9 +372,7 @@ impl<N: NodeIdT> BinaryAgreement<N> {
CoinState::Decided(_) => return Ok(Step::default()), // Coin has already decided.
CoinState::InProgress(ref mut ts) => ts.sign().map_err(Error::InvokeCoin)?,
};
let mut step = self.on_coin_step(ts_step)?;
step.extend(self.try_update_epoch()?);
Ok(step)
Ok(self.on_coin_step(ts_step)?.join(self.try_update_epoch()?))
}
/// Counts the number of received `Conf` messages with values in `bin_values`.
@ -397,7 +393,7 @@ impl<N: NodeIdT> BinaryAgreement<N> {
self.coin_state = self.coin_state();
debug!(
"{:?} BinaryAgreement instance {:?} started epoch {}, {} terminated",
self.netinfo.our_id(),
self.our_id(),
self.proposer_id,
self.epoch,
self.received_conf.len(),

View File

@ -147,10 +147,9 @@ impl<N: NodeIdT> SbvBroadcast<N> {
if !self.netinfo.is_validator() {
return Ok(Step::default());
}
let mut step: Step<_> = Target::All.message(msg.clone()).into();
let our_id = &self.netinfo.our_id().clone();
step.extend(self.handle_message(our_id, msg)?);
Ok(step)
let step: Step<_> = Target::All.message(msg.clone()).into();
let our_id = &self.our_id().clone();
Ok(step.join(self.handle_message(our_id, msg)?))
}
/// Multicasts a `BVal(b)` message, and handles it.

View File

@ -87,7 +87,7 @@ impl<N: NodeIdT> Broadcast<N> {
/// Initiates the broadcast. This must only be called in the proposer node.
pub fn broadcast(&mut self, input: Vec<u8>) -> Result<Step<N>> {
if *self.netinfo.our_id() != self.proposer_id {
if *self.our_id() != self.proposer_id {
return Err(Error::InstanceCannotPropose);
}
if self.value_sent {
@ -97,10 +97,9 @@ impl<N: NodeIdT> Broadcast<N> {
// Split the value into chunks/shards, encode them with erasure codes.
// Assemble a Merkle tree from data and parity shards. Take all proofs
// from this tree and send them, each to its own node.
let (proof, mut step) = self.send_shards(input)?;
let our_id = &self.netinfo.our_id().clone();
step.extend(self.handle_value(our_id, proof)?);
Ok(step)
let (proof, step) = self.send_shards(input)?;
let our_id = &self.our_id().clone();
Ok(step.join(self.handle_value(our_id, proof)?))
}
/// Handles a message received from `sender_id`.
@ -172,13 +171,13 @@ impl<N: NodeIdT> Broadcast<N> {
// Send each proof to a node.
for (index, id) in self.netinfo.all_ids().enumerate() {
let proof = mtree.proof(index).ok_or(Error::ProofConstructionFailed)?;
if *id == *self.netinfo.our_id() {
if *id == *self.our_id() {
// The proof is addressed to this node.
result = Ok(proof);
} else {
// Rest of the proofs are sent to remote nodes.
let msg = Target::Node(id.clone()).message(Message::Value(proof));
step.messages.push_back(msg);
step.messages.push(msg);
}
}
@ -191,7 +190,7 @@ impl<N: NodeIdT> Broadcast<N> {
if *sender_id != self.proposer_id {
info!(
"Node {:?} received Value from {:?} instead of {:?}.",
self.netinfo.our_id(),
self.our_id(),
sender_id,
self.proposer_id
);
@ -199,7 +198,7 @@ impl<N: NodeIdT> Broadcast<N> {
return Ok(Fault::new(sender_id.clone(), fault_kind).into());
}
if self.echo_sent {
info!("Node {:?} received multiple Values.", self.netinfo.our_id());
info!("Node {:?} received multiple Values.", self.our_id());
if self.echos.get(self.our_id()) == Some(&p) {
return Ok(Step::default());
} else {
@ -208,7 +207,7 @@ impl<N: NodeIdT> Broadcast<N> {
}
// If the proof is invalid, log the faulty node behavior and ignore.
if !self.validate_proof(&p, &self.netinfo.our_id()) {
if !self.validate_proof(&p, &self.our_id()) {
return Ok(Fault::new(sender_id.clone(), FaultKind::InvalidProof).into());
}
@ -222,7 +221,7 @@ impl<N: NodeIdT> Broadcast<N> {
if self.echos.contains_key(sender_id) {
info!(
"Node {:?} received multiple Echos from {:?}.",
self.netinfo.our_id(),
self.our_id(),
sender_id,
);
return Ok(Step::default());
@ -252,7 +251,7 @@ impl<N: NodeIdT> Broadcast<N> {
if self.readys.contains_key(sender_id) {
info!(
"Node {:?} received multiple Readys from {:?}.",
self.netinfo.our_id(),
self.our_id(),
sender_id
);
return Ok(Step::default());
@ -267,8 +266,7 @@ impl<N: NodeIdT> Broadcast<N> {
// Enqueue a broadcast of a Ready message.
step.extend(self.send_ready(hash)?);
}
step.extend(self.compute_output(hash)?);
Ok(step)
Ok(step.join(self.compute_output(hash)?))
}
/// Sends an `Echo` message and handles it. Does nothing if we are only an observer.
@ -278,10 +276,9 @@ impl<N: NodeIdT> Broadcast<N> {
return Ok(Step::default());
}
let echo_msg = Message::Echo(p.clone());
let mut step: Step<_> = Target::All.message(echo_msg).into();
let our_id = &self.netinfo.our_id().clone();
step.extend(self.handle_echo(our_id, p)?);
Ok(step)
let step: Step<_> = Target::All.message(echo_msg).into();
let our_id = &self.our_id().clone();
Ok(step.join(self.handle_echo(our_id, p)?))
}
/// Sends a `Ready` message and handles it. Does nothing if we are only an observer.
@ -291,10 +288,9 @@ impl<N: NodeIdT> Broadcast<N> {
return Ok(Step::default());
}
let ready_msg = Message::Ready(*hash);
let mut step: Step<_> = Target::All.message(ready_msg).into();
let our_id = &self.netinfo.our_id().clone();
step.extend(self.handle_ready(our_id, hash)?);
Ok(step)
let step: Step<_> = Target::All.message(ready_msg).into();
let our_id = &self.our_id().clone();
Ok(step.join(self.handle_ready(our_id, hash)?))
}
/// Checks whether the conditions for output are met for this hash, and if so, sets the output
@ -336,14 +332,14 @@ impl<N: NodeIdT> Broadcast<N> {
if !p.validate(self.netinfo.num_nodes()) {
info!(
"Node {:?} received invalid proof: {:?}",
self.netinfo.our_id(),
self.our_id(),
HexProof(&p)
);
false
} else if self.netinfo.node_index(id) != Some(p.index()) {
info!(
"Node {:?} received proof for wrong position: {:?}.",
self.netinfo.our_id(),
self.our_id(),
HexProof(&p)
);
false

View File

@ -15,7 +15,8 @@ use super::{
};
use fault_log::{Fault, FaultKind, FaultLog};
use honey_badger::{self, HoneyBadger, Message as HbMessage};
use sync_key_gen::{Ack, Part, PartOutcome, SyncKeyGen};
use sync_key_gen::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen};
use threshold_decryption::EncryptionSchedule;
use util::{self, SubRng};
use {Contribution, DistAlgorithm, NetworkInfo, NodeIdT, Target};
@ -280,7 +281,7 @@ where
} else {
step.extend(match kg_msg {
KeyGenMessage::Part(part) => self.handle_part(&s_id, part)?,
KeyGenMessage::Ack(ack) => self.handle_ack(&s_id, ack)?.into(),
KeyGenMessage::Ack(ack) => self.handle_ack(&s_id, ack)?,
});
}
}
@ -304,7 +305,7 @@ where
} else {
ChangeState::None
};
step.output.push_back(Batch {
step.output.push(Batch {
epoch: batch_epoch,
change,
netinfo: Arc::new(self.netinfo.clone()),
@ -387,7 +388,9 @@ where
/// Handles a `Part` message that was output by Honey Badger.
fn handle_part(&mut self, sender_id: &N, part: Part) -> Result<Step<C, N>> {
let outcome = if let Some(kgs) = self.key_gen_state.as_mut() {
kgs.key_gen.handle_part(&mut self.rng, &sender_id, part)
kgs.key_gen
.handle_part(&mut self.rng, &sender_id, part)
.map_err(ErrorKind::SyncKeyGen)?
} else {
// No key generation ongoing.
let fault_kind = FaultKind::UnexpectedKeyGenPart;
@ -395,20 +398,33 @@ where
};
match outcome {
Some(PartOutcome::Valid(ack)) => self.send_transaction(KeyGenMessage::Ack(ack)),
Some(PartOutcome::Invalid(fault_log)) => Ok(fault_log.into()),
None => Ok(Step::default()),
PartOutcome::Valid(Some(ack)) => self.send_transaction(KeyGenMessage::Ack(ack)),
PartOutcome::Valid(None) => Ok(Step::default()),
PartOutcome::Invalid(fault) => {
let fault_kind = FaultKind::SyncKeyGenPart(fault);
Ok(Fault::new(sender_id.clone(), fault_kind).into())
}
}
}
/// Handles an `Ack` message that was output by Honey Badger.
fn handle_ack(&mut self, sender_id: &N, ack: Ack) -> Result<FaultLog<N>> {
if let Some(kgs) = self.key_gen_state.as_mut() {
Ok(kgs.key_gen.handle_ack(sender_id, ack))
fn handle_ack(&mut self, sender_id: &N, ack: Ack) -> Result<Step<C, N>> {
let outcome = if let Some(kgs) = self.key_gen_state.as_mut() {
kgs.key_gen
.handle_ack(sender_id, ack)
.map_err(ErrorKind::SyncKeyGen)?
} else {
// No key generation ongoing.
let fault_kind = FaultKind::UnexpectedKeyGenAck;
return Ok(Fault::new(sender_id.clone(), fault_kind).into());
};
match outcome {
AckOutcome::Valid => Ok(Step::default()),
AckOutcome::Invalid(fault) => {
let fault_kind = FaultKind::SyncKeyGenAck(fault);
Ok(Fault::new(sender_id.clone(), fault_kind).into())
}
}
}
@ -418,7 +434,7 @@ where
bincode::serialize(&kg_msg).map_err(|err| ErrorKind::SendTransactionBincode(*err))?;
let sig = Box::new(self.netinfo.secret_key().sign(ser));
if self.netinfo.is_validator() {
let our_id = self.netinfo.our_id().clone();
let our_id = self.our_id().clone();
let signed_msg =
SignedKeyGenMsg(self.start_epoch, our_id, kg_msg.clone(), *sig.clone());
self.key_gen_msg_buffer.push(signed_msg);

View File

@ -5,20 +5,7 @@
//! calling algorithm via `DistAlgorihm`'s `.handle_input()` and
//! `.handle_message()` trait methods.
/// A fault log entry.
#[derive(Clone, Copy, Eq, PartialEq, Debug, Fail)]
pub enum AckMessageFault {
#[fail(display = "Wrong node count")]
NodeCount,
#[fail(display = "Sender does not exist")]
SenderExist,
#[fail(display = "Value decryption failed")]
ValueDecryption,
#[fail(display = "Value deserialization failed")]
ValueDeserialization,
#[fail(display = "Invalid value")]
ValueInvalid,
}
pub use sync_key_gen::{AckFault, PartFault};
/// Represents each reason why a node could be considered faulty.
#[derive(Clone, Copy, Debug, PartialEq)]
@ -60,12 +47,10 @@ pub enum FaultKind {
/// `DynamicHoneyBadger` received a message (Accept, Propose, or Change)
/// with an invalid signature.
IncorrectPayloadSignature,
/// `DynamicHoneyBadger`/`SyncKeyGen` received an invalid Ack message.
AckMessage(AckMessageFault),
/// `DynamicHoneyBadger`/`SyncKeyGen` received an invalid Part message.
InvalidPartMessage,
/// `DynamicHoneyBadger`/`SyncKeyGen` received multiple Part messages from a node.
MultiplePartMessages,
/// `DynamicHoneyBadger`/`SyncKeyGen` received an invalid `Ack` message.
SyncKeyGenAck(AckFault),
/// `DynamicHoneyBadger`/`SyncKeyGen` received an invalid `Part` message.
SyncKeyGenPart(PartFault),
/// `DynamicHoneyBadger` received a change vote with an invalid signature.
InvalidVoteSignature,
/// A validator committed an invalid vote in `DynamicHoneyBadger`.

View File

@ -1,7 +1,7 @@
#![cfg_attr(feature = "cargo-clippy", allow(borrowed_box))]
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::collections::{BTreeMap, BTreeSet};
use std::marker::PhantomData;
use std::mem::replace;
use std::sync::Arc;
@ -310,7 +310,7 @@ where
/// Checks whether the subset has output, and if it does, sends out our decryption shares.
fn process_subset(&mut self, cs_step: cs::Step<N>) -> Result<Step<C, N>> {
let mut step = Step::default();
let cs_outputs: VecDeque<_> = step.extend_with(cs_step, |cs_msg| {
let cs_outputs = step.extend_with(cs_step, |cs_msg| {
MessageContent::Subset(cs_msg).with_epoch(self.epoch)
});
let mut has_seen_done = false;

View File

@ -101,8 +101,7 @@ where
let rng = &mut self.rng;
epoch_state.propose(proposal, rng)?
};
step.extend(self.try_output_batches()?);
Ok(step)
Ok(step.join(self.try_output_batches()?))
}
/// Handles a message received from `sender_id`.
@ -120,11 +119,10 @@ where
.or_insert_with(Vec::new)
.push((sender_id.clone(), content));
} else if self.epoch <= epoch {
let mut step = self
let step = self
.epoch_state_mut(epoch)?
.handle_message_content(sender_id, content)?;
step.extend(self.try_output_batches()?);
return Ok(step);
return Ok(step.join(self.try_output_batches()?));
} // And ignore all messages from past epochs.
Ok(Step::default())
}
@ -172,7 +170,7 @@ where
.and_then(EpochState::try_output_batch)
{
// Queue the output and advance the epoch.
step.output.push_back(batch);
step.output.push(batch);
step.fault_log.extend(fault_log);
step.extend(self.update_epoch()?);
}

View File

@ -154,7 +154,7 @@ pub mod transaction_queue;
pub mod util;
pub use crypto::pairing;
pub use fault_log::{AckMessageFault, Fault, FaultKind, FaultLog};
pub use fault_log::{Fault, FaultKind, FaultLog};
pub use messaging::{SourcedMessage, Target, TargetedMessage};
pub use network_info::NetworkInfo;
pub use traits::{Contribution, DistAlgorithm, Message, NodeIdT, Step};

View File

@ -55,68 +55,81 @@ impl<N: NodeIdT> NetworkInfo<N> {
}
/// The ID of the node the algorithm runs on.
#[inline]
pub fn our_id(&self) -> &N {
&self.our_id
}
/// ID of all nodes in the network.
#[inline]
pub fn all_ids(&self) -> impl Iterator<Item = &N> {
self.public_keys.keys()
}
/// The total number _N_ of nodes.
#[inline]
pub fn num_nodes(&self) -> usize {
self.num_nodes
}
/// The maximum number _f_ of faulty, Byzantine nodes up to which Honey Badger is guaranteed to
/// be correct.
#[inline]
pub fn num_faulty(&self) -> usize {
self.num_faulty
}
/// The minimum number _N - f_ of correct nodes with which Honey Badger is guaranteed to be
/// correct.
#[inline]
pub fn num_correct(&self) -> usize {
self.num_nodes - self.num_faulty
}
/// Returns our secret key share for threshold cryptography.
#[inline]
pub fn secret_key_share(&self) -> &SecretKeyShare {
&self.secret_key_share
}
/// Returns our secret key for encryption and signing.
#[inline]
pub fn secret_key(&self) -> &SecretKey {
&self.secret_key
}
/// Returns the public key set for threshold cryptography.
#[inline]
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`.
#[inline]
pub fn public_key_share(&self, id: &N) -> Option<&PublicKeyShare> {
self.public_key_shares.get(id)
}
/// Returns a map of all node IDs to their public key shares.
#[inline]
pub fn public_key_share_map(&self) -> &BTreeMap<N, PublicKeyShare> {
&self.public_key_shares
}
/// Returns a map of all node IDs to their public keys.
#[inline]
pub fn public_key(&self, id: &N) -> Option<&PublicKey> {
self.public_keys.get(id)
}
/// Returns a map of all node IDs to their public keys.
#[inline]
pub fn public_key_map(&self) -> &BTreeMap<N, PublicKey> {
&self.public_keys
}
/// The index of a node in a canonical numbering of all nodes.
#[inline]
pub fn node_index(&self, id: &N) -> Option<usize> {
self.node_indices.get(id).cloned()
}
@ -127,18 +140,21 @@ impl<N: NodeIdT> NetworkInfo<N> {
/// 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.
#[inline]
pub fn invocation_id(&self) -> Vec<u8> {
self.public_key_set.public_key().to_bytes()
}
/// Returns `true` if this node takes part in the consensus itself. If not, it is only an
/// observer.
#[inline]
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.
#[inline]
pub fn is_node_validator(&self, id: &N) -> bool {
self.public_keys.contains_key(id)
}

View File

@ -251,20 +251,19 @@ where
/// This stores a pending vote for the change. It will be included in some future batch, and
/// once enough validators have been voted for the same change, it will take effect.
pub fn vote_for(&mut self, change: Change<N>) -> Result<Step<T, N, Q>> {
let mut step = self
Ok(self
.dyn_hb
.handle_input(Input::Change(change))
.map_err(ErrorKind::Input)?
.convert();
step.extend(self.propose()?);
Ok(step)
.convert()
.join(self.propose()?))
}
/// Handles a message received from `sender_id`.
///
/// This must be called with every message we receive from another node.
pub fn handle_message(&mut self, sender_id: &N, message: Message<N>) -> Result<Step<T, N, Q>> {
let mut step = self
let step = self
.dyn_hb
.handle_message(sender_id, message)
.map_err(ErrorKind::HandleMessage)?
@ -272,8 +271,7 @@ where
for batch in &step.output {
self.queue.remove_multiple(batch.iter());
}
step.extend(self.propose()?);
Ok(step)
Ok(step.join(self.propose()?))
}
/// Returns a reference to the internal `DynamicHoneyBadger` instance.

View File

@ -160,7 +160,7 @@ impl<N: NodeIdT + Rand> Subset<N> {
if !self.netinfo.is_validator() {
return Ok(Step::default());
}
let id = self.netinfo.our_id().clone();
let id = self.our_id().clone();
debug!("{:?} Proposing {:0.10}", id, HexFmt(&value));
self.process_broadcast(&id, |bc| bc.handle_input(value))
}
@ -234,7 +234,7 @@ impl<N: NodeIdT + Rand> Subset<N> {
let val_to_insert = if let Some(true) = self.ba_results.get(proposer_id) {
debug!(" {:?} → {:0.10}", proposer_id, HexFmt(&value));
step.output
.extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value)));
.push(SubsetOutput::Contribution(proposer_id.clone(), value));
None
} else {
Some(value)
@ -247,8 +247,7 @@ impl<N: NodeIdT + Rand> Subset<N> {
error!("Duplicate insert in broadcast_results: {:?}", inval)
}
let set_binary_agreement_input = |ba: &mut BinaryAgreement<N>| ba.handle_input(true);
step.extend(self.process_binary_agreement(proposer_id, set_binary_agreement_input)?);
Ok(step)
Ok(step.join(self.process_binary_agreement(proposer_id, set_binary_agreement_input)?))
}
/// Callback to be invoked on receipt of the decision value of the Binary Agreement
@ -289,7 +288,7 @@ impl<N: NodeIdT + Rand> Subset<N> {
debug!(
"{:?} Updated Binary Agreement results: {:?}",
self.netinfo.our_id(),
self.our_id(),
self.ba_results
);
@ -311,15 +310,18 @@ impl<N: NodeIdT + Rand> Subset<N> {
}
}
}
if let Some(Some(value)) = self.broadcast_results.insert(proposer_id.clone(), None) {
if let Some(value) = self
.broadcast_results
.get_mut(proposer_id)
.and_then(Option::take)
{
debug!(" {:?} → {:0.10}", proposer_id, HexFmt(&value));
step.output
.extend(Some(SubsetOutput::Contribution(proposer_id.clone(), value)));
.push(SubsetOutput::Contribution(proposer_id.clone(), value));
}
}
step.output.extend(self.try_binary_agreement_completion());
Ok(step)
Ok(step.with_output(self.try_binary_agreement_completion()))
}
/// Returns the number of Binary Agreement instances that have decided "yes".
@ -339,7 +341,7 @@ impl<N: NodeIdT + Rand> Subset<N> {
}
debug!(
"{:?} All Binary Agreement instances have terminated",
self.netinfo.our_id()
self.our_id()
);
// All instances of BinaryAgreement that delivered `true` (or "1" in the paper).
let delivered_1: BTreeSet<&N> = self
@ -362,10 +364,7 @@ impl<N: NodeIdT + Rand> Subset<N> {
.collect();
if delivered_1.len() == broadcast_results.len() {
debug!(
"{:?} Binary Agreement instances completed:",
self.netinfo.our_id()
);
debug!("{:?} Binary Agreement instances completed:", self.our_id());
self.decided = true;
Some(SubsetOutput::Done)
} else {

View File

@ -57,7 +57,7 @@
//! use std::collections::BTreeMap;
//!
//! use threshold_crypto::{PublicKey, SecretKey, SignatureShare};
//! use hbbft::sync_key_gen::{PartOutcome, SyncKeyGen};
//! use hbbft::sync_key_gen::{AckOutcome, PartOutcome, SyncKeyGen};
//!
//! // Use a default random number generator for any randomness:
//! let mut rng = rand::thread_rng();
@ -88,10 +88,15 @@
//! let mut acks = Vec::new();
//! for (sender_id, part) in parts {
//! for (&id, node) in &mut nodes {
//! match node.handle_part(&mut rng, &sender_id, part.clone()) {
//! Some(PartOutcome::Valid(ack)) => acks.push((id, ack)),
//! Some(PartOutcome::Invalid(faults)) => panic!("Invalid part: {:?}", faults),
//! None => panic!("We are not an observer, so we should send Ack."),
//! match node
//! .handle_part(&mut rng, &sender_id, part.clone())
//! .expect("Failed to handle Part")
//! {
//! PartOutcome::Valid(Some(ack)) => acks.push((id, ack)),
//! PartOutcome::Invalid(fault) => panic!("Invalid Part: {:?}", fault),
//! PartOutcome::Valid(None) => {
//! panic!("We are not an observer, so we should send Ack.")
//! }
//! }
//! }
//! }
@ -99,7 +104,10 @@
//! // Finally, we handle all the `Ack`s.
//! for (sender_id, ack) in acks {
//! for node in nodes.values_mut() {
//! node.handle_ack(&sender_id, ack.clone());
//! match node.handle_ack(&sender_id, ack.clone()).expect("Failed to handle Ack") {
//! AckOutcome::Valid => (),
//! AckOutcome::Invalid(fault) => panic!("Invalid Ack: {:?}", fault),
//! }
//! }
//! }
//!
@ -159,7 +167,6 @@
//! method above. The sum of the secret keys we received from each node is then used as our secret
//! key. No single node knows the secret master key.
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::{self, Debug, Formatter};
@ -174,12 +181,12 @@ use crypto::{Fr, G1Affine};
use pairing::{CurveAffine, Field};
use rand;
use fault_log::{AckMessageFault as Fault, FaultKind, FaultLog};
use {NetworkInfo, NodeIdT};
// TODO: No need to send our own row and value to ourselves.
// A sync-key-gen error.
/// A local error while handling an `Ack` or `Part` message, that was not caused by that message
/// being invalid.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum Error {
#[fail(display = "Error creating SyncKeyGen: {}", _0)]
@ -188,6 +195,16 @@ pub enum Error {
Generation(CryptoError),
#[fail(display = "Error acknowledging part: {}", _0)]
Ack(CryptoError),
#[fail(display = "Unknown sender")]
UnknownSender,
#[fail(display = "Serialization error: {}", _0)]
Serialize(String),
}
impl From<bincode::Error> for Error {
fn from(err: bincode::Error) -> Error {
Error::Serialize(format!("{:?}", err))
}
}
/// A submission by a validator for the key generation. It must to be sent to all participating
@ -253,15 +270,21 @@ impl ProposalState {
}
/// The outcome of handling and verifying a `Part` message.
pub enum PartOutcome<N: Clone> {
pub enum PartOutcome {
/// The message was valid: the part of it that was encrypted to us matched the public
/// commitment, so we can multicast an `Ack` message for it.
Valid(Ack),
// If the Part message passed to `handle_part()` is invalid, the
// fault is logged and passed onto the caller.
/// The message was invalid: the part encrypted to us was malformed or didn't match the
/// commitment. We now know that the proposer is faulty, and dont' send an `Ack`.
Invalid(FaultLog<N>),
/// commitment, so we can multicast an `Ack` message for it. If we are an observer or we have
/// already handled the same `Part` before, this contains `None` instead.
Valid(Option<Ack>),
/// The message was invalid: We now know that the proposer is faulty, and dont' send an `Ack`.
Invalid(PartFault),
}
/// The outcome of handling and verifying an `Ack` message.
pub enum AckOutcome {
/// The message was valid.
Valid,
/// The message was invalid: The sender is faulty.
Invalid(AckFault),
}
/// A synchronous algorithm for dealerless distributed key generation.
@ -316,8 +339,7 @@ impl<N: NodeIdT> SyncKeyGen<N> {
let commit = our_part.commitment();
let encrypt = |(i, pk): (usize, &PublicKey)| {
let row = our_part.row(i + 1);
let bytes = bincode::serialize(&row).expect("failed to serialize row");
Ok(pk.encrypt_with_rng(rng, &bytes))
Ok(pk.encrypt_with_rng(rng, &bincode::serialize(&row)?))
};
let rows = key_gen
.pub_keys
@ -338,65 +360,44 @@ impl<N: NodeIdT> SyncKeyGen<N> {
&mut self,
rng: &mut R,
sender_id: &N,
Part(commit, rows): Part,
) -> Option<PartOutcome<N>> {
let sender_idx = self.node_index(sender_id)?; // TODO: Return an error.
let opt_commit_row = self.our_idx.map(|idx| commit.row(idx + 1));
match self.parts.entry(sender_idx) {
Entry::Occupied(entry) => {
if *entry.get() != ProposalState::new(commit) {
debug!("Received multiple parts from node {:?}.", sender_id);
let fault_log =
FaultLog::init(sender_id.clone(), FaultKind::MultiplePartMessages);
return Some(PartOutcome::Invalid(fault_log));
}
return None;
}
Entry::Vacant(entry) => {
entry.insert(ProposalState::new(commit));
}
}
// If we are only an observer, return `None`. We don't need to send `Ack`.
let our_idx = self.our_idx?;
let commit_row = opt_commit_row?;
let ser_row = self.sec_key.decrypt(rows.get(our_idx as usize)?)?;
let row: Poly = if let Ok(row) = bincode::deserialize(&ser_row) {
row
} else {
// Log the faulty node and ignore invalid messages.
let fault_log = FaultLog::init(sender_id.clone(), FaultKind::InvalidPartMessage);
return Some(PartOutcome::Invalid(fault_log));
part: Part,
) -> Result<PartOutcome, Error> {
let sender_idx = self.node_index(sender_id).ok_or(Error::UnknownSender)?;
let row = match self.handle_part_or_fault(sender_idx, part) {
Ok(Some(row)) => row,
Ok(None) => return Ok(PartOutcome::Valid(None)),
Err(fault) => return Ok(PartOutcome::Invalid(fault)),
};
if row.commitment() != commit_row {
error!("Invalid part from node {:?}.", sender_id);
let fault_log = FaultLog::init(sender_id.clone(), FaultKind::InvalidPartMessage);
return Some(PartOutcome::Invalid(fault_log));
}
// The row is valid: now encrypt one value for each node.
let encrypt = |(idx, pk): (usize, &PublicKey)| {
// The row is valid. Encrypt one value for each node and broadcast an `Ack`.
let mut values = Vec::new();
for (idx, pk) in self.pub_keys.values().enumerate() {
let val = row.evaluate(idx + 1);
let wrap = FieldWrap::new(val);
// TODO: Handle errors.
let ser_val = bincode::serialize(&wrap).expect("failed to serialize value");
pk.encrypt_with_rng(rng, ser_val)
};
let values = self.pub_keys.values().enumerate().map(encrypt).collect();
Some(PartOutcome::Valid(Ack(sender_idx, values)))
let ser_val = bincode::serialize(&FieldWrap::new(val))?;
values.push(pk.encrypt_with_rng(rng, ser_val));
}
Ok(PartOutcome::Valid(Some(Ack(sender_idx, values))))
}
/// Handles an `Ack` message.
///
/// All participating nodes must handle the exact same sequence of messages.
/// Note that `handle_ack` also needs to explicitly be called with this instance's own `Ack`s.
pub fn handle_ack(&mut self, sender_id: &N, ack: Ack) -> FaultLog<N> {
let mut fault_log = FaultLog::new();
if let Some(sender_idx) = self.node_index(sender_id) {
if let Err(fault) = self.handle_ack_or_err(sender_idx, ack) {
debug!("Invalid ack from node {:?}: {}", sender_id, fault);
fault_log.append(sender_id.clone(), FaultKind::AckMessage(fault));
}
pub fn handle_ack(&mut self, sender_id: &N, ack: Ack) -> Result<AckOutcome, Error> {
let sender_idx = self.node_index(sender_id).ok_or(Error::UnknownSender)?;
Ok(match self.handle_ack_or_fault(sender_idx, ack) {
Ok(()) => AckOutcome::Valid,
Err(fault) => AckOutcome::Invalid(fault),
})
}
/// Returns the index of the node, or `None` if it is unknown.
fn node_index(&self, node_id: &N) -> Option<u64> {
if let Some(node_idx) = self.pub_keys.keys().position(|id| id == node_id) {
Some(node_idx as u64)
} else {
error!("Unknown node {:?}", node_id);
None
}
fault_log
}
/// Returns the number of complete parts. If this is at least `threshold + 1`, the keys can
@ -466,52 +467,102 @@ impl<N: NodeIdT> SyncKeyGen<N> {
self.pub_keys.len()
}
/// Handles an `Ack` message or returns an error string.
fn handle_ack_or_err(
/// Handles a `Part` message, or returns a `PartFault` if it is invalid.
fn handle_part_or_fault(
&mut self,
sender_idx: u64,
Part(commit, rows): Part,
) -> Result<Option<Poly>, PartFault> {
if rows.len() != self.pub_keys.len() {
return Err(PartFault::RowCount);
}
if let Some(state) = self.parts.get(&sender_idx) {
if *state != ProposalState::new(commit) {
return Err(PartFault::MultipleParts);
}
return Ok(None); // We already handled this `Part` before.
}
// Retrieve our own row's commitment, and store the full commitment.
let opt_idx_commit_row = self.our_idx.map(|idx| (idx, commit.row(idx + 1)));
self.parts.insert(sender_idx, ProposalState::new(commit));
let (our_idx, commit_row) = match opt_idx_commit_row {
Some((idx, row)) => (idx, row),
None => return Ok(None), // We are only an observer. Nothing to send or decrypt.
};
// We are a validator: Decrypt and deserialize our row and compare it to the commitment.
let ser_row = self
.sec_key
.decrypt(&rows[our_idx as usize])
.ok_or(PartFault::DecryptRow)?;
let row: Poly = bincode::deserialize(&ser_row).map_err(|_| PartFault::DeserializeRow)?;
if row.commitment() != commit_row {
return Err(PartFault::RowCommitment);
}
Ok(Some(row))
}
/// Handles an `Ack` message, or returns an `AckFault` if it is invalid.
fn handle_ack_or_fault(
&mut self,
sender_idx: u64,
Ack(proposer_idx, values): Ack,
) -> Result<(), Fault> {
) -> Result<(), AckFault> {
if values.len() != self.pub_keys.len() {
return Err(Fault::NodeCount);
return Err(AckFault::ValueCount);
}
let part = self
.parts
.get_mut(&proposer_idx)
.ok_or_else(|| Fault::SenderExist)?;
.ok_or(AckFault::MissingPart)?;
if !part.acks.insert(sender_idx) {
return Ok(());
return Ok(()); // We already handled this `Ack` before.
}
let our_idx = match self.our_idx {
Some(our_idx) => our_idx,
None => return Ok(()), // We are only an observer. Nothing to decrypt for us.
};
let ser_val: Vec<u8> = self
// We are a validator: Decrypt and deserialize our value and compare it to the commitment.
let ser_val = self
.sec_key
.decrypt(&values[our_idx as usize])
.ok_or_else(|| Fault::ValueDecryption)?;
.ok_or(AckFault::DecryptValue)?;
let val = bincode::deserialize::<FieldWrap<Fr, Fr>>(&ser_val)
.map_err(|err| {
error!(
"Secure value deserialization failed while handling ack: {:?}",
err
);
Fault::ValueDeserialization
})?.into_inner();
.map_err(|_| AckFault::DeserializeValue)?
.into_inner();
if part.commit.evaluate(our_idx + 1, sender_idx + 1) != G1Affine::one().mul(val) {
return Err(Fault::ValueInvalid);
return Err(AckFault::ValueCommitment);
}
part.values.insert(sender_idx + 1, val);
Ok(())
}
/// Returns the index of the node, or `None` if it is unknown.
fn node_index(&self, node_id: &N) -> Option<u64> {
if let Some(node_idx) = self.pub_keys.keys().position(|id| id == node_id) {
Some(node_idx as u64)
} else {
error!("Unknown node {:?}", node_id);
None
}
}
}
/// An error in an `Ack` message sent by a faulty node.
#[derive(Clone, Copy, Eq, PartialEq, Debug, Fail)]
pub enum AckFault {
#[fail(display = "The number of values differs from the number of nodes")]
ValueCount,
#[fail(display = "No corresponding Part received")]
MissingPart,
#[fail(display = "Value decryption failed")]
DecryptValue,
#[fail(display = "Value deserialization failed")]
DeserializeValue,
#[fail(display = "Value doesn not match the commitment")]
ValueCommitment,
}
/// An error in a `Part` message sent by a faulty node.
#[derive(Clone, Copy, Eq, PartialEq, Debug, Fail)]
pub enum PartFault {
#[fail(display = "The number of rows differs from the number of nodes")]
RowCount,
#[fail(display = "Received multiple different Part messages from the same sender")]
MultipleParts,
#[fail(display = "Could not decrypt our row in the Part message")]
DecryptRow,
#[fail(display = "Could not deserialize our row in the Part message")]
DeserializeRow,
#[fail(display = "Row does not match the commitment")]
RowCommitment,
}

View File

@ -125,7 +125,7 @@ impl<N: NodeIdT> ThresholdDecryption<N> {
step.fault_log.extend(self.remove_invalid_shares());
if self.netinfo.is_validator() {
let msg = Target::All.message(Message(share.clone()));
step.messages.push_back(msg);
step.messages.push(msg);
self.shares.insert(our_id, share);
}
step.extend(self.try_output()?);

View File

@ -115,7 +115,7 @@ impl<N: NodeIdT> ThresholdSign<N> {
}
let msg = Message(self.netinfo.secret_key_share().sign_g2(self.msg_hash));
let mut step: Step<_> = Target::All.message(msg.clone()).into();
let id = self.netinfo.our_id().clone();
let id = self.our_id().clone();
step.extend(self.handle_message(&id, msg)?);
Ok(step)
}
@ -146,13 +146,13 @@ impl<N: NodeIdT> ThresholdSign<N> {
fn try_output(&mut self) -> Result<Step<N>> {
debug!(
"{:?} received {} shares, had_input = {}",
self.netinfo.our_id(),
self.our_id(),
self.received_shares.len(),
self.had_input
);
if self.had_input && self.received_shares.len() > self.netinfo.num_faulty() {
let sig = self.combine_and_verify_sig()?;
debug!("{:?} output {:?}", self.netinfo.our_id(), sig);
debug!("{:?} output {:?}", self.our_id(), sig);
self.terminated = true;
let step = self.handle_input(())?; // Before terminating, make sure we sent our share.
Ok(step.with_output(sig))
@ -177,10 +177,7 @@ impl<N: NodeIdT> ThresholdSign<N> {
.verify_g2(&sig, self.msg_hash)
{
// Abort
error!(
"{:?} main public key verification failed",
self.netinfo.our_id()
);
error!("{:?} main public key verification failed", self.our_id());
Err(Error::VerificationFailed)
} else {
Ok(sig)

View File

@ -1,6 +1,5 @@
//! Common supertraits for distributed algorithms.
use std::collections::VecDeque;
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::once;
@ -31,9 +30,9 @@ where
D: DistAlgorithm,
<D as DistAlgorithm>::NodeId: NodeIdT,
{
pub output: VecDeque<D::Output>,
pub output: Vec<D::Output>,
pub fault_log: FaultLog<D::NodeId>,
pub messages: VecDeque<TargetedMessage<D::Message, D::NodeId>>,
pub messages: Vec<TargetedMessage<D::Message, D::NodeId>>,
}
impl<D> Default for Step<D>
@ -43,9 +42,9 @@ where
{
fn default() -> Step<D> {
Step {
output: VecDeque::default(),
output: Vec::default(),
fault_log: FaultLog::default(),
messages: VecDeque::default(),
messages: Vec::default(),
}
}
}
@ -56,9 +55,9 @@ where
{
/// Creates a new `Step` from the given collections.
pub fn new(
output: VecDeque<D::Output>,
output: Vec<D::Output>,
fault_log: FaultLog<D::NodeId>,
messages: VecDeque<TargetedMessage<D::Message, D::NodeId>>,
messages: Vec<TargetedMessage<D::Message, D::NodeId>>,
) -> Self {
Step {
output,
@ -68,8 +67,8 @@ where
}
/// Returns the same step, with the given additional output.
pub fn with_output(mut self, output: D::Output) -> Self {
self.output.push_back(output);
pub fn with_output<T: Into<Option<D::Output>>>(mut self, output: T) -> Self {
self.output.extend(output.into());
self
}
@ -89,7 +88,7 @@ where
}
/// Extends `self` with `other`s messages and fault logs, and returns `other.output`.
pub fn extend_with<D2, FM>(&mut self, other: Step<D2>, f_msg: FM) -> VecDeque<D2::Output>
pub fn extend_with<D2, FM>(&mut self, other: Step<D2>, f_msg: FM) -> Vec<D2::Output>
where
D2: DistAlgorithm<NodeId = D::NodeId>,
FM: Fn(D2::Message) -> D::Message,
@ -107,6 +106,12 @@ where
self.messages.extend(other.messages);
}
/// Extends this step with `other` and returns the result.
pub fn join(mut self, other: Self) -> Self {
self.extend(other);
self
}
/// Converts this step into an equivalent step for a different `DistAlgorithm`.
// This cannot be a `From` impl, because it would conflict with `impl From<T> for T`.
pub fn convert<D2>(self) -> Step<D2>

View File

@ -673,7 +673,7 @@ where
// Perform sorting and drain `Vec` back into `VecDeque`.
msgs.sort_by(f);
self.messages.extend(msgs.into_iter());
self.messages.extend(msgs);
}
}

View File

@ -24,7 +24,7 @@ pub struct TestNode<D: DistAlgorithm> {
/// The values this node has output so far.
outputs: Vec<D::Output>,
/// Outgoing messages to be sent to other nodes.
messages: VecDeque<TargetedMessage<D::Message, D::NodeId>>,
messages: Vec<TargetedMessage<D::Message, D::NodeId>>,
/// Collected fault logs.
faults: Vec<Fault<D::NodeId>>,
}

View File

@ -41,9 +41,13 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) {
for (sender_id, proposal) in proposals[..=threshold].iter().enumerate() {
for (node_id, node) in nodes.iter_mut().enumerate() {
let proposal = proposal.clone().expect("proposal");
let ack = match node.handle_part(&mut rand::thread_rng(), &sender_id, proposal) {
Some(PartOutcome::Valid(ack)) => ack,
_ => panic!("invalid proposal"),
let ack = match node
.handle_part(&mut rand::thread_rng(), &sender_id, proposal)
.expect("failed to handle part")
{
PartOutcome::Valid(Some(ack)) => ack,
PartOutcome::Valid(None) => panic!("missing ack message"),
PartOutcome::Invalid(fault) => panic!("invalid proposal: {:?}", fault),
};
// Only the first `threshold + 1` manage to commit their `Ack`s.
if node_id <= 2 * threshold {
@ -56,7 +60,8 @@ fn test_sync_key_gen_with(threshold: usize, node_num: usize) {
for (sender_id, ack) in acks {
for node in &mut nodes {
assert!(!node.is_ready()); // Not enough `Ack`s yet.
node.handle_ack(&sender_id, ack.clone());
node.handle_ack(&sender_id, ack.clone())
.expect("error handling ack");
}
}