2018-04-26 06:22:18 -07:00
|
|
|
|
//! Asynchronous Common Subset algorithm.
|
|
|
|
|
|
|
|
|
|
use std::collections::{HashMap, HashSet, VecDeque};
|
2018-05-02 00:15:47 -07:00
|
|
|
|
use std::fmt::{Debug, Display};
|
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;
|
|
|
|
|
use agreement::Agreement;
|
|
|
|
|
|
2018-05-01 07:08:40 -07:00
|
|
|
|
use broadcast;
|
2018-05-01 09:32:01 -07:00
|
|
|
|
use broadcast::{Broadcast, TargetedBroadcastMessage};
|
2018-05-01 07:08:40 -07:00
|
|
|
|
|
2018-05-02 03:57:28 -07:00
|
|
|
|
use messaging::ProposedValue;
|
2018-05-01 07:08:40 -07:00
|
|
|
|
|
2018-05-03 02:10:31 -07:00
|
|
|
|
use proto::{AgreementMessage, BroadcastMessage};
|
2018-05-01 07:08:40 -07:00
|
|
|
|
|
2018-05-02 03:57:28 -07:00
|
|
|
|
/// Input from a remote node to Common Subset.
|
2018-05-02 00:15:47 -07:00
|
|
|
|
pub enum Input<NodeUid> {
|
2018-05-01 10:12:05 -07:00
|
|
|
|
/// Message from a remote node `uid` to the broadcast instance `uid`.
|
|
|
|
|
Broadcast(NodeUid, BroadcastMessage<ProposedValue>),
|
|
|
|
|
/// Message from a remote node `uid` to the agreement instance `uid`.
|
|
|
|
|
Agreement(NodeUid, AgreementMessage),
|
2018-05-01 07:08:40 -07:00
|
|
|
|
}
|
2018-04-26 06:22:18 -07:00
|
|
|
|
|
2018-05-02 03:57:28 -07:00
|
|
|
|
/// Output from Common Subset to remote nodes.
|
2018-05-03 02:10:31 -07:00
|
|
|
|
///
|
|
|
|
|
/// FIXME: We can do an interface that doesn't need this type and instead works
|
|
|
|
|
/// directly with the `TargetBroadcastMessage` and `AgreementMessage`.
|
2018-05-02 00:15:47 -07:00
|
|
|
|
pub enum Output<NodeUid> {
|
2018-05-03 02:10:31 -07:00
|
|
|
|
/// A broadcast message to be sent to the destination set in the
|
|
|
|
|
/// `TargetedBroadcastMessage`.
|
|
|
|
|
Broadcast(TargetedBroadcastMessage<NodeUid>),
|
|
|
|
|
/// An agreement message to be broadcast to all nodes. There are no
|
|
|
|
|
/// one-to-one agreement messages.
|
|
|
|
|
Agreement(AgreementMessage),
|
2018-05-02 00:15:47 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct CommonSubset<NodeUid: Eq + Hash> {
|
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
|
|
|
|
agreement_true_outputs: HashSet<NodeUid>,
|
|
|
|
|
broadcast_instances: HashMap<NodeUid, Broadcast<NodeUid>>,
|
|
|
|
|
agreement_instances: HashMap<NodeUid, Agreement>,
|
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-02 07:47:30 -07:00
|
|
|
|
impl<NodeUid: Clone + Debug + Display + Eq + Hash + Ord> CommonSubset<NodeUid> {
|
2018-05-03 02:10:31 -07:00
|
|
|
|
pub fn new(uid: NodeUid, all_uids: &HashSet<NodeUid>, num_nodes: usize) -> Result<Self, Error> {
|
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(),
|
|
|
|
|
Broadcast::new(uid.clone(), uid0.clone(), all_uids.clone())?,
|
|
|
|
|
);
|
2018-05-02 06:10:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create all agreement instances.
|
2018-05-03 02:10:31 -07:00
|
|
|
|
let mut agreement_instances: HashMap<NodeUid, Agreement> = HashMap::new();
|
2018-05-02 07:47:30 -07:00
|
|
|
|
for uid0 in all_uids {
|
2018-05-02 06:10:26 -07:00
|
|
|
|
agreement_instances.insert(uid0.clone(), Agreement::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(CommonSubset {
|
2018-04-26 06:22:18 -07:00
|
|
|
|
uid,
|
|
|
|
|
num_nodes,
|
|
|
|
|
num_faulty_nodes,
|
2018-05-02 03:57:28 -07:00
|
|
|
|
agreement_true_outputs: HashSet::new(),
|
2018-05-02 06:10:26 -07:00
|
|
|
|
broadcast_instances: broadcast_instances,
|
2018-05-02 03:57:28 -07:00
|
|
|
|
agreement_instances: HashMap::new(),
|
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(
|
|
|
|
|
&self,
|
|
|
|
|
value: ProposedValue,
|
|
|
|
|
) -> Result<VecDeque<Output<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-02 03:57:28 -07:00
|
|
|
|
if let Some(instance) = self.broadcast_instances.get(&self.uid) {
|
2018-05-01 09:32:01 -07:00
|
|
|
|
Ok(instance
|
|
|
|
|
.propose_value(value)?
|
|
|
|
|
.into_iter()
|
2018-05-02 00:15:47 -07:00
|
|
|
|
.map(Output::Broadcast)
|
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-03 02:10:31 -07:00
|
|
|
|
pub fn on_broadcast_result(&mut self, uid: NodeUid) -> Result<Option<AgreementMessage>, Error> {
|
2018-05-02 03:57:28 -07:00
|
|
|
|
if let Some(agreement_instance) = self.agreement_instances.get_mut(&uid) {
|
|
|
|
|
if !agreement_instance.has_input() {
|
2018-05-03 02:10:31 -07:00
|
|
|
|
Ok(Some(agreement_instance.set_input(true)))
|
|
|
|
|
} 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-02 03:57:28 -07:00
|
|
|
|
/// Receive input from a remote node.
|
2018-05-03 02:10:31 -07:00
|
|
|
|
pub fn on_input(
|
|
|
|
|
&mut self,
|
|
|
|
|
message: Input<NodeUid>,
|
|
|
|
|
) -> Result<VecDeque<Output<NodeUid>>, Error> {
|
2018-05-01 10:12:05 -07:00
|
|
|
|
match message {
|
2018-05-02 00:15:47 -07:00
|
|
|
|
Input::Broadcast(uid, bmessage) => {
|
2018-05-02 03:57:28 -07:00
|
|
|
|
let mut instance_result = None;
|
|
|
|
|
let input_result = {
|
|
|
|
|
if let Some(broadcast_instance) = self.broadcast_instances.get(&uid) {
|
2018-05-03 02:10:31 -07:00
|
|
|
|
broadcast_instance
|
|
|
|
|
.handle_broadcast_message(&uid, &bmessage)
|
2018-05-02 03:57:28 -07:00
|
|
|
|
.map(|(value, queue)| {
|
2018-05-02 06:10:26 -07:00
|
|
|
|
instance_result = value;
|
2018-05-03 02:10:31 -07:00
|
|
|
|
queue.into_iter().map(Output::Broadcast).collect()
|
2018-05-02 03:57:28 -07:00
|
|
|
|
})
|
|
|
|
|
.map_err(Error::from)
|
2018-05-03 02:10:31 -07:00
|
|
|
|
} else {
|
2018-05-02 03:57:28 -07:00
|
|
|
|
Err(Error::NoSuchBroadcastInstance)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if instance_result.is_some() {
|
2018-05-02 06:10:26 -07:00
|
|
|
|
self.on_broadcast_result(uid)?;
|
2018-05-01 10:12:05 -07:00
|
|
|
|
}
|
2018-05-02 03:57:28 -07:00
|
|
|
|
input_result
|
2018-05-03 02:10:31 -07:00
|
|
|
|
}
|
2018-05-02 00:15:47 -07:00
|
|
|
|
Input::Agreement(_uid, _message) => {
|
2018-05-02 03:57:28 -07:00
|
|
|
|
// FIXME: send the message to the Agreement instance and
|
|
|
|
|
// conditionally call `on_agreement_output`
|
|
|
|
|
|
2018-05-01 10:12:05 -07:00
|
|
|
|
Err(Error::NotImplemented)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-26 06:22:18 -07:00
|
|
|
|
|
2018-05-02 03:57:28 -07:00
|
|
|
|
/// Callback to be invoked on receipt of a returned value of the Agreement
|
|
|
|
|
/// instance `uid`.
|
2018-05-03 02:10:31 -07:00
|
|
|
|
///
|
|
|
|
|
/// FIXME: It is likely that only one `AgreementMessage` is required because
|
|
|
|
|
/// Figure 11 does not count the number of messages but the number of nodes
|
|
|
|
|
/// that sent messages.
|
|
|
|
|
fn on_agreement_result(&mut self, uid: NodeUid, result: bool) -> VecDeque<AgreementMessage> {
|
|
|
|
|
let mut outgoing = 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.
|
|
|
|
|
if result {
|
|
|
|
|
self.agreement_true_outputs.insert(uid);
|
|
|
|
|
|
2018-05-03 02:10:31 -07:00
|
|
|
|
if self.agreement_true_outputs.len() >= self.num_nodes - self.num_faulty_nodes {
|
2018-05-02 03:57:28 -07:00
|
|
|
|
let instances = &mut self.agreement_instances;
|
|
|
|
|
for (_uid0, instance) in instances.iter_mut() {
|
|
|
|
|
if !instance.has_input() {
|
2018-05-03 02:10:31 -07:00
|
|
|
|
outgoing.push_back(instance.set_input(false));
|
2018-05-02 03:57:28 -07:00
|
|
|
|
}
|
2018-04-30 08:55:51 -07:00
|
|
|
|
}
|
2018-04-26 06:22:18 -07:00
|
|
|
|
}
|
2018-04-30 08:55:51 -07:00
|
|
|
|
}
|
2018-05-03 02:10:31 -07:00
|
|
|
|
outgoing
|
2018-04-30 08:55:51 -07:00
|
|
|
|
}
|
2018-04-26 06:22:18 -07:00
|
|
|
|
|
2018-05-02 07:47:30 -07:00
|
|
|
|
pub fn on_agreement_completion(&self) -> Option<HashSet<ProposedValue>> {
|
|
|
|
|
// 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-03 02:10:31 -07:00
|
|
|
|
let instance_uids: HashSet<NodeUid> = self.agreement_instances
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|(k, _)| k.clone())
|
|
|
|
|
.collect();
|
|
|
|
|
let completed_uids: HashSet<NodeUid> = self.agreement_results
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|(k, _)| k.clone())
|
|
|
|
|
.collect();
|
2018-05-02 07:47:30 -07:00
|
|
|
|
if instance_uids == completed_uids {
|
|
|
|
|
// All instances of Agreement that delivered `true`.
|
2018-05-03 02:10:31 -07:00
|
|
|
|
let delivered_1: HashSet<NodeUid> = self.agreement_results
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(_, v)| **v)
|
|
|
|
|
.map(|(k, _)| k.clone())
|
|
|
|
|
.collect();
|
2018-05-02 07:47:30 -07:00
|
|
|
|
// Results of Broadcast instances in `delivered_1`
|
2018-05-03 02:10:31 -07:00
|
|
|
|
let broadcast_results: HashSet<ProposedValue> = self.broadcast_results
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(k, _)| delivered_1.get(k).is_some())
|
|
|
|
|
.map(|(_, v)| v.clone())
|
|
|
|
|
.collect();
|
2018-05-02 07:47:30 -07:00
|
|
|
|
|
|
|
|
|
if delivered_1.len() == broadcast_results.len() {
|
|
|
|
|
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-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)
|
|
|
|
|
}
|
|
|
|
|
}
|