//! This module provides abstractions for working with protocols. //! //! This library tries to abstract away as much of the internal machinery //! of protocols as much as possible. To use a protocol, you just need to be able //! to deliver messages to and from that protocol, and eventually it will produce //! a result, without you having to worry about how many rounds it has, or how //! to serialize the emssages it produces. pub(crate) mod echo_broadcast; pub(crate) mod helpers; pub(crate) mod internal; use std::fmt; use crate::errors::{MessageError, ProtocolError}; use crate::participants::Participant; /// Represents the data making up a message. /// /// We choose to just represent messages as opaque vectors of bytes, with all /// the serialization logic handled internally. pub type MessageData = Vec; /// Represents an action by a participant in the protocol. /// /// The basic flow is that each participant receives messages from other participants, /// and then reacts with some kind of action. /// /// This action can consist of sending a message, doing nothing, etc. /// /// Eventually, the participant returns a value, ending the protocol. #[derive(Clone)] pub enum Action { /// Don't do anything. Wait, /// The protocol has more computation to do, but offers the caller a chance /// to run other tasks first. Call [`Protocol::poke`] again to continue; /// unlike [`Action::Wait`], do not wait for a message. Yield, /// Send a message to all other participants. /// /// Participants *never* sends messages to themselves. SendMany(MessageData), /// Send a private message to another participant. /// /// It's imperactive that only this participant can read this message, /// so you might want to use some form of encryption. SendPrivate(Participant, MessageData), /// End the protocol by returning a value. Return(T), } impl fmt::Debug for Action { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Wait => write!(f, "Action::Wait"), Self::Yield => write!(f, "Action::Yield"), Self::SendMany(data) => f .debug_tuple("Action::SendMany") .field(&format_args!("[{} bytes]", data.len())) .finish(), Self::SendPrivate(participant, data) => f .debug_tuple("Action::SendPrivate") .field(participant) .field(&format_args!("[{} bytes]", data.len())) .finish(), Self::Return(_) => write!(f, "Action::Return()"), } } } /// A trait for protocols. /// /// Basically, this represents a struct for the behavior of a single participant /// in a protocol. The idea is that the computation of that participant is driven /// mainly by receiving messages from other participants. pub trait Protocol { type Output; /// Poke the protocol, receiving a new action. /// /// The idea is that the protocol should be poked until it returns an error, /// or it returns an action with a return value, or it returns a wait action. /// /// Upon returning a wait action, that protocol will not advance any further /// until a new message arrives. /// /// Upon returning a yield action, the caller should yield to its executor /// (e.g. `tokio::task::yield_now`) and then poke again; the protocol can /// make progress without receiving a message first. fn poke(&mut self) -> Result, ProtocolError>; /// Inform the protocol of a new message. fn message(&mut self, from: Participant, data: MessageData) -> Result<(), MessageError>; } impl Protocol for Box> { type Output = T; fn poke(&mut self) -> Result, ProtocolError> { (**self).poke() } fn message(&mut self, from: Participant, data: MessageData) -> Result<(), MessageError> { (**self).message(from, data) } }