Split FaultKind. (#371)

This commit is contained in:
phahulin 2018-12-18 18:17:46 +03:00
parent 108ac574bb
commit 5bfcd6c692
31 changed files with 403 additions and 162 deletions

View File

@ -439,7 +439,8 @@ fn main() {
.build_with_transactions(txs.clone(), rng)
.expect("instantiate QueueingHoneyBadger");
let (sq, mut step) = SenderQueue::builder(qhb, peer_ids.into_iter()).build(our_id);
assert!(step.extend_with(qhb_step, Message::from).is_empty());
let output = step.extend_with(qhb_step, |fault| fault, Message::from);
assert!(output.is_empty());
(sq, step)
};

View File

@ -10,8 +10,8 @@ use rand::Rng;
use super::bool_multimap::BoolMultimap;
use super::bool_set::{self, BoolSet};
use super::sbv_broadcast::{self, Message as SbvMessage, SbvBroadcast};
use super::{Error, Message, MessageContent, Result, Step};
use crate::fault_log::{Fault, FaultKind};
use super::{Error, FaultKind, Message, MessageContent, Result, Step};
use crate::fault_log::Fault;
use crate::threshold_sign::{self, Message as TsMessage, ThresholdSign};
use crate::{DistAlgorithm, NetworkInfo, NodeIdT, SessionIdT, Target};
@ -177,6 +177,7 @@ impl<N: NodeIdT, S: SessionIdT> DistAlgorithm for BinaryAgreement<N, S> {
type Output = bool;
type Message = Message;
type Error = Error;
type FaultKind = FaultKind;
fn handle_input<R: Rng>(&mut self, input: Self::Input, _rng: &mut R) -> Result<Step<N>> {
self.propose(input)
@ -299,9 +300,11 @@ impl<N: NodeIdT, S: SessionIdT> BinaryAgreement<N, S> {
/// decides.
fn handle_sbvb_step(&mut self, sbvb_step: sbv_broadcast::Step<N>) -> Result<Step<N>> {
let mut step = Step::default();
let output = step.extend_with(sbvb_step, |msg| {
MessageContent::SbvBroadcast(msg).with_epoch(self.epoch)
});
let output = step.extend_with(
sbvb_step,
|fault| fault,
|msg| MessageContent::SbvBroadcast(msg).with_epoch(self.epoch),
);
if self.conf_values.is_some() {
return Ok(step); // The `Conf` round has already started.
}
@ -393,7 +396,7 @@ impl<N: NodeIdT, S: SessionIdT> BinaryAgreement<N, S> {
let mut step = Step::default();
let epoch = self.epoch;
let to_msg = |c_msg| MessageContent::Coin(Box::new(c_msg)).with_epoch(epoch);
let ts_output = step.extend_with(ts_step, to_msg);
let ts_output = step.extend_with(ts_step, FaultKind::CoinFault, to_msg);
if let Some(sig) = ts_output.into_iter().next() {
// Take the parity of the signature as the coin value.
self.coin_state = sig.parity().into();

View File

@ -105,8 +105,30 @@ impl From<bincode::Error> for Error {
/// A `BinaryAgreement` result.
pub type Result<T> = ::std::result::Result<T, Error>;
/// A faulty Binary Agreement message received from a peer.
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum FaultKind {
/// `BinaryAgreement` received a duplicate `BVal` message.
#[fail(display = "`BinaryAgreement` received a duplicate `BVal` message.")]
DuplicateBVal,
/// `BinaryAgreement` received a duplicate `Aux` message.
#[fail(display = "`BinaryAgreement` received a duplicate `Aux` message.")]
DuplicateAux,
/// `BinaryAgreement` received multiple `Conf` messages.
#[fail(display = "`BinaryAgreement` received multiple `Conf` messages.")]
MultipleConf,
/// `BinaryAgreement` received multiple `Term` messages.
#[fail(display = "`BinaryAgreement` received multiple `Term` messages.")]
MultipleTerm,
/// `BinaryAgreement` received a message with an epoch too far ahead.
#[fail(display = "`BinaryAgreement` received a message with an epoch too far ahead.")]
AgreementEpoch,
/// `BinaryAgreement` received a Coin Fault.
#[fail(display = "`BinaryAgreement` received a Coin Fault.")]
CoinFault(threshold_sign::FaultKind),
}
/// A `BinaryAgreement` step, containing at most one output.
pub type Step<N> = crate::Step<Message, bool, N>;
pub type Step<N> = crate::Step<Message, bool, N, FaultKind>;
/// The content of a message belonging to a particular `BinaryAgreement` epoch.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]

View File

@ -17,11 +17,11 @@ use serde_derive::{Deserialize, Serialize};
use super::bool_multimap::BoolMultimap;
use super::bool_set::{self, BoolSet};
use super::Result;
use crate::fault_log::{Fault, FaultKind};
use super::{FaultKind, Result};
use crate::fault_log::Fault;
use crate::{NetworkInfo, NodeIdT, Target};
pub type Step<N> = crate::Step<Message, BoolSet, N>;
pub type Step<N> = crate::Step<Message, BoolSet, N, FaultKind>;
/// A message belonging to the Synchronized Binary Value Broadcast phase of a `BinaryAgreement`
/// epoch.

View File

@ -11,8 +11,8 @@ use reed_solomon_erasure::ReedSolomon;
use super::merkle::{Digest, MerkleTree, Proof};
use super::message::HexProof;
use super::{Error, Message, Result};
use crate::fault_log::{Fault, FaultKind};
use super::{Error, FaultKind, Message, Result};
use crate::fault_log::Fault;
use crate::{DistAlgorithm, NetworkInfo, NodeIdT, Target};
type RseResult<T> = result::Result<T, rse::Error>;
@ -49,6 +49,7 @@ impl<N: NodeIdT> DistAlgorithm for Broadcast<N> {
type Output = Self::Input;
type Message = Message;
type Error = Error;
type FaultKind = FaultKind;
fn handle_input<R: Rng>(&mut self, input: Self::Input, _rng: &mut R) -> Result<Step<N>> {
self.broadcast(input)

View File

@ -22,3 +22,26 @@ pub enum Error {
/// A broadcast result.
pub type Result<T> = ::std::result::Result<T, Error>;
/// Represents each reason why a broadcast message could be faulty.
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum FaultKind {
/// `Broadcast` received a `Value` from a node other than the proposer.
#[fail(display = "`Broadcast` received a `Value` from a node other than the proposer.")]
ReceivedValueFromNonProposer,
/// `Broadcast` received multiple different `Value`s from the proposer.
#[fail(display = "`Broadcast` received multiple different `Value`s from the proposer.")]
MultipleValues,
/// `Broadcast` received multiple different `Echo`s from the same sender.
#[fail(display = "`Broadcast` received multiple different `Echo`s from the same sender.")]
MultipleEchos,
/// `Broadcast` received multiple different `Ready`s from the same sender.
#[fail(display = "`Broadcast` received multiple different `Ready`s from the same sender.")]
MultipleReadys,
/// `Broadcast` recevied an Echo message containing an invalid proof.
#[fail(display = "`Broadcast` recevied an Echo message containing an invalid proof.")]
InvalidProof,
///`Broadcast` received shards with valid proofs, that couldn't be decoded.
#[fail(display = "`Broadcast` received shards with valid proofs, that couldn't be decoded.")]
BroadcastDecoding,
}

View File

@ -190,5 +190,5 @@ pub(crate) mod merkle;
mod message;
pub use self::broadcast::{Broadcast, Step};
pub use self::error::{Error, Result};
pub use self::error::{Error, FaultKind, Result};
pub use self::message::Message;

View File

@ -11,11 +11,11 @@ use serde::{de::DeserializeOwned, Serialize};
use super::votes::{SignedVote, VoteCounter};
use super::{
Batch, Change, ChangeState, DynamicHoneyBadgerBuilder, EncryptionSchedule, Error, Input,
InternalContrib, JoinPlan, KeyGenMessage, KeyGenState, Message, Params, Result,
Batch, Change, ChangeState, DynamicHoneyBadgerBuilder, EncryptionSchedule, Error, FaultKind,
Input, InternalContrib, JoinPlan, KeyGenMessage, KeyGenState, Message, Params, Result,
SignedKeyGenMsg, Step,
};
use crate::fault_log::{Fault, FaultKind, FaultLog};
use crate::fault_log::{Fault, FaultLog};
use crate::honey_badger::{self, HoneyBadger, Message as HbMessage};
use crate::sync_key_gen::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen};
@ -52,6 +52,7 @@ where
type Output = Batch<C, N>;
type Message = Message<N>;
type Error = Error;
type FaultKind = FaultKind;
fn handle_input<R: Rng>(&mut self, input: Self::Input, rng: &mut R) -> Result<Step<C, N>> {
// User contributions are forwarded to `HoneyBadger` right away. Votes are signed and
@ -280,7 +281,7 @@ where
sender_id: &N,
kg_msg: KeyGenMessage,
sig: Signature,
) -> Result<FaultLog<N>> {
) -> Result<FaultLog<N, FaultKind>> {
if !self.verify_signature(sender_id, &sig, &kg_msg)? {
let fault_kind = FaultKind::InvalidKeyGenMessageSignature;
return Ok(Fault::new(sender_id.clone(), fault_kind).into());
@ -311,7 +312,9 @@ where
rng: &mut R,
) -> Result<Step<C, N>> {
let mut step: Step<C, N> = Step::default();
let output = step.extend_with(hb_step, |hb_msg| Message::HoneyBadger(self.era, hb_msg));
let output = step.extend_with(hb_step, FaultKind::HbFault, |hb_msg| {
Message::HoneyBadger(self.era, hb_msg)
});
for hb_batch in output {
let batch_era = self.era;
let batch_epoch = hb_batch.epoch + batch_era;

View File

@ -29,3 +29,65 @@ pub enum Error {
/// The result of `DynamicHoneyBadger` handling an input or message.
pub type Result<T> = ::std::result::Result<T, Error>;
/// Represents each way an an incoming message can be considered faulty.
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum FaultKind {
/// `DynamicHoneyBadger` received a key generation message with an invalid signature.
#[fail(
display = "`DynamicHoneyBadger` received a key generation message with an invalid signature."
)]
InvalidKeyGenMessageSignature,
/// `DynamicHoneyBadger` received a key generation message with an invalid era.
#[fail(
display = "`DynamicHoneyBadger` received a key generation message with an invalid era."
)]
InvalidKeyGenMessageEra,
/// `DynamicHoneyBadger` received a key generation message when there was no key generation in
/// progress.
#[fail(
display = "`DynamicHoneyBadger` received a key generation message when there was no key
generation in progress."
)]
UnexpectedKeyGenMessage,
/// `DynamicHoneyBadger` received a signed `Ack` when no key generation in progress.
#[fail(
display = "`DynamicHoneyBadger` received a signed `Ack` when no key generation in progress."
)]
UnexpectedKeyGenAck,
/// `DynamicHoneyBadger` received a signed `Part` when no key generation in progress.
#[fail(
display = "`DynamicHoneyBadger` received a signed `Part` when no key generation in progress."
)]
UnexpectedKeyGenPart,
/// `DynamicHoneyBadger` received more key generation messages from the peer than expected.
#[fail(
display = "`DynamicHoneyBadger` received more key generation messages from the peer than
expected."
)]
TooManyKeyGenMessages,
/// `DynamicHoneyBadger` received a message (Accept, Propose, or Change with an invalid
/// signature.
#[fail(
display = "`DynamicHoneyBadger` received a message (Accept, Propose, or Change
with an invalid signature."
)]
IncorrectPayloadSignature,
/// `DynamicHoneyBadger`/`SyncKeyGen` received an invalid `Ack` message.
#[fail(display = "`DynamicHoneyBadger`/`SyncKeyGen` received an invalid `Ack` message.")]
SyncKeyGenAck(sync_key_gen::AckFault),
/// `DynamicHoneyBadger`/`SyncKeyGen` received an invalid `Part` message.
#[fail(display = "`DynamicHoneyBadger`/`SyncKeyGen` received an invalid `Part` message.")]
SyncKeyGenPart(sync_key_gen::PartFault),
/// `DynamicHoneyBadger` received a change vote with an invalid signature.
#[fail(display = "`DynamicHoneyBadger` received a change vote with an invalid signature.")]
InvalidVoteSignature,
/// A validator committed an invalid vote in `DynamicHoneyBadger`.
#[fail(display = "A validator committed an invalid vote in `DynamicHoneyBadger`.")]
InvalidCommittedVote,
/// `DynamicHoneyBadger` received a message with an invalid era.
#[fail(display = "`DynamicHoneyBadger` received a message with an invalid era.")]
UnexpectedDhbMessageEra,
/// `DynamicHoneyBadger` received a fault from `HoneyBadger`.
#[fail(display = "`DynamicHoneyBadger` received a fault from `HoneyBadger`.")]
HbFault(honey_badger::FaultKind),
}

View File

@ -86,7 +86,7 @@ pub use self::batch::Batch;
pub use self::builder::DynamicHoneyBadgerBuilder;
pub use self::change::{Change, ChangeState};
pub use self::dynamic_honey_badger::DynamicHoneyBadger;
pub use self::error::{Error, Result};
pub use self::error::{Error, FaultKind, Result};
/// A `DynamicHoneyBadger` step, possibly containing multiple outputs.
pub type Step<C, N> = crate::DaStep<DynamicHoneyBadger<C, N>>;

View File

@ -6,10 +6,12 @@ use bincode;
use serde::Serialize;
use serde_derive::{Deserialize, Serialize};
use super::{Change, Error, Result};
use crate::fault_log::{FaultKind, FaultLog};
use super::{Change, Error, FaultKind, Result};
use crate::fault_log;
use crate::{NetworkInfo, NodeIdT};
pub type FaultLog<N> = fault_log::FaultLog<N, FaultKind>;
/// A buffer and counter collecting pending and committed votes for validator set changes.
///
/// This is reset whenever the set of validators changes or a change reaches _f + 1_ votes. We call
@ -190,8 +192,8 @@ mod tests {
use std::iter;
use std::sync::Arc;
use super::{Change, SignedVote, VoteCounter};
use crate::fault_log::{FaultKind, FaultLog};
use super::{Change, FaultKind, SignedVote, VoteCounter};
use crate::fault_log::FaultLog;
use crate::NetworkInfo;
use rand;

View File

@ -1,135 +1,90 @@
//! Functionality for logging faulty node behavior encountered by each
//! algorithm.
//!
//! Each algorithm can propogate their faulty node logs upwards to a
//! calling algorithm via `DistAlgorihm`'s `.handle_input()` and
//! `.handle_message()` trait methods.
//! Each algorithm can propogate their faulty node logs upwards to a calling algorithm via
//! `DistAlgorihm`'s `.handle_input()` and `.handle_message()` trait methods.
pub use crate::sync_key_gen::{AckFault, PartFault};
/// Represents each reason why a node could be considered faulty.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FaultKind {
/// `Coin` received a signature share from an unverified sender.
UnverifiedSignatureShareSender,
/// `HoneyBadger` received a decryption share from an unverified sender.
UnverifiedDecryptionShareSender,
/// `HoneyBadger` received a decryption share for an unaccepted proposer.
UnexpectedDecryptionShare,
/// `HoneyBadger` was unable to deserialize a proposer's ciphertext.
DeserializeCiphertext,
/// `HoneyBadger` received an invalid ciphertext from the proposer.
InvalidCiphertext,
/// `HoneyBadger` received a message with an invalid epoch.
UnexpectedHbMessageEpoch,
/// `ThresholdDecrypt` received multiple shares from the same sender.
MultipleDecryptionShares,
/// `Broadcast` received a `Value` from a node other than the proposer.
ReceivedValueFromNonProposer,
/// `Broadcast` received multiple different `Value`s from the proposer.
MultipleValues,
/// `Broadcast` received multiple different `Echo`s from the same sender.
MultipleEchos,
/// `Broadcast` received multiple different `Ready`s from the same sender.
MultipleReadys,
/// `Broadcast` recevied an Echo message containing an invalid proof.
InvalidProof,
/// `Broadcast` received shards with valid proofs, that couldn't be decoded.
BroadcastDecoding,
/// `HoneyBadger` could not deserialize bytes (i.e. a serialized Batch)
/// from a given proposer into a vector of transactions.
BatchDeserializationFailed,
/// `DynamicHoneyBadger` received a key generation message with an invalid signature.
InvalidKeyGenMessageSignature,
/// `DynamicHoneyBadger` received a key generation message with an invalid era.
InvalidKeyGenMessageEra,
/// `DynamicHoneyBadger` received a key generation message when there was no key generation in
/// progress.
UnexpectedKeyGenMessage,
/// `DynamicHoneyBadger` received a signed `Ack` when no key generation in progress.
UnexpectedKeyGenAck,
/// `DynamicHoneyBadger` received a signed `Part` when no key generation in progress.
UnexpectedKeyGenPart,
/// `DynamicHoneyBadger` received more key generation messages from the peer than expected.
TooManyKeyGenMessages,
/// `DynamicHoneyBadger` received a message (Accept, Propose, or Change)
/// with an invalid signature.
IncorrectPayloadSignature,
/// `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`.
InvalidCommittedVote,
/// `DynamicHoneyBadger` received a message with an invalid era.
UnexpectedDhbMessageEra,
/// `BinaryAgreement` received a duplicate `BVal` message.
DuplicateBVal,
/// `BinaryAgreement` received a duplicate `Aux` message.
DuplicateAux,
/// `BinaryAgreement` received multiple `Conf` messages.
MultipleConf,
/// `BinaryAgreement` received multiple `Term` messages.
MultipleTerm,
/// `BinaryAgreement` received a message with an epoch too far ahead.
AgreementEpoch,
}
pub use failure::Fail;
/// A structure representing the context of a faulty node. This structure
/// describes which node is faulty (`node_id`) and which faulty behavior
/// that the node exhibited ('kind').
#[derive(Clone, Debug, PartialEq)]
pub struct Fault<N> {
pub struct Fault<N, F: Fail> {
/// The faulty node's ID.
pub node_id: N,
/// The kind of fault the node is blamed for.
pub kind: FaultKind,
pub kind: F,
}
impl<N> Fault<N> {
impl<N, F> Fault<N, F>
where
F: Fail,
{
/// Creates a new fault, blaming `node_id` for the `kind`.
pub fn new(node_id: N, kind: FaultKind) -> Self {
pub fn new(node_id: N, kind: F) -> Self {
Fault { node_id, kind }
}
/// Applies `f_fault` to `kind`, leaves `node_id` unchanged
pub fn map<F2, FF>(self, f_fault: FF) -> Fault<N, F2>
where
F2: Fail,
FF: Fn(F) -> F2,
{
Fault {
node_id: self.node_id,
kind: f_fault(self.kind),
}
}
}
/// Creates a new `FaultLog` where `self` is the first element in the log
/// vector.
impl<N> Into<FaultLog<N>> for Fault<N> {
fn into(self) -> FaultLog<N> {
impl<N, F> Into<FaultLog<N, F>> for Fault<N, F>
where
F: Fail,
{
fn into(self) -> FaultLog<N, F> {
FaultLog(vec![self])
}
}
/// A structure used to contain reports of faulty node behavior.
#[derive(Debug, PartialEq)]
pub struct FaultLog<N>(pub Vec<Fault<N>>);
pub struct FaultLog<N, F: Fail>(pub Vec<Fault<N, F>>);
impl<N> FaultLog<N> {
impl<N, F> FaultLog<N, F>
where
F: Fail,
{
/// Creates an empty `FaultLog`.
pub fn new() -> Self {
FaultLog::default()
}
/// Creates a new `FaultLog` initialized with a single log.
pub fn init(node_id: N, kind: FaultKind) -> Self {
pub fn init(node_id: N, kind: F) -> Self {
Fault::new(node_id, kind).into()
}
/// Creates a new `Fault` and pushes it onto the fault log.
pub fn append(&mut self, node_id: N, kind: FaultKind) {
pub fn append(&mut self, node_id: N, kind: F) {
self.0.push(Fault::new(node_id, kind));
}
/// Consumes a `Fault` and pushes it onto the fault log.
pub fn append_fault(&mut self, fault: Fault<N, F>) {
self.0.push(fault);
}
/// Consumes `new_logs`, appending its logs onto the end of `self`.
pub fn extend(&mut self, new_logs: FaultLog<N>) {
pub fn extend(&mut self, new_logs: FaultLog<N, F>) {
self.0.extend(new_logs.0);
}
/// Consumes `self`, appending its logs onto the end of `logs`.
pub fn merge_into(self, logs: &mut FaultLog<N>) {
pub fn merge_into(self, logs: &mut FaultLog<N, F>) {
logs.extend(self);
}
@ -137,10 +92,47 @@ impl<N> FaultLog<N> {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Applies `f_fault` to each element in log, modifying its `kind` only
pub fn map<F2, FF>(self, f_fault: FF) -> FaultLog<N, F2>
where
F2: Fail,
FF: Fn(F) -> F2,
{
FaultLog(self.into_iter().map(|f| f.map(&f_fault)).collect())
}
}
impl<N> Default for FaultLog<N> {
impl<N, F> Default for FaultLog<N, F>
where
F: Fail,
{
fn default() -> Self {
FaultLog(vec![])
}
}
impl<N, F> IntoIterator for FaultLog<N, F>
where
F: Fail,
{
type Item = Fault<N, F>;
type IntoIter = std::vec::IntoIter<Fault<N, F>>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<N, F> std::iter::FromIterator<Fault<N, F>> for FaultLog<N, F>
where
F: Fail,
{
fn from_iter<I: IntoIterator<Item = Fault<N, F>>>(iter: I) -> Self {
let mut log = FaultLog::new();
for i in iter {
log.append_fault(i);
}
log
}
}

View File

@ -13,8 +13,8 @@ use rand::Rng;
use serde::{de::DeserializeOwned, Serialize};
use serde_derive::{Deserialize, Serialize};
use super::{Batch, Error, MessageContent, Result, Step};
use crate::fault_log::{Fault, FaultKind, FaultLog};
use super::{Batch, Error, FaultKind, FaultLog, MessageContent, Result, Step};
use crate::fault_log::Fault;
use crate::subset::{self as cs, Subset, SubsetOutput};
use crate::threshold_decrypt::{self as td, ThresholdDecrypt};
use crate::{Contribution, NetworkInfo, NodeIdT};
@ -306,7 +306,7 @@ where
/// Checks whether the subset has output, and if it does, sends out our decryption shares.
fn process_subset(&mut self, cs_step: CsStep<N>) -> Result<Step<C, N>> {
let mut step = Step::default();
let cs_outputs = step.extend_with(cs_step, |cs_msg| {
let cs_outputs = step.extend_with(cs_step, FaultKind::SubsetFault, |cs_msg| {
MessageContent::Subset(cs_msg).with_epoch(self.epoch)
});
let mut has_seen_done = false;
@ -357,7 +357,7 @@ where
/// Processes a Threshold Decrypt step.
fn process_decryption(&mut self, proposer_id: N, td_step: td::Step<N>) -> Result<Step<C, N>> {
let mut step = Step::default();
let opt_output = step.extend_with(td_step, |share| {
let opt_output = step.extend_with(td_step, FaultKind::DecryptionFault, |share| {
MessageContent::DecryptionShare {
proposer_id: proposer_id.clone(),
share,

View File

@ -1,6 +1,7 @@
use bincode;
use failure::Fail;
use crate::fault_log;
use crate::subset;
use crate::threshold_decrypt;
@ -29,3 +30,36 @@ pub enum Error {
/// The result of `HoneyBadger` handling an input or a message.
pub type Result<T> = ::std::result::Result<T, Error>;
/// Faults detectable from receiving honey badger messages
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum FaultKind {
/// `HoneyBadger` received a decryption share for an unaccepted proposer.
#[fail(display = "`HoneyBadger` received a decryption share for an unaccepted proposer.")]
UnexpectedDecryptionShare,
/// `HoneyBadger` was unable to deserialize a proposer's ciphertext.
#[fail(display = "`HoneyBadger` was unable to deserialize a proposer's ciphertext.")]
DeserializeCiphertext,
/// `HoneyBadger` received an invalid ciphertext from the proposer.
#[fail(display = "`HoneyBadger` received an invalid ciphertext from the proposer.")]
InvalidCiphertext,
/// `HoneyBadger` received a message with an invalid epoch.
#[fail(display = "`HoneyBadger` received a message with an invalid epoch.")]
UnexpectedHbMessageEpoch,
/// `HoneyBadger` could not deserialize bytes (i.e. a serialized Batch) from a given proposer
/// into a vector of transactions.
#[fail(
display = "`HoneyBadger` could not deserialize bytes (i.e. a serialized Batch) from a
given proposer into a vector of transactions."
)]
BatchDeserializationFailed,
/// `HoneyBadger` received a fault from `Subset`.
#[fail(display = "`HoneyBadger` received a fault from `Subset`.")]
SubsetFault(subset::FaultKind),
/// `HoneyBadger` received a fault from `ThresholdDecrypt`.
#[fail(display = "`HoneyBadger` received a fault from `ThresholdDecrypt`.")]
DecryptionFault(threshold_decrypt::FaultKind),
}
/// The type of fault log whose entries are `HoneyBadger` faults.
pub type FaultLog<N> = fault_log::FaultLog<N, FaultKind>;

View File

@ -8,8 +8,8 @@ use serde::{de::DeserializeOwned, Serialize};
use serde_derive::{Deserialize, Serialize};
use super::epoch_state::EpochState;
use super::{Batch, Error, HoneyBadgerBuilder, Message, Result};
use crate::{Contribution, DistAlgorithm, Fault, FaultKind, NetworkInfo, NodeIdT};
use super::{Batch, Error, FaultKind, HoneyBadgerBuilder, Message, Result};
use crate::{Contribution, DistAlgorithm, Fault, NetworkInfo, NodeIdT};
use super::Params;
@ -45,6 +45,7 @@ where
type Output = Batch<C, N>;
type Message = Message<N>;
type Error = Error;
type FaultKind = FaultKind;
fn handle_input<R: Rng>(&mut self, input: Self::Input, rng: &mut R) -> Result<Step<C, N>> {
self.propose(&input, rng)

View File

@ -33,7 +33,7 @@ mod params;
pub use self::batch::Batch;
pub use self::builder::HoneyBadgerBuilder;
pub use self::epoch_state::SubsetHandlingStrategy;
pub use self::error::{Error, Result};
pub use self::error::{Error, FaultKind, FaultLog, Result};
pub use self::honey_badger::{EncryptionSchedule, HoneyBadger, Step};
pub use self::message::{Message, MessageContent};
pub use self::params::Params;

View File

@ -140,7 +140,7 @@ pub mod transaction_queue;
pub mod util;
pub use crate::crypto::pairing;
pub use crate::fault_log::{Fault, FaultKind, FaultLog};
pub use crate::fault_log::{Fault, FaultLog};
pub use crate::messaging::{SourcedMessage, Target, TargetedMessage};
pub use crate::network_info::NetworkInfo;
pub use crate::traits::{

View File

@ -33,7 +33,7 @@ use serde::{de::DeserializeOwned, Serialize};
use crate::crypto::{PublicKey, SecretKey};
use crate::dynamic_honey_badger::{
self, Batch as DhbBatch, DynamicHoneyBadger, JoinPlan, Message, Step as DhbStep,
self, Batch as DhbBatch, DynamicHoneyBadger, FaultKind, JoinPlan, Message, Step as DhbStep,
};
use crate::transaction_queue::TransactionQueue;
use crate::{Contribution, DistAlgorithm, NetworkInfo, NodeIdT};
@ -162,7 +162,7 @@ pub struct QueueingHoneyBadger<T, N: Ord, Q> {
}
/// A `QueueingHoneyBadger` step, possibly containing multiple outputs.
pub type Step<T, N> = crate::Step<Message<N>, Batch<T, N>, N>;
pub type Step<T, N> = crate::Step<Message<N>, Batch<T, N>, N, FaultKind>;
impl<T, N, Q> DistAlgorithm for QueueingHoneyBadger<T, N, Q>
where
@ -176,6 +176,7 @@ where
type Output = Batch<T, N>;
type Message = Message<N>;
type Error = Error;
type FaultKind = FaultKind;
fn handle_input<R: Rng>(&mut self, input: Self::Input, rng: &mut R) -> Result<Step<T, N>> {
// User transactions are forwarded to `HoneyBadger` right away. Internal messages are

View File

@ -120,6 +120,7 @@ where
type Output = D::Output;
type Message = Message<D::Message>;
type Error = D::Error;
type FaultKind = D::FaultKind;
fn handle_input<R: Rng>(
&mut self,
@ -201,7 +202,7 @@ where
let mut step = f(&mut self.algo)?;
let mut sender_queue_step = self.update_epoch(&step);
self.defer_messages(&mut step);
sender_queue_step.extend(step.map(|output| output, Message::from));
sender_queue_step.extend(step.map(|output| output, |fault| fault, Message::from));
Ok(sender_queue_step)
}

View File

@ -27,3 +27,14 @@ pub enum Error {
/// A subset result.
pub type Result<T> = result::Result<T, Error>;
/// Faults that can be detected in Subset.
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum FaultKind {
/// `Subset` received a faulty Broadcast message.
#[fail(display = "`Subset` received a faulty Broadcast message.")]
BroadcastFault(broadcast::FaultKind),
/// `Subset` received a faulty Binary Agreement message.
#[fail(display = "`Subset` received a faulty Binary Agreement message.")]
BaFault(binary_agreement::FaultKind),
}

View File

@ -28,6 +28,6 @@ mod message;
mod proposal_state;
mod subset;
pub use self::error::{Error, Result};
pub use self::error::{Error, FaultKind, Result};
pub use self::message::{Message, MessageContent};
pub use self::subset::{Step, Subset, SubsetOutput};

View File

@ -2,7 +2,7 @@ use std::mem;
use std::sync::Arc;
use super::subset::BaSessionId;
use super::{Error, MessageContent, Result};
use super::{Error, FaultKind, MessageContent, Result};
use crate::binary_agreement;
use crate::broadcast::{self, Broadcast};
use crate::{NetworkInfo, NodeIdT, SessionIdT};
@ -11,7 +11,7 @@ type BaInstance<N, S> = binary_agreement::BinaryAgreement<N, BaSessionId<S>>;
type ValueAndStep<N> = (Option<Vec<u8>>, Step<N>);
type BaResult<N> = binary_agreement::Result<binary_agreement::Step<N>>;
pub type Step<N> = crate::Step<MessageContent, Vec<u8>, N>;
pub type Step<N> = crate::Step<MessageContent, Vec<u8>, N, FaultKind>;
/// The state of a proposal's broadcast and agreement process.
#[derive(Debug)]
@ -138,7 +138,13 @@ impl<N: NodeIdT, S: SessionIdT> ProposalState<N, S> {
fn convert_bc(result: broadcast::Result<broadcast::Step<N>>) -> Result<ValueAndStep<N>> {
let bc_step = result.map_err(Error::HandleBroadcast)?;
let mut step = Step::default();
let opt_value = step.extend_with(bc_step, MessageContent::Broadcast).pop();
let opt_value = step
.extend_with(
bc_step,
FaultKind::BroadcastFault,
MessageContent::Broadcast,
)
.pop();
Ok((opt_value, step))
}
@ -146,7 +152,9 @@ impl<N: NodeIdT, S: SessionIdT> ProposalState<N, S> {
fn convert_ba(result: BaResult<N>) -> Result<(Option<bool>, Step<N>)> {
let ba_step = result.map_err(Error::HandleAgreement)?;
let mut step = Step::default();
let opt_decision = step.extend_with(ba_step, MessageContent::Agreement).pop();
let opt_decision = step
.extend_with(ba_step, FaultKind::BaFault, MessageContent::Agreement)
.pop();
Ok((opt_decision, step))
}

View File

@ -8,12 +8,12 @@ use log::debug;
use serde_derive::Serialize;
use super::proposal_state::{ProposalState, Step as ProposalStep};
use super::{Error, Message, MessageContent, Result};
use super::{Error, FaultKind, Message, MessageContent, Result};
use crate::{util, DistAlgorithm, NetworkInfo, NodeIdT, SessionIdT};
use rand::Rng;
/// A `Subset` step, possibly containing several outputs.
pub type Step<N> = crate::Step<Message<N>, SubsetOutput<N>, N>;
pub type Step<N> = crate::Step<Message<N>, SubsetOutput<N>, N, FaultKind>;
/// An output with an accepted contribution or the end of the set.
#[derive(Derivative, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -47,6 +47,7 @@ impl<N: NodeIdT, S: SessionIdT> DistAlgorithm for Subset<N, S> {
type Output = SubsetOutput<N>;
type Message = Message<N>;
type Error = Error;
type FaultKind = FaultKind;
fn handle_input<R: Rng>(&mut self, input: Self::Input, _rng: &mut R) -> Result<Step<N>> {
self.propose(input)
@ -135,7 +136,7 @@ impl<N: NodeIdT, S: SessionIdT> Subset<N, S> {
fn convert_step(proposer_id: &N, prop_step: ProposalStep<N>) -> Step<N> {
let from_p_msg = |p_msg: MessageContent| p_msg.with(proposer_id.clone());
let mut step = Step::default();
if let Some(value) = step.extend_with(prop_step, from_p_msg).pop() {
if let Some(value) = step.extend_with(prop_step, |fault| fault, from_p_msg).pop() {
let contribution = SubsetOutput::Contribution(proposer_id.clone(), value);
step.output.push(contribution);
}

View File

@ -20,7 +20,7 @@ use rand::Rng;
use rand_derive::Rand;
use serde_derive::{Deserialize, Serialize};
use crate::fault_log::{Fault, FaultKind, FaultLog};
use crate::fault_log::{self, Fault};
use crate::{DistAlgorithm, NetworkInfo, NodeIdT, Target};
/// A threshold decryption error.
@ -46,6 +46,20 @@ pub enum Error {
/// A threshold decryption result.
pub type Result<T> = ::std::result::Result<T, Error>;
/// A threshold decryption message fault
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum FaultKind {
/// `ThresholdDecrypt` received multiple shares from the same sender.
#[fail(display = "`ThresholdDecrypt` received multiple shares from the same sender.")]
MultipleDecryptionShares,
/// `HoneyBadger` received a decryption share from an unverified sender.
#[fail(display = "`HoneyBadger` received a decryption share from an unverified sender.")]
UnverifiedDecryptionShareSender,
}
/// The type of fault log whose entries are `ThresholdDecrypt` faults.
pub type FaultLog<N> = fault_log::FaultLog<N, FaultKind>;
/// A Threshold Decryption message.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
pub struct Message(pub DecryptionShare);
@ -74,6 +88,7 @@ impl<N: NodeIdT> DistAlgorithm for ThresholdDecrypt<N> {
type Output = Vec<u8>;
type Message = Message;
type Error = Error;
type FaultKind = FaultKind;
fn handle_input<R: Rng>(&mut self, _input: (), _rng: &mut R) -> Result<Step<N>> {
self.start_decryption()

View File

@ -26,7 +26,7 @@ use rand::Rng;
use rand_derive::Rand;
use serde_derive::{Deserialize, Serialize};
use crate::fault_log::{Fault, FaultKind, FaultLog};
use crate::fault_log::{Fault, FaultLog};
use crate::{DistAlgorithm, NetworkInfo, NodeIdT, Target};
/// A threshold signing error.
@ -52,6 +52,22 @@ pub enum Error {
/// A threshold signing result.
pub type Result<T> = ::std::result::Result<T, Error>;
/// A threshold sign message fault
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum FaultKind {
/// `ThresholdSign` (`Coin`) received a signature share from an unverified sender.
#[fail(
display = "`ThresholdSign` (`Coin`) received a signature share from an unverified sender."
)]
UnverifiedSignatureShareSender,
/// `HoneyBadger` received a signatures share for the random value even though it is disabled.
#[fail(
display = "`HoneyBadger` received a signatures share for the random value even though it
is disabled."
)]
UnexpectedSignatureShare,
}
/// A threshold signing message, containing a signature share.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
pub struct Message(pub SignatureShare);
@ -81,6 +97,7 @@ impl<N: NodeIdT> DistAlgorithm for ThresholdSign<N> {
type Output = Signature;
type Message = Message;
type Error = Error;
type FaultKind = FaultKind;
/// Sends our threshold signature share if not yet sent.
fn handle_input<R: Rng>(&mut self, _input: (), _rng: &mut R) -> Result<Step<N>> {
@ -180,7 +197,7 @@ impl<N: NodeIdT> ThresholdSign<N> {
}
/// Removes all shares that are invalid, and returns faults for their senders.
fn remove_invalid_shares(&mut self) -> FaultLog<N> {
fn remove_invalid_shares(&mut self) -> FaultLog<N, FaultKind> {
let faulty_senders: Vec<N> = self
.received_shares
.iter()

View File

@ -21,6 +21,10 @@ impl<C> Contribution for C where C: Eq + Debug + Hash + Send + Sync {}
pub trait NodeIdT: Eq + Ord + Clone + Debug + Hash + Send + Sync {}
impl<N> NodeIdT for N where N: Eq + Ord + Clone + Debug + Hash + Send + Sync {}
/// A distributed algorithm fault.
pub trait FaultT: Clone + Debug + Fail + PartialEq {}
impl<N> FaultT for N where N: Clone + Debug + Fail + PartialEq {}
/// Messages.
pub trait Message: Debug + Send + Sync {}
impl<M> Message for M where M: Debug + Send + Sync {}
@ -57,19 +61,22 @@ impl<E> EpochT for E where E: Copy + Message + Default + Eq + Ord + Serialize +
/// catch it, instead of potentially stalling the algorithm.
#[must_use = "The algorithm step result must be used."]
#[derive(Debug)]
pub struct Step<M, O, N> {
pub struct Step<M, O, N, F: Fail> {
/// The algorithm's output, after consensus has been reached. This is guaranteed to be the same
/// in all nodes.
pub output: Vec<O>,
/// A list of nodes that are not following consensus, together with information about the
/// detected misbehavior.
pub fault_log: FaultLog<N>,
pub fault_log: FaultLog<N, F>,
/// A list of messages that must be sent to other nodes. Each entry contains a message and a
/// `Target`.
pub messages: Vec<TargetedMessage<M, N>>,
}
impl<M, O, N> Default for Step<M, O, N> {
impl<M, O, N, F> Default for Step<M, O, N, F>
where
F: Fail,
{
fn default() -> Self {
Step {
output: Vec::default(),
@ -79,11 +86,14 @@ impl<M, O, N> Default for Step<M, O, N> {
}
}
impl<M, O, N> Step<M, O, N> {
impl<M, O, N, F> Step<M, O, N, F>
where
F: Fail,
{
/// Creates a new `Step` from the given collections.
pub fn new(
output: Vec<O>,
fault_log: FaultLog<N>,
fault_log: FaultLog<N, F>,
messages: Vec<TargetedMessage<M, N>>,
) -> Self {
Step {
@ -99,27 +109,42 @@ impl<M, O, N> Step<M, O, N> {
self
}
/// Converts `self` into a step of another type, given conversion methods for output and
/// messages.
pub fn map<M2, O2, FO, FM>(self, f_out: FO, f_msg: FM) -> Step<M2, O2, N>
/// Converts `self` into a step of another type, given conversion methods for output, faults,
/// and messages.
pub fn map<M2, O2, F2, FO, FF, FM>(
self,
f_out: FO,
f_fail: FF,
f_msg: FM,
) -> Step<M2, O2, N, F2>
where
F2: Fail,
FO: Fn(O) -> O2,
FF: Fn(F) -> F2,
FM: Fn(M) -> M2,
{
Step {
output: self.output.into_iter().map(f_out).collect(),
fault_log: self.fault_log,
fault_log: self.fault_log.map(f_fail),
messages: self.messages.into_iter().map(|tm| tm.map(&f_msg)).collect(),
}
}
/// Extends `self` with `other`s messages and fault logs, and returns `other.output`.
#[must_use]
pub fn extend_with<M2, O2, FM>(&mut self, other: Step<M2, O2, N>, f_msg: FM) -> Vec<O2>
pub fn extend_with<M2, O2, F2, FF, FM>(
&mut self,
other: Step<M2, O2, N, F2>,
f_fail: FF,
f_msg: FM,
) -> Vec<O2>
where
F2: Fail,
FF: Fn(F2) -> F,
FM: Fn(M2) -> M,
{
self.fault_log.extend(other.fault_log);
let fails = other.fault_log.map(f_fail);
self.fault_log.extend(fails);
let msgs = other.messages.into_iter().map(|tm| tm.map(&f_msg));
self.messages.extend(msgs);
other.output
@ -144,8 +169,11 @@ impl<M, O, N> Step<M, O, N> {
}
}
impl<M, O, N> From<FaultLog<N>> for Step<M, O, N> {
fn from(fault_log: FaultLog<N>) -> Self {
impl<M, O, N, F> From<FaultLog<N, F>> for Step<M, O, N, F>
where
F: Fail,
{
fn from(fault_log: FaultLog<N, F>) -> Self {
Step {
fault_log,
..Step::default()
@ -153,8 +181,11 @@ impl<M, O, N> From<FaultLog<N>> for Step<M, O, N> {
}
}
impl<M, O, N> From<Fault<N>> for Step<M, O, N> {
fn from(fault: Fault<N>) -> Self {
impl<M, O, N, F> From<Fault<N, F>> for Step<M, O, N, F>
where
F: Fail,
{
fn from(fault: Fault<N, F>) -> Self {
Step {
fault_log: fault.into(),
..Step::default()
@ -162,7 +193,10 @@ impl<M, O, N> From<Fault<N>> for Step<M, O, N> {
}
}
impl<M, O, N> From<TargetedMessage<M, N>> for Step<M, O, N> {
impl<M, O, N, F> From<TargetedMessage<M, N>> for Step<M, O, N, F>
where
F: Fail,
{
fn from(msg: TargetedMessage<M, N>) -> Self {
Step {
messages: once(msg).collect(),
@ -171,9 +205,10 @@ impl<M, O, N> From<TargetedMessage<M, N>> for Step<M, O, N> {
}
}
impl<I, M, O, N> From<I> for Step<M, O, N>
impl<I, M, O, N, F> From<I> for Step<M, O, N, F>
where
I: IntoIterator<Item = TargetedMessage<M, N>>,
F: Fail,
{
fn from(msgs: I) -> Self {
Step {
@ -195,13 +230,18 @@ pub trait Epoched {
}
/// An alias for the type of `Step` returned by `D`'s methods.
pub type DaStep<D> =
Step<<D as DistAlgorithm>::Message, <D as DistAlgorithm>::Output, <D as DistAlgorithm>::NodeId>;
pub type DaStep<D> = Step<
<D as DistAlgorithm>::Message,
<D as DistAlgorithm>::Output,
<D as DistAlgorithm>::NodeId,
<D as DistAlgorithm>::FaultKind,
>;
impl<'i, M, O, N> Step<M, O, N>
impl<'i, M, O, N, F> Step<M, O, N, F>
where
N: NodeIdT,
M: 'i + Clone + SenderQueueableMessage,
F: Fail,
{
/// Removes and returns any messages that are not yet accepted by remote nodes according to the
/// mapping `remote_epochs`. This way the returned messages are postponed until later, and the
@ -266,6 +306,8 @@ pub trait DistAlgorithm: Send + Sync {
type Message: Message;
/// The errors that can occur during execution.
type Error: Fail;
/// The kinds of message faults that can be detected during execution.
type FaultKind: FaultT;
/// Handles an input provided by the user, and returns
fn handle_input<R: Rng>(

View File

@ -41,7 +41,7 @@ where
/// The execution time limit has been reached or exceeded.
TimeLimitHit(time::Duration),
/// A `Fault` is encountered in a step of a `DistAlgorithm`.
Fault(Fault<D::NodeId>),
Fault(Fault<D::NodeId, D::FaultKind>),
/// An error occurred while generating initial keys for threshold cryptography.
InitialKeyGeneration(crypto::error::Error),
}

View File

@ -75,7 +75,7 @@ pub struct Node<D: DistAlgorithm> {
/// Captured algorithm outputs, in order.
outputs: Vec<D::Output>,
/// Collected fault log, in order.
faults: Vec<Fault<D::NodeId>>,
faults: Vec<Fault<D::NodeId, D::FaultKind>>,
}
impl<D> fmt::Debug for Node<D>
@ -141,7 +141,7 @@ impl<D: DistAlgorithm> Node<D> {
///
/// All faults are collected for reference purposes.
#[inline]
pub fn faults(&self) -> &[Fault<D::NodeId>] {
pub fn faults(&self) -> &[Fault<D::NodeId, D::FaultKind>] {
self.faults.as_slice()
}
@ -934,7 +934,7 @@ where
.ok_or_else(|| CrankError::NodeDisappearedInCrank(msg.to.clone())))
.is_faulty();
let step: Step<_, _, _> = if is_faulty {
let step: Step<_, _, _, _> = if is_faulty {
// The swap-dance is painful here, as we are creating an `opt_step` just to avoid
// borrow issues.
let mut adv = self.adversary.take();

View File

@ -379,7 +379,7 @@ where
.expect("failed to reconstruct the pivot node");
let (sq, mut sq_step) = SenderQueue::builder(dhb, peer_ids.into_iter()).build(id);
*node.algorithm_mut() = sq;
sq_step.extend(dhb_step.map(|output| output, Message::from));
sq_step.extend(dhb_step.map(|output| output, |fault| fault, Message::from));
net.insert_node(node);
sq_step
}

View File

@ -32,7 +32,7 @@ pub struct TestNode<D: DistAlgorithm> {
/// Outgoing messages to be sent to other nodes.
messages: Vec<TargetedMessage<D::Message, D::NodeId>>,
/// Collected fault logs.
faults: Vec<Fault<D::NodeId>>,
faults: Vec<Fault<D::NodeId, D::FaultKind>>,
}
impl<D: DistAlgorithm> TestNode<D> {
@ -594,7 +594,7 @@ where
}
/// Verifies that no correct node is reported as faulty.
fn check_faults<I: IntoIterator<Item = Fault<D::NodeId>>>(&self, faults: I) {
fn check_faults<I: IntoIterator<Item = Fault<D::NodeId, D::FaultKind>>>(&self, faults: I) {
for fault in faults {
if self.nodes.contains_key(&fault.node_id) {
panic!("Unexpected fault: {:?}", fault);

View File

@ -169,7 +169,7 @@ where
.expect("failed to rebuild the node with a join plan");
let (sq, mut sq_step) = SenderQueue::builder(qhb, peer_ids.into_iter()).build(our_id);
*node.instance_mut() = sq;
sq_step.extend(qhb_step.map(|output| output, Message::from));
sq_step.extend(qhb_step.map(|output| output, |fault| fault, Message::from));
network.nodes.insert(our_id, node);
sq_step
}
@ -193,7 +193,8 @@ fn new_queueing_hb(
.build(&mut rng)
.expect("failed to build QueueingHoneyBadger");
let (sq, mut step) = SenderQueue::builder(qhb, peer_ids).build(our_id);
assert!(step.extend_with(qhb_step, Message::from).is_empty());
let output = step.extend_with(qhb_step, |fault| fault, Message::from);
assert!(output.is_empty());
(sq, step)
}