Skip to content

Commit

Permalink
Add normal broadcasts along with echo broadcasts
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Oct 28, 2024
1 parent ac06f6b commit 7e80c65
Show file tree
Hide file tree
Showing 13 changed files with 355 additions and 54 deletions.
29 changes: 28 additions & 1 deletion examples/src/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ impl ProtocolError for SimpleProtocolError {
BTreeSet::new()
}

fn required_normal_broadcasts(&self) -> BTreeSet<RoundId> {
BTreeSet::new()
}

fn required_combined_echos(&self) -> BTreeSet<RoundId> {
match self {
Self::Round1InvalidPosition => BTreeSet::new(),
Expand All @@ -37,8 +41,10 @@ impl ProtocolError for SimpleProtocolError {
fn verify_messages_constitute_error(
&self,
_echo_broadcast: &EchoBroadcast,
_normal_broadcast: &NormalBroadcast,
direct_message: &DirectMessage,
_echo_broadcasts: &BTreeMap<RoundId, EchoBroadcast>,
_normal_broadcasts: &BTreeMap<RoundId, NormalBroadcast>,
_direct_messages: &BTreeMap<RoundId, DirectMessage>,
combined_echos: &BTreeMap<RoundId, Vec<EchoBroadcast>>,
) -> Result<(), ProtocolValidationError> {
Expand Down Expand Up @@ -134,6 +140,12 @@ struct Round1Echo {
my_position: u8,
}

#[derive(Serialize, Deserialize)]
struct Round1Broadcast {
x: u8,
my_position: u8,
}

struct Round1Payload {
x: u8,
}
Expand Down Expand Up @@ -183,6 +195,17 @@ impl<Id: 'static + Debug + Clone + Ord + Send + Sync> Round<Id> for Round1<Id> {
&self.context.other_ids
}

fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result<NormalBroadcast, LocalError> {
debug!("{:?}: making normal broadcast", self.context.id);

let message = Round1Broadcast {
x: 0,
my_position: self.context.ids_to_positions[&self.context.id],
};

Self::serialize_normal_broadcast(message)
}

fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result<EchoBroadcast, LocalError> {
debug!("{:?}: making echo broadcast", self.context.id);

Expand Down Expand Up @@ -213,11 +236,13 @@ impl<Id: 'static + Debug + Clone + Ord + Send + Sync> Round<Id> for Round1<Id> {
_rng: &mut impl CryptoRngCore,
from: &Id,
echo_broadcast: EchoBroadcast,
normal_broadcast: NormalBroadcast,
direct_message: DirectMessage,
) -> Result<Payload, ReceiveError<Id, Self::Protocol>> {
debug!("{:?}: receiving message from {:?}", self.context.id, from);

let _echo = echo_broadcast.deserialize::<SimpleProtocol, Round1Echo>()?;
let _normal = normal_broadcast.deserialize::<SimpleProtocol, Round1Broadcast>()?;
let message = direct_message.deserialize::<SimpleProtocol, Round1Message>()?;

debug!("{:?}: received message: {:?}", self.context.id, message);
Expand Down Expand Up @@ -296,7 +321,7 @@ impl<Id: 'static + Debug + Clone + Ord + Send + Sync> Round<Id> for Round2<Id> {
) -> Result<DirectMessage, LocalError> {
debug!("{:?}: making direct message for {:?}", self.context.id, destination);

let message = Round1Message {
let message = Round2Message {
my_position: self.context.ids_to_positions[&self.context.id],
your_position: self.context.ids_to_positions[destination],
};
Expand All @@ -309,11 +334,13 @@ impl<Id: 'static + Debug + Clone + Ord + Send + Sync> Round<Id> for Round2<Id> {
_rng: &mut impl CryptoRngCore,
from: &Id,
echo_broadcast: EchoBroadcast,
normal_broadcast: NormalBroadcast,
direct_message: DirectMessage,
) -> Result<Payload, ReceiveError<Id, Self::Protocol>> {
debug!("{:?}: receiving message from {:?}", self.context.id, from);

echo_broadcast.assert_is_none()?;
normal_broadcast.assert_is_none()?;

let message = direct_message.deserialize::<SimpleProtocol, Round1Message>()?;

Expand Down
8 changes: 6 additions & 2 deletions manul/benches/empty_rounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use criterion::{criterion_group, criterion_main, Criterion};
use manul::{
protocol::{
Artifact, DeserializationError, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, FirstRound,
LocalError, Payload, Protocol, ProtocolError, ProtocolMessage, ProtocolValidationError, ReceiveError, Round,
RoundId,
LocalError, NormalBroadcast, Payload, Protocol, ProtocolError, ProtocolMessage, ProtocolValidationError,
ReceiveError, Round, RoundId,
},
session::{signature::Keypair, SessionOutcome},
testing::{run_sync, TestSessionParams, TestSigner, TestVerifier},
Expand All @@ -26,8 +26,10 @@ impl ProtocolError for EmptyProtocolError {
fn verify_messages_constitute_error(
&self,
_echo_broadcast: &EchoBroadcast,
_normal_broadcast: &NormalBroadcast,
_direct_message: &DirectMessage,
_echo_broadcasts: &BTreeMap<RoundId, EchoBroadcast>,
_normal_broadcasts: &BTreeMap<RoundId, NormalBroadcast>,
_direct_messages: &BTreeMap<RoundId, DirectMessage>,
_combined_echos: &BTreeMap<RoundId, Vec<EchoBroadcast>>,
) -> Result<(), ProtocolValidationError> {
Expand Down Expand Up @@ -132,13 +134,15 @@ impl<Id: 'static + Debug + Clone + Ord + Send + Sync> Round<Id> for EmptyRound<I
_rng: &mut impl CryptoRngCore,
_from: &Id,
echo_broadcast: EchoBroadcast,
normal_broadcast: NormalBroadcast,
direct_message: DirectMessage,
) -> Result<Payload, ReceiveError<Id, Self::Protocol>> {
if self.inputs.echo {
let _echo_broadcast = echo_broadcast.deserialize::<EmptyProtocol, Round1EchoBroadcast>()?;
} else {
echo_broadcast.assert_is_none()?;
}
normal_broadcast.assert_is_none()?;
let _direct_message = direct_message.deserialize::<EmptyProtocol, Round1DirectMessage>()?;
Ok(Payload::new(Round1Payload))
}
Expand Down
4 changes: 2 additions & 2 deletions manul/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ mod round;

pub use errors::{
DeserializationError, DirectMessageError, EchoBroadcastError, FinalizeError, LocalError, MessageValidationError,
ProtocolValidationError, ReceiveError, RemoteError,
NormalBroadcastError, ProtocolValidationError, ReceiveError, RemoteError,
};
pub use message::{DirectMessage, EchoBroadcast, ProtocolMessage};
pub use message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessage};
pub use round::{
AnotherRound, Artifact, FinalizeOutcome, FirstRound, Payload, Protocol, ProtocolError, Round, RoundId,
};
Expand Down
22 changes: 22 additions & 0 deletions manul/src/protocol/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub(crate) enum ReceiveErrorType<Id, P: Protocol> {
InvalidDirectMessage(DirectMessageError),
/// The given echo broadcast cannot be deserialized.
InvalidEchoBroadcast(EchoBroadcastError),
/// The given normal broadcast cannot be deserialized.
InvalidNormalBroadcast(NormalBroadcastError),
/// A provable error occurred.
Protocol(P::ProtocolError),
/// An unprovable error occurred.
Expand Down Expand Up @@ -113,6 +115,15 @@ where
}
}

impl<Id, P> From<NormalBroadcastError> for ReceiveError<Id, P>
where
P: Protocol,
{
fn from(error: NormalBroadcastError) -> Self {
Self(ReceiveErrorType::InvalidNormalBroadcast(error))
}
}

/// An error that can occur during [`Round::finalize`](`super::Round::finalize`).
#[derive(Debug)]
pub enum FinalizeError<P: Protocol> {
Expand Down Expand Up @@ -208,3 +219,14 @@ impl From<String> for EchoBroadcastError {
Self(message)
}
}

/// An error during deserialization of a normal broadcast.
#[derive(displaydoc::Display, Debug, Clone)]
#[displaydoc("Normal broadcast error: {0}")]
pub struct NormalBroadcastError(String);

impl From<String> for NormalBroadcastError {
fn from(message: String) -> Self {
Self(message)
}
}
20 changes: 19 additions & 1 deletion manul/src/protocol/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use alloc::string::{String, ToString};
use serde::{Deserialize, Serialize};

use super::{
errors::{DirectMessageError, EchoBroadcastError, LocalError, MessageValidationError},
errors::{DirectMessageError, EchoBroadcastError, LocalError, MessageValidationError, NormalBroadcastError},
round::Protocol,
};

Expand Down Expand Up @@ -137,3 +137,21 @@ impl ProtocolMessageWrapper for EchoBroadcast {
impl ProtocolMessage for EchoBroadcast {
type Error = EchoBroadcastError;
}

/// A serialized regular (non-echo) broadcast.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NormalBroadcast(Option<MessagePayload>);

impl ProtocolMessageWrapper for NormalBroadcast {
fn new_inner(maybe_message: Option<MessagePayload>) -> Self {
Self(maybe_message)
}

fn maybe_message(&self) -> &Option<MessagePayload> {
&self.0
}
}

impl ProtocolMessage for NormalBroadcast {
type Error = NormalBroadcastError;
}
13 changes: 11 additions & 2 deletions manul/src/protocol/object_safe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rand_core::{CryptoRng, CryptoRngCore, RngCore};

use super::{
errors::{FinalizeError, LocalError, ReceiveError},
message::{DirectMessage, EchoBroadcast},
message::{DirectMessage, EchoBroadcast, NormalBroadcast},
round::{Artifact, FinalizeOutcome, Payload, Protocol, Round, RoundId},
};

Expand Down Expand Up @@ -55,11 +55,14 @@ pub(crate) trait ObjectSafeRound<Id>: 'static + Send + Sync + Debug {

fn make_echo_broadcast(&self, rng: &mut dyn CryptoRngCore) -> Result<EchoBroadcast, LocalError>;

fn make_normal_broadcast(&self, rng: &mut dyn CryptoRngCore) -> Result<NormalBroadcast, LocalError>;

fn receive_message(
&self,
rng: &mut dyn CryptoRngCore,
from: &Id,
echo_broadcast: EchoBroadcast,
normal_broadcast: NormalBroadcast,
direct_message: DirectMessage,
) -> Result<Payload, ReceiveError<Id, Self::Protocol>>;

Expand Down Expand Up @@ -127,16 +130,22 @@ where
self.round.make_echo_broadcast(&mut boxed_rng)
}

fn make_normal_broadcast(&self, rng: &mut dyn CryptoRngCore) -> Result<NormalBroadcast, LocalError> {
let mut boxed_rng = BoxedRng(rng);
self.round.make_normal_broadcast(&mut boxed_rng)
}

fn receive_message(
&self,
rng: &mut dyn CryptoRngCore,
from: &Id,
echo_broadcast: EchoBroadcast,
normal_broadcast: NormalBroadcast,
direct_message: DirectMessage,
) -> Result<Payload, ReceiveError<Id, Self::Protocol>> {
let mut boxed_rng = BoxedRng(rng);
self.round
.receive_message(&mut boxed_rng, from, echo_broadcast, direct_message)
.receive_message(&mut boxed_rng, from, echo_broadcast, normal_broadcast, direct_message)
}

fn finalize(
Expand Down
56 changes: 51 additions & 5 deletions manul/src/protocol/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use super::{
errors::{
DeserializationError, FinalizeError, LocalError, MessageValidationError, ProtocolValidationError, ReceiveError,
},
message::{DirectMessage, EchoBroadcast, ProtocolMessage},
message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessage},
object_safe::{ObjectSafeRound, ObjectSafeRoundWrapper},
};

Expand Down Expand Up @@ -144,7 +144,7 @@ pub trait Protocol: Debug + Sized {
#[allow(unused_variables)] message: &DirectMessage,
) -> Result<(), MessageValidationError> {
Err(MessageValidationError::InvalidEvidence(format!(
"There are no direct messages in {round_id:?}"
"Invalid round number: {round_id:?}"
)))
}

Expand All @@ -157,7 +157,20 @@ pub trait Protocol: Debug + Sized {
#[allow(unused_variables)] message: &EchoBroadcast,
) -> Result<(), MessageValidationError> {
Err(MessageValidationError::InvalidEvidence(format!(
"There are no echo broadcasts in {round_id:?}"
"Invalid round number: {round_id:?}"
)))
}

/// Returns `Ok(())` if the given echo broadcast cannot be deserialized
/// assuming it is an echo broadcast from the round `round_id`.
///
/// Normally one would use [`EchoBroadcast::verify_is_not`] when implementing this.
fn verify_normal_broadcast_is_invalid(
round_id: RoundId,
#[allow(unused_variables)] message: &NormalBroadcast,
) -> Result<(), MessageValidationError> {
Err(MessageValidationError::InvalidEvidence(format!(
"Invalid round number: {round_id:?}"
)))
}
}
Expand All @@ -181,6 +194,13 @@ pub trait ProtocolError: Debug + Clone + Send {
BTreeSet::new()
}

/// The rounds normal broadcasts from which are required to prove malicious behavior for this error.
///
/// **Note:** Should not include the round where the error happened.
fn required_normal_broadcasts(&self) -> BTreeSet<RoundId> {
BTreeSet::new()
}

/// The rounds combined echos from which are required to prove malicious behavior for this error.
///
/// **Note:** Should not include the round where the error happened.
Expand All @@ -203,11 +223,14 @@ pub trait ProtocolError: Debug + Clone + Send {
/// [`required_echo_broadcasts`](`Self::required_echo_broadcasts`).
/// `combined_echos` are bundled echos from other parties from the previous rounds,
/// as requested by [`required_combined_echos`](`Self::required_combined_echos`).
#[allow(clippy::too_many_arguments)]
fn verify_messages_constitute_error(
&self,
echo_broadcast: &EchoBroadcast,
normal_broadcast: &NormalBroadcast,
direct_message: &DirectMessage,
echo_broadcasts: &BTreeMap<RoundId, EchoBroadcast>,
normal_broadcasts: &BTreeMap<RoundId, NormalBroadcast>,
direct_messages: &BTreeMap<RoundId, DirectMessage>,
combined_echos: &BTreeMap<RoundId, Vec<EchoBroadcast>>,
) -> Result<(), ProtocolValidationError>;
Expand Down Expand Up @@ -342,6 +365,7 @@ pub trait Round<Id>: 'static + Send + Sync + Debug {
/// only via [`make_direct_message_with_artifact`](`Self::make_direct_message_with_artifact`).
///
/// Return [`DirectMessage::none`] if this round does not send direct messages.
/// This is also the blanket implementation.
fn make_direct_message(
&self,
#[allow(unused_variables)] rng: &mut impl CryptoRngCore,
Expand All @@ -350,9 +374,10 @@ pub trait Round<Id>: 'static + Send + Sync + Debug {
Ok(DirectMessage::none())
}

/// Returns the echo broadcast for this round, or `None` if the round does not require echo-broadcasting.
/// Returns the echo broadcast for this round.
///
/// Returns `None` if not implemented.
/// Return [`EchoBroadcast::none`] if this round does not send echo-broadcast messages.
/// This is also the blanket implementation.
///
/// The execution layer will guarantee that all the destinations are sure they all received the same broadcast.
/// This also means that a message with the broadcasts from all nodes signed by each node is available
Expand All @@ -364,6 +389,20 @@ pub trait Round<Id>: 'static + Send + Sync + Debug {
Ok(EchoBroadcast::none())
}

/// Returns the normal broadcast for this round.
///
/// Return [`NormalBroadcast::none`] if this round does not send normal broadcast messages.
/// This is also the blanket implementation.
///
/// Unlike the echo broadcasts, these will be just sent to every node from [`Self::message_destinations`]
/// without any confirmation required.
fn make_normal_broadcast(
&self,
#[allow(unused_variables)] rng: &mut impl CryptoRngCore,
) -> Result<NormalBroadcast, LocalError> {
Ok(NormalBroadcast::none())
}

/// Processes the received message and generates the payload that will be used in [`finalize`](`Self::finalize`).
///
/// Note that there is no need to authenticate the message at this point;
Expand All @@ -373,6 +412,7 @@ pub trait Round<Id>: 'static + Send + Sync + Debug {
rng: &mut impl CryptoRngCore,
from: &Id,
echo_broadcast: EchoBroadcast,
normal_broadcast: NormalBroadcast,
direct_message: DirectMessage,
) -> Result<Payload, ReceiveError<Id, Self::Protocol>>;

Expand Down Expand Up @@ -400,6 +440,12 @@ pub trait Round<Id>: 'static + Send + Sync + Debug {
EchoBroadcast::new::<Self::Protocol, _>(message)
}

/// A convenience method to create a [`NormalBroadcast`] object
/// to return in [`make_normal_broadcast`](`Self::make_normal_broadcast`).
fn serialize_normal_broadcast(message: impl Serialize) -> Result<NormalBroadcast, LocalError> {
NormalBroadcast::new::<Self::Protocol, _>(message)
}

/// A convenience method to create a [`DirectMessage`] object
/// to return in [`make_direct_message`](`Self::make_direct_message`).
fn serialize_direct_message(message: impl Serialize) -> Result<DirectMessage, LocalError> {
Expand Down
Loading

0 comments on commit 7e80c65

Please sign in to comment.