mirror of https://github.com/poanetwork/hbbft.git
Merge branch 'master' into optional_threshold_encryption_209
This commit is contained in:
commit
00985edc46
12
README.md
12
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()?);
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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()?);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>>,
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue