2018-04-26 06:22:18 -07:00
|
|
|
|
//! Asynchronous Common Subset algorithm.
|
|
|
|
|
|
2018-05-04 02:19:36 -07:00
|
|
|
|
// TODO: This module is work in progress. Remove this attribute when it's not needed anymore.
|
|
|
|
|
#![allow(unused)]
|
|
|
|
|
|
2018-04-26 06:22:18 -07:00
|
|
|
|
use std::collections::{HashMap, HashSet, VecDeque};
|
2018-05-15 04:49:39 -07:00
|
|
|
|
use std::fmt::Debug;
|
2018-05-03 02:10:31 -07:00
|
|
|
|
use std::hash::Hash;
|
2018-04-26 06:22:18 -07:00
|
|
|
|
|
2018-05-01 10:12:05 -07:00
|
|
|
|
use agreement;
|
2018-05-08 09:25:57 -07:00
|
|
|
|
use agreement::{Agreement, AgreementMessage};
|
2018-05-01 10:12:05 -07:00
|
|
|
|
|
2018-05-01 07:08:40 -07:00
|
|
|
|
use broadcast;
|
2018-05-12 07:09:07 -07:00
|
|
|
|
use broadcast::{Broadcast, BroadcastMessage};
|
|
|
|
|
|
2018-05-14 05:35:06 -07:00
|
|
|
|
use messaging::{DistAlgorithm, Target, TargetedMessage};
|
2018-05-01 07:08:40 -07:00
|
|
|
|
|
2018-05-05 06:39:32 -07:00
|
|
|
|
// TODO: Make this a generic argument of `Broadcast`.
|
|
|
|
|
type ProposedValue = Vec<u8>;
|
2018-05-07 02:59:14 -07:00
|
|
|
|
// Type of output from the Common Subset message handler.
|
2018-05-12 07:09:07 -07:00
|
|
|
|
type CommonSubsetOutput<NodeUid> = (
|
2018-05-15 10:18:05 -07:00
|
|
|
|
Option<HashMap<NodeUid, ProposedValue>>,
|
2018-05-12 07:09:07 -07:00
|
|
|
|
VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/// Message from Common Subset to remote nodes.
|
2018-05-14 09:30:07 -07:00
|
|
|
|
#[cfg_attr(feature = "serialization-serde", derive(Serialize))]
|
2018-05-15 04:49:39 -07:00
|
|
|
|
#[derive(Clone, Debug)]
|
2018-05-12 07:09:07 -07:00
|
|
|
|
pub enum Message<NodeUid> {
|
|
|
|
|
/// A message for the broadcast algorithm concerning the set element proposed by the given node.
|
|
|
|
|
Broadcast(NodeUid, BroadcastMessage),
|
|
|
|
|
/// A message for the agreement algorithm concerning the set element proposed by the given
|
|
|
|
|
/// node.
|
|
|
|
|
Agreement(NodeUid, AgreementMessage),
|
2018-05-02 00:15:47 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 07:09:07 -07:00
|
|
|
|
/// Asynchronous Common Subset algorithm instance
|
|
|
|
|
///
|
|
|
|
|
/// The Asynchronous Common Subset protocol assumes a network of `N` nodes that send signed
|
|
|
|
|
/// messages to each other, with at most `f` of them malicious, where `3 * f < N`. Handling the
|
|
|
|
|
/// networking and signing is the responsibility of the user: only when a message has been
|
|
|
|
|
/// verified to be "from node i", it can be handed to the `CommonSubset` instance.
|
|
|
|
|
///
|
|
|
|
|
/// Each participating node proposes an element for inclusion. Under the above conditions, the
|
|
|
|
|
/// protocol guarantees that all of the good nodes output the same set, consisting of at least
|
|
|
|
|
/// `N - f` of the proposed elements.
|
|
|
|
|
///
|
|
|
|
|
/// The algorithm works as follows:
|
|
|
|
|
///
|
|
|
|
|
/// * `CommonSubset` instantiates one `Broadcast` algorithm for each of the participating nodes.
|
|
|
|
|
/// At least `N - f` of these - the ones whose proposer is not malicious - will eventually output
|
|
|
|
|
/// the element proposed by that node.
|
|
|
|
|
/// * It also instantiates an `Agreement` instance for each participating node, to decide whether
|
|
|
|
|
/// that node's proposed element should be included in the common set. Whenever an element is
|
|
|
|
|
/// received via broadcast, we input "yes" (`true`) into the corresponding `Agreement` instance.
|
|
|
|
|
/// * When `N - f` `Agreement` instances have decided "yes", we input "no" (`false`) into the
|
|
|
|
|
/// remaining ones, where we haven't provided input yet.
|
|
|
|
|
/// * Once all `Agreement` instances have decided, `CommonSubset` returns the set of all proposed
|
|
|
|
|
/// values for which the decision was "yes".
|
2018-05-08 07:20:32 -07:00
|
|
|
|
pub struct CommonSubset<NodeUid: Eq + Hash + Ord> {
|
2018-04-26 06:22:18 -07:00
|
|
|
|
uid: NodeUid,
|
|
|
|
|
num_nodes: usize,
|
|
|
|
|
num_faulty_nodes: usize,
|
2018-05-02 03:57:28 -07:00
|
|
|
|
broadcast_instances: HashMap<NodeUid, Broadcast<NodeUid>>,
|
2018-05-06 14:39:01 -07:00
|
|
|
|
agreement_instances: HashMap<NodeUid, Agreement<NodeUid>>,
|
2018-05-02 07:47:30 -07:00
|
|
|
|
broadcast_results: HashMap<NodeUid, ProposedValue>,
|
|
|
|
|
agreement_results: HashMap<NodeUid, bool>,
|
2018-04-26 06:22:18 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-15 04:49:39 -07:00
|
|
|
|
impl<NodeUid: Clone + Debug + Eq + Hash + Ord> CommonSubset<NodeUid> {
|
2018-05-06 14:39:01 -07:00
|
|
|
|
pub fn new(uid: NodeUid, all_uids: &HashSet<NodeUid>) -> Result<Self, Error> {
|
|
|
|
|
let num_nodes = all_uids.len();
|
2018-04-26 06:22:18 -07:00
|
|
|
|
let num_faulty_nodes = (num_nodes - 1) / 3;
|
|
|
|
|
|
2018-05-02 06:10:26 -07:00
|
|
|
|
// Create all broadcast instances.
|
2018-05-03 02:10:31 -07:00
|
|
|
|
let mut broadcast_instances: HashMap<NodeUid, Broadcast<NodeUid>> = HashMap::new();
|
2018-05-02 07:47:30 -07:00
|
|
|
|
for uid0 in all_uids {
|
2018-05-03 01:07:37 -07:00
|
|
|
|
broadcast_instances.insert(
|
|
|
|
|
uid0.clone(),
|
2018-05-08 07:20:32 -07:00
|
|
|
|
Broadcast::new(
|
|
|
|
|
uid.clone(),
|
|
|
|
|
uid0.clone(),
|
|
|
|
|
all_uids.iter().cloned().collect(),
|
|
|
|
|
)?,
|
2018-05-03 01:07:37 -07:00
|
|
|
|
);
|
2018-05-02 06:10:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create all agreement instances.
|
2018-05-06 14:39:01 -07:00
|
|
|
|
let mut agreement_instances: HashMap<NodeUid, Agreement<NodeUid>> = HashMap::new();
|
2018-05-02 07:47:30 -07:00
|
|
|
|
for uid0 in all_uids {
|
2018-05-06 14:39:01 -07:00
|
|
|
|
agreement_instances.insert(uid0.clone(), Agreement::new(uid0.clone(), num_nodes));
|
2018-05-02 06:10:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(CommonSubset {
|
2018-04-26 06:22:18 -07:00
|
|
|
|
uid,
|
|
|
|
|
num_nodes,
|
|
|
|
|
num_faulty_nodes,
|
2018-05-04 02:19:36 -07:00
|
|
|
|
broadcast_instances,
|
2018-05-06 14:39:01 -07:00
|
|
|
|
agreement_instances,
|
2018-05-02 07:47:30 -07:00
|
|
|
|
broadcast_results: HashMap::new(),
|
|
|
|
|
agreement_results: HashMap::new(),
|
2018-05-02 06:10:26 -07:00
|
|
|
|
})
|
2018-04-26 06:22:18 -07:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-01 07:08:40 -07:00
|
|
|
|
/// Common Subset input message handler. It receives a value for broadcast
|
|
|
|
|
/// and redirects it to the corresponding broadcast instance.
|
2018-05-03 02:10:31 -07:00
|
|
|
|
pub fn send_proposed_value(
|
2018-05-14 00:35:34 -07:00
|
|
|
|
&mut self,
|
2018-05-03 02:10:31 -07:00
|
|
|
|
value: ProposedValue,
|
2018-05-12 07:09:07 -07:00
|
|
|
|
) -> Result<VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>, Error> {
|
2018-05-01 07:08:40 -07:00
|
|
|
|
// Upon receiving input v_i , input v_i to RBC_i. See Figure 2.
|
2018-05-14 00:35:34 -07:00
|
|
|
|
if let Some(instance) = self.broadcast_instances.get_mut(&self.uid) {
|
2018-05-14 05:35:06 -07:00
|
|
|
|
instance.input(value)?;
|
2018-05-14 00:35:34 -07:00
|
|
|
|
let uid = self.uid.clone();
|
2018-05-01 09:32:01 -07:00
|
|
|
|
Ok(instance
|
2018-05-14 05:35:06 -07:00
|
|
|
|
.message_iter()
|
2018-05-14 00:35:34 -07:00
|
|
|
|
.map(|msg| msg.map(|b_msg| Message::Broadcast(uid.clone(), b_msg)))
|
2018-05-01 09:32:01 -07:00
|
|
|
|
.collect())
|
|
|
|
|
} else {
|
2018-05-02 00:15:47 -07:00
|
|
|
|
Err(Error::NoSuchBroadcastInstance)
|
2018-05-01 07:08:40 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-01 10:12:05 -07:00
|
|
|
|
|
|
|
|
|
/// Upon delivery of v_j from RBC_j, if input has not yet been provided to
|
|
|
|
|
/// BA_j, then provide input 1 to BA_j. See Figure 11.
|
2018-05-07 02:59:14 -07:00
|
|
|
|
fn on_broadcast_result(&mut self, uid: &NodeUid) -> Result<Option<AgreementMessage>, Error> {
|
2018-05-06 14:39:01 -07:00
|
|
|
|
if let Some(agreement_instance) = self.agreement_instances.get_mut(&uid) {
|
2018-05-10 04:09:22 -07:00
|
|
|
|
if agreement_instance.accepts_input() {
|
2018-05-09 07:27:31 -07:00
|
|
|
|
Ok(Some(agreement_instance.set_input(true)?))
|
2018-05-03 02:10:31 -07:00
|
|
|
|
} else {
|
|
|
|
|
Ok(None)
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
2018-05-03 02:10:31 -07:00
|
|
|
|
} else {
|
2018-05-02 00:15:47 -07:00
|
|
|
|
Err(Error::NoSuchBroadcastInstance)
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 07:09:07 -07:00
|
|
|
|
/// Receives a message form a remote node `sender_id`, and returns an optional result of the
|
|
|
|
|
/// Common Subset algorithm - a set of proposed values - and a queue of messages to be sent to
|
|
|
|
|
/// remote nodes, or an error.
|
|
|
|
|
pub fn handle_message(
|
|
|
|
|
&mut self,
|
|
|
|
|
sender_id: &NodeUid,
|
|
|
|
|
message: Message<NodeUid>,
|
|
|
|
|
) -> Result<CommonSubsetOutput<NodeUid>, Error> {
|
|
|
|
|
match message {
|
|
|
|
|
Message::Broadcast(p_id, b_msg) => self.handle_broadcast(sender_id, &p_id, b_msg),
|
|
|
|
|
Message::Agreement(p_id, a_msg) => self.handle_agreement(sender_id, &p_id, &a_msg),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 03:01:25 -07:00
|
|
|
|
/// Receives a broadcast message from a remote node `sender_id` concerning a
|
|
|
|
|
/// value proposed by the node `proposer_id`. The output contains an
|
|
|
|
|
/// optional result of the Common Subset algorithm - a set of proposed
|
|
|
|
|
/// values - and a queue of messages to be sent to remote nodes, or an
|
|
|
|
|
/// error.
|
2018-05-12 07:09:07 -07:00
|
|
|
|
fn handle_broadcast(
|
2018-05-08 09:25:57 -07:00
|
|
|
|
&mut self,
|
2018-05-10 03:01:25 -07:00
|
|
|
|
sender_id: &NodeUid,
|
|
|
|
|
proposer_id: &NodeUid,
|
2018-05-10 08:50:07 -07:00
|
|
|
|
bmessage: BroadcastMessage,
|
2018-05-08 09:25:57 -07:00
|
|
|
|
) -> Result<CommonSubsetOutput<NodeUid>, Error> {
|
|
|
|
|
let mut instance_result = None;
|
2018-05-12 07:09:07 -07:00
|
|
|
|
let input_result: Result<
|
|
|
|
|
VecDeque<TargetedMessage<Message<NodeUid>, NodeUid>>,
|
|
|
|
|
Error,
|
|
|
|
|
> = {
|
2018-05-14 00:35:34 -07:00
|
|
|
|
if let Some(broadcast_instance) = self.broadcast_instances.get_mut(proposer_id) {
|
2018-05-14 05:35:06 -07:00
|
|
|
|
broadcast_instance.handle_message(sender_id, bmessage)?;
|
|
|
|
|
instance_result = broadcast_instance.next_output();
|
|
|
|
|
Ok(broadcast_instance
|
|
|
|
|
.message_iter()
|
|
|
|
|
.map(|msg| msg.map(|b_msg| Message::Broadcast(proposer_id.clone(), b_msg)))
|
|
|
|
|
.collect())
|
2018-05-08 09:25:57 -07:00
|
|
|
|
} else {
|
|
|
|
|
Err(Error::NoSuchBroadcastInstance)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let mut opt_message: Option<AgreementMessage> = None;
|
|
|
|
|
if let Some(value) = instance_result {
|
2018-05-10 03:01:25 -07:00
|
|
|
|
self.broadcast_results.insert(proposer_id.clone(), value);
|
|
|
|
|
opt_message = self.on_broadcast_result(proposer_id)?;
|
2018-05-08 09:25:57 -07:00
|
|
|
|
}
|
|
|
|
|
input_result.map(|mut queue| {
|
|
|
|
|
if let Some(agreement_message) = opt_message {
|
|
|
|
|
// Append the message to agreement nodes to the common output queue.
|
2018-05-12 07:09:07 -07:00
|
|
|
|
queue.push_back(
|
|
|
|
|
Target::All.message(Message::Agreement(proposer_id.clone(), agreement_message)),
|
|
|
|
|
);
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
2018-05-08 09:25:57 -07:00
|
|
|
|
(None, queue)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 03:01:25 -07:00
|
|
|
|
/// Receives an agreement message from a remote node `sender_id` concerning
|
|
|
|
|
/// a value proposed by the node `proposer_id`. The output contains an
|
|
|
|
|
/// optional result of the Common Subset algorithm - a set of proposed
|
|
|
|
|
/// values - and a queue of messages to be sent to remote nodes, or an
|
|
|
|
|
/// error.
|
2018-05-12 07:09:07 -07:00
|
|
|
|
fn handle_agreement(
|
2018-05-08 09:25:57 -07:00
|
|
|
|
&mut self,
|
2018-05-10 03:01:25 -07:00
|
|
|
|
sender_id: &NodeUid,
|
|
|
|
|
proposer_id: &NodeUid,
|
2018-05-08 09:25:57 -07:00
|
|
|
|
amessage: &AgreementMessage,
|
|
|
|
|
) -> Result<CommonSubsetOutput<NodeUid>, Error> {
|
2018-05-09 01:55:34 -07:00
|
|
|
|
// The result defaults to error.
|
|
|
|
|
let mut result = Err(Error::NoSuchAgreementInstance);
|
|
|
|
|
|
|
|
|
|
// Send the message to the local instance of Agreement
|
2018-05-10 03:01:25 -07:00
|
|
|
|
if let Some(agreement_instance) = self.agreement_instances.get_mut(proposer_id) {
|
2018-05-09 01:55:34 -07:00
|
|
|
|
// Optional output of agreement and outgoing agreement
|
|
|
|
|
// messages to remote nodes.
|
|
|
|
|
result = if agreement_instance.terminated() {
|
|
|
|
|
// This instance has terminated and does not accept input.
|
|
|
|
|
Ok((None, VecDeque::new()))
|
|
|
|
|
} else {
|
|
|
|
|
// Send the message to the agreement instance.
|
|
|
|
|
agreement_instance
|
2018-05-10 04:09:22 -07:00
|
|
|
|
.handle_agreement_message(sender_id, &amessage)
|
2018-05-09 01:55:34 -07:00
|
|
|
|
.map_err(Error::from)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-12 07:09:07 -07:00
|
|
|
|
let (output, mut outgoing) = result?;
|
2018-05-08 09:25:57 -07:00
|
|
|
|
|
2018-05-12 07:09:07 -07:00
|
|
|
|
// Process Agreement outputs.
|
|
|
|
|
if let Some(b) = output {
|
2018-05-15 10:18:05 -07:00
|
|
|
|
outgoing.append(&mut self.on_agreement_output(proposer_id, b)?);
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
2018-05-12 07:09:07 -07:00
|
|
|
|
|
|
|
|
|
// Check whether Agreement has completed.
|
|
|
|
|
let into_msg = |a_msg| Target::All.message(Message::Agreement(proposer_id.clone(), a_msg));
|
|
|
|
|
Ok((
|
|
|
|
|
self.try_agreement_completion(),
|
|
|
|
|
outgoing.into_iter().map(into_msg).collect(),
|
|
|
|
|
))
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
2018-04-26 06:22:18 -07:00
|
|
|
|
|
2018-05-15 10:18:05 -07:00
|
|
|
|
/// Callback to be invoked on receipt of the decision value of the Agreement
|
2018-05-02 03:57:28 -07:00
|
|
|
|
/// instance `uid`.
|
2018-05-15 10:18:05 -07:00
|
|
|
|
fn on_agreement_output(
|
2018-05-09 01:55:34 -07:00
|
|
|
|
&mut self,
|
2018-05-10 03:01:25 -07:00
|
|
|
|
element_proposer_id: &NodeUid,
|
2018-05-09 01:55:34 -07:00
|
|
|
|
result: bool,
|
2018-05-09 07:27:31 -07:00
|
|
|
|
) -> Result<VecDeque<AgreementMessage>, Error> {
|
2018-05-12 07:09:07 -07:00
|
|
|
|
self.agreement_results
|
|
|
|
|
.insert(element_proposer_id.clone(), result);
|
2018-05-15 10:18:05 -07:00
|
|
|
|
debug!("Updated Agreement results: {:?}", self.agreement_results);
|
2018-05-12 07:09:07 -07:00
|
|
|
|
if !result || self.count_true() < self.num_nodes - self.num_faulty_nodes {
|
|
|
|
|
return Ok(VecDeque::new());
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-02 03:57:28 -07:00
|
|
|
|
// Upon delivery of value 1 from at least N − f instances of BA, provide
|
|
|
|
|
// input 0 to each instance of BA that has not yet been provided input.
|
2018-05-12 07:09:07 -07:00
|
|
|
|
let mut outgoing = VecDeque::new();
|
|
|
|
|
for instance in self.agreement_instances.values_mut() {
|
|
|
|
|
if instance.accepts_input() {
|
|
|
|
|
outgoing.push_back(instance.set_input(false)?);
|
2018-04-26 06:22:18 -07:00
|
|
|
|
}
|
2018-04-30 08:55:51 -07:00
|
|
|
|
}
|
2018-05-09 07:27:31 -07:00
|
|
|
|
Ok(outgoing)
|
2018-04-30 08:55:51 -07:00
|
|
|
|
}
|
2018-04-26 06:22:18 -07:00
|
|
|
|
|
2018-05-12 07:09:07 -07:00
|
|
|
|
/// Returns the number of agreement instances that have decided "yes".
|
|
|
|
|
fn count_true(&self) -> usize {
|
|
|
|
|
self.agreement_results.values().filter(|v| **v).count()
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-15 10:18:05 -07:00
|
|
|
|
fn try_agreement_completion(&self) -> Option<HashMap<NodeUid, ProposedValue>> {
|
2018-05-02 07:47:30 -07:00
|
|
|
|
// Once all instances of BA have completed, let C ⊂ [1..N] be
|
|
|
|
|
// the indexes of each BA that delivered 1. Wait for the output
|
|
|
|
|
// v_j for each RBC_j such that j∈C. Finally output ∪ j∈C v_j.
|
2018-05-07 02:59:14 -07:00
|
|
|
|
if self.agreement_instances
|
2018-05-08 09:25:57 -07:00
|
|
|
|
.values()
|
|
|
|
|
.all(|instance| instance.terminated())
|
2018-05-07 02:59:14 -07:00
|
|
|
|
{
|
2018-05-15 10:18:05 -07:00
|
|
|
|
debug!("All Agreement instances have terminated");
|
2018-05-07 02:59:14 -07:00
|
|
|
|
// All instances of Agreement that delivered `true` (or "1" in the paper).
|
|
|
|
|
let delivered_1: HashSet<&NodeUid> = self.agreement_results
|
2018-05-03 02:10:31 -07:00
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(_, v)| **v)
|
2018-05-07 02:59:14 -07:00
|
|
|
|
.map(|(k, _)| k)
|
2018-05-03 02:10:31 -07:00
|
|
|
|
.collect();
|
2018-05-15 10:18:05 -07:00
|
|
|
|
debug!("Agreement instances that delivered 1: {:?}", delivered_1);
|
|
|
|
|
|
2018-05-02 07:47:30 -07:00
|
|
|
|
// Results of Broadcast instances in `delivered_1`
|
2018-05-15 10:18:05 -07:00
|
|
|
|
let broadcast_results: HashMap<NodeUid, ProposedValue> = self.broadcast_results
|
2018-05-03 02:10:31 -07:00
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(k, _)| delivered_1.get(k).is_some())
|
2018-05-15 10:18:05 -07:00
|
|
|
|
.map(|(k, v)| (k.clone(), v.clone()))
|
2018-05-03 02:10:31 -07:00
|
|
|
|
.collect();
|
2018-05-15 10:18:05 -07:00
|
|
|
|
debug!("Broadcast results among the Agreement instances that delivered 1: {:?}",
|
|
|
|
|
broadcast_results);
|
2018-05-02 07:47:30 -07:00
|
|
|
|
|
|
|
|
|
if delivered_1.len() == broadcast_results.len() {
|
2018-05-15 10:18:05 -07:00
|
|
|
|
debug!("Agreement instances completed with {:?}", broadcast_results);
|
2018-05-02 07:47:30 -07:00
|
|
|
|
Some(broadcast_results)
|
2018-05-03 02:10:31 -07:00
|
|
|
|
} else {
|
2018-05-02 07:47:30 -07:00
|
|
|
|
None
|
|
|
|
|
}
|
2018-05-03 02:10:31 -07:00
|
|
|
|
} else {
|
2018-05-02 07:47:30 -07:00
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-26 06:22:18 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub enum Error {
|
|
|
|
|
UnexpectedMessage,
|
|
|
|
|
NotImplemented,
|
2018-05-02 00:15:47 -07:00
|
|
|
|
NoSuchBroadcastInstance,
|
2018-05-06 14:39:01 -07:00
|
|
|
|
NoSuchAgreementInstance,
|
2018-05-01 07:08:40 -07:00
|
|
|
|
Broadcast(broadcast::Error),
|
2018-05-02 03:57:28 -07:00
|
|
|
|
Agreement(agreement::Error),
|
2018-04-26 06:22:18 -07:00
|
|
|
|
}
|
2018-05-01 07:08:40 -07:00
|
|
|
|
|
|
|
|
|
impl From<broadcast::Error> for Error {
|
|
|
|
|
fn from(err: broadcast::Error) -> Error {
|
|
|
|
|
Error::Broadcast(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-02 03:57:28 -07:00
|
|
|
|
|
|
|
|
|
impl From<agreement::Error> for Error {
|
|
|
|
|
fn from(err: agreement::Error) -> Error {
|
|
|
|
|
Error::Agreement(err)
|
|
|
|
|
}
|
|
|
|
|
}
|