Skip to content

Commit

Permalink
Simplify the processing of mismatched broadcasts in the echo round
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Oct 28, 2024
1 parent 02e0bd2 commit f1b2b3c
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 105 deletions.
40 changes: 29 additions & 11 deletions manul/src/session/echo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,23 @@ pub(crate) enum EchoRoundError<Id> {
/// to speed up the verification process.
InvalidEcho(Id),
/// The originally received message and the one received in the echo pack were both valid,
/// but had different payload.
/// but different.
///
/// This is the fault of the sender of that specific broadcast.
/// The attached identifier points out the that sender.
InvalidBroadcast(Id),
/// The originally received message and the one received in the echo pack were both valid,
/// have the same payload, but different (albeit valid) signatures.
///
/// This is the fault of the sender of that specific broadcast.
/// The attached identifier points out the that sender.
TwiceSigned(Id),
MismatchedBroadcasts {
guilty_party: Id,
error: MismatchedBroadcastsError,
we_received: SignedMessage<EchoBroadcast>,
echoed_to_us: SignedMessage<EchoBroadcast>,
},
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub(crate) enum MismatchedBroadcastsError {
/// The originally received message and the echoed one had different payloads.
DifferentPayloads,
/// The originally received message and the echoed one had different signatures.
DifferentSignatures,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -225,13 +231,25 @@ where
// `sender` sent us and `from` messages with different payloads.
// Provable fault of `sender`.
if verified_echo.payload() != previously_received_echo.payload() {
return Err(EchoRoundError::InvalidBroadcast(sender.clone()).into());
return Err(EchoRoundError::MismatchedBroadcasts {
guilty_party: sender.clone(),
error: MismatchedBroadcastsError::DifferentPayloads,
we_received: previously_received_echo.clone(),
echoed_to_us: echo.clone(),
}
.into());
}

// At this point, we know that the echoed broadcast is not identical to what we initially received,
// but somehow they both have the correct metadata, and correct signatures.
// Something strange is going on.
return Err(EchoRoundError::TwiceSigned(sender.clone()).into());
return Err(EchoRoundError::MismatchedBroadcasts {
guilty_party: sender.clone(),
error: MismatchedBroadcastsError::DifferentSignatures,
we_received: previously_received_echo.clone(),
echoed_to_us: echo.clone(),
}
.into());
}

Ok(Payload::empty())
Expand Down
110 changes: 17 additions & 93 deletions manul/src/session/evidence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::fmt::Debug;
use serde::{Deserialize, Serialize};

use super::{
echo::{EchoRoundError, EchoRoundMessage},
echo::{EchoRoundError, EchoRoundMessage, MismatchedBroadcastsError},
message::{MessageVerificationError, MissingMessage, SignedMessage},
session::SessionParameters,
transcript::Transcript,
Expand Down Expand Up @@ -151,7 +151,6 @@ where
verifier: &SP::Verifier,
normal_broadcast: SignedMessage<NormalBroadcast>,
error: EchoRoundError<SP::Verifier>,
transcript: &Transcript<P, SP>,
) -> Result<Self, LocalError> {
let description = format!("{:?}", error);
match error {
Expand All @@ -164,64 +163,21 @@ where
phantom: core::marker::PhantomData,
}),
}),
EchoRoundError::InvalidBroadcast(from) => {
// We could avoid all this if we attached the SignedMessage objects
// directly to the error. But then it would have to be generic over `S`,
// which the `Round` trait knows nothing about.
let round_id = normal_broadcast.metadata().round_id().non_echo();
let we_received = transcript.get_echo_broadcast(round_id, &from)?;

let deserialized = normal_broadcast
.payload()
.deserialize::<P, EchoRoundMessage<SP>>()
.map_err(|error| {
LocalError::new(format!("Failed to deserialize the given direct message: {:?}", error))
})?;
let echoed_to_us = deserialized.echo_broadcasts.get(&from).ok_or_else(|| {
LocalError::new(format!(
"The echo message from {from:?} is missing from the echo packet"
))
})?;

Ok(Self {
guilty_party: from,
description,
evidence: EvidenceEnum::MismatchedBroadcasts(MismatchedBroadcastsEvidence {
we_received,
echoed_to_us: echoed_to_us.clone(),
phantom: core::marker::PhantomData,
}),
})
}
EchoRoundError::TwiceSigned(from) => {
// We could avoid all this if we attached the SignedMessage objects
// directly to the error. But then it would have to be generic over `S`,
// which the `Round` trait knows nothing about.
let round_id = normal_broadcast.metadata().round_id().non_echo();
let we_received = transcript.get_echo_broadcast(round_id, &from)?;

let deserialized = normal_broadcast
.payload()
.deserialize::<P, EchoRoundMessage<SP>>()
.map_err(|error| {
LocalError::new(format!("Failed to deserialize the given direct message: {:?}", error))
})?;
let echoed_to_us = deserialized.echo_broadcasts.get(&from).ok_or_else(|| {
LocalError::new(format!(
"The echo message from {from:?} is missing from the echo packet"
))
})?;

Ok(Self {
guilty_party: from,
description,
evidence: EvidenceEnum::TwiceSignedBroadcasts(TwiceSignedBroadcastsEvidence {
we_received,
echoed_to_us: echoed_to_us.clone(),
phantom: core::marker::PhantomData,
}),
})
}
EchoRoundError::MismatchedBroadcasts {
guilty_party,
error,
we_received,
echoed_to_us,
} => Ok(Self {
guilty_party,
description,
evidence: EvidenceEnum::MismatchedBroadcasts(MismatchedBroadcastsEvidence {
error,
we_received,
echoed_to_us,
phantom: core::marker::PhantomData,
}),
}),
}
}

Expand Down Expand Up @@ -286,7 +242,6 @@ where
EvidenceEnum::InvalidNormalBroadcast(evidence) => evidence.verify::<SP>(party),
EvidenceEnum::InvalidEchoPack(evidence) => evidence.verify(party),
EvidenceEnum::MismatchedBroadcasts(evidence) => evidence.verify::<SP>(party),
EvidenceEnum::TwiceSignedBroadcasts(evidence) => evidence.verify::<SP>(party),
}
}
}
Expand All @@ -299,7 +254,6 @@ enum EvidenceEnum<P: Protocol, SP: SessionParameters> {
InvalidNormalBroadcast(InvalidNormalBroadcastEvidence<P>),
InvalidEchoPack(InvalidEchoPackEvidence<P, SP>),
MismatchedBroadcasts(MismatchedBroadcastsEvidence<P>),
TwiceSignedBroadcasts(TwiceSignedBroadcastsEvidence<P>),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -349,6 +303,7 @@ where

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MismatchedBroadcastsEvidence<P: Protocol> {
error: MismatchedBroadcastsError,
we_received: SignedMessage<EchoBroadcast>,
echoed_to_us: SignedMessage<EchoBroadcast>,
phantom: core::marker::PhantomData<P>,
Expand All @@ -375,37 +330,6 @@ where
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TwiceSignedBroadcastsEvidence<P: Protocol> {
we_received: SignedMessage<EchoBroadcast>,
echoed_to_us: SignedMessage<EchoBroadcast>,
phantom: core::marker::PhantomData<P>,
}

impl<P> TwiceSignedBroadcastsEvidence<P>
where
P: Protocol,
{
fn verify<SP>(&self, verifier: &SP::Verifier) -> Result<(), EvidenceError>
where
SP: SessionParameters,
{
let we_received = self.we_received.clone().verify::<P, SP>(verifier)?;
let echoed_to_us = self.echoed_to_us.clone().verify::<P, SP>(verifier)?;

if self.we_received != self.echoed_to_us
&& we_received.metadata() == echoed_to_us.metadata()
&& we_received.payload() == echoed_to_us.payload()
{
return Ok(());
}

Err(EvidenceError::InvalidEvidence(
"The attached messages don't constitute malicious behavior".into(),
))
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvalidDirectMessageEvidence<P: Protocol> {
direct_message: SignedMessage<DirectMessage>,
Expand Down
2 changes: 1 addition & 1 deletion manul/src/session/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ where
}
ReceiveErrorType::Echo(error) => {
let (_echo_broadcast, normal_broadcast, _direct_message) = processed.message.into_parts();
let evidence = Evidence::new_echo_round_error(&from, normal_broadcast, error, transcript)?;
let evidence = Evidence::new_echo_round_error(&from, normal_broadcast, error)?;
self.register_provable_error(&from, evidence)
}
ReceiveErrorType::Local(error) => Err(error),
Expand Down

0 comments on commit f1b2b3c

Please sign in to comment.