Skip to content

Commit 959da23

Browse files
committed
Splitting error processing between rounds
1 parent c172784 commit 959da23

File tree

10 files changed

+210
-15
lines changed

10 files changed

+210
-15
lines changed

examples/dining_cryptographers.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ use manul::{
5656
dev::{run_sync, BinaryFormat, TestHasher, TestSignature, TestSigner, TestVerifier},
5757
protocol::{
5858
BoxedRound, BoxedRoundInfo, CommunicationInfo, EchoRoundParticipation, EntryPoint, FinalizeOutcome, LocalError,
59-
NoMessage, NoProtocolErrors, Protocol, ReceiveError, RoundId, StaticProtocolMessage, StaticRound,
60-
TransitionInfo,
59+
NoMessage, NoProtocolErrors, NoProvableErrors, Protocol, ReceiveError, RoundId, StaticProtocolMessage,
60+
StaticRound, TransitionInfo,
6161
},
6262
session::SessionParameters,
6363
};
@@ -77,6 +77,7 @@ impl Protocol<DinerId> for DiningCryptographersProtocol {
7777
// XOR/¬XOR of the two bits of each of the three diners (one is their own cointoss, the other shared with their
7878
// neighbour).
7979
type Result = (bool, bool, bool);
80+
type SharedData = ();
8081

8182
type ProtocolError = NoProtocolErrors;
8283

@@ -109,6 +110,7 @@ pub struct Round2 {
109110

110111
impl StaticRound<DinerId> for Round1 {
111112
type Protocol = DiningCryptographersProtocol;
113+
type ProvableError = NoProvableErrors<Self>;
112114

113115
type DirectMessage = Round1Message;
114116
type EchoBroadcast = NoMessage;
@@ -201,6 +203,7 @@ impl StaticRound<DinerId> for Round1 {
201203

202204
impl StaticRound<DinerId> for Round2 {
203205
type Protocol = DiningCryptographersProtocol;
206+
type ProvableError = NoProvableErrors<Self>;
204207

205208
type DirectMessage = NoMessage;
206209
type EchoBroadcast = NoMessage;

examples/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
extern crate alloc;
22

33
pub mod simple;
4-
pub mod simple_chain;
4+
//pub mod simple_chain;
55

66
#[cfg(test)]
77
mod simple_test;

examples/src/simple.rs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use alloc::collections::{BTreeMap, BTreeSet};
22
use core::fmt::Debug;
33

44
use manul::protocol::{
5-
BoxedFormat, BoxedRound, BoxedRoundInfo, CommunicationInfo, EchoBroadcast, EntryPoint, FinalizeOutcome, LocalError,
6-
NoMessage, PartyId, Protocol, ProtocolError, ProtocolMessage, ProtocolMessagePart, ProtocolValidationError,
7-
ReceiveError, RequiredMessageParts, RequiredMessages, RoundId, StaticProtocolMessage, StaticRound, TransitionInfo,
5+
BoxedFormat, BoxedRound, BoxedRoundInfo, CommunicationInfo, EchoBroadcast, EntryPoint, EvidenceMessages,
6+
FinalizeOutcome, LocalError, NoMessage, PartyId, Protocol, ProtocolError, ProtocolMessage, ProtocolMessagePart,
7+
ProtocolValidationError, ProvableError, ReceiveError, RequiredMessageParts, RequiredMessages, RoundId,
8+
StaticProtocolMessage, StaticRound, TransitionInfo,
89
};
910
use rand_core::CryptoRngCore;
1011
use serde::{Deserialize, Serialize};
@@ -22,6 +23,53 @@ pub enum SimpleProtocolError {
2223
Round2InvalidPosition,
2324
}
2425

26+
#[derive(displaydoc::Display, Debug, Clone, Copy, Serialize, Deserialize)]
27+
pub(crate) struct Round1ProvableError;
28+
29+
impl<Id: PartyId> ProvableError<Id> for Round1ProvableError {
30+
type Round = Round1<Id>;
31+
fn required_previous_messages(&self) -> RequiredMessages {
32+
RequiredMessages::new(RequiredMessageParts::direct_message(), None, None)
33+
}
34+
fn verify_evidence(
35+
&self,
36+
_from: &Id,
37+
_shared_randomness: &[u8],
38+
_shared_data: &<<Self::Round as StaticRound<Id>>::Protocol as Protocol<Id>>::SharedData,
39+
messages: EvidenceMessages<Id, Self::Round>,
40+
) -> std::result::Result<(), ProtocolValidationError> {
41+
let _message: Round1Message = messages.direct_message()?;
42+
// Message contents would be checked here
43+
Ok(())
44+
}
45+
}
46+
47+
#[derive(displaydoc::Display, Debug, Clone, Copy, Serialize, Deserialize)]
48+
pub(crate) struct Round2ProvableError;
49+
50+
impl<Id: PartyId> ProvableError<Id> for Round2ProvableError {
51+
type Round = Round2<Id>;
52+
fn required_previous_messages(&self) -> RequiredMessages {
53+
RequiredMessages::new(
54+
RequiredMessageParts::direct_message(),
55+
Some([(1.into(), RequiredMessageParts::direct_message())].into()),
56+
Some([1.into()].into()),
57+
)
58+
}
59+
fn verify_evidence(
60+
&self,
61+
_from: &Id,
62+
_shared_randomness: &[u8],
63+
_shared_data: &<<Self::Round as StaticRound<Id>>::Protocol as Protocol<Id>>::SharedData,
64+
messages: EvidenceMessages<Id, Self::Round>,
65+
) -> std::result::Result<(), ProtocolValidationError> {
66+
let _r2_message: Round2Message = messages.direct_message()?;
67+
let _r1_echos: BTreeMap<Id, Round1Echo> = messages.combined_echos::<Round1<Id>>(1)?;
68+
// Message contents would be checked here
69+
Ok(())
70+
}
71+
}
72+
2573
impl<Id> ProtocolError<Id> for SimpleProtocolError {
2674
type AssociatedData = ();
2775

@@ -73,6 +121,7 @@ impl<Id> ProtocolError<Id> for SimpleProtocolError {
73121

74122
impl<Id: PartyId> Protocol<Id> for SimpleProtocol {
75123
type Result = u8;
124+
type SharedData = ();
76125
type ProtocolError = SimpleProtocolError;
77126
fn round_info(round_id: &RoundId) -> Option<BoxedRoundInfo<Id, Self>> {
78127
match round_id {
@@ -164,6 +213,7 @@ impl<Id: PartyId> EntryPoint<Id> for SimpleProtocolEntryPoint<Id> {
164213

165214
impl<Id: PartyId> StaticRound<Id> for Round1<Id> {
166215
type Protocol = SimpleProtocol;
216+
type ProvableError = Round1ProvableError;
167217

168218
fn transition_info(&self) -> TransitionInfo {
169219
TransitionInfo::new_linear(1)
@@ -259,6 +309,7 @@ pub(crate) struct Round2Message {
259309

260310
impl<Id: PartyId> StaticRound<Id> for Round2<Id> {
261311
type Protocol = SimpleProtocol;
312+
type ProvableError = Round2ProvableError;
262313

263314
fn transition_info(&self) -> TransitionInfo {
264315
TransitionInfo::new_linear_terminating(2)

manul/src/combinators.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
//! Combinators operating on protocols.
22
3-
pub mod chain;
3+
//pub mod chain;
44
pub mod extend;

manul/src/combinators/extend.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ use dyn_clone::DynClone;
77
use rand_core::CryptoRngCore;
88

99
use crate::protocol::{
10-
Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EntryPoint, FinalizeOutcome,
11-
LocalError, NormalBroadcast, PartyId, Payload, Protocol, ProtocolMessage, ReceiveError, Round, RoundId,
12-
StaticProtocolMessage, StaticRound, StaticRoundAdapter, TransitionInfo,
10+
Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EntryPoint, EvidenceMessages,
11+
FinalizeOutcome, LocalError, NormalBroadcast, PartyId, Payload, Protocol, ProtocolMessage, ProtocolValidationError,
12+
ProvableError, ReceiveError, RequiredMessages, Round, RoundId, StaticProtocolMessage, StaticRound,
13+
StaticRoundAdapter, TransitionInfo,
1314
};
1415

1516
pub trait Extension<Id>: 'static + Debug + Send + Sync + Clone {
@@ -57,6 +58,26 @@ pub trait Extension<Id>: 'static + Debug + Send + Sync + Clone {
5758
}
5859
}
5960

61+
#[derive_where::derive_where(Debug, Clone, Serialize, Deserialize)]
62+
struct ExtendedProvableError<Id, Ext: Extension<Id>>(<Ext::Round as StaticRound<Id>>::ProvableError);
63+
64+
impl<Id: PartyId, Ext: Extension<Id>> ProvableError<Id> for ExtendedProvableError<Id, Ext> {
65+
type Round = ExtendedRound<Id, Ext>;
66+
fn required_previous_messages(&self) -> RequiredMessages {
67+
self.0.required_previous_messages()
68+
}
69+
fn verify_evidence(
70+
&self,
71+
from: &Id,
72+
shared_randomness: &[u8],
73+
shared_data: &<<Self::Round as StaticRound<Id>>::Protocol as Protocol<Id>>::SharedData,
74+
messages: EvidenceMessages<Id, Self::Round>,
75+
) -> Result<(), ProtocolValidationError> {
76+
let messages = messages.into_round::<Ext::Round>();
77+
self.0.verify_evidence(from, shared_randomness, shared_data, messages)
78+
}
79+
}
80+
6081
#[allow(clippy::type_complexity)]
6182
#[derive_where::derive_where(Debug)]
6283
struct ExtendedRound<Id, Ext: Extension<Id>> {
@@ -71,6 +92,7 @@ where
7192
Ext: Extension<Id>,
7293
{
7394
type Protocol = <Ext::Round as StaticRound<Id>>::Protocol;
95+
type ProvableError = ExtendedProvableError<Id, Ext>;
7496

7597
type DirectMessage = <Ext::Round as StaticRound<Id>>::DirectMessage;
7698
type NormalBroadcast = <Ext::Round as StaticRound<Id>>::NormalBroadcast;

manul/src/protocol.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ pub use round::{
3333
};
3434
pub use round_id::{RoundId, TransitionInfo};
3535
pub use round_info::BoxedRoundInfo;
36-
pub use static_round::{NoMessage, StaticProtocolMessage, StaticRound};
36+
pub use static_round::{
37+
EvidenceMessages, NoMessage, NoProvableErrors, ProvableError, StaticProtocolMessage, StaticRound,
38+
};
3739

3840
pub(crate) use errors::ReceiveErrorType;
3941
pub(crate) use message::ProtocolMessagePartHashable;

manul/src/protocol/round.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ pub trait Protocol<Id>: 'static + Sized {
7171
/// The successful result of an execution of this protocol.
7272
type Result: Debug;
7373

74+
type SharedData: Debug;
75+
7476
/// An object of this type will be returned when a provable error happens during [`Round::receive_message`].
7577
type ProtocolError: ProtocolError<Id>;
7678

manul/src/protocol/static_round.rs

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ use serde::{Deserialize, Serialize};
88

99
use super::{
1010
boxed_format::BoxedFormat,
11-
errors::{LocalError, ReceiveError},
11+
errors::{LocalError, ProtocolValidationError, ReceiveError},
1212
message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessage, ProtocolMessagePart},
1313
round::{
14-
Artifact, CommunicationInfo, DynTypeId, FinalizeOutcome, PartyId, Payload, Protocol, ProtocolError, Round,
14+
Artifact, CommunicationInfo, DynTypeId, FinalizeOutcome, PartyId, Payload, Protocol, ProtocolError,
15+
RequiredMessages, Round,
1516
},
1617
round_id::{RoundId, TransitionInfo},
1718
};
@@ -48,6 +49,8 @@ pub trait StaticRound<Id>: 'static + Debug + Send + Sync + DynTypeId {
4849
/// The protocol this round is a part of.
4950
type Protocol: Protocol<Id>;
5051

52+
type ProvableError: ProvableError<Id, Round = Self>;
53+
5154
/// Returns the information about the position of this round in the state transition graph.
5255
///
5356
/// See [`TransitionInfo`] documentation for more details.
@@ -295,3 +298,112 @@ where
295298
self.round.finalize(rng, payloads, artifacts)
296299
}
297300
}
301+
302+
/// Describes provable errors originating during protocol execution.
303+
///
304+
/// Provable here means that we can create an evidence object entirely of messages signed by some party,
305+
/// which, in combination, prove the party's malicious actions.
306+
pub trait ProvableError<Id>: Debug + Clone + Serialize + for<'de> Deserialize<'de> {
307+
type Round: StaticRound<Id>;
308+
309+
/// Specifies the messages of the guilty party that need to be stored as the evidence
310+
/// to prove its malicious behavior.
311+
fn required_previous_messages(&self) -> RequiredMessages;
312+
313+
/// Returns `Ok(())` if the attached messages indeed prove that a malicious action happened.
314+
///
315+
/// The signatures and metadata of the messages will be checked by the calling code,
316+
/// the responsibility of this method is just to check the message contents.
317+
///
318+
/// `message` contain the message parts that triggered the error
319+
/// during [`Round::receive_message`].
320+
///
321+
/// `previous_messages` are message parts from the previous rounds, as requested by
322+
/// [`required_messages`](Self::required_messages).
323+
///
324+
/// Note that if some message part was not requested by above methods, it will be set to an empty one
325+
/// in the [`ProtocolMessage`], even if it was present originally.
326+
///
327+
/// `combined_echos` are bundled echos from other parties from the previous rounds,
328+
/// as requested by [`required_messages`](Self::required_messages).
329+
fn verify_evidence(
330+
&self,
331+
from: &Id,
332+
shared_randomness: &[u8],
333+
shared_data: &<<Self::Round as StaticRound<Id>>::Protocol as Protocol<Id>>::SharedData,
334+
messages: EvidenceMessages<Id, Self::Round>,
335+
) -> Result<(), ProtocolValidationError>;
336+
}
337+
338+
#[derive(Debug)]
339+
pub struct EvidenceMessages<Id, R: StaticRound<Id>> {
340+
message: ProtocolMessage,
341+
previous_messages: BTreeMap<RoundId, ProtocolMessage>,
342+
combined_echos: BTreeMap<RoundId, BTreeMap<Id, EchoBroadcast>>,
343+
format: BoxedFormat,
344+
phantom: PhantomData<R>,
345+
}
346+
347+
impl<Id, R: StaticRound<Id>> EvidenceMessages<Id, R> {
348+
pub fn previous_echo_broadcast<PR: StaticRound<Id>>(
349+
&self,
350+
round_num: u8,
351+
) -> Result<PR::EchoBroadcast, ProtocolValidationError> {
352+
Ok(self
353+
.previous_messages
354+
.get(&RoundId::new(round_num))
355+
.unwrap()
356+
.echo_broadcast
357+
.deserialize::<PR::EchoBroadcast>(&self.format)
358+
.unwrap())
359+
}
360+
361+
pub fn combined_echos<PR: StaticRound<Id>>(
362+
&self,
363+
round_num: u8,
364+
) -> Result<BTreeMap<Id, PR::EchoBroadcast>, ProtocolValidationError> {
365+
todo!()
366+
}
367+
368+
pub fn direct_message(&self) -> Result<R::DirectMessage, ProtocolValidationError> {
369+
todo!()
370+
}
371+
372+
pub(crate) fn into_round<NR>(self) -> EvidenceMessages<Id, NR>
373+
where
374+
NR: StaticRound<
375+
Id,
376+
EchoBroadcast = R::EchoBroadcast,
377+
NormalBroadcast = R::NormalBroadcast,
378+
DirectMessage = R::DirectMessage,
379+
>,
380+
{
381+
EvidenceMessages::<Id, NR> {
382+
message: self.message,
383+
previous_messages: self.previous_messages,
384+
combined_echos: self.combined_echos,
385+
format: self.format,
386+
phantom: PhantomData,
387+
}
388+
}
389+
}
390+
391+
#[derive_where::derive_where(Clone)]
392+
#[derive(Debug, Copy, Serialize, Deserialize)]
393+
pub struct NoProvableErrors<R>(PhantomData<R>);
394+
395+
impl<Id: PartyId, R: StaticRound<Id>> ProvableError<Id> for NoProvableErrors<R> {
396+
type Round = R;
397+
fn required_previous_messages(&self) -> RequiredMessages {
398+
unimplemented!()
399+
}
400+
fn verify_evidence(
401+
&self,
402+
from: &Id,
403+
shared_randomness: &[u8],
404+
shared_data: &<<Self::Round as StaticRound<Id>>::Protocol as Protocol<Id>>::SharedData,
405+
messages: EvidenceMessages<Id, Self::Round>,
406+
) -> Result<(), ProtocolValidationError> {
407+
unimplemented!()
408+
}
409+
}

manul/src/session/session.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,7 @@ mod tests {
835835

836836
impl Protocol<TestVerifier> for DummyProtocol {
837837
type Result = ();
838+
type SharedData = ();
838839
type ProtocolError = NoProtocolErrors;
839840
fn round_info(_round_id: &RoundId) -> Option<BoxedRoundInfo<TestVerifier, Self>> {
840841
unimplemented!()

manul/src/tests/partial_echo.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::{
1212
dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier},
1313
protocol::{
1414
BoxedRound, BoxedRoundInfo, CommunicationInfo, EchoRoundParticipation, EntryPoint, FinalizeOutcome, LocalError,
15-
NoMessage, NoProtocolErrors, PartyId, Protocol, ReceiveError, RoundId, StaticProtocolMessage, StaticRound,
16-
TransitionInfo,
15+
NoMessage, NoProtocolErrors, NoProvableErrors, PartyId, Protocol, ReceiveError, RoundId, StaticProtocolMessage,
16+
StaticRound, TransitionInfo,
1717
},
1818
signature::Keypair,
1919
};
@@ -23,6 +23,7 @@ struct PartialEchoProtocol<Id>(PhantomData<Id>);
2323

2424
impl<Id: PartyId> Protocol<Id> for PartialEchoProtocol<Id> {
2525
type Result = ();
26+
type SharedData = ();
2627
type ProtocolError = NoProtocolErrors;
2728

2829
fn round_info(round_id: &RoundId) -> Option<BoxedRoundInfo<Id, Self>> {
@@ -70,6 +71,7 @@ impl<Id: PartyId + Serialize + for<'de> Deserialize<'de>> EntryPoint<Id> for Inp
7071

7172
impl<Id: PartyId + Serialize + for<'de> Deserialize<'de>> StaticRound<Id> for Round1<Id> {
7273
type Protocol = PartialEchoProtocol<Id>;
74+
type ProvableError = NoProvableErrors<Self>;
7375

7476
type DirectMessage = NoMessage;
7577
type NormalBroadcast = NoMessage;

0 commit comments

Comments
 (0)