use alloc::{boxed::Box, collections::BTreeMap};
use core::fmt::Debug;
use rand_core::CryptoRngCore;
use crate::protocol::{
Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EntryPoint, FinalizeOutcome,
LocalError, NormalBroadcast, PartyId, Payload, Protocol, ProtocolMessage, ReceiveError, Round, RoundId,
TransitionInfo,
};
pub trait Behavior: 'static + Debug + Send + Sync {}
impl<T: 'static + Debug + Send + Sync> Behavior for T {}
pub trait Misbehaving<Id, B>: 'static
where
Id: PartyId,
B: Behavior,
{
type EntryPoint: Debug + EntryPoint<Id>;
#[allow(unused_variables)]
fn modify_echo_broadcast(
rng: &mut dyn CryptoRngCore,
round: &BoxedRound<Id, <Self::EntryPoint as EntryPoint<Id>>::Protocol>,
behavior: &B,
format: &BoxedFormat,
echo_broadcast: EchoBroadcast,
) -> Result<EchoBroadcast, LocalError> {
Ok(echo_broadcast)
}
#[allow(unused_variables)]
fn modify_normal_broadcast(
rng: &mut dyn CryptoRngCore,
round: &BoxedRound<Id, <Self::EntryPoint as EntryPoint<Id>>::Protocol>,
behavior: &B,
format: &BoxedFormat,
normal_broadcast: NormalBroadcast,
) -> Result<NormalBroadcast, LocalError> {
Ok(normal_broadcast)
}
#[allow(unused_variables, clippy::too_many_arguments)]
fn modify_direct_message(
rng: &mut dyn CryptoRngCore,
round: &BoxedRound<Id, <Self::EntryPoint as EntryPoint<Id>>::Protocol>,
behavior: &B,
format: &BoxedFormat,
destination: &Id,
direct_message: DirectMessage,
artifact: Option<Artifact>,
) -> Result<(DirectMessage, Option<Artifact>), LocalError> {
Ok((direct_message, artifact))
}
#[allow(unused_variables)]
fn override_finalize(
rng: &mut dyn CryptoRngCore,
round: BoxedRound<Id, <Self::EntryPoint as EntryPoint<Id>>::Protocol>,
behavior: &B,
payloads: BTreeMap<Id, Payload>,
artifacts: BTreeMap<Id, Artifact>,
) -> Result<FinalizeOverride<Id, <Self::EntryPoint as EntryPoint<Id>>::Protocol>, LocalError> {
Ok(FinalizeOverride::UseDefault {
round,
payloads,
artifacts,
})
}
}
#[derive(Debug)]
pub enum FinalizeOverride<Id: PartyId, P: Protocol<Id>> {
UseDefault {
round: BoxedRound<Id, P>,
payloads: BTreeMap<Id, Payload>,
artifacts: BTreeMap<Id, Artifact>,
},
Override(FinalizeOutcome<Id, P>),
}
#[derive_where::derive_where(Debug)]
pub struct MisbehavingEntryPoint<Id, B, M>
where
Id: PartyId,
B: Behavior,
M: Misbehaving<Id, B>,
{
entry_point: M::EntryPoint,
behavior: Option<B>,
}
impl<Id, B, M> MisbehavingEntryPoint<Id, B, M>
where
Id: PartyId,
B: Behavior,
M: Misbehaving<Id, B>,
{
pub fn new(entry_point: M::EntryPoint, behavior: Option<B>) -> Self {
Self { entry_point, behavior }
}
}
impl<Id, B, M> EntryPoint<Id> for MisbehavingEntryPoint<Id, B, M>
where
Id: PartyId,
B: Behavior,
M: Misbehaving<Id, B>,
{
type Protocol = <M::EntryPoint as EntryPoint<Id>>::Protocol;
fn entry_round_id() -> RoundId {
M::EntryPoint::entry_round_id()
}
fn make_round(
self,
rng: &mut dyn CryptoRngCore,
shared_randomness: &[u8],
id: &Id,
) -> Result<BoxedRound<Id, Self::Protocol>, LocalError> {
let round = self.entry_point.make_round(rng, shared_randomness, id)?;
Ok(BoxedRound::new_dynamic(MisbehavingRound::<Id, B, M> {
round,
behavior: self.behavior,
}))
}
}
#[derive_where::derive_where(Debug)]
struct MisbehavingRound<Id, B, M>
where
Id: PartyId,
B: Behavior,
M: Misbehaving<Id, B>,
{
round: BoxedRound<Id, <M::EntryPoint as EntryPoint<Id>>::Protocol>,
behavior: Option<B>,
}
impl<Id, B, M> MisbehavingRound<Id, B, M>
where
Id: PartyId,
B: Behavior,
M: Misbehaving<Id, B>,
{
fn map_outcome(
outcome: FinalizeOutcome<Id, <Self as Round<Id>>::Protocol>,
behavior: Option<B>,
) -> FinalizeOutcome<Id, <Self as Round<Id>>::Protocol> {
match outcome {
FinalizeOutcome::Result(result) => FinalizeOutcome::Result(result),
FinalizeOutcome::AnotherRound(round) => {
FinalizeOutcome::AnotherRound(BoxedRound::new_dynamic(Self { round, behavior }))
}
}
}
}
impl<Id, B, M> Round<Id> for MisbehavingRound<Id, B, M>
where
Id: PartyId,
B: Behavior,
M: Misbehaving<Id, B>,
{
type Protocol = <M::EntryPoint as EntryPoint<Id>>::Protocol;
fn transition_info(&self) -> TransitionInfo {
self.round.as_ref().transition_info()
}
fn communication_info(&self) -> CommunicationInfo<Id> {
self.round.as_ref().communication_info()
}
fn make_direct_message(
&self,
rng: &mut dyn CryptoRngCore,
format: &BoxedFormat,
destination: &Id,
) -> Result<(DirectMessage, Option<Artifact>), LocalError> {
let (direct_message, artifact) = self.round.as_ref().make_direct_message(rng, format, destination)?;
if let Some(behavior) = self.behavior.as_ref() {
M::modify_direct_message(
rng,
&self.round,
behavior,
format,
destination,
direct_message,
artifact,
)
} else {
Ok((direct_message, artifact))
}
}
fn make_echo_broadcast(
&self,
rng: &mut dyn CryptoRngCore,
format: &BoxedFormat,
) -> Result<EchoBroadcast, LocalError> {
let echo_broadcast = self.round.as_ref().make_echo_broadcast(rng, format)?;
if let Some(behavior) = self.behavior.as_ref() {
M::modify_echo_broadcast(rng, &self.round, behavior, format, echo_broadcast)
} else {
Ok(echo_broadcast)
}
}
fn make_normal_broadcast(
&self,
rng: &mut dyn CryptoRngCore,
format: &BoxedFormat,
) -> Result<NormalBroadcast, LocalError> {
let normal_broadcast = self.round.as_ref().make_normal_broadcast(rng, format)?;
if let Some(behavior) = self.behavior.as_ref() {
M::modify_normal_broadcast(rng, &self.round, behavior, format, normal_broadcast)
} else {
Ok(normal_broadcast)
}
}
fn receive_message(
&self,
format: &BoxedFormat,
from: &Id,
message: ProtocolMessage,
) -> Result<Payload, ReceiveError<Id, Self::Protocol>> {
self.round.as_ref().receive_message(format, from, message)
}
fn finalize(
self: Box<Self>,
rng: &mut dyn CryptoRngCore,
payloads: BTreeMap<Id, Payload>,
artifacts: BTreeMap<Id, Artifact>,
) -> Result<FinalizeOutcome<Id, Self::Protocol>, LocalError> {
let (round, payloads, artifacts) = if let Some(behavior) = self.behavior.as_ref() {
let result = M::override_finalize(rng, self.round, behavior, payloads, artifacts)?;
match result {
FinalizeOverride::UseDefault {
round,
payloads,
artifacts,
} => (round, payloads, artifacts),
FinalizeOverride::Override(outcome) => return Ok(Self::map_outcome(outcome, self.behavior)),
}
} else {
(self.round, payloads, artifacts)
};
let outcome = round.into_boxed().finalize(rng, payloads, artifacts)?;
Ok(Self::map_outcome(outcome, self.behavior))
}
}