Extend the documentation, simplify errors.

This changes and adds several doc comments, adds the `missing_docs` lint
and simplifies some of the error types.
This commit is contained in:
Andreas Fackler 2018-11-26 15:35:24 +01:00 committed by Andreas Fackler
parent ae37879239
commit b2071fe2be
32 changed files with 232 additions and 269 deletions

View File

@ -98,7 +98,7 @@ impl ReceivedMessages {
}
MessageContent::Coin(msg) => {
if self.coin.is_none() {
self.coin = Some(msg.to_sig().clone());
self.coin = Some(msg.0);
} else {
return Some(FaultKind::AgreementEpoch);
}
@ -130,7 +130,7 @@ impl ReceivedMessages {
messages.push(MessageContent::Term(b));
}
if let Some(ss) = coin {
messages.push(MessageContent::Coin(Box::new(TsMessage::new(ss))));
messages.push(MessageContent::Coin(Box::new(TsMessage(ss))));
}
messages
}

View File

@ -1,3 +1,5 @@
//! A single-byte representation of a set of boolean values.
use rand_derive::Rand;
use serde_derive::{Deserialize, Serialize};

View File

@ -83,28 +83,31 @@ pub use self::sbv_broadcast::Message as SbvMessage;
/// An Binary Agreement error.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum Error {
#[fail(display = "Error handling threshold sign message: {}", _0)]
/// Error handling a `ThresholdSign` message.
#[fail(display = "Error handling ThresholdSign message: {}", _0)]
HandleThresholdSign(threshold_sign::Error),
/// Error invoking the common coin.
#[fail(display = "Error invoking the common coin: {}", _0)]
InvokeCoin(threshold_sign::Error),
// Strings because `io` and `bincode` errors lack `Eq` and `Clone`.
#[fail(display = "Error writing epoch for nonce: {}", _0)]
Io(String),
#[fail(display = "Error serializing session ID for nonce: {}", _0)]
// String because `io` and `bincode` errors lack `Eq` and `Clone`.
/// Error serializing the session ID for the common coin.
#[fail(display = "Error serializing session ID for coin: {}", _0)]
Serialize(String),
}
impl From<bincode::Error> for Error {
fn from(err: bincode::Error) -> Error {
Error::Io(format!("{:?}", err))
Error::Serialize(format!("{:?}", err))
}
}
/// An Binary Agreement result.
pub type Result<T> = ::std::result::Result<T, Error>;
/// A `BinaryAgreement` step, containing at most one output.
pub type Step<N> = ::Step<Message, bool, N>;
/// The content of a message belonging to a particular `BinaryAgreement` epoch.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub enum MessageContent {
/// Synchronized Binary Value Broadcast message.
@ -138,7 +141,9 @@ impl MessageContent {
/// Messages sent during the Binary Agreement stage.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
pub struct Message {
/// The `BinaryAgreement` epoch this message belongs to.
pub epoch: u64,
/// The message content for the `epoch`.
pub content: MessageContent,
}

View File

@ -22,9 +22,13 @@ use {NetworkInfo, NodeIdT, Target};
pub type Step<N> = ::Step<Message, BoolSet, N>;
/// A message belonging to the Synchronized Binary Value Broadcast phase of a `BinaryAgreement`
/// epoch.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum Message {
/// Contains the sender's estimate for the current epoch.
BVal(bool),
/// A confirmation that the sender has received _2 f + 1_ `BVal`s with the same value.
Aux(bool),
}

View File

@ -37,6 +37,7 @@ pub struct Broadcast<N> {
readys: BTreeMap<N, Vec<u8>>,
}
/// A `Broadcast` step, containing at most one output.
pub type Step<N> = ::DaStep<Broadcast<N>>;
impl<N: NodeIdT> DistAlgorithm for Broadcast<N> {

View File

@ -4,29 +4,32 @@ use reed_solomon_erasure as rse;
/// A broadcast error.
#[derive(Clone, PartialEq, Debug, Fail)]
pub enum Error {
/// Failed to create a `ReedSolomon` instance.
#[fail(display = "CodingNewReedSolomon error: {}", _0)]
CodingNewReedSolomon(#[cause] rse::Error),
/// Failed to encode the value.
#[fail(display = "CodingEncodeReedSolomon error: {}", _0)]
CodingEncodeReedSolomon(#[cause] rse::Error),
/// Failed to reconstruct the value.
#[fail(display = "CodingReconstructShardsReedSolomon error: {}", _0)]
CodingReconstructShardsReedSolomon(#[cause] rse::Error),
/// Failed to reconstruct the value.
// TODO: This should be unreachable.
#[fail(
display = "CodingReconstructShardsTrivialReedSolomon error: {}",
_0
)]
CodingReconstructShardsTrivialReedSolomon(#[cause] rse::Error),
/// Observers cannot propose a value.
#[fail(display = "Instance cannot propose")]
InstanceCannotPropose,
/// Multiple inputs received. Only a single value can be proposed.
#[fail(display = "Multiple inputs received")]
MultipleInputs,
#[fail(display = "Not implemented")]
NotImplemented,
/// Failed to construct a Merkle tree proof.
#[fail(display = "Proof construction failed")]
ProofConstructionFailed,
#[fail(display = "Root hash mismatch")]
RootHashMismatch,
#[fail(display = "Threading")]
Threading,
/// Unknown sender
#[fail(display = "Unknown sender")]
UnknownSender,
}

View File

@ -10,8 +10,11 @@ use super::merkle::{Digest, MerkleTree, Proof};
/// consensus algorithm.
#[derive(Serialize, Deserialize, Clone, PartialEq)]
pub enum Message {
/// A share of the value, sent from the sender to another validator.
Value(Proof<Vec<u8>>),
/// A copy of the value received from the sender, multicast by a validator.
Echo(Proof<Vec<u8>>),
/// Indicates that the sender knows that every node will eventually be able to decode.
Ready(Digest),
}

View File

@ -11,8 +11,8 @@ use serde::{de::DeserializeOwned, Serialize};
use super::votes::{SignedVote, VoteCounter};
use super::{
Batch, Change, ChangeState, DynamicHoneyBadgerBuilder, EncryptionSchedule, Error, ErrorKind,
Input, InternalContrib, JoinPlan, KeyGenMessage, KeyGenState, Message, Params, Result,
Batch, Change, ChangeState, DynamicHoneyBadgerBuilder, EncryptionSchedule, Error, Input,
InternalContrib, JoinPlan, KeyGenMessage, KeyGenState, Message, Params, Result,
SignedKeyGenMsg, Step,
};
use fault_log::{Fault, FaultKind, FaultLog};
@ -150,11 +150,11 @@ where
.collect();
let step = self
.honey_badger
.handle_input(InternalContrib {
.propose(&InternalContrib {
contrib,
key_gen_messages,
votes: self.vote_counter.pending_votes().cloned().collect(),
}).map_err(ErrorKind::ProposeHoneyBadger)?;
}).map_err(Error::ProposeHoneyBadger)?;
self.process_output(step)
}
@ -254,13 +254,13 @@ where
message: HbMessage<N>,
) -> Result<Step<C, N>> {
if !self.netinfo.is_node_validator(sender_id) {
return Err(ErrorKind::UnknownSender.into());
return Err(Error::UnknownSender);
}
// Handle the message.
let step = self
.honey_badger
.handle_message(sender_id, message)
.map_err(ErrorKind::HandleHoneyBadgerMessageHoneyBadger)?;
.map_err(Error::HandleHoneyBadgerMessage)?;
self.process_output(step)
}
@ -337,7 +337,7 @@ where
let change = if let Some(kgs) = self.take_ready_key_gen() {
// If DKG completed, apply the change, restart Honey Badger, and inform the user.
debug!("{}: DKG for complete for: {:?}", self, kgs.public_keys());
self.netinfo = kgs.key_gen.into_network_info()?;
self.netinfo = kgs.key_gen.into_network_info().map_err(Error::SyncKeyGen)?;
let params = self.honey_badger.params().clone();
self.restart_honey_badger(batch_epoch + 1, params);
ChangeState::Complete(Change::NodeChange(self.netinfo.public_key_map().clone()))
@ -396,7 +396,8 @@ where
let sk = self.netinfo.secret_key().clone();
let our_id = self.our_id().clone();
let (key_gen, part) =
SyncKeyGen::new(&mut self.rng, our_id, sk, pub_keys.clone(), threshold)?;
SyncKeyGen::new(&mut self.rng, our_id, sk, pub_keys.clone(), threshold)
.map_err(Error::SyncKeyGen)?;
self.key_gen_state = Some(KeyGenState::new(key_gen));
if let Some(part) = part {
self.send_transaction(KeyGenMessage::Part(part))
@ -423,7 +424,7 @@ where
let outcome = if let Some(kgs) = self.key_gen_state.as_mut() {
kgs.key_gen
.handle_part(&mut self.rng, &sender_id, part)
.map_err(ErrorKind::SyncKeyGen)?
.map_err(Error::SyncKeyGen)?
} else {
// No key generation ongoing.
let fault_kind = FaultKind::UnexpectedKeyGenPart;
@ -445,7 +446,7 @@ where
let outcome = if let Some(kgs) = self.key_gen_state.as_mut() {
kgs.key_gen
.handle_ack(sender_id, ack)
.map_err(ErrorKind::SyncKeyGen)?
.map_err(Error::SyncKeyGen)?
} else {
// No key generation ongoing.
let fault_kind = FaultKind::UnexpectedKeyGenAck;
@ -463,8 +464,7 @@ where
/// Signs and sends a `KeyGenMessage` and also tries to commit it.
fn send_transaction(&mut self, kg_msg: KeyGenMessage) -> Result<Step<C, N>> {
let ser =
bincode::serialize(&kg_msg).map_err(|err| ErrorKind::SendTransactionBincode(*err))?;
let ser = bincode::serialize(&kg_msg).map_err(|err| Error::SerializeKeyGen(*err))?;
let sig = Box::new(self.netinfo.secret_key().sign(ser));
if self.netinfo.is_validator() {
let our_id = self.our_id().clone();
@ -502,8 +502,7 @@ where
sig: &Signature,
kg_msg: &KeyGenMessage,
) -> Result<bool> {
let ser =
bincode::serialize(kg_msg).map_err(|err| ErrorKind::VerifySignatureBincode(*err))?;
let ser = bincode::serialize(kg_msg).map_err(|err| Error::SerializeKeyGen(*err))?;
let verify = |opt_pk: Option<&PublicKey>| opt_pk.map_or(false, |pk| pk.verify(&sig, &ser));
let kgs = self.key_gen_state.as_ref();
let current_key = self.netinfo.public_key(node_id);

View File

@ -1,94 +1,34 @@
use std::fmt::{self, Display};
use bincode;
use crypto;
use failure::{Backtrace, Context, Fail};
use failure::Fail;
use honey_badger;
use sync_key_gen;
/// Dynamic honey badger error variants.
#[derive(Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "SendTransactionBincode error: {}", _0)]
SendTransactionBincode(bincode::ErrorKind),
#[fail(display = "VerifySignatureBincode error: {}", _0)]
VerifySignatureBincode(bincode::ErrorKind),
#[fail(display = "SignVoteForBincode error: {}", _0)]
SignVoteForBincode(bincode::ErrorKind),
#[fail(display = "ValidateBincode error: {}", _0)]
ValidateBincode(bincode::ErrorKind),
#[fail(display = "Crypto error: {}", _0)]
Crypto(crypto::error::Error),
#[fail(display = "ProposeHoneyBadger error: {}", _0)]
ProposeHoneyBadger(honey_badger::Error),
pub enum Error {
/// Failed to serialize a key generation message for signing.
#[fail(display = "Error serializing a key gen message: {}", _0)]
SerializeKeyGen(bincode::ErrorKind),
/// Failed to serialize a vote for signing.
#[fail(display = "Error serializing a vote: {}", _0)]
SerializeVote(bincode::ErrorKind),
/// Failed to propose a contribution in `HoneyBadger`.
#[fail(
display = "HandleHoneyBadgerMessageHoneyBadger error: {}",
display = "Error proposing a contribution in HoneyBadger: {}",
_0
)]
HandleHoneyBadgerMessageHoneyBadger(honey_badger::Error),
#[fail(display = "SyncKeyGen error: {}", _0)]
ProposeHoneyBadger(honey_badger::Error),
/// Failed to handle a `HoneyBadger` message.
#[fail(display = "Error handling a HoneyBadger message: {}", _0)]
HandleHoneyBadgerMessage(honey_badger::Error),
/// Failed to handle a `SyncKeyGen` message.
#[fail(display = "Error handling SyncKeyGen message: {}", _0)]
SyncKeyGen(sync_key_gen::Error),
/// Unknown sender
#[fail(display = "Unknown sender")]
UnknownSender,
}
/// A dynamic honey badger error.
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
impl Fail for Error {
fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl Error {
pub fn kind(&self) -> &ErrorKind {
self.inner.get_context()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner }
}
}
impl From<crypto::error::Error> for Error {
fn from(e: crypto::error::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Crypto(e)),
}
}
}
impl From<sync_key_gen::Error> for Error {
fn from(e: sync_key_gen::Error) -> Error {
Error {
inner: Context::new(ErrorKind::SyncKeyGen(e)),
}
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
/// The result of `DynamicHoneyBadger` handling an input or message.
pub type Result<T> = ::std::result::Result<T, Error>;

View File

@ -87,8 +87,9 @@ 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, ErrorKind, Result};
pub use self::error::{Error, Result};
/// A `DynamicHoneyBadger` step, possibly containing multiple outputs.
pub type Step<C, N> = ::DaStep<DynamicHoneyBadger<C, N>>;
/// The user input for `DynamicHoneyBadger`.

View File

@ -6,7 +6,7 @@ use crypto::Signature;
use serde::Serialize;
use serde_derive::{Deserialize, Serialize};
use super::{Change, ErrorKind, Result};
use super::{Change, Error, Result};
use fault_log::{FaultKind, FaultLog};
use {NetworkInfo, NodeIdT};
@ -49,8 +49,7 @@ where
era: self.era,
num: self.pending.get(&voter).map_or(0, |sv| sv.vote.num + 1),
};
let ser_vote =
bincode::serialize(&vote).map_err(|err| ErrorKind::SignVoteForBincode(*err))?;
let ser_vote = bincode::serialize(&vote).map_err(|err| Error::SerializeVote(*err))?;
let signed_vote = SignedVote {
vote,
voter: voter.clone(),
@ -149,8 +148,8 @@ where
/// Returns `true` if the signature is valid.
fn validate(&self, signed_vote: &SignedVote<N>) -> Result<bool> {
let ser_vote = bincode::serialize(&signed_vote.vote)
.map_err(|err| ErrorKind::ValidateBincode(*err))?;
let ser_vote =
bincode::serialize(&signed_vote.vote).map_err(|err| Error::SerializeVote(*err))?;
let pk_opt = self.netinfo.public_key(&signed_vote.voter);
Ok(pk_opt.map_or(false, |pk| pk.verify(&signed_vote.sig, ser_vote)))
}

View File

@ -82,11 +82,14 @@ pub enum FaultKind {
/// that the node exhibited ('kind').
#[derive(Debug, PartialEq)]
pub struct Fault<N> {
/// The faulty node's ID.
pub node_id: N,
/// The kind of fault the node is blamed for.
pub kind: FaultKind,
}
impl<N> Fault<N> {
/// Creates a new fault, blaming `node_id` for the `kind`.
pub fn new(node_id: N, kind: FaultKind) -> Self {
Fault { node_id, kind }
}

View File

@ -13,7 +13,7 @@ use rand::{Rand, Rng};
use serde::{de::DeserializeOwned, Serialize};
use serde_derive::{Deserialize, Serialize};
use super::{Batch, ErrorKind, MessageContent, Result, Step};
use super::{Batch, Error, MessageContent, Result, Step};
use fault_log::{Fault, FaultKind, FaultLog};
use subset::{self as cs, Subset, SubsetOutput};
use threshold_decrypt::{self as td, ThresholdDecrypt};
@ -77,7 +77,7 @@ where
match self {
SubsetState::Ongoing(ref mut cs) => cs.handle_input(proposal),
SubsetState::Complete(_) => return Ok(cs::Step::default()),
}.map_err(|err| ErrorKind::InputSubset(err).into())
}.map_err(Error::InputSubset)
}
/// Handles a message in the Subset instance, unless it has already completed.
@ -85,7 +85,7 @@ where
match self {
SubsetState::Ongoing(ref mut cs) => cs.handle_message(sender_id, msg),
SubsetState::Complete(_) => return Ok(cs::Step::default()),
}.map_err(|err| ErrorKind::HandleSubsetMessage(err).into())
}.map_err(Error::HandleSubsetMessage)
}
/// Returns the number of contributions that we have already received or, after completion, how
@ -211,7 +211,7 @@ where
require_decryption: bool,
) -> Result<Self> {
let epoch_id = EpochId { hb_id, epoch };
let cs = Subset::new(netinfo.clone(), epoch_id).map_err(ErrorKind::CreateSubset)?;
let cs = Subset::new(netinfo.clone(), epoch_id).map_err(Error::CreateSubset)?;
Ok(EpochState {
epoch,
netinfo,
@ -226,15 +226,14 @@ where
/// If the instance hasn't terminated yet, inputs our encrypted contribution.
pub fn propose<R: Rng>(&mut self, proposal: &C, rng: &mut R) -> Result<Step<C, N>> {
let ser_prop =
bincode::serialize(&proposal).map_err(|err| ErrorKind::ProposeBincode(*err))?;
let ser_prop = bincode::serialize(&proposal).map_err(|err| Error::ProposeBincode(*err))?;
let cs_step = self.subset.handle_input(if self.require_decryption {
let ciphertext = self
.netinfo
.public_key_set()
.public_key()
.encrypt_with_rng(rng, ser_prop);
bincode::serialize(&ciphertext).map_err(|err| ErrorKind::ProposeBincode(*err))?
bincode::serialize(&ciphertext).map_err(|err| Error::ProposeBincode(*err))?
} else {
ser_prop
})?;
@ -271,7 +270,7 @@ where
entry.insert(DecryptionState::new(self.netinfo.clone()))
}
}.handle_message(sender_id, share)
.map_err(ErrorKind::ThresholdDecrypt)?;
.map_err(Error::ThresholdDecrypt)?;
self.process_decryption(proposer_id, td_step)
}
}
@ -392,7 +391,7 @@ where
Err(td::Error::InvalidCiphertext(_)) => {
Ok(Fault::new(proposer_id, FaultKind::InvalidCiphertext).into())
}
Err(err) => Err(ErrorKind::ThresholdDecrypt(err).into()),
Err(err) => Err(Error::ThresholdDecrypt(err)),
}
}
}

View File

@ -1,68 +1,31 @@
use std::fmt::{self, Display, Formatter};
use bincode;
use failure::{Backtrace, Context, Fail};
use failure::Fail;
use subset;
use threshold_decrypt;
/// Honey badger error variants.
#[derive(Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "ProposeBincode error: {}", _0)]
pub enum Error {
/// Failed to serialize contribution.
#[fail(display = "Error serializing contribution: {}", _0)]
ProposeBincode(bincode::ErrorKind),
/// Failed to instantiate `Subset`.
#[fail(display = "Failed to instantiate Subset: {}", _0)]
CreateSubset(subset::Error),
/// Failed to input contribution to `Subset`.
#[fail(display = "Failed to input contribution to Subset: {}", _0)]
InputSubset(subset::Error),
/// Failed to handle `Subset` message.
#[fail(display = "Failed to handle Subset message: {}", _0)]
HandleSubsetMessage(subset::Error),
/// Failed to decrypt a contribution.
#[fail(display = "Threshold decryption error: {}", _0)]
ThresholdDecrypt(threshold_decrypt::Error),
/// Unknown sender
#[fail(display = "Unknown sender")]
UnknownSender,
}
/// A honey badger error.
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
impl Fail for Error {
fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl Error {
pub fn kind(&self) -> &ErrorKind {
self.inner.get_context()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner }
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
/// The result of `HoneyBadger` handling an input or a message.
pub type Result<T> = ::std::result::Result<T, Error>;

View File

@ -8,7 +8,7 @@ use serde::{de::DeserializeOwned, Serialize};
use serde_derive::{Deserialize, Serialize};
use super::epoch_state::EpochState;
use super::{Batch, Error, ErrorKind, HoneyBadgerBuilder, Message, Result};
use super::{Batch, Error, HoneyBadgerBuilder, Message, Result};
use {util, Contribution, DistAlgorithm, Fault, FaultKind, NetworkInfo, NodeIdT};
use super::Params;
@ -36,6 +36,7 @@ pub struct HoneyBadger<C, N: Rand> {
pub(super) rng: Box<dyn Rng + Send + Sync>,
}
/// A `HoneyBadger` step, possibly containing multiple outputs.
pub type Step<C, N> = ::DaStep<HoneyBadger<C, N>>;
impl<C, N> DistAlgorithm for HoneyBadger<C, N>
@ -107,7 +108,7 @@ where
/// 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<C, N>> {
if !self.netinfo.is_node_validator(sender_id) {
return Err(ErrorKind::UnknownSender.into());
return Err(Error::UnknownSender);
}
let Message { epoch, content } = message;
if epoch > self.epoch + self.params.max_future_epochs {
@ -200,14 +201,19 @@ where
/// How frequently Threshold Encryption should be used.
#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash, Debug)]
pub enum EncryptionSchedule {
/// Always encrypt. All contributions are encrypted in every epoch.
Always,
/// Never encrypt. All contributions are plaintext in every epoch.
Never,
/// Every _n_-th epoch uses encryption. In all other epochs, contributions are plaintext.
EveryNthEpoch(u32),
/// How many with encryption, followed by how many without encryption.
/// With `TickTock(n, m)`, `n` epochs use encryption, followed by `m` epochs that don't.
/// `m` out of `n + m` epochs will use plaintext contributions.
TickTock(u32, u32),
}
impl EncryptionSchedule {
/// Returns `true` if the contributions in the `epoch` should be encrypted.
pub fn use_on_epoch(self, epoch: u64) -> bool {
match self {
EncryptionSchedule::Always => true,

View File

@ -12,14 +12,17 @@ use threshold_decrypt;
pub enum MessageContent<N: Rand> {
/// A message belonging to the subset algorithm in the given epoch.
Subset(subset::Message<N>),
/// A decrypted share of the output of `proposer_id`.
/// A decryption share of the output of `proposer_id`.
DecryptionShare {
/// The ID of the node that proposed the contribution that is being decrypted.
proposer_id: N,
/// The decryption share: _f + 1_ of these are required to decrypt the contribution.
share: threshold_decrypt::Message,
},
}
impl<N: Rand> MessageContent<N> {
/// Wraps this content in a `Message` with the given epoch.
pub fn with_epoch(self, epoch: u64) -> Message<N> {
Message {
epoch,

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, ErrorKind, Result};
pub use self::error::{Error, Result};
pub use self::honey_badger::{EncryptionSchedule, HoneyBadger, Step};
pub use self::message::{Message, MessageContent};
pub use self::params::Params;

View File

@ -6,34 +6,42 @@
//!
//! ## Consensus
//!
//! A consensus algorithm is a protocol that helps a number of nodes agree on some data value.
//! Byzantine fault tolerant systems can tolerate a number of faulty nodes _f_ (broken, or even
//! controlled by an attacker), as long as the total number _N_ of nodes is greater than _3 f_.
//! Asynchronous protocols do not make assumptions about timing: Even if an adversary controls
//! network scheduling and can delay message delivery, consensus will still be reached as long as
//! all messages are _eventually_ delivered.
//!
//! The Honey Badger consensus algorithm is both Byzantine fault tolerant and asynchronous. It is
//! also modular, and the subalgorithms it is composed of are exposed in this crate as well, and
//! usable separately.
//!
//! Consensus algorithms are fundamental to resilient, distributed systems such as decentralized
//! databases and blockchains. Byzantine fault tolerant systems can reach consensus with a number
//! of faulty nodes _f_ (including complete takeover by an attacker), as long as the total number
//! _N_ of nodes is greater than _3 f_.
//!
//! The Honey Badger consensus algorithm is both Byzantine fault tolerant and asynchronous. It does
//! not make timing assumptions about message delivery. An adversary can control network scheduling
//! and delay messages without impacting consensus, and progress can be made in adverse networking
//! conditions.
//! databases and blockchains.
//!
//!
//! ## Crate Implementation
//! ## Usage
//!
//! This protocol does not function in a standalone context, it must be instantiated in an
//! application that handles networking.
//! This crate is meant to be used as a component in a distributed application where the consensus
//! problem arises. The application will usually run on different nodes, connected to each other
//! via a network. The nodes give an input to the algorithm, exchange several messages, and
//! eventually the algorithm returns an output, which is guaranteed to be the same in each correct
//! node.
//!
//! * The network must contain a number of nodes that are known to each other by some unique
//! identifiers (IDs) and are able to exchange authenticated (cryptographically signed) messages.
//! However, the `hbbft` crate only implements the abstract protocols, not the networking. It is
//! the application's responsibility to serialize, sign and send the messages to the other nodes.
//! That is why in addition to methods for giving input, the algorithms also have a
//! `handle_message` method. The application is required to call this for every (validly signed,
//! deserialized) message that was received from a peer.
//! The methods return a [Step](struct.Step.html) which may contain messages, fault logs and outputs.
//! Messages are tagged with a peer they need to be sent to. A fault log is produced if a peer
//! didn't follow the protocol, and therefore is now known to be faulty. The output is the result
//! of the agreement, and guaranteed to be the same in all nodes.
//!
//! * The user must define a type of _input_ - the _transactions_ - to the system and nodes must
//! handle system networking.
//!
//! * Messages received from other nodes must be passed into the instance, and messages produced by
//! the instance sent to corresponding nodes.
//!
//! The algorithm outputs _batches_ of transactions. The order and content of these batches is
//! guaranteed to be the same for all correct nodes, assuming enough nodes (_N > 3 f_) are
//! functional and correct.
//! The network must contain a number of nodes that are known to each other by some unique
//! identifiers (IDs), which is a generic type argument to the algorithms. Where applicable, the
//! type of the input and output is also generic.
//!
//!
//! ## Algorithms
@ -114,6 +122,7 @@
// We put algorithm structs in `src/algorithm/algorithm.rs`.
#![cfg_attr(feature = "cargo-clippy", allow(module_inception))]
#![warn(missing_docs)]
extern crate bincode;
extern crate byteorder;

View File

@ -1,18 +1,18 @@
/// Message sent by a given source.
#[derive(Clone, Debug)]
pub struct SourcedMessage<M, N> {
/// The ID of the sender.
pub source: N,
/// The message's content.
pub message: M,
}
/// Message destination can be either of the two:
///
/// 1) `All`: all remote nodes.
///
/// 2) `Node(id)`: remote node `id`.
/// The destination of a message.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Target<N> {
/// The message must be sent to all remote nodes.
All,
/// The message must be sent to the node with the given ID.
Node(N),
}
@ -29,7 +29,9 @@ impl<N> Target<N> {
/// Message with a designated target.
#[derive(Clone, Debug, PartialEq)]
pub struct TargetedMessage<M, N> {
/// The node or nodes that this message must be delivered to.
pub target: Target<N>,
/// The content of the message that must be serialized and sent to the target.
pub message: M,
}

View File

@ -22,6 +22,11 @@ pub struct NetworkInfo<N> {
}
impl<N: NodeIdT> NetworkInfo<N> {
/// Creates a new `NetworkInfo` with the given ID and keys.
///
/// All nodes in the network must share the same public information. Validators' IDs must be
/// keys in the `public_keys` map, and their secret key share must match their share in the
/// `public_key_set`.
pub fn new(
our_id: N,
secret_key_share: SecretKeyShare,

View File

@ -22,13 +22,12 @@
//! entries, any two nodes will likely make almost disjoint contributions instead of proposing
//! the same transaction multiple times.
use std::fmt::{self, Display};
use std::marker::PhantomData;
use std::{cmp, iter};
use crypto::PublicKey;
use derivative::Derivative;
use failure::{Backtrace, Context, Fail};
use failure::Fail;
use rand::{Rand, Rng};
use serde::{de::DeserializeOwned, Serialize};
@ -40,57 +39,19 @@ pub use dynamic_honey_badger::{Change, ChangeState, Input};
/// Queueing honey badger error variants.
#[derive(Debug, Fail)]
pub enum ErrorKind {
pub enum Error {
/// Failed to handle input.
#[fail(display = "Input error: {}", _0)]
Input(dynamic_honey_badger::Error),
/// Failed to handle a message.
#[fail(display = "Handle message error: {}", _0)]
HandleMessage(dynamic_honey_badger::Error),
/// Failed to propose a contribution.
#[fail(display = "Propose error: {}", _0)]
Propose(dynamic_honey_badger::Error),
}
/// A queueing honey badger error.
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
impl Fail for Error {
fn cause(&self) -> Option<&Fail> {
self.inner.cause()
}
fn backtrace(&self) -> Option<&Backtrace> {
self.inner.backtrace()
}
}
impl Error {
pub fn kind(&self) -> &ErrorKind {
self.inner.get_context()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner }
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
/// The result of `QueueingHoneyBadger` handling an input or message.
pub type Result<T> = ::std::result::Result<T, Error>;
/// A Queueing Honey Badger builder, to configure the parameters and create new instances of
@ -105,7 +66,7 @@ pub struct QueueingHoneyBadgerBuilder<T, N: Rand + Ord, Q> {
_phantom: PhantomData<T>,
}
pub type QueueingHoneyBadgerWithStep<T, N, Q> = (QueueingHoneyBadger<T, N, Q>, Step<T, N>);
type QueueingHoneyBadgerWithStep<T, N, Q> = (QueueingHoneyBadger<T, N, Q>, Step<T, N>);
impl<T, N, Q> QueueingHoneyBadgerBuilder<T, N, Q>
where
@ -187,6 +148,7 @@ pub struct QueueingHoneyBadger<T, N: Rand + Ord, Q> {
rng: Box<dyn Rng + Send + Sync>,
}
/// A `QueueingHoneyBadger` step, possibly containing multiple outputs.
pub type Step<T, N> = ::Step<Message<N>, Batch<T, N>, N>;
impl<T, N, Q> DistAlgorithm for QueueingHoneyBadger<T, N, Q>
@ -294,7 +256,7 @@ where
where
F: FnOnce(&mut DynamicHoneyBadger<Vec<T>, N>) -> dynamic_honey_badger::Result<Step<T, N>>,
{
let step = f(&mut self.dyn_hb).map_err(ErrorKind::Input)?;
let step = f(&mut self.dyn_hb).map_err(Error::Input)?;
self.queue
.remove_multiple(step.output.iter().flat_map(Batch::iter));
Ok(step.join(self.propose()?))
@ -324,11 +286,12 @@ where
step.extend(
self.dyn_hb
.handle_input(Input::User(proposal))
.map_err(ErrorKind::Propose)?,
.map_err(Error::Propose)?,
);
}
Ok(step)
}
}
/// A batch containing a list of transactions from at least two thirds of the validators.
pub type Batch<T, N> = DhbBatch<Vec<T>, N>;

View File

@ -3,9 +3,12 @@ use serde_derive::{Deserialize, Serialize};
use super::SenderQueueableMessage;
/// A `SenderQueue` message.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Message<M: SenderQueueableMessage> {
/// The announcement that this node has reached the given epoch.
EpochStarted(M::Epoch),
/// A message of the wrapped algorithm.
Algo(M),
}

View File

@ -5,11 +5,10 @@
//! epoch matches the epoch of the message. Thus no queueing is required for incoming messages since
//! any incoming messages with non-matching epochs can be safely discarded.
mod dynamic_honey_badger;
mod honey_badger;
mod message;
pub mod dynamic_honey_badger;
pub mod honey_badger;
pub mod queueing_honey_badger;
mod queueing_honey_badger;
use std::collections::BTreeMap;
use std::fmt::Debug;
@ -19,7 +18,9 @@ use {DaStep, DistAlgorithm, Epoched, NodeIdT, Target};
pub use self::message::Message;
/// A message type that is suitable for use with a sender queue.
pub trait SenderQueueableMessage {
/// The epoch type of the wrapped algorithm.
type Epoch: EpochT;
/// Whether the message needs to be deferred.
@ -37,6 +38,7 @@ pub trait SenderQueueableMessage {
fn first_epoch(&self) -> Self::Epoch;
}
/// An output type that is suitable for use with a sender queue.
pub trait SenderQueueableOutput<N, M>
where
N: NodeIdT,
@ -46,12 +48,14 @@ where
fn added_peers(&self) -> Vec<N>;
}
/// A `DistAlgorithm` that can be wrapped by a sender queue.
pub trait SenderQueueableDistAlgorithm: Epoched + DistAlgorithm {
/// The maximum number of subsequent future epochs that the `DistAlgorithm` is allowed to handle
/// messages for.
fn max_future_epochs(&self) -> u64;
}
/// A map with outgoing messages, per epoch and per target node.
pub type OutgoingQueue<D> = BTreeMap<
<D as DistAlgorithm>::NodeId,
BTreeMap<<D as Epoched>::Epoch, Vec<<D as DistAlgorithm>::Message>>,
@ -79,6 +83,7 @@ where
peer_epochs: BTreeMap<D::NodeId, D::Epoch>,
}
/// A `SenderQueue` step. The output corresponds to the wrapped algorithm.
pub type Step<D> = ::DaStep<SenderQueue<D>>;
impl<D> DistAlgorithm for SenderQueue<D>
@ -131,10 +136,14 @@ where
SenderQueueBuilder::new(algo, peer_ids)
}
/// Handles an input. This will call the wrapped algorithm's `handle_input`.
pub fn handle_input(&mut self, input: D::Input) -> Result<DaStep<Self>, D::Error> {
self.apply(|algo| algo.handle_input(input))
}
/// 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: &D::NodeId,
@ -263,6 +272,7 @@ where
D::NodeId: NodeIdT,
D::Output: SenderQueueableOutput<D::NodeId, D::Message>,
{
/// Creates a new builder, with an empty outgoing queue and the specified known peers.
pub fn new<I>(algo: D, peer_ids: I) -> Self
where
I: Iterator<Item = D::NodeId>,
@ -274,16 +284,19 @@ where
}
}
/// Sets the outgoing queue, if pending messages are already known in advance.
pub fn outgoing_queue(mut self, outgoing_queue: OutgoingQueue<D>) -> Self {
self.outgoing_queue = outgoing_queue;
self
}
/// Sets the peer epochs that are already known in advance.
pub fn peer_epochs(mut self, peer_epochs: BTreeMap<D::NodeId, D::Epoch>) -> Self {
self.peer_epochs = peer_epochs;
self
}
/// Creates a new sender queue and returns the `Step` with the initial message.
pub fn build(self, our_id: D::NodeId) -> (SenderQueue<D>, DaStep<SenderQueue<D>>) {
let epoch = self.algo.epoch();
let sq = SenderQueue {

View File

@ -8,17 +8,22 @@ use broadcast;
/// A subset error.
#[derive(Clone, PartialEq, Debug, Fail)]
pub enum Error {
/// Error creating `BinaryAgreement`.
#[fail(display = "Error creating BinaryAgreement: {}", _0)]
NewAgreement(binary_agreement::Error),
/// Error creating `Broadcast`.
#[fail(display = "Error creating Broadcast: {}", _0)]
NewBroadcast(broadcast::Error),
/// Error handling a `Broadcast` input or message.
#[fail(display = "Error handling Broadcast input/message: {}", _0)]
HandleBroadcast(broadcast::Error),
/// Error handling a `BinaryAgreement` input or message.
#[fail(
display = "Error handling BinaryAgreement input/message: {}",
_0
)]
HandleAgreement(binary_agreement::Error),
/// Unknown proposer.
#[fail(display = "Unknown proposer ID")]
UnknownProposer,
}

View File

@ -24,7 +24,8 @@ pub enum MessageContent {
}
impl MessageContent {
pub fn with<N: Rand>(self, proposer_id: N) -> Message<N> {
/// Returns a `Message` with this content and the specified proposer ID.
pub(super) fn with<N: Rand>(self, proposer_id: N) -> Message<N> {
Message {
proposer_id,
content: self,

View File

@ -12,15 +12,19 @@ use super::{Error, Message, MessageContent, Result};
use rand::Rand;
use {util, DistAlgorithm, NetworkInfo, NodeIdT, SessionIdT};
/// A `Subset` step, possibly containing several outputs.
pub type Step<N> = ::Step<Message<N>, SubsetOutput<N>, N>;
/// An output with an accepted contribution or the end of the set.
#[derive(Derivative, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derivative(Debug)]
pub enum SubsetOutput<N> {
/// A contribution was accepted into the set.
Contribution(
N,
#[derivative(Debug(format_with = "util::fmt_hex"))] Vec<u8>,
),
/// The set is complete.
Done,
}

View File

@ -190,14 +190,16 @@ use {NetworkInfo, NodeIdT};
/// being invalid.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum Error {
/// Error creating `SyncKeyGen`.
#[fail(display = "Error creating SyncKeyGen: {}", _0)]
Creation(CryptoError),
/// Error generating keys.
#[fail(display = "Error generating keys: {}", _0)]
Generation(CryptoError),
#[fail(display = "Error acknowledging part: {}", _0)]
Ack(CryptoError),
/// Unknown sender
#[fail(display = "Unknown sender")]
UnknownSender,
/// Failed to serialize message.
#[fail(display = "Serialization error: {}", _0)]
Serialize(String),
}
@ -544,29 +546,39 @@ impl<N: NodeIdT> SyncKeyGen<N> {
/// An error in an `Ack` message sent by a faulty node.
#[derive(Clone, Copy, Eq, PartialEq, Debug, Fail)]
pub enum AckFault {
/// The number of values differs from the number of nodes.
#[fail(display = "The number of values differs from the number of nodes")]
ValueCount,
/// No corresponding Part received.
#[fail(display = "No corresponding Part received")]
MissingPart,
/// Value decryption failed.
#[fail(display = "Value decryption failed")]
DecryptValue,
/// Value deserialization failed.
#[fail(display = "Value deserialization failed")]
DeserializeValue,
#[fail(display = "Value doesn not match the commitment")]
/// Value doesn't match the commitment.
#[fail(display = "Value doesn't 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 {
/// The number of rows differs from the number of nodes.
#[fail(display = "The number of rows differs from the number of nodes")]
RowCount,
/// Received multiple different Part messages from the same sender.
#[fail(display = "Received multiple different Part messages from the same sender")]
MultipleParts,
/// Could not decrypt our row in the Part message.
#[fail(display = "Could not decrypt our row in the Part message")]
DecryptRow,
/// Could not deserialize our row in the Part message.
#[fail(display = "Could not deserialize our row in the Part message")]
DeserializeRow,
/// Row does not match the commitment.
#[fail(display = "Row does not match the commitment")]
RowCommitment,
}

View File

@ -25,15 +25,20 @@ use {DistAlgorithm, NetworkInfo, NodeIdT, Target};
/// A threshold decryption error.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum Error {
/// Redundant input provided.
#[fail(display = "Redundant input provided: {:?}", _0)]
MultipleInputs(Box<Ciphertext>),
/// Invalid ciphertext
#[fail(display = "Invalid ciphertext: {:?}", _0)]
InvalidCiphertext(Box<Ciphertext>),
/// Unknown sender
#[fail(display = "Unknown sender")]
UnknownSender,
/// Decryption failed
#[fail(display = "Decryption failed: {:?}", _0)]
Decryption(crypto::error::Error),
#[fail(display = "Start decryption called before setting ciphertext")]
/// Tried to decrypt before setting a cipherext.
#[fail(display = "Tried to decrypt before setting ciphertext")]
CiphertextIsNone,
}
@ -59,6 +64,7 @@ pub struct ThresholdDecrypt<N> {
terminated: bool,
}
/// A `ThresholdDecrypt` step. It will contain at most one output.
pub type Step<N> = ::DaStep<ThresholdDecrypt<N>>;
impl<N: NodeIdT> DistAlgorithm for ThresholdDecrypt<N> {

View File

@ -31,14 +31,22 @@ use {DistAlgorithm, NetworkInfo, NodeIdT, Target};
/// A threshold signing error.
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum Error {
/// Redundant input provided.
#[fail(display = "Redundant input provided")]
MultipleMessagesToSign,
#[fail(display = "CombineAndVerifySigCrypto error: {}", _0)]
/// Error combining and verifying signature shares.
#[fail(
display = "Error combining and verifying signature shares: {}",
_0
)]
CombineAndVerifySigCrypto(crypto::error::Error),
/// Unknown sender
#[fail(display = "Unknown sender")]
UnknownSender,
/// Signature verification failed.
#[fail(display = "Signature verification failed")]
VerificationFailed,
/// Document hash is not set, cannot sign or verify signatures.
#[fail(display = "Document hash is not set, cannot sign or verify signatures")]
DocumentHashIsNone,
}
@ -46,18 +54,9 @@ pub enum Error {
/// A threshold signing result.
pub type Result<T> = ::std::result::Result<T, Error>;
/// A threshold signing message, containing a signature share.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Rand)]
pub struct Message(SignatureShare);
impl Message {
pub fn new(sig: SignatureShare) -> Self {
Message(sig)
}
pub fn to_sig(&self) -> &SignatureShare {
&self.0
}
}
pub struct Message(pub SignatureShare);
/// A threshold signing algorithm instance. On input, broadcasts our threshold signature share. Upon
/// receiving at least `num_faulty + 1` shares, attempts to combine them into a signature. If that
@ -75,6 +74,7 @@ pub struct ThresholdSign<N> {
terminated: bool,
}
/// A step returned from `ThresholdSign`. It contains at most one output.
pub type Step<N> = ::DaStep<ThresholdSign<N>>;
impl<N: NodeIdT> DistAlgorithm for ThresholdSign<N> {

View File

@ -57,8 +57,14 @@ impl<E> EpochT for E where E: Copy + Message + Default + Eq + Ord + Serialize +
#[must_use = "The algorithm step result must be used."]
#[derive(Debug)]
pub struct Step<M, O, N> {
/// 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>,
/// 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>>,
}

View File

@ -1,3 +1,5 @@
//! An interface for a transaction queue
use std::collections::HashSet;
use std::{cmp, fmt};

View File

@ -10,6 +10,7 @@ use rand;
/// Workaround trait for creating new random number generators
pub trait SubRng {
/// Returns a new random number generator in a `Box`.
fn sub_rng(&mut self) -> Box<dyn rand::Rng + Send + Sync>;
}