use alloc::{
boxed::Box,
collections::{BTreeMap, BTreeSet},
format,
};
use core::{
any::Any,
fmt::{Debug, Display},
};
use rand_core::CryptoRngCore;
use serde::{Deserialize, Serialize};
use super::{
boxed_format::BoxedFormat,
boxed_round::BoxedRound,
errors::{LocalError, MessageValidationError, ProtocolValidationError, ReceiveError},
message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessage, ProtocolMessagePart},
round_id::{RoundId, TransitionInfo},
};
#[derive(Debug, Clone)]
pub struct CommunicationInfo<Id> {
pub message_destinations: BTreeSet<Id>,
pub expecting_messages_from: BTreeSet<Id>,
pub echo_round_participation: EchoRoundParticipation<Id>,
}
impl<Id: PartyId> CommunicationInfo<Id> {
pub fn regular(other_parties: &BTreeSet<Id>) -> Self {
Self {
message_destinations: other_parties.clone(),
expecting_messages_from: other_parties.clone(),
echo_round_participation: EchoRoundParticipation::Default,
}
}
}
#[derive(Debug)]
pub enum FinalizeOutcome<Id: PartyId, P: Protocol<Id>> {
AnotherRound(BoxedRound<Id, P>),
Result(P::Result),
}
pub trait Protocol<Id>: 'static {
type Result: Debug;
type ProtocolError: ProtocolError<Id>;
fn verify_direct_message_is_invalid(
format: &BoxedFormat,
round_id: &RoundId,
message: &DirectMessage,
) -> Result<(), MessageValidationError>;
fn verify_echo_broadcast_is_invalid(
format: &BoxedFormat,
round_id: &RoundId,
message: &EchoBroadcast,
) -> Result<(), MessageValidationError>;
fn verify_normal_broadcast_is_invalid(
format: &BoxedFormat,
round_id: &RoundId,
message: &NormalBroadcast,
) -> Result<(), MessageValidationError>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RequiredMessageParts {
pub(crate) echo_broadcast: bool,
pub(crate) normal_broadcast: bool,
pub(crate) direct_message: bool,
}
impl RequiredMessageParts {
fn new(echo_broadcast: bool, normal_broadcast: bool, direct_message: bool) -> Self {
debug_assert!(echo_broadcast || normal_broadcast || direct_message);
Self {
echo_broadcast,
normal_broadcast,
direct_message,
}
}
pub fn echo_broadcast() -> Self {
Self::new(true, false, false)
}
pub fn normal_broadcast() -> Self {
Self::new(false, true, false)
}
pub fn direct_message() -> Self {
Self::new(false, false, true)
}
pub fn and_echo_broadcast(&self) -> Self {
Self::new(true, self.normal_broadcast, self.direct_message)
}
pub fn and_normal_broadcast(&self) -> Self {
Self::new(self.echo_broadcast, true, self.direct_message)
}
pub fn and_direct_message(&self) -> Self {
Self::new(self.echo_broadcast, self.normal_broadcast, true)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RequiredMessages {
pub(crate) this_round: RequiredMessageParts,
pub(crate) previous_rounds: Option<BTreeMap<RoundId, RequiredMessageParts>>,
pub(crate) combined_echos: Option<BTreeSet<RoundId>>,
}
impl RequiredMessages {
pub fn new(
this_round: RequiredMessageParts,
previous_rounds: Option<BTreeMap<RoundId, RequiredMessageParts>>,
combined_echos: Option<BTreeSet<RoundId>>,
) -> Self {
Self {
this_round,
previous_rounds,
combined_echos,
}
}
}
pub trait ProtocolError<Id>: Display + Debug + Clone + Serialize + for<'de> Deserialize<'de> {
type AssociatedData: Debug;
fn required_messages(&self) -> RequiredMessages;
#[allow(clippy::too_many_arguments)]
fn verify_messages_constitute_error(
&self,
format: &BoxedFormat,
guilty_party: &Id,
shared_randomness: &[u8],
associated_data: &Self::AssociatedData,
message: ProtocolMessage,
previous_messages: BTreeMap<RoundId, ProtocolMessage>,
combined_echos: BTreeMap<RoundId, BTreeMap<Id, EchoBroadcast>>,
) -> Result<(), ProtocolValidationError>;
}
#[derive(displaydoc::Display, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct NoProtocolErrors;
impl<Id> ProtocolError<Id> for NoProtocolErrors {
type AssociatedData = ();
fn required_messages(&self) -> RequiredMessages {
panic!("Attempt to use an empty error type in an evidence. This is a bug in the protocol implementation.")
}
fn verify_messages_constitute_error(
&self,
_format: &BoxedFormat,
_guilty_party: &Id,
_shared_randomness: &[u8],
_associated_data: &Self::AssociatedData,
_message: ProtocolMessage,
_previous_messages: BTreeMap<RoundId, ProtocolMessage>,
_combined_echos: BTreeMap<RoundId, BTreeMap<Id, EchoBroadcast>>,
) -> Result<(), ProtocolValidationError> {
panic!("Attempt to use an empty error type in an evidence. This is a bug in the protocol implementation.")
}
}
#[derive(Debug)]
pub struct Payload(pub Box<dyn Any + Send + Sync>);
impl Payload {
pub fn new<T: 'static + Send + Sync>(payload: T) -> Self {
Self(Box::new(payload))
}
pub fn empty() -> Self {
Self::new(())
}
pub fn downcast<T: 'static>(self) -> Result<T, LocalError> {
Ok(*(self
.0
.downcast::<T>()
.map_err(|_| LocalError::new(format!("Failed to downcast into {}", core::any::type_name::<T>())))?))
}
}
#[derive(Debug)]
pub struct Artifact(pub Box<dyn Any + Send + Sync>);
impl Artifact {
pub fn new<T: 'static + Send + Sync>(artifact: T) -> Self {
Self(Box::new(artifact))
}
pub fn downcast<T: 'static>(self) -> Result<T, LocalError> {
Ok(*(self
.0
.downcast::<T>()
.map_err(|_| LocalError::new(format!("Failed to downcast into {}", core::any::type_name::<T>())))?))
}
}
pub trait EntryPoint<Id: PartyId> {
type Protocol: Protocol<Id>;
fn entry_round_id() -> RoundId;
fn make_round(
self,
rng: &mut dyn CryptoRngCore,
shared_randomness: &[u8],
id: &Id,
) -> Result<BoxedRound<Id, Self::Protocol>, LocalError>;
}
pub trait PartyId: 'static + Debug + Clone + Ord + Send + Sync + Serialize + for<'de> Deserialize<'de> {}
impl<T> PartyId for T where T: 'static + Debug + Clone + Ord + Send + Sync + Serialize + for<'de> Deserialize<'de> {}
#[derive(Debug, Clone)]
pub enum EchoRoundParticipation<Id> {
Default,
Send,
Receive {
echo_targets: BTreeSet<Id>,
},
}
mod sealed {
pub trait DynTypeId: 'static {
fn get_type_id(&self) -> core::any::TypeId {
core::any::TypeId::of::<Self>()
}
}
impl<T: 'static> DynTypeId for T {}
}
use sealed::DynTypeId;
pub trait Round<Id: PartyId>: 'static + Debug + Send + Sync + DynTypeId {
type Protocol: Protocol<Id>;
fn transition_info(&self) -> TransitionInfo;
fn communication_info(&self) -> CommunicationInfo<Id>;
fn make_direct_message(
&self,
#[allow(unused_variables)] rng: &mut dyn CryptoRngCore,
#[allow(unused_variables)] format: &BoxedFormat,
#[allow(unused_variables)] destination: &Id,
) -> Result<(DirectMessage, Option<Artifact>), LocalError> {
Ok((DirectMessage::none(), None))
}
fn make_echo_broadcast(
&self,
#[allow(unused_variables)] rng: &mut dyn CryptoRngCore,
#[allow(unused_variables)] format: &BoxedFormat,
) -> Result<EchoBroadcast, LocalError> {
Ok(EchoBroadcast::none())
}
fn make_normal_broadcast(
&self,
#[allow(unused_variables)] rng: &mut dyn CryptoRngCore,
#[allow(unused_variables)] format: &BoxedFormat,
) -> Result<NormalBroadcast, LocalError> {
Ok(NormalBroadcast::none())
}
fn receive_message(
&self,
format: &BoxedFormat,
from: &Id,
message: ProtocolMessage,
) -> Result<Payload, ReceiveError<Id, Self::Protocol>>;
fn finalize(
self: Box<Self>,
rng: &mut dyn CryptoRngCore,
payloads: BTreeMap<Id, Payload>,
artifacts: BTreeMap<Id, Artifact>,
) -> Result<FinalizeOutcome<Id, Self::Protocol>, LocalError>;
}