From 8f91c691b7ee996c49b08c8064af62874a354cf2 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Sun, 20 Oct 2024 22:15:16 -0700 Subject: [PATCH 1/2] Supply the format from the session level --- examples/src/format.rs | 21 ++++++++ examples/src/lib.rs | 3 ++ examples/src/simple.rs | 65 +++++++++++------------ examples/src/simple_malicious.rs | 40 +++++++++------ examples/tests/async_runner.rs | 9 ++-- manul/Cargo.toml | 1 + manul/benches/empty_rounds.rs | 45 ++++++++++------ manul/src/protocol.rs | 1 + manul/src/protocol/message.rs | 19 ++++--- manul/src/protocol/object_safe.rs | 45 ++++++++++++---- manul/src/protocol/round.rs | 39 ++++---------- manul/src/session.rs | 5 +- manul/src/session/echo.rs | 16 +++--- manul/src/session/evidence.rs | 80 +++++++++++++---------------- manul/src/session/message.rs | 44 ++++++++-------- manul/src/session/session.rs | 85 +++++++++++++++++++++++++------ manul/src/testing/identity.rs | 7 +-- manul/src/testing/macros.rs | 44 +++++++++++----- 18 files changed, 352 insertions(+), 217 deletions(-) create mode 100644 examples/src/format.rs diff --git a/examples/src/format.rs b/examples/src/format.rs new file mode 100644 index 0000000..5f73e99 --- /dev/null +++ b/examples/src/format.rs @@ -0,0 +1,21 @@ +use manul::{ + protocol::{DeserializationError, LocalError}, + session::Format, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug)] +pub struct Bincode; + +impl Format for Bincode { + fn serialize(value: T) -> Result, LocalError> { + bincode::serde::encode_to_vec(value, bincode::config::standard()) + .map(|vec| vec.into()) + .map_err(|err| LocalError::new(err.to_string())) + } + + fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result { + bincode::serde::decode_borrowed_from_slice(bytes, bincode::config::standard()) + .map_err(|err| DeserializationError::new(err.to_string())) + } +} diff --git a/examples/src/lib.rs b/examples/src/lib.rs index c3aff76..066da06 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -1,6 +1,9 @@ extern crate alloc; +mod format; pub mod simple; #[cfg(test)] mod simple_malicious; + +pub use format::Bincode; diff --git a/examples/src/simple.rs b/examples/src/simple.rs index 40aa96d..dd2f724 100644 --- a/examples/src/simple.rs +++ b/examples/src/simple.rs @@ -40,6 +40,7 @@ impl ProtocolError for SimpleProtocolError { fn verify_messages_constitute_error( &self, + deserializer: &Deserializer, _echo_broadcast: &EchoBroadcast, _normal_broadcast: &NormalBroadcast, direct_message: &DirectMessage, @@ -50,12 +51,12 @@ impl ProtocolError for SimpleProtocolError { ) -> Result<(), ProtocolValidationError> { match self { SimpleProtocolError::Round1InvalidPosition => { - let _message = direct_message.deserialize::()?; + let _message = direct_message.deserialize::(deserializer)?; // Message contents would be checked here Ok(()) } SimpleProtocolError::Round2InvalidPosition => { - let _r1_message = direct_message.deserialize::()?; + let _r1_message = direct_message.deserialize::(deserializer)?; let r1_echos_serialized = combined_echos .get(&RoundId::new(1)) .ok_or_else(|| LocalError::new("Could not find combined echos for Round 1"))?; @@ -63,7 +64,7 @@ impl ProtocolError for SimpleProtocolError { // Deserialize the echos let _r1_echos = r1_echos_serialized .iter() - .map(|echo| echo.deserialize::()) + .map(|echo| echo.deserialize::(deserializer)) .collect::, _>>()?; // Message contents would be checked here @@ -78,35 +79,26 @@ impl Protocol for SimpleProtocol { type ProtocolError = SimpleProtocolError; type CorrectnessProof = (); - fn serialize(value: T) -> Result, LocalError> { - bincode::serde::encode_to_vec(value, bincode::config::standard()) - .map(|vec| vec.into()) - .map_err(|err| LocalError::new(err.to_string())) - } - - fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result { - bincode::serde::decode_borrowed_from_slice(bytes, bincode::config::standard()) - .map_err(|err| DeserializationError::new(err.to_string())) - } - fn verify_direct_message_is_invalid( + deserializer: &Deserializer, round_id: RoundId, message: &DirectMessage, ) -> Result<(), MessageValidationError> { match round_id { - r if r == RoundId::new(1) => message.verify_is_not::(), - r if r == RoundId::new(2) => message.verify_is_not::(), + r if r == RoundId::new(1) => message.verify_is_not::(deserializer), + r if r == RoundId::new(2) => message.verify_is_not::(deserializer), _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), } } fn verify_echo_broadcast_is_invalid( + deserializer: &Deserializer, round_id: RoundId, message: &EchoBroadcast, ) -> Result<(), MessageValidationError> { match round_id { r if r == RoundId::new(1) => message.verify_is_some(), - r if r == RoundId::new(2) => message.verify_is_not::(), + r if r == RoundId::new(2) => message.verify_is_not::(deserializer), _ => Err(MessageValidationError::InvalidEvidence("Invalid round number".into())), } } @@ -195,7 +187,11 @@ impl Round for Round1 { &self.context.other_ids } - fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + fn make_normal_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + serializer: &Serializer, + ) -> Result { debug!("{:?}: making normal broadcast", self.context.id); let message = Round1Broadcast { @@ -203,22 +199,27 @@ impl Round for Round1 { my_position: self.context.ids_to_positions[&self.context.id], }; - Self::serialize_normal_broadcast(message) + NormalBroadcast::new(serializer, message) } - fn make_echo_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + serializer: &Serializer, + ) -> Result { debug!("{:?}: making echo broadcast", self.context.id); let message = Round1Echo { my_position: self.context.ids_to_positions[&self.context.id], }; - Self::serialize_echo_broadcast(message) + EchoBroadcast::new(serializer, message) } fn make_direct_message( &self, _rng: &mut impl CryptoRngCore, + serializer: &Serializer, destination: &Id, ) -> Result { debug!("{:?}: making direct message for {:?}", self.context.id, destination); @@ -227,13 +228,13 @@ impl Round for Round1 { my_position: self.context.ids_to_positions[&self.context.id], your_position: self.context.ids_to_positions[destination], }; - let dm = Self::serialize_direct_message(message)?; - Ok(dm) + DirectMessage::new(serializer, message) } fn receive_message( &self, _rng: &mut impl CryptoRngCore, + deserializer: &Deserializer, from: &Id, echo_broadcast: EchoBroadcast, normal_broadcast: NormalBroadcast, @@ -241,9 +242,9 @@ impl Round for Round1 { ) -> Result> { debug!("{:?}: receiving message from {:?}", self.context.id, from); - let _echo = echo_broadcast.deserialize::()?; - let _normal = normal_broadcast.deserialize::()?; - let message = direct_message.deserialize::()?; + let _echo = echo_broadcast.deserialize::(deserializer)?; + let _normal = normal_broadcast.deserialize::(deserializer)?; + let message = direct_message.deserialize::(deserializer)?; debug!("{:?}: received message: {:?}", self.context.id, message); @@ -312,11 +313,10 @@ impl Round for Round2 { &self.context.other_ids } - // Does not send echo broadcasts - fn make_direct_message( &self, _rng: &mut impl CryptoRngCore, + serializer: &Serializer, destination: &Id, ) -> Result { debug!("{:?}: making direct message for {:?}", self.context.id, destination); @@ -325,13 +325,13 @@ impl Round for Round2 { my_position: self.context.ids_to_positions[&self.context.id], your_position: self.context.ids_to_positions[destination], }; - let dm = Self::serialize_direct_message(message)?; - Ok(dm) + DirectMessage::new(serializer, message) } fn receive_message( &self, _rng: &mut impl CryptoRngCore, + deserializer: &Deserializer, from: &Id, echo_broadcast: EchoBroadcast, normal_broadcast: NormalBroadcast, @@ -342,7 +342,7 @@ impl Round for Round2 { echo_broadcast.assert_is_none()?; normal_broadcast.assert_is_none()?; - let message = direct_message.deserialize::()?; + let message = direct_message.deserialize::(deserializer)?; debug!("{:?}: received message: {:?}", self.context.id, message); @@ -397,6 +397,7 @@ mod tests { use tracing_subscriber::EnvFilter; use super::{Inputs, Round1}; + use crate::Bincode; #[test] fn round() { @@ -421,7 +422,7 @@ mod tests { .with_env_filter(EnvFilter::from_default_env()) .finish(); let reports = tracing::subscriber::with_default(my_subscriber, || { - run_sync::, TestSessionParams>(&mut OsRng, inputs).unwrap() + run_sync::, TestSessionParams>(&mut OsRng, inputs).unwrap() }); for (_id, report) in reports { diff --git a/examples/src/simple_malicious.rs b/examples/src/simple_malicious.rs index 6977889..c4a4b46 100644 --- a/examples/src/simple_malicious.rs +++ b/examples/src/simple_malicious.rs @@ -6,13 +6,16 @@ use manul::{ Artifact, DirectMessage, FinalizeError, FinalizeOutcome, FirstRound, LocalError, Payload, ProtocolMessagePart, Round, }, - session::signature::Keypair, + session::{signature::Keypair, Serializer}, testing::{round_override, run_sync, RoundOverride, RoundWrapper, TestSessionParams, TestSigner, TestVerifier}, }; use rand_core::{CryptoRngCore, OsRng}; use tracing_subscriber::EnvFilter; -use crate::simple::{Inputs, Round1, Round1Message, Round2, Round2Message}; +use crate::{ + simple::{Inputs, Round1, Round1Message, Round2, Round2Message}, + Bincode, +}; #[derive(Debug, Clone, Copy)] enum Behavior { @@ -60,19 +63,22 @@ impl FirstRound for Malicio } impl RoundOverride for MaliciousRound1 { - fn make_direct_message(&self, rng: &mut impl CryptoRngCore, destination: &Id) -> Result { + fn make_direct_message( + &self, + rng: &mut impl CryptoRngCore, + serializer: &Serializer, + destination: &Id, + ) -> Result { if matches!(self.behavior, Behavior::SerializedGarbage) { - let dm = DirectMessage::new::<>::Protocol, _>(&[99u8]).unwrap(); - Ok(dm) + DirectMessage::new(serializer, [99u8]) } else if matches!(self.behavior, Behavior::AttributableFailure) { let message = Round1Message { my_position: self.round.context.ids_to_positions[&self.round.context.id], your_position: self.round.context.ids_to_positions[&self.round.context.id], }; - let dm = DirectMessage::new::<>::Protocol, _>(&message)?; - Ok(dm) + DirectMessage::new(serializer, &message) } else { - self.inner_round_ref().make_direct_message(rng, destination) + self.inner_round_ref().make_direct_message(rng, serializer, destination) } } @@ -120,16 +126,20 @@ impl RoundWrapper for Malic } impl RoundOverride for MaliciousRound2 { - fn make_direct_message(&self, rng: &mut impl CryptoRngCore, destination: &Id) -> Result { + fn make_direct_message( + &self, + rng: &mut impl CryptoRngCore, + serializer: &Serializer, + destination: &Id, + ) -> Result { if matches!(self.behavior, Behavior::AttributableFailureRound2) { let message = Round2Message { my_position: self.round.context.ids_to_positions[&self.round.context.id], your_position: self.round.context.ids_to_positions[&self.round.context.id], }; - let dm = DirectMessage::new::<>::Protocol, _>(&message)?; - Ok(dm) + DirectMessage::new(serializer, &message) } else { - self.inner_round_ref().make_direct_message(rng, destination) + self.inner_round_ref().make_direct_message(rng, serializer, destination) } } } @@ -167,7 +177,7 @@ fn serialized_garbage() { .with_env_filter(EnvFilter::from_default_env()) .finish(); let mut reports = tracing::subscriber::with_default(my_subscriber, || { - run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() + run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() }); let v0 = signers[0].verifying_key(); @@ -213,7 +223,7 @@ fn attributable_failure() { .with_env_filter(EnvFilter::from_default_env()) .finish(); let mut reports = tracing::subscriber::with_default(my_subscriber, || { - run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() + run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() }); let v0 = signers[0].verifying_key(); @@ -259,7 +269,7 @@ fn attributable_failure_round2() { .with_env_filter(EnvFilter::from_default_env()) .finish(); let mut reports = tracing::subscriber::with_default(my_subscriber, || { - run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() + run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() }); let v0 = signers[0].verifying_key(); diff --git a/examples/tests/async_runner.rs b/examples/tests/async_runner.rs index 7e52af0..b56bfb1 100644 --- a/examples/tests/async_runner.rs +++ b/examples/tests/async_runner.rs @@ -12,7 +12,10 @@ use manul::{ }, testing::{TestSessionParams, TestSigner}, }; -use manul_example::simple::{Inputs, Round1, SimpleProtocol}; +use manul_example::{ + simple::{Inputs, Round1, SimpleProtocol}, + Bincode, +}; use rand::Rng; use rand_core::OsRng; use tokio::{ @@ -241,7 +244,7 @@ where #[tokio::test] async fn async_run() { // The kind of Session we need to run the `SimpleProtocol`. - type SimpleSession = Session; + type SimpleSession = Session>; // Create 4 parties let signers = (0..3).map(TestSigner::new).collect::>(); @@ -249,7 +252,7 @@ async fn async_run() { .iter() .map(|signer| signer.verifying_key()) .collect::>(); - let session_id = SessionId::random::(&mut OsRng); + let session_id = SessionId::random::>(&mut OsRng); // Create 4 `Session`s let sessions = signers diff --git a/manul/Cargo.toml b/manul/Cargo.toml index 7f1c8f2..a181f28 100644 --- a/manul/Cargo.toml +++ b/manul/Cargo.toml @@ -19,6 +19,7 @@ rand_core = { version = "0.6.4", default-features = false } tracing = { version = "0.1", default-features = false } displaydoc = { version = "0.2", default-features = false } rand = { version = "0.8", default-features = false, optional = true } +bincode = { version = "2.0.0-rc.3", default-features = false, features = ["alloc", "serde"] } [dev-dependencies] impls = "1" diff --git a/manul/benches/empty_rounds.rs b/manul/benches/empty_rounds.rs index 632ad32..38a4663 100644 --- a/manul/benches/empty_rounds.rs +++ b/manul/benches/empty_rounds.rs @@ -10,7 +10,7 @@ use manul::{ LocalError, NormalBroadcast, Payload, Protocol, ProtocolError, ProtocolMessagePart, ProtocolValidationError, ReceiveError, Round, RoundId, }, - session::{signature::Keypair, SessionOutcome}, + session::{signature::Keypair, Deserializer, Format, Serializer, SessionOutcome}, testing::{run_sync, TestSessionParams, TestSigner, TestVerifier}, }; use rand_core::{CryptoRngCore, OsRng}; @@ -25,6 +25,7 @@ pub struct EmptyProtocolError; impl ProtocolError for EmptyProtocolError { fn verify_messages_constitute_error( &self, + _deserializer: &Deserializer, _echo_broadcast: &EchoBroadcast, _normal_broadcast: &NormalBroadcast, _direct_message: &DirectMessage, @@ -37,11 +38,10 @@ impl ProtocolError for EmptyProtocolError { } } -impl Protocol for EmptyProtocol { - type Result = (); - type ProtocolError = EmptyProtocolError; - type CorrectnessProof = (); +#[derive(Debug)] +pub struct Bincode; +impl Format for Bincode { fn serialize(value: T) -> Result, LocalError> { bincode::serde::encode_to_vec(value, bincode::config::standard()) .map(|vec| vec.into()) @@ -54,6 +54,12 @@ impl Protocol for EmptyProtocol { } } +impl Protocol for EmptyProtocol { + type Result = (); + type ProtocolError = EmptyProtocolError; + type CorrectnessProof = (); +} + #[derive(Debug)] struct EmptyRound { round_counter: u8, @@ -111,9 +117,13 @@ impl Round for EmptyRound Result { + fn make_echo_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + serializer: &Serializer, + ) -> Result { if self.inputs.echo { - Self::serialize_echo_broadcast(Round1EchoBroadcast) + EchoBroadcast::new(serializer, Round1EchoBroadcast) } else { Ok(EchoBroadcast::none()) } @@ -122,9 +132,10 @@ impl Round for EmptyRound Result<(DirectMessage, Option), LocalError> { - let dm = Self::serialize_direct_message(Round1DirectMessage)?; + let dm = DirectMessage::new(serializer, Round1DirectMessage)?; let artifact = Artifact::new(Round1Artifact); Ok((dm, Some(artifact))) } @@ -132,18 +143,19 @@ impl Round for EmptyRound Result> { if self.inputs.echo { - let _echo_broadcast = echo_broadcast.deserialize::()?; + let _echo_broadcast = echo_broadcast.deserialize::(deserializer)?; } else { echo_broadcast.assert_is_none()?; } normal_broadcast.assert_is_none()?; - let _direct_message = direct_message.deserialize::()?; + let _direct_message = direct_message.deserialize::(deserializer)?; Ok(Payload::new(Round1Payload)) } @@ -211,12 +223,13 @@ fn bench_empty_rounds(c: &mut Criterion) { group.bench_function("25 nodes, 5 rounds, no echo", |b| { b.iter(|| { - assert!( - run_sync::, TestSessionParams>(&mut OsRng, inputs_no_echo.clone()) - .unwrap() - .values() - .all(|report| matches!(report.outcome, SessionOutcome::Result(_))) + assert!(run_sync::, TestSessionParams>( + &mut OsRng, + inputs_no_echo.clone() ) + .unwrap() + .values() + .all(|report| matches!(report.outcome, SessionOutcome::Result(_)))) }) }); @@ -242,7 +255,7 @@ fn bench_empty_rounds(c: &mut Criterion) { group.bench_function("25 nodes, 5 rounds, echo each round", |b| { b.iter(|| { assert!( - run_sync::, TestSessionParams>(&mut OsRng, inputs_echo.clone()) + run_sync::, TestSessionParams>(&mut OsRng, inputs_echo.clone()) .unwrap() .values() .all(|report| matches!(report.outcome, SessionOutcome::Result(_))) diff --git a/manul/src/protocol.rs b/manul/src/protocol.rs index f4bc469..82971c0 100644 --- a/manul/src/protocol.rs +++ b/manul/src/protocol.rs @@ -16,6 +16,7 @@ mod message; mod object_safe; mod round; +pub use crate::session::{Deserializer, Serializer, SessionId}; pub use errors::{ DeserializationError, DirectMessageError, EchoBroadcastError, FinalizeError, LocalError, MessageValidationError, NormalBroadcastError, ProtocolValidationError, ReceiveError, RemoteError, diff --git a/manul/src/protocol/message.rs b/manul/src/protocol/message.rs index 0e3201b..7475c45 100644 --- a/manul/src/protocol/message.rs +++ b/manul/src/protocol/message.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use super::{ errors::{DirectMessageError, EchoBroadcastError, LocalError, MessageValidationError, NormalBroadcastError}, - round::Protocol, + Deserializer, Serializer, }; mod private { @@ -43,8 +43,8 @@ pub trait ProtocolMessagePart: ProtocolMessageWrapper { } /// Creates a new serialized message. - fn new(message: T) -> Result { - let payload = MessagePayload(P::serialize(message)?); + fn new(serializer: &Serializer, message: T) -> Result { + let payload = MessagePayload(serializer.serialize(message)?); Ok(Self::new_inner(Some(payload))) } @@ -68,8 +68,11 @@ pub trait ProtocolMessagePart: ProtocolMessageWrapper { /// /// This is intended to be used in the implementations of /// [`Protocol::verify_direct_message_is_invalid`] or [`Protocol::verify_echo_broadcast_is_invalid`]. - fn verify_is_not Deserialize<'de>>(&self) -> Result<(), MessageValidationError> { - if self.deserialize::().is_err() { + fn verify_is_not Deserialize<'de>>( + &self, + deserializer: &Deserializer, + ) -> Result<(), MessageValidationError> { + if self.deserialize::(deserializer).is_err() { Ok(()) } else { Err(MessageValidationError::InvalidEvidence( @@ -93,12 +96,14 @@ pub trait ProtocolMessagePart: ProtocolMessageWrapper { } /// Deserializes the message into `T`. - fn deserialize Deserialize<'de>>(&self) -> Result { + fn deserialize Deserialize<'de>>(&self, deserializer: &Deserializer) -> Result { let payload = self .maybe_message() .as_ref() .ok_or_else(|| "The payload is `None` and cannot be deserialized".into())?; - P::deserialize(&payload.0).map_err(|err| err.to_string().into()) + deserializer + .deserialize(&payload.0) + .map_err(|err| err.to_string().into()) } } diff --git a/manul/src/protocol/object_safe.rs b/manul/src/protocol/object_safe.rs index dfa09de..9693be6 100644 --- a/manul/src/protocol/object_safe.rs +++ b/manul/src/protocol/object_safe.rs @@ -12,6 +12,7 @@ use super::{ message::{DirectMessage, EchoBroadcast, NormalBroadcast}, round::{Artifact, FinalizeOutcome, Payload, Protocol, Round, RoundId}, }; +use crate::session::{Deserializer, Serializer}; /// Since object-safe trait methods cannot take `impl CryptoRngCore` arguments, /// this structure wraps the dynamic object and exposes a `CryptoRngCore` interface, @@ -50,16 +51,26 @@ pub(crate) trait ObjectSafeRound: 'static + Send + Sync + Debug { fn make_direct_message_with_artifact( &self, rng: &mut dyn CryptoRngCore, + serializer: &Serializer, destination: &Id, ) -> Result<(DirectMessage, Option), LocalError>; - fn make_echo_broadcast(&self, rng: &mut dyn CryptoRngCore) -> Result; + fn make_echo_broadcast( + &self, + rng: &mut dyn CryptoRngCore, + serializer: &Serializer, + ) -> Result; - fn make_normal_broadcast(&self, rng: &mut dyn CryptoRngCore) -> Result; + fn make_normal_broadcast( + &self, + rng: &mut dyn CryptoRngCore, + serializer: &Serializer, + ) -> Result; fn receive_message( &self, rng: &mut dyn CryptoRngCore, + deserializer: &Deserializer, from: &Id, echo_broadcast: EchoBroadcast, normal_broadcast: NormalBroadcast, @@ -118,34 +129,50 @@ where fn make_direct_message_with_artifact( &self, rng: &mut dyn CryptoRngCore, + serializer: &Serializer, destination: &Id, ) -> Result<(DirectMessage, Option), LocalError> { let mut boxed_rng = BoxedRng(rng); self.round - .make_direct_message_with_artifact(&mut boxed_rng, destination) + .make_direct_message_with_artifact(&mut boxed_rng, serializer, destination) } - fn make_echo_broadcast(&self, rng: &mut dyn CryptoRngCore) -> Result { + fn make_echo_broadcast( + &self, + rng: &mut dyn CryptoRngCore, + serializer: &Serializer, + ) -> Result { let mut boxed_rng = BoxedRng(rng); - self.round.make_echo_broadcast(&mut boxed_rng) + self.round.make_echo_broadcast(&mut boxed_rng, serializer) } - fn make_normal_broadcast(&self, rng: &mut dyn CryptoRngCore) -> Result { + fn make_normal_broadcast( + &self, + rng: &mut dyn CryptoRngCore, + serializer: &Serializer, + ) -> Result { let mut boxed_rng = BoxedRng(rng); - self.round.make_normal_broadcast(&mut boxed_rng) + self.round.make_normal_broadcast(&mut boxed_rng, serializer) } fn receive_message( &self, rng: &mut dyn CryptoRngCore, + deserializer: &Deserializer, from: &Id, echo_broadcast: EchoBroadcast, normal_broadcast: NormalBroadcast, direct_message: DirectMessage, ) -> Result> { let mut boxed_rng = BoxedRng(rng); - self.round - .receive_message(&mut boxed_rng, from, echo_broadcast, normal_broadcast, direct_message) + self.round.receive_message( + &mut boxed_rng, + deserializer, + from, + echo_broadcast, + normal_broadcast, + direct_message, + ) } fn finalize( diff --git a/manul/src/protocol/round.rs b/manul/src/protocol/round.rs index 4b96a3a..db1d761 100644 --- a/manul/src/protocol/round.rs +++ b/manul/src/protocol/round.rs @@ -10,12 +10,11 @@ use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; use super::{ - errors::{ - DeserializationError, FinalizeError, LocalError, MessageValidationError, ProtocolValidationError, ReceiveError, - }, + errors::{FinalizeError, LocalError, MessageValidationError, ProtocolValidationError, ReceiveError}, message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessagePart}, object_safe::{ObjectSafeRound, ObjectSafeRoundWrapper}, }; +use crate::session::{Deserializer, Serializer}; /// Possible successful outcomes of [`Round::finalize`]. #[derive(Debug)] @@ -129,17 +128,12 @@ pub trait Protocol: Debug + Sized { /// It proves that the node did its job correctly, to be adjudicated by a third party. type CorrectnessProof: Send + Serialize + for<'de> Deserialize<'de> + Debug; - /// Serializes the given object into a bytestring. - fn serialize(value: T) -> Result, LocalError>; - - /// Tries to deserialize the given bytestring as an object of type `T`. - fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result; - /// Returns `Ok(())` if the given direct message cannot be deserialized /// assuming it is a direct message from the round `round_id`. /// /// Normally one would use [`DirectMessage::verify_is_not`] when implementing this. fn verify_direct_message_is_invalid( + #[allow(unused_variables)] deserializer: &Deserializer, round_id: RoundId, #[allow(unused_variables)] message: &DirectMessage, ) -> Result<(), MessageValidationError> { @@ -153,6 +147,7 @@ pub trait Protocol: Debug + Sized { /// /// Normally one would use [`EchoBroadcast::verify_is_not`] when implementing this. fn verify_echo_broadcast_is_invalid( + #[allow(unused_variables)] deserializer: &Deserializer, round_id: RoundId, #[allow(unused_variables)] message: &EchoBroadcast, ) -> Result<(), MessageValidationError> { @@ -226,6 +221,7 @@ pub trait ProtocolError: Debug + Clone + Send { #[allow(clippy::too_many_arguments)] fn verify_messages_constitute_error( &self, + deserializer: &Deserializer, echo_broadcast: &EchoBroadcast, normal_broadcast: &NormalBroadcast, direct_message: &DirectMessage, @@ -354,9 +350,10 @@ pub trait Round: 'static + Send + Sync + Debug { fn make_direct_message_with_artifact( &self, rng: &mut impl CryptoRngCore, + serializer: &Serializer, destination: &Id, ) -> Result<(DirectMessage, Option), LocalError> { - Ok((self.make_direct_message(rng, destination)?, None)) + Ok((self.make_direct_message(rng, serializer, destination)?, None)) } /// Returns the direct message to the given destination. @@ -369,6 +366,7 @@ pub trait Round: 'static + Send + Sync + Debug { fn make_direct_message( &self, #[allow(unused_variables)] rng: &mut impl CryptoRngCore, + #[allow(unused_variables)] serializer: &Serializer, #[allow(unused_variables)] destination: &Id, ) -> Result { Ok(DirectMessage::none()) @@ -385,6 +383,7 @@ pub trait Round: 'static + Send + Sync + Debug { fn make_echo_broadcast( &self, #[allow(unused_variables)] rng: &mut impl CryptoRngCore, + #[allow(unused_variables)] serializer: &Serializer, ) -> Result { Ok(EchoBroadcast::none()) } @@ -399,6 +398,7 @@ pub trait Round: 'static + Send + Sync + Debug { fn make_normal_broadcast( &self, #[allow(unused_variables)] rng: &mut impl CryptoRngCore, + #[allow(unused_variables)] serializer: &Serializer, ) -> Result { Ok(NormalBroadcast::none()) } @@ -410,6 +410,7 @@ pub trait Round: 'static + Send + Sync + Debug { fn receive_message( &self, rng: &mut impl CryptoRngCore, + deserializer: &Deserializer, from: &Id, echo_broadcast: EchoBroadcast, normal_broadcast: NormalBroadcast, @@ -433,22 +434,4 @@ pub trait Round: 'static + Send + Sync + Debug { /// The execution layer will not call [`finalize`](`Self::finalize`) until all these nodes have responded /// (and the corresponding [`receive_message`](`Self::receive_message`) finished successfully). fn expecting_messages_from(&self) -> &BTreeSet; - - /// A convenience method to create an [`EchoBroadcast`] object - /// to return in [`make_echo_broadcast`](`Self::make_echo_broadcast`). - fn serialize_echo_broadcast(message: impl Serialize) -> Result { - EchoBroadcast::new::(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::new::(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::new::(message) - } } diff --git a/manul/src/session.rs b/manul/src/session.rs index db7c68f..cf90d77 100644 --- a/manul/src/session.rs +++ b/manul/src/session.rs @@ -16,7 +16,10 @@ mod transcript; pub use crate::protocol::{LocalError, RemoteError}; pub use message::MessageBundle; -pub use session::{CanFinalize, RoundAccumulator, RoundOutcome, Session, SessionId, SessionParameters}; +pub use session::{ + CanFinalize, Deserializer, Format, RoundAccumulator, RoundOutcome, Serializer, Session, SessionId, + SessionParameters, +}; pub use transcript::{SessionOutcome, SessionReport}; pub(crate) use echo::EchoRoundError; diff --git a/manul/src/session/echo.rs b/manul/src/session/echo.rs index e04ccf4..37d0b25 100644 --- a/manul/src/session/echo.rs +++ b/manul/src/session/echo.rs @@ -12,7 +12,7 @@ use tracing::debug; use super::{ message::{MessageVerificationError, SignedMessage}, - session::SessionParameters, + session::{Deserializer, Serializer, SessionParameters}, LocalError, }; use crate::{ @@ -126,7 +126,11 @@ where &self.destinations } - fn make_normal_broadcast(&self, _rng: &mut impl CryptoRngCore) -> Result { + fn make_normal_broadcast( + &self, + _rng: &mut impl CryptoRngCore, + serializer: &Serializer, + ) -> Result { debug!("{:?}: making an echo round message", self.verifier); // Don't send our own message the second time @@ -141,8 +145,7 @@ where let message = EchoRoundMessage:: { echo_broadcasts: echo_broadcasts.into(), }; - let bc = NormalBroadcast::new::(&message)?; - Ok(bc) + NormalBroadcast::new(serializer, &message) } fn expecting_messages_from(&self) -> &BTreeSet { @@ -152,6 +155,7 @@ where fn receive_message( &self, _rng: &mut impl CryptoRngCore, + deserializer: &Deserializer, from: &SP::Verifier, echo_broadcast: EchoBroadcast, normal_broadcast: NormalBroadcast, @@ -162,7 +166,7 @@ where echo_broadcast.assert_is_none()?; direct_message.assert_is_none()?; - let message = normal_broadcast.deserialize::>()?; + let message = normal_broadcast.deserialize::>(deserializer)?; // Check that the received message contains entries from `destinations` sans `from` // It is an unprovable fault. @@ -209,7 +213,7 @@ where continue; } - let verified_echo = match echo.clone().verify::(sender) { + let verified_echo = match echo.clone().verify::(sender) { Ok(echo) => echo, Err(MessageVerificationError::Local(error)) => return Err(error.into()), // This means `from` sent us an incorrectly signed message. diff --git a/manul/src/session/evidence.rs b/manul/src/session/evidence.rs index 7474582..e574bc1 100644 --- a/manul/src/session/evidence.rs +++ b/manul/src/session/evidence.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use super::{ echo::{EchoRoundError, EchoRoundMessage, MismatchedBroadcastsError}, message::{MessageVerificationError, MissingMessage, SignedMessage}, - session::SessionParameters, + session::{Deserializer, SessionParameters}, transcript::Transcript, LocalError, }; @@ -160,7 +160,6 @@ where evidence: EvidenceEnum::InvalidEchoPack(InvalidEchoPackEvidence { normal_broadcast, invalid_echo_sender: from, - phantom: core::marker::PhantomData, }), }), EchoRoundError::MismatchedBroadcasts { @@ -175,7 +174,6 @@ where error, we_received, echoed_to_us, - phantom: core::marker::PhantomData, }), }), } @@ -236,11 +234,11 @@ where pub fn verify(&self, party: &SP::Verifier) -> Result<(), EvidenceError> { match &self.evidence { - EvidenceEnum::Protocol(evidence) => evidence.verify::(party), - EvidenceEnum::InvalidDirectMessage(evidence) => evidence.verify::(party), - EvidenceEnum::InvalidEchoBroadcast(evidence) => evidence.verify::(party), + EvidenceEnum::Protocol(evidence) => evidence.verify::(party, &Deserializer), + EvidenceEnum::InvalidDirectMessage(evidence) => evidence.verify::(party, &Deserializer), + EvidenceEnum::InvalidEchoBroadcast(evidence) => evidence.verify::(party, &Deserializer), EvidenceEnum::InvalidNormalBroadcast(evidence) => evidence.verify::(party), - EvidenceEnum::InvalidEchoPack(evidence) => evidence.verify(party), + EvidenceEnum::InvalidEchoPack(evidence) => evidence.verify(party, &Deserializer), EvidenceEnum::MismatchedBroadcasts(evidence) => evidence.verify::(party), } } @@ -252,25 +250,23 @@ enum EvidenceEnum { InvalidDirectMessage(InvalidDirectMessageEvidence

), InvalidEchoBroadcast(InvalidEchoBroadcastEvidence

), InvalidNormalBroadcast(InvalidNormalBroadcastEvidence

), - InvalidEchoPack(InvalidEchoPackEvidence), - MismatchedBroadcasts(MismatchedBroadcastsEvidence

), + InvalidEchoPack(InvalidEchoPackEvidence), + MismatchedBroadcasts(MismatchedBroadcastsEvidence), } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct InvalidEchoPackEvidence { +pub struct InvalidEchoPackEvidence { normal_broadcast: SignedMessage, invalid_echo_sender: SP::Verifier, - phantom: core::marker::PhantomData

, } -impl InvalidEchoPackEvidence +impl InvalidEchoPackEvidence where - P: Protocol, SP: SessionParameters, { - fn verify(&self, verifier: &SP::Verifier) -> Result<(), EvidenceError> { - let verified = self.normal_broadcast.clone().verify::(verifier)?; - let deserialized = verified.payload().deserialize::>()?; + fn verify(&self, verifier: &SP::Verifier, deserializer: &Deserializer) -> Result<(), EvidenceError> { + let verified = self.normal_broadcast.clone().verify::(verifier)?; + let deserialized = verified.payload().deserialize::>(deserializer)?; let invalid_echo = deserialized .echo_broadcasts .get(&self.invalid_echo_sender) @@ -281,7 +277,7 @@ where )) })?; - let verified_echo = match invalid_echo.clone().verify::(&self.invalid_echo_sender) { + let verified_echo = match invalid_echo.clone().verify::(&self.invalid_echo_sender) { Ok(echo) => echo, Err(MessageVerificationError::Local(error)) => return Err(EvidenceError::Local(error)), // The message was indeed incorrectly signed - fault proven @@ -302,23 +298,19 @@ where } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MismatchedBroadcastsEvidence { +pub struct MismatchedBroadcastsEvidence { error: MismatchedBroadcastsError, we_received: SignedMessage, echoed_to_us: SignedMessage, - phantom: core::marker::PhantomData

, } -impl

MismatchedBroadcastsEvidence

-where - P: Protocol, -{ +impl MismatchedBroadcastsEvidence { fn verify(&self, verifier: &SP::Verifier) -> Result<(), EvidenceError> where SP: SessionParameters, { - let we_received = self.we_received.clone().verify::(verifier)?; - let echoed_to_us = self.echoed_to_us.clone().verify::(verifier)?; + let we_received = self.we_received.clone().verify::(verifier)?; + let echoed_to_us = self.echoed_to_us.clone().verify::(verifier)?; if we_received.metadata() == echoed_to_us.metadata() && we_received.payload() != echoed_to_us.payload() { return Ok(()); @@ -340,12 +332,13 @@ impl

InvalidDirectMessageEvidence

where P: Protocol, { - fn verify(&self, verifier: &SP::Verifier) -> Result<(), EvidenceError> + fn verify(&self, verifier: &SP::Verifier, deserializer: &Deserializer) -> Result<(), EvidenceError> where SP: SessionParameters, { - let verified_direct_message = self.direct_message.clone().verify::(verifier)?; + let verified_direct_message = self.direct_message.clone().verify::(verifier)?; Ok(P::verify_direct_message_is_invalid( + deserializer, self.direct_message.metadata().round_id(), verified_direct_message.payload(), )?) @@ -362,12 +355,13 @@ impl

InvalidEchoBroadcastEvidence

where P: Protocol, { - fn verify(&self, verifier: &SP::Verifier) -> Result<(), EvidenceError> + fn verify(&self, verifier: &SP::Verifier, deserializer: &Deserializer) -> Result<(), EvidenceError> where SP: SessionParameters, { - let verified_echo_broadcast = self.echo_broadcast.clone().verify::(verifier)?; + let verified_echo_broadcast = self.echo_broadcast.clone().verify::(verifier)?; Ok(P::verify_echo_broadcast_is_invalid( + deserializer, self.echo_broadcast.metadata().round_id(), verified_echo_broadcast.payload(), )?) @@ -388,7 +382,7 @@ where where SP: SessionParameters, { - let verified_normal_broadcast = self.normal_broadcast.clone().verify::(verifier)?; + let verified_normal_broadcast = self.normal_broadcast.clone().verify::(verifier)?; Ok(P::verify_normal_broadcast_is_invalid( self.normal_broadcast.metadata().round_id(), verified_normal_broadcast.payload(), @@ -412,17 +406,17 @@ impl

ProtocolEvidence

where P: Protocol, { - fn verify(&self, verifier: &SP::Verifier) -> Result<(), EvidenceError> + fn verify(&self, verifier: &SP::Verifier, deserializer: &Deserializer) -> Result<(), EvidenceError> where SP: SessionParameters, { let session_id = self.direct_message.metadata().session_id(); - let verified_direct_message = self.direct_message.clone().verify::(verifier)?.payload().clone(); + let verified_direct_message = self.direct_message.clone().verify::(verifier)?.payload().clone(); let mut verified_direct_messages = BTreeMap::new(); for (round_id, direct_message) in self.direct_messages.iter() { - let verified_direct_message = direct_message.clone().verify::(verifier)?; + let verified_direct_message = direct_message.clone().verify::(verifier)?; let metadata = verified_direct_message.metadata(); if metadata.session_id() != session_id || metadata.round_id() != *round_id { return Err(EvidenceError::InvalidEvidence( @@ -432,7 +426,7 @@ where verified_direct_messages.insert(*round_id, verified_direct_message.payload().clone()); } - let verified_echo_broadcast = self.echo_broadcast.clone().verify::(verifier)?.payload().clone(); + let verified_echo_broadcast = self.echo_broadcast.clone().verify::(verifier)?.payload().clone(); if self.echo_broadcast.metadata().session_id() != session_id || self.echo_broadcast.metadata().round_id() != self.direct_message.metadata().round_id() { @@ -441,12 +435,7 @@ where )); } - let verified_normal_broadcast = self - .normal_broadcast - .clone() - .verify::(verifier)? - .payload() - .clone(); + let verified_normal_broadcast = self.normal_broadcast.clone().verify::(verifier)?.payload().clone(); if self.normal_broadcast.metadata().session_id() != session_id || self.normal_broadcast.metadata().round_id() != self.direct_message.metadata().round_id() { @@ -457,7 +446,7 @@ where let mut verified_echo_broadcasts = BTreeMap::new(); for (round_id, echo_broadcast) in self.echo_broadcasts.iter() { - let verified_echo_broadcast = echo_broadcast.clone().verify::(verifier)?; + let verified_echo_broadcast = echo_broadcast.clone().verify::(verifier)?; let metadata = verified_echo_broadcast.metadata(); if metadata.session_id() != session_id || metadata.round_id() != *round_id { return Err(EvidenceError::InvalidEvidence( @@ -469,7 +458,7 @@ where let mut verified_normal_broadcasts = BTreeMap::new(); for (round_id, normal_broadcast) in self.normal_broadcasts.iter() { - let verified_normal_broadcast = normal_broadcast.clone().verify::(verifier)?; + let verified_normal_broadcast = normal_broadcast.clone().verify::(verifier)?; let metadata = verified_normal_broadcast.metadata(); if metadata.session_id() != session_id || metadata.round_id() != *round_id { return Err(EvidenceError::InvalidEvidence( @@ -481,7 +470,7 @@ where let mut combined_echos = BTreeMap::new(); for (round_id, combined_echo) in self.combined_echos.iter() { - let verified_combined_echo = combined_echo.clone().verify::(verifier)?; + let verified_combined_echo = combined_echo.clone().verify::(verifier)?; let metadata = verified_combined_echo.metadata(); if metadata.session_id() != session_id || metadata.round_id().non_echo() != *round_id { return Err(EvidenceError::InvalidEvidence( @@ -490,11 +479,11 @@ where } let echo_set = verified_combined_echo .payload() - .deserialize::>()?; + .deserialize::>(deserializer)?; let mut verified_echo_set = Vec::new(); for (other_verifier, echo_broadcast) in echo_set.echo_broadcasts.iter() { - let verified_echo_broadcast = echo_broadcast.clone().verify::(other_verifier)?; + let verified_echo_broadcast = echo_broadcast.clone().verify::(other_verifier)?; let metadata = verified_echo_broadcast.metadata(); if metadata.session_id() != session_id || metadata.round_id() != *round_id { return Err(EvidenceError::InvalidEvidence( @@ -507,6 +496,7 @@ where } Ok(self.error.verify_messages_constitute_error( + deserializer, &verified_echo_broadcast, &verified_normal_broadcast, &verified_direct_message, diff --git a/manul/src/session/message.rs b/manul/src/session/message.rs index 425dccc..09144c9 100644 --- a/manul/src/session/message.rs +++ b/manul/src/session/message.rs @@ -7,29 +7,27 @@ use serde_encoded_bytes::{Hex, SliceLike}; use signature::{DigestVerifier, RandomizedDigestSigner}; use super::{ - session::{SessionId, SessionParameters}, + session::{Format, SessionId, SessionParameters}, LocalError, }; -use crate::protocol::{DeserializationError, DirectMessage, EchoBroadcast, NormalBroadcast, Protocol, RoundId}; +use crate::protocol::{DeserializationError, DirectMessage, EchoBroadcast, NormalBroadcast, RoundId}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub(crate) struct SerializedSignature(#[serde(with = "SliceLike::")] Box<[u8]>); impl SerializedSignature { - pub fn new(signature: &SP::Signature) -> Result + pub fn new(signature: &SP::Signature) -> Result where - P: Protocol, SP: SessionParameters, { - P::serialize(signature).map(Self) + SP::Format::serialize(signature).map(Self) } - pub fn deserialize(&self) -> Result + pub fn deserialize(&self) -> Result where - P: Protocol, SP: SessionParameters, { - P::deserialize::(&self.0) + SP::Format::deserialize::(&self.0) } } @@ -81,7 +79,7 @@ impl SignedMessage where M: Serialize, { - pub fn new( + pub fn new( rng: &mut impl CryptoRngCore, signer: &SP::Signer, session_id: &SessionId, @@ -89,18 +87,17 @@ where message: M, ) -> Result where - P: Protocol, SP: SessionParameters, { let metadata = MessageMetadata::new(session_id, round_id); let message_with_metadata = MessageWithMetadata { metadata, message }; - let message_bytes = P::serialize(&message_with_metadata)?; + let message_bytes = SP::Format::serialize(&message_with_metadata)?; let digest = SP::Digest::new_with_prefix(b"SignedMessage").chain_update(message_bytes); let signature = signer .try_sign_digest_with_rng(rng, digest) .map_err(|err| LocalError::new(format!("Failed to sign: {:?}", err)))?; Ok(Self { - signature: SerializedSignature::new::(&signature)?, + signature: SerializedSignature::new::(&signature)?, message_with_metadata, }) } @@ -113,16 +110,16 @@ where &self.message_with_metadata.message } - pub(crate) fn verify(self, verifier: &SP::Verifier) -> Result, MessageVerificationError> + pub(crate) fn verify(self, verifier: &SP::Verifier) -> Result, MessageVerificationError> where - P: Protocol, SP: SessionParameters, { - let message_bytes = P::serialize(&self.message_with_metadata).map_err(MessageVerificationError::Local)?; + let message_bytes = + SP::Format::serialize(&self.message_with_metadata).map_err(MessageVerificationError::Local)?; let digest = SP::Digest::new_with_prefix(b"SignedMessage").chain_update(message_bytes); let signature = self .signature - .deserialize::() + .deserialize::() .map_err(|_| MessageVerificationError::InvalidSignature)?; if verifier.verify_digest(digest, &signature).is_ok() { Ok(VerifiedMessage { @@ -174,7 +171,7 @@ pub struct MessageBundle { } impl MessageBundle { - pub(crate) fn new( + pub(crate) fn new( rng: &mut impl CryptoRngCore, signer: &SP::Signer, session_id: &SessionId, @@ -184,10 +181,9 @@ impl MessageBundle { normal_broadcast: SignedMessage, ) -> Result where - P: Protocol, SP: SessionParameters, { - let direct_message = SignedMessage::new::(rng, signer, session_id, round_id, direct_message)?; + let direct_message = SignedMessage::new::(rng, signer, session_id, round_id, direct_message)?; Ok(Self { direct_message, echo_broadcast, @@ -231,14 +227,14 @@ impl CheckedMessageBundle { &self.metadata } - pub fn verify(self, verifier: &SP::Verifier) -> Result, MessageVerificationError> + pub fn verify(self, verifier: &SP::Verifier) -> Result, MessageVerificationError> where - P: Protocol, SP: SessionParameters, { - let direct_message = self.direct_message.verify::(verifier)?; - let echo_broadcast = self.echo_broadcast.verify::(verifier)?; - let normal_broadcast = self.normal_broadcast.verify::(verifier)?; + let direct_message = self.direct_message.verify::(verifier)?; + let echo_broadcast = self.echo_broadcast.verify::(verifier)?; + let normal_broadcast = self.normal_broadcast.verify::(verifier)?; + Ok(VerifiedMessageBundle { from: verifier.clone(), metadata: self.metadata, diff --git a/manul/src/session/session.rs b/manul/src/session/session.rs index d57a357..e0278ce 100644 --- a/manul/src/session/session.rs +++ b/manul/src/session/session.rs @@ -2,6 +2,7 @@ use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, format, + string::ToString, vec::Vec, }; use core::fmt::Debug; @@ -21,11 +22,47 @@ use super::{ LocalError, RemoteError, }; use crate::protocol::{ - Artifact, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, FirstRound, NormalBroadcast, - ObjectSafeRound, ObjectSafeRoundWrapper, Payload, Protocol, ProtocolMessagePart, ReceiveError, ReceiveErrorType, - Round, RoundId, + Artifact, DeserializationError, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, FirstRound, + NormalBroadcast, ObjectSafeRound, ObjectSafeRoundWrapper, Payload, Protocol, ProtocolMessagePart, ReceiveError, + ReceiveErrorType, Round, RoundId, }; +/// A (de)serializer that will be used for the protocol messages. +pub trait Format { + /// Serializes the given object into a bytestring. + fn serialize(value: T) -> Result, LocalError>; + + /// Tries to deserialize the given bytestring as an object of type `T`. + fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result; +} + +/// [temporary serializer] +#[derive(Debug, Clone, Copy)] +pub struct Serializer; + +impl Serializer { + // TODO: temporary implementation, should be made via `erased_serde` + /// Serializes the given object into a bytestring. + pub fn serialize(&self, value: T) -> Result, LocalError> { + bincode::serde::encode_to_vec(value, bincode::config::standard()) + .map(|vec| vec.into()) + .map_err(|err| LocalError::new(err.to_string())) + } +} + +/// [temporary deserializer] +#[derive(Debug, Clone, Copy)] +pub struct Deserializer; + +impl Deserializer { + // TODO: temporary implementation, should be made via `erased_serde` + /// Tries to deserialize the given bytestring as an object of type `T`. + pub fn deserialize<'de, T: Deserialize<'de>>(&self, bytes: &'de [u8]) -> Result { + bincode::serde::decode_borrowed_from_slice(bytes, bincode::config::standard()) + .map_err(|err| DeserializationError::new(err.to_string())) + } +} + /// A set of types needed to execute a session. /// /// These will be generally determined by the user, depending on what signature type @@ -49,6 +86,9 @@ pub trait SessionParameters { /// The signature type corresponding to [`Signer`](`Self::Signer`) and [`Verifier`](`Self::Verifier`). type Signature: Serialize + for<'de> Deserialize<'de>; + + /// The type used to (de)serialize messages. + type Format: Format; } /// A session identifier shared between the parties. @@ -162,11 +202,11 @@ where ) -> Result { let verifier = signer.verifying_key(); - let echo = round.make_echo_broadcast(rng)?; - let echo_broadcast = SignedMessage::new::(rng, &signer, &session_id, round.id(), echo)?; + let echo = round.make_echo_broadcast(rng, &Serializer)?; + let echo_broadcast = SignedMessage::new::(rng, &signer, &session_id, round.id(), echo)?; - let normal = round.make_normal_broadcast(rng)?; - let normal_broadcast = SignedMessage::new::(rng, &signer, &session_id, round.id(), normal)?; + let normal = round.make_normal_broadcast(rng, &Serializer)?; + let normal_broadcast = SignedMessage::new::(rng, &signer, &session_id, round.id(), normal)?; let message_destinations = round.message_destinations().clone(); @@ -212,9 +252,11 @@ where rng: &mut impl CryptoRngCore, destination: &SP::Verifier, ) -> Result<(MessageBundle, ProcessedArtifact), LocalError> { - let (direct_message, artifact) = self.round.make_direct_message_with_artifact(rng, destination)?; + let (direct_message, artifact) = self + .round + .make_direct_message_with_artifact(rng, &Serializer, destination)?; - let bundle = MessageBundle::new::( + let bundle = MessageBundle::new::( rng, &self.signer, &self.session_id, @@ -314,7 +356,7 @@ where // Verify the signature now - let verified_message = match checked_message.verify::(from) { + let verified_message = match checked_message.verify::(from) { Ok(verified_message) => verified_message, Err(MessageVerificationError::InvalidSignature) => { let err = "The signature could not be deserialized."; @@ -364,6 +406,7 @@ where ) -> ProcessedMessage { let processed = self.round.receive_message( rng, + &Deserializer, message.from(), message.echo_broadcast().clone(), message.normal_broadcast().clone(), @@ -737,7 +780,9 @@ mod tests { use impls::impls; use serde::{Deserialize, Serialize}; - use super::{MessageBundle, ProcessedArtifact, ProcessedMessage, Session, VerifiedMessageBundle}; + use super::{ + Deserializer, Format, MessageBundle, ProcessedArtifact, ProcessedMessage, Session, VerifiedMessageBundle, + }; use crate::{ protocol::{ DeserializationError, DirectMessage, EchoBroadcast, LocalError, NormalBroadcast, Protocol, ProtocolError, @@ -765,6 +810,7 @@ mod tests { impl ProtocolError for DummyProtocolError { fn verify_messages_constitute_error( &self, + _deserializer: &Deserializer, _echo_broadcast: &EchoBroadcast, _normal_broadcast: &NormalBroadcast, _direct_message: &DirectMessage, @@ -781,6 +827,11 @@ mod tests { type Result = (); type ProtocolError = DummyProtocolError; type CorrectnessProof = (); + } + + struct DummyFormat; + + impl Format for DummyFormat { fn serialize(_: T) -> Result, LocalError> where T: Serialize, @@ -795,17 +846,19 @@ mod tests { } } + type SP = TestSessionParams; + // We need `Session` to be `Send` so that we send a `Session` object to a task // to run the loop there. - assert!(impls!(Session: Send)); + assert!(impls!(Session: Send)); // This is needed so that message processing offloaded to a task could use `&Session`. - assert!(impls!(Session: Sync)); + assert!(impls!(Session: Sync)); // These objects are sent to/from message processing tasks assert!(impls!(MessageBundle: Send)); - assert!(impls!(ProcessedArtifact: Send)); - assert!(impls!(VerifiedMessageBundle: Send)); - assert!(impls!(ProcessedMessage: Send)); + assert!(impls!(ProcessedArtifact: Send)); + assert!(impls!(VerifiedMessageBundle: Send)); + assert!(impls!(ProcessedMessage: Send)); } } diff --git a/manul/src/testing/identity.rs b/manul/src/testing/identity.rs index db3b1a1..1200980 100644 --- a/manul/src/testing/identity.rs +++ b/manul/src/testing/identity.rs @@ -2,7 +2,7 @@ use digest::generic_array::typenum; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; -use crate::session::SessionParameters; +use crate::session::{Format, SessionParameters}; /// A simple signer for testing purposes. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] @@ -89,13 +89,14 @@ impl digest::OutputSizeUser for TestHasher { /// An implementation of [`SessionParameters`] using the testing signer/verifier types. #[derive(Debug, Clone, Copy)] -pub struct TestSessionParams; +pub struct TestSessionParams(core::marker::PhantomData); -impl SessionParameters for TestSessionParams { +impl SessionParameters for TestSessionParams { type Signer = TestSigner; type Verifier = TestVerifier; type Signature = TestSignature; type Digest = TestHasher; + type Format = F; } #[cfg(test)] diff --git a/manul/src/testing/macros.rs b/manul/src/testing/macros.rs index 5d6f8a0..d74304b 100644 --- a/manul/src/testing/macros.rs +++ b/manul/src/testing/macros.rs @@ -5,6 +5,7 @@ use rand_core::CryptoRngCore; use crate::protocol::{ Artifact, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, LocalError, NormalBroadcast, Payload, Round, }; +use crate::session::Serializer; /// A trait defining a wrapper around an existing type implementing [`Round`]. pub trait RoundWrapper: 'static + Sized + Send + Sync { @@ -28,25 +29,39 @@ pub trait RoundOverride: RoundWrapper { fn make_direct_message_with_artifact( &self, rng: &mut impl CryptoRngCore, + serializer: &Serializer, destination: &Id, ) -> Result<(DirectMessage, Option), LocalError> { - let dm = self.make_direct_message(rng, destination)?; + let dm = self.make_direct_message(rng, serializer, destination)?; Ok((dm, None)) } /// An override for [`Round::make_direct_message`]. - fn make_direct_message(&self, rng: &mut impl CryptoRngCore, destination: &Id) -> Result { - self.inner_round_ref().make_direct_message(rng, destination) + fn make_direct_message( + &self, + rng: &mut impl CryptoRngCore, + serializer: &Serializer, + destination: &Id, + ) -> Result { + self.inner_round_ref().make_direct_message(rng, serializer, destination) } /// An override for [`Round::make_echo_broadcast`]. - fn make_echo_broadcast(&self, rng: &mut impl CryptoRngCore) -> Result { - self.inner_round_ref().make_echo_broadcast(rng) + fn make_echo_broadcast( + &self, + rng: &mut impl CryptoRngCore, + serializer: &Serializer, + ) -> Result { + self.inner_round_ref().make_echo_broadcast(rng, serializer) } /// An override for [`Round::make_normal_broadcast`]. - fn make_normal_broadcast(&self, rng: &mut impl CryptoRngCore) -> Result { - self.inner_round_ref().make_normal_broadcast(rng) + fn make_normal_broadcast( + &self, + rng: &mut impl CryptoRngCore, + serializer: &Serializer, + ) -> Result { + self.inner_round_ref().make_normal_broadcast(rng, serializer) } /// An override for [`Round::finalize`]. @@ -96,43 +111,48 @@ macro_rules! round_override { fn make_direct_message( &self, rng: &mut impl CryptoRngCore, + serializer: &$crate::session::Serializer, destination: &Id, ) -> Result<$crate::protocol::DirectMessage, $crate::protocol::LocalError> { - >::make_direct_message(self, rng, destination) + >::make_direct_message(self, rng, serializer, destination) } fn make_direct_message_with_artifact( &self, rng: &mut impl CryptoRngCore, + serializer: &$crate::session::Serializer, destination: &Id, ) -> Result<($crate::protocol::DirectMessage, Option<$crate::protocol::Artifact>), $crate::protocol::LocalError> { - >::make_direct_message_with_artifact(self, rng, destination) + >::make_direct_message_with_artifact(self, rng, serializer, destination) } fn make_echo_broadcast( &self, rng: &mut impl CryptoRngCore, + serializer: &$crate::session::Serializer, ) -> Result<$crate::protocol::EchoBroadcast, $crate::protocol::LocalError> { - >::make_echo_broadcast(self, rng) + >::make_echo_broadcast(self, rng, serializer) } fn make_normal_broadcast( &self, rng: &mut impl CryptoRngCore, + serializer: &$crate::session::Serializer, ) -> Result<$crate::protocol::NormalBroadcast, $crate::protocol::LocalError> { - >::make_normal_broadcast(self, rng) + >::make_normal_broadcast(self, rng, serializer) } fn receive_message( &self, rng: &mut impl CryptoRngCore, + deserializer: &$crate::session::Deserializer, from: &Id, echo_broadcast: $crate::protocol::EchoBroadcast, normal_broadcast: $crate::protocol::NormalBroadcast, direct_message: $crate::protocol::DirectMessage, ) -> Result<$crate::protocol::Payload, $crate::protocol::ReceiveError> { self.inner_round_ref() - .receive_message(rng, from, echo_broadcast, normal_broadcast, direct_message) + .receive_message(rng, deserializer, from, echo_broadcast, normal_broadcast, direct_message) } fn finalize( From e1cab5e7d36d88ce1f24a3e847ebd3cc19937ed3 Mon Sep 17 00:00:00 2001 From: Bogdan Opanchuk Date: Tue, 29 Oct 2024 21:53:11 -0700 Subject: [PATCH 2/2] Use dynamic serializers/deserializers Simplify Deserializer logic Add JSON format Simplify signature serialization/deserialization Adjust generic bounds Fix deps --- CHANGELOG.md | 2 + Cargo.lock | 155 ++++++++++++++++++++++++++-- examples/Cargo.toml | 2 +- examples/src/format.rs | 21 ---- examples/src/lib.rs | 3 - examples/src/simple.rs | 5 +- examples/src/simple_malicious.rs | 24 ++--- examples/tests/async_runner.rs | 21 ++-- manul/Cargo.toml | 13 ++- manul/benches/empty_rounds.rs | 39 +++---- manul/src/protocol.rs | 3 +- manul/src/protocol/message.rs | 20 ++-- manul/src/protocol/object_safe.rs | 2 +- manul/src/protocol/round.rs | 8 +- manul/src/protocol/serialization.rs | 98 ++++++++++++++++++ manul/src/session.rs | 7 +- manul/src/session/echo.rs | 10 +- manul/src/session/evidence.rs | 16 +-- manul/src/session/message.rs | 16 +-- manul/src/session/session.rs | 133 ++++++++++-------------- manul/src/session/wire_format.rs | 45 ++++++++ manul/src/testing.rs | 2 + manul/src/testing/identity.rs | 6 +- manul/src/testing/macros.rs | 14 +-- manul/src/testing/wire_format.rs | 69 +++++++++++++ 25 files changed, 517 insertions(+), 217 deletions(-) delete mode 100644 examples/src/format.rs create mode 100644 manul/src/protocol/serialization.rs create mode 100644 manul/src/session/wire_format.rs create mode 100644 manul/src/testing/wire_format.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dba0f9..5335d8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Artifact::empty()` removed, the user should return `None` instead. ([#46]) - `EchoBroadcast` and `DirectMessage` now use `ProtocolMessagePart` trait for their methods. ([#47]) - Added normal broadcasts support in addition to echo ones; signatures of `Round` methods changed accordingly; added `Round::make_normal_broadcast()`. ([#47]) +- Serialization format is a part of `SessionParameters` now; `Round` and `Protocol` methods receive dynamic serializers/deserializers. ([#33]) ### Added @@ -27,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#32]: https://github.com/entropyxyz/manul/pull/32 +[#33]: https://github.com/entropyxyz/manul/pull/33 [#36]: https://github.com/entropyxyz/manul/pull/36 [#37]: https://github.com/entropyxyz/manul/pull/37 [#40]: https://github.com/entropyxyz/manul/pull/40 diff --git a/Cargo.lock b/Cargo.lock index 613a6a6..0555796 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -71,15 +80,6 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "bincode" -version = "2.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" -dependencies = [ - "serde", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -165,6 +165,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "cpufeatures" version = "0.2.14" @@ -210,6 +216,12 @@ dependencies = [ "itertools", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -278,6 +290,28 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -315,6 +349,29 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + [[package]] name = "hermit-abi" version = "0.4.0" @@ -389,6 +446,16 @@ version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" @@ -399,15 +466,17 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" name = "manul" version = "0.0.2-dev" dependencies = [ - "bincode", "criterion", "digest", "displaydoc", + "erased-serde", "impls", + "postcard", "rand", "rand_core", "serde", "serde-encoded-bytes", + "serde-persistent-deserializer", "serde_asn1_der", "serde_json", "signature", @@ -418,9 +487,9 @@ dependencies = [ name = "manul-example" version = "0.0.0" dependencies = [ - "bincode", "digest", "manul", + "postcard", "rand", "rand_core", "serde", @@ -534,6 +603,19 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "postcard" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -661,6 +743,15 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.18" @@ -676,6 +767,18 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.210" @@ -696,6 +799,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-persistent-deserializer" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f52002cbca6cb233262e7ab1a04d4214c3a13f4ee93cec344286ff14b01147" +dependencies = [ + "serde", +] + [[package]] name = "serde_asn1_der" version = "0.8.0" @@ -764,6 +876,21 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "2.0.77" @@ -878,6 +1005,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + [[package]] name = "typenum" version = "1.17.0" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index dace9db..04edd4c 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" [dependencies] manul = { path = "../manul" } -bincode = { version = "2.0.0-rc.3", default-features = false, features = ["alloc", "serde"] } +postcard = { version = "1", features = ["alloc"] } serde = "1" sha3 = "0.10" rand_core = "0.6" diff --git a/examples/src/format.rs b/examples/src/format.rs deleted file mode 100644 index 5f73e99..0000000 --- a/examples/src/format.rs +++ /dev/null @@ -1,21 +0,0 @@ -use manul::{ - protocol::{DeserializationError, LocalError}, - session::Format, -}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug)] -pub struct Bincode; - -impl Format for Bincode { - fn serialize(value: T) -> Result, LocalError> { - bincode::serde::encode_to_vec(value, bincode::config::standard()) - .map(|vec| vec.into()) - .map_err(|err| LocalError::new(err.to_string())) - } - - fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result { - bincode::serde::decode_borrowed_from_slice(bytes, bincode::config::standard()) - .map_err(|err| DeserializationError::new(err.to_string())) - } -} diff --git a/examples/src/lib.rs b/examples/src/lib.rs index 066da06..c3aff76 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -1,9 +1,6 @@ extern crate alloc; -mod format; pub mod simple; #[cfg(test)] mod simple_malicious; - -pub use format::Bincode; diff --git a/examples/src/simple.rs b/examples/src/simple.rs index dd2f724..a152086 100644 --- a/examples/src/simple.rs +++ b/examples/src/simple.rs @@ -391,13 +391,12 @@ mod tests { use manul::{ session::{signature::Keypair, SessionOutcome}, - testing::{run_sync, TestSessionParams, TestSigner, TestVerifier}, + testing::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, }; use rand_core::OsRng; use tracing_subscriber::EnvFilter; use super::{Inputs, Round1}; - use crate::Bincode; #[test] fn round() { @@ -422,7 +421,7 @@ mod tests { .with_env_filter(EnvFilter::from_default_env()) .finish(); let reports = tracing::subscriber::with_default(my_subscriber, || { - run_sync::, TestSessionParams>(&mut OsRng, inputs).unwrap() + run_sync::, TestSessionParams>(&mut OsRng, inputs).unwrap() }); for (_id, report) in reports { diff --git a/examples/src/simple_malicious.rs b/examples/src/simple_malicious.rs index c4a4b46..b4ecae5 100644 --- a/examples/src/simple_malicious.rs +++ b/examples/src/simple_malicious.rs @@ -4,18 +4,18 @@ use core::fmt::Debug; use manul::{ protocol::{ Artifact, DirectMessage, FinalizeError, FinalizeOutcome, FirstRound, LocalError, Payload, ProtocolMessagePart, - Round, + Round, Serializer, + }, + session::signature::Keypair, + testing::{ + round_override, run_sync, BinaryFormat, RoundOverride, RoundWrapper, TestSessionParams, TestSigner, + TestVerifier, }, - session::{signature::Keypair, Serializer}, - testing::{round_override, run_sync, RoundOverride, RoundWrapper, TestSessionParams, TestSigner, TestVerifier}, }; use rand_core::{CryptoRngCore, OsRng}; use tracing_subscriber::EnvFilter; -use crate::{ - simple::{Inputs, Round1, Round1Message, Round2, Round2Message}, - Bincode, -}; +use crate::simple::{Inputs, Round1, Round1Message, Round2, Round2Message}; #[derive(Debug, Clone, Copy)] enum Behavior { @@ -76,7 +76,7 @@ impl RoundOverride for Mali my_position: self.round.context.ids_to_positions[&self.round.context.id], your_position: self.round.context.ids_to_positions[&self.round.context.id], }; - DirectMessage::new(serializer, &message) + DirectMessage::new(serializer, message) } else { self.inner_round_ref().make_direct_message(rng, serializer, destination) } @@ -137,7 +137,7 @@ impl RoundOverride for Mali my_position: self.round.context.ids_to_positions[&self.round.context.id], your_position: self.round.context.ids_to_positions[&self.round.context.id], }; - DirectMessage::new(serializer, &message) + DirectMessage::new(serializer, message) } else { self.inner_round_ref().make_direct_message(rng, serializer, destination) } @@ -177,7 +177,7 @@ fn serialized_garbage() { .with_env_filter(EnvFilter::from_default_env()) .finish(); let mut reports = tracing::subscriber::with_default(my_subscriber, || { - run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() + run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() }); let v0 = signers[0].verifying_key(); @@ -223,7 +223,7 @@ fn attributable_failure() { .with_env_filter(EnvFilter::from_default_env()) .finish(); let mut reports = tracing::subscriber::with_default(my_subscriber, || { - run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() + run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() }); let v0 = signers[0].verifying_key(); @@ -269,7 +269,7 @@ fn attributable_failure_round2() { .with_env_filter(EnvFilter::from_default_env()) .finish(); let mut reports = tracing::subscriber::with_default(my_subscriber, || { - run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() + run_sync::, TestSessionParams>(&mut OsRng, run_inputs).unwrap() }); let v0 = signers[0].verifying_key(); diff --git a/examples/tests/async_runner.rs b/examples/tests/async_runner.rs index b56bfb1..20bccc2 100644 --- a/examples/tests/async_runner.rs +++ b/examples/tests/async_runner.rs @@ -10,12 +10,9 @@ use manul::{ signature::Keypair, CanFinalize, LocalError, MessageBundle, RoundOutcome, Session, SessionId, SessionParameters, SessionReport, }, - testing::{TestSessionParams, TestSigner}, -}; -use manul_example::{ - simple::{Inputs, Round1, SimpleProtocol}, - Bincode, + testing::{BinaryFormat, TestSessionParams, TestSigner}, }; +use manul_example::simple::{Inputs, Round1, SimpleProtocol}; use rand::Rng; use rand_core::OsRng; use tokio::{ @@ -43,8 +40,8 @@ async fn run_session( session: Session, ) -> Result, LocalError> where - P: 'static + Protocol, - SP: 'static + SessionParameters + Debug, + P: Protocol, + SP: SessionParameters + Debug, { let rng = &mut OsRng; @@ -158,7 +155,7 @@ async fn message_dispatcher( txs: BTreeMap>>, rx: mpsc::Receiver>, ) where - SP: SessionParameters, + SP: SessionParameters + Debug, { let mut rx = rx; let mut messages = Vec::>::new(); @@ -199,8 +196,8 @@ async fn message_dispatcher( async fn run_nodes(sessions: Vec>) -> Vec> where - P: 'static + Protocol + Send, - SP: 'static + SessionParameters + Debug, + P: Protocol + Send, + SP: SessionParameters + Debug, P::Result: Send, SP::Signer: Send, { @@ -244,7 +241,7 @@ where #[tokio::test] async fn async_run() { // The kind of Session we need to run the `SimpleProtocol`. - type SimpleSession = Session>; + type SimpleSession = Session>; // Create 4 parties let signers = (0..3).map(TestSigner::new).collect::>(); @@ -252,7 +249,7 @@ async fn async_run() { .iter() .map(|signer| signer.verifying_key()) .collect::>(); - let session_id = SessionId::random::>(&mut OsRng); + let session_id = SessionId::random::>(&mut OsRng); // Create 4 `Session`s let sessions = signers diff --git a/manul/Cargo.toml b/manul/Cargo.toml index a181f28..3ecc86d 100644 --- a/manul/Cargo.toml +++ b/manul/Cargo.toml @@ -12,25 +12,30 @@ categories = ["cryptography", "no-std"] [dependencies] serde = { version = "1", default-features = false, features = ["alloc", "serde_derive"] } +erased-serde = { version = "0.4", default-features = false, features = ["alloc"] } serde-encoded-bytes = { version = "0.1", default-features = false, features = ["hex", "base64"] } digest = { version = "0.10", default-features = false } signature = { version = "2", default-features = false, features = ["digest", "rand_core"] } rand_core = { version = "0.6.4", default-features = false } tracing = { version = "0.1", default-features = false } displaydoc = { version = "0.2", default-features = false } + rand = { version = "0.8", default-features = false, optional = true } -bincode = { version = "2.0.0-rc.3", default-features = false, features = ["alloc", "serde"] } +serde-persistent-deserializer = { version = "0.3", optional = true } +postcard = { version = "1", default-features = false, features = ["alloc"], optional = true } +serde_json = { version = "1", default-features = false, features = ["alloc"], optional = true } [dev-dependencies] impls = "1" rand = { version = "0.8", default-features = false } -bincode = { version = "2.0.0-rc.3", default-features = false, features = ["alloc", "serde"] } serde_asn1_der = "0.8" -serde_json = "1" criterion = "0.5" +serde-persistent-deserializer = "0.3" +postcard = { version = "1", default-features = false, features = ["alloc"] } +serde_json = { version = "1", default-features = false, features = ["alloc"] } [features] -testing = ["rand"] +testing = ["rand", "postcard", "serde_json", "serde-persistent-deserializer"] [package.metadata.docs.rs] all-features = true diff --git a/manul/benches/empty_rounds.rs b/manul/benches/empty_rounds.rs index 38a4663..072de69 100644 --- a/manul/benches/empty_rounds.rs +++ b/manul/benches/empty_rounds.rs @@ -6,12 +6,12 @@ use core::fmt::Debug; use criterion::{criterion_group, criterion_main, Criterion}; use manul::{ protocol::{ - Artifact, DeserializationError, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, FirstRound, - LocalError, NormalBroadcast, Payload, Protocol, ProtocolError, ProtocolMessagePart, ProtocolValidationError, - ReceiveError, Round, RoundId, + Artifact, Deserializer, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, FirstRound, LocalError, + NormalBroadcast, Payload, Protocol, ProtocolError, ProtocolMessagePart, ProtocolValidationError, ReceiveError, + Round, RoundId, Serializer, }, - session::{signature::Keypair, Deserializer, Format, Serializer, SessionOutcome}, - testing::{run_sync, TestSessionParams, TestSigner, TestVerifier}, + session::{signature::Keypair, SessionOutcome}, + testing::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, }; use rand_core::{CryptoRngCore, OsRng}; use serde::{Deserialize, Serialize}; @@ -38,22 +38,6 @@ impl ProtocolError for EmptyProtocolError { } } -#[derive(Debug)] -pub struct Bincode; - -impl Format for Bincode { - fn serialize(value: T) -> Result, LocalError> { - bincode::serde::encode_to_vec(value, bincode::config::standard()) - .map(|vec| vec.into()) - .map_err(|err| LocalError::new(err.to_string())) - } - - fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result { - bincode::serde::decode_borrowed_from_slice(bytes, bincode::config::standard()) - .map_err(|err| DeserializationError::new(err.to_string())) - } -} - impl Protocol for EmptyProtocol { type Result = (); type ProtocolError = EmptyProtocolError; @@ -223,7 +207,7 @@ fn bench_empty_rounds(c: &mut Criterion) { group.bench_function("25 nodes, 5 rounds, no echo", |b| { b.iter(|| { - assert!(run_sync::, TestSessionParams>( + assert!(run_sync::, TestSessionParams>( &mut OsRng, inputs_no_echo.clone() ) @@ -254,12 +238,13 @@ fn bench_empty_rounds(c: &mut Criterion) { group.bench_function("25 nodes, 5 rounds, echo each round", |b| { b.iter(|| { - assert!( - run_sync::, TestSessionParams>(&mut OsRng, inputs_echo.clone()) - .unwrap() - .values() - .all(|report| matches!(report.outcome, SessionOutcome::Result(_))) + assert!(run_sync::, TestSessionParams>( + &mut OsRng, + inputs_echo.clone() ) + .unwrap() + .values() + .all(|report| matches!(report.outcome, SessionOutcome::Result(_)))) }) }); diff --git a/manul/src/protocol.rs b/manul/src/protocol.rs index 82971c0..b0b421f 100644 --- a/manul/src/protocol.rs +++ b/manul/src/protocol.rs @@ -15,8 +15,8 @@ mod errors; mod message; mod object_safe; mod round; +mod serialization; -pub use crate::session::{Deserializer, Serializer, SessionId}; pub use errors::{ DeserializationError, DirectMessageError, EchoBroadcastError, FinalizeError, LocalError, MessageValidationError, NormalBroadcastError, ProtocolValidationError, ReceiveError, RemoteError, @@ -25,6 +25,7 @@ pub use message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessage pub use round::{ AnotherRound, Artifact, FinalizeOutcome, FirstRound, Payload, Protocol, ProtocolError, Round, RoundId, }; +pub use serialization::{Deserializer, Serializer}; pub(crate) use errors::ReceiveErrorType; pub(crate) use object_safe::{ObjectSafeRound, ObjectSafeRoundWrapper}; diff --git a/manul/src/protocol/message.rs b/manul/src/protocol/message.rs index 7475c45..ccbacee 100644 --- a/manul/src/protocol/message.rs +++ b/manul/src/protocol/message.rs @@ -43,7 +43,10 @@ pub trait ProtocolMessagePart: ProtocolMessageWrapper { } /// Creates a new serialized message. - fn new(serializer: &Serializer, message: T) -> Result { + fn new(serializer: &Serializer, message: T) -> Result + where + T: 'static + Serialize, + { let payload = MessagePayload(serializer.serialize(message)?); Ok(Self::new_inner(Some(payload))) } @@ -67,9 +70,10 @@ pub trait ProtocolMessagePart: ProtocolMessageWrapper { /// Returns `Ok(())` if the message cannot be deserialized into `T`. /// /// This is intended to be used in the implementations of - /// [`Protocol::verify_direct_message_is_invalid`] or [`Protocol::verify_echo_broadcast_is_invalid`]. - fn verify_is_not Deserialize<'de>>( - &self, + /// [`Protocol::verify_direct_message_is_invalid`](`crate::protocol::Protocol::verify_direct_message_is_invalid`) or + /// [`Protocol::verify_echo_broadcast_is_invalid`](`crate::protocol::Protocol::verify_echo_broadcast_is_invalid`). + fn verify_is_not<'de, T: Deserialize<'de>>( + &'de self, deserializer: &Deserializer, ) -> Result<(), MessageValidationError> { if self.deserialize::(deserializer).is_err() { @@ -84,7 +88,8 @@ pub trait ProtocolMessagePart: ProtocolMessageWrapper { /// Returns `Ok(())` if the message contains a payload. /// /// This is intended to be used in the implementations of - /// [`Protocol::verify_direct_message_is_invalid`] or [`Protocol::verify_echo_broadcast_is_invalid`]. + /// [`Protocol::verify_direct_message_is_invalid`](`crate::protocol::Protocol::verify_direct_message_is_invalid`) or + /// [`Protocol::verify_echo_broadcast_is_invalid`](`crate::protocol::Protocol::verify_echo_broadcast_is_invalid`). fn verify_is_some(&self) -> Result<(), MessageValidationError> { if self.maybe_message().is_some() { Ok(()) @@ -96,7 +101,10 @@ pub trait ProtocolMessagePart: ProtocolMessageWrapper { } /// Deserializes the message into `T`. - fn deserialize Deserialize<'de>>(&self, deserializer: &Deserializer) -> Result { + fn deserialize<'de, T>(&'de self, deserializer: &Deserializer) -> Result + where + T: Deserialize<'de>, + { let payload = self .maybe_message() .as_ref() diff --git a/manul/src/protocol/object_safe.rs b/manul/src/protocol/object_safe.rs index 9693be6..4eacf02 100644 --- a/manul/src/protocol/object_safe.rs +++ b/manul/src/protocol/object_safe.rs @@ -11,8 +11,8 @@ use super::{ errors::{FinalizeError, LocalError, ReceiveError}, message::{DirectMessage, EchoBroadcast, NormalBroadcast}, round::{Artifact, FinalizeOutcome, Payload, Protocol, Round, RoundId}, + serialization::{Deserializer, Serializer}, }; -use crate::session::{Deserializer, Serializer}; /// Since object-safe trait methods cannot take `impl CryptoRngCore` arguments, /// this structure wraps the dynamic object and exposes a `CryptoRngCore` interface, diff --git a/manul/src/protocol/round.rs b/manul/src/protocol/round.rs index db1d761..a0d6846 100644 --- a/manul/src/protocol/round.rs +++ b/manul/src/protocol/round.rs @@ -13,8 +13,8 @@ use super::{ errors::{FinalizeError, LocalError, MessageValidationError, ProtocolValidationError, ReceiveError}, message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessagePart}, object_safe::{ObjectSafeRound, ObjectSafeRoundWrapper}, + serialization::{Deserializer, Serializer}, }; -use crate::session::{Deserializer, Serializer}; /// Possible successful outcomes of [`Round::finalize`]. #[derive(Debug)] @@ -28,7 +28,7 @@ pub enum FinalizeOutcome { impl FinalizeOutcome where Id: 'static + Debug, - P: 'static + Protocol, + P: Protocol, { /// A helper method to create an [`AnotherRound`](`Self::AnotherRound`) variant. pub fn another_round(round: impl Round) -> Self { @@ -44,7 +44,7 @@ pub struct AnotherRound(Box AnotherRound where Id: 'static + Debug, - P: 'static + Protocol, + P: Protocol, { /// Wraps an object implementing [`Round`]. pub fn new(round: impl Round) -> Self { @@ -116,7 +116,7 @@ impl RoundId { } /// A distributed protocol. -pub trait Protocol: Debug + Sized { +pub trait Protocol: 'static + Debug + Sized { /// The successful result of an execution of this protocol. type Result: Debug; diff --git a/manul/src/protocol/serialization.rs b/manul/src/protocol/serialization.rs new file mode 100644 index 0000000..cb0abb7 --- /dev/null +++ b/manul/src/protocol/serialization.rs @@ -0,0 +1,98 @@ +use alloc::{boxed::Box, format}; +use core::{fmt::Debug, marker::PhantomData}; + +use serde::{Deserialize, Serialize}; + +use super::errors::{DeserializationError, LocalError}; +use crate::session::WireFormat; + +// Serialization + +trait ObjectSafeSerializer: Debug { + fn serialize(&self, value: Box) -> Result, LocalError>; +} + +#[derive(Debug)] +struct SerializerWrapper(PhantomData); + +impl ObjectSafeSerializer for SerializerWrapper { + fn serialize(&self, value: Box) -> Result, LocalError> { + F::serialize(&value) + } +} + +/// A serializer for protocol messages. +#[derive(Debug)] +pub struct Serializer(Box); + +impl Serializer { + pub(crate) fn new() -> Self { + Self(Box::new(SerializerWrapper::(PhantomData))) + } + + /// Serializes a `serde`-serializable object. + pub fn serialize(&self, value: T) -> Result, LocalError> + where + T: 'static + Serialize, + { + let boxed_value: Box = Box::new(value); + self.0.serialize(boxed_value) + } +} + +// Deserialization + +#[derive(Debug)] +struct DeserializerFactoryWrapper(PhantomData); + +trait ObjectSafeDeserializerFactory: Debug { + fn make_erased_deserializer<'de>(&self, bytes: &'de [u8]) -> Box + 'de>; +} + +impl ObjectSafeDeserializerFactory for DeserializerFactoryWrapper +where + F: WireFormat, +{ + fn make_erased_deserializer<'de>(&self, bytes: &'de [u8]) -> Box + 'de> { + let deserializer = F::deserializer(bytes); + Box::new(>::erase(deserializer)) + } +} + +/// A deserializer for protocol messages. +#[derive(Debug)] +pub struct Deserializer(Box); + +impl Deserializer { + pub(crate) fn new() -> Self + where + F: WireFormat, + { + Self(Box::new(DeserializerFactoryWrapper(PhantomData::))) + } + + /// Deserializes a `serde`-deserializable object. + pub fn deserialize<'de, T>(&self, bytes: &'de [u8]) -> Result + where + T: Deserialize<'de>, + { + let mut deserializer = self.0.make_erased_deserializer(bytes); + erased_serde::deserialize::(&mut deserializer) + .map_err(|err| DeserializationError::new(format!("Deserialization error: {err:?}"))) + } +} + +#[cfg(test)] +mod tests { + use impls::impls; + + use super::{Deserializer, Serializer}; + + #[test] + fn test_concurrency_bounds() { + assert!(impls!(Serializer: Send)); + assert!(impls!(Serializer: Sync)); + assert!(impls!(Deserializer: Send)); + assert!(impls!(Deserializer: Sync)); + } +} diff --git a/manul/src/session.rs b/manul/src/session.rs index cf90d77..49bea89 100644 --- a/manul/src/session.rs +++ b/manul/src/session.rs @@ -13,14 +13,13 @@ mod message; #[allow(clippy::module_inception)] mod session; mod transcript; +mod wire_format; pub use crate::protocol::{LocalError, RemoteError}; pub use message::MessageBundle; -pub use session::{ - CanFinalize, Deserializer, Format, RoundAccumulator, RoundOutcome, Serializer, Session, SessionId, - SessionParameters, -}; +pub use session::{CanFinalize, RoundAccumulator, RoundOutcome, Session, SessionId, SessionParameters}; pub use transcript::{SessionOutcome, SessionReport}; +pub use wire_format::WireFormat; pub(crate) use echo::EchoRoundError; diff --git a/manul/src/session/echo.rs b/manul/src/session/echo.rs index 37d0b25..05049d6 100644 --- a/manul/src/session/echo.rs +++ b/manul/src/session/echo.rs @@ -12,13 +12,13 @@ use tracing::debug; use super::{ message::{MessageVerificationError, SignedMessage}, - session::{Deserializer, Serializer, SessionParameters}, + session::SessionParameters, LocalError, }; use crate::{ protocol::{ - Artifact, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, NormalBroadcast, ObjectSafeRound, - Payload, Protocol, ProtocolMessagePart, ReceiveError, Round, RoundId, + Artifact, Deserializer, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, NormalBroadcast, + ObjectSafeRound, Payload, Protocol, ProtocolMessagePart, ReceiveError, Round, RoundId, Serializer, }, utils::SerializableMap, }; @@ -75,7 +75,7 @@ pub struct EchoRound { impl EchoRound where P: Protocol, - SP: SessionParameters, + SP: SessionParameters + Debug, { pub fn new( verifier: SP::Verifier, @@ -145,7 +145,7 @@ where let message = EchoRoundMessage:: { echo_broadcasts: echo_broadcasts.into(), }; - NormalBroadcast::new(serializer, &message) + NormalBroadcast::new(serializer, message) } fn expecting_messages_from(&self) -> &BTreeSet { diff --git a/manul/src/session/evidence.rs b/manul/src/session/evidence.rs index e574bc1..3912623 100644 --- a/manul/src/session/evidence.rs +++ b/manul/src/session/evidence.rs @@ -6,14 +6,15 @@ use serde::{Deserialize, Serialize}; use super::{ echo::{EchoRoundError, EchoRoundMessage, MismatchedBroadcastsError}, message::{MessageVerificationError, MissingMessage, SignedMessage}, - session::{Deserializer, SessionParameters}, + session::SessionParameters, transcript::Transcript, LocalError, }; use crate::{ protocol::{ - DirectMessage, DirectMessageError, EchoBroadcast, EchoBroadcastError, MessageValidationError, NormalBroadcast, - NormalBroadcastError, Protocol, ProtocolError, ProtocolMessagePart, ProtocolValidationError, RoundId, + Deserializer, DirectMessage, DirectMessageError, EchoBroadcast, EchoBroadcastError, MessageValidationError, + NormalBroadcast, NormalBroadcastError, Protocol, ProtocolError, ProtocolMessagePart, ProtocolValidationError, + RoundId, }, utils::SerializableMap, }; @@ -233,12 +234,13 @@ where } pub fn verify(&self, party: &SP::Verifier) -> Result<(), EvidenceError> { + let deserializer = Deserializer::new::(); match &self.evidence { - EvidenceEnum::Protocol(evidence) => evidence.verify::(party, &Deserializer), - EvidenceEnum::InvalidDirectMessage(evidence) => evidence.verify::(party, &Deserializer), - EvidenceEnum::InvalidEchoBroadcast(evidence) => evidence.verify::(party, &Deserializer), + EvidenceEnum::Protocol(evidence) => evidence.verify::(party, &deserializer), + EvidenceEnum::InvalidDirectMessage(evidence) => evidence.verify::(party, &deserializer), + EvidenceEnum::InvalidEchoBroadcast(evidence) => evidence.verify::(party, &deserializer), EvidenceEnum::InvalidNormalBroadcast(evidence) => evidence.verify::(party), - EvidenceEnum::InvalidEchoPack(evidence) => evidence.verify(party, &Deserializer), + EvidenceEnum::InvalidEchoPack(evidence) => evidence.verify(party, &deserializer), EvidenceEnum::MismatchedBroadcasts(evidence) => evidence.verify::(party), } } diff --git a/manul/src/session/message.rs b/manul/src/session/message.rs index 09144c9..3e25e39 100644 --- a/manul/src/session/message.rs +++ b/manul/src/session/message.rs @@ -7,7 +7,8 @@ use serde_encoded_bytes::{Hex, SliceLike}; use signature::{DigestVerifier, RandomizedDigestSigner}; use super::{ - session::{Format, SessionId, SessionParameters}, + session::{SessionId, SessionParameters}, + wire_format::WireFormat, LocalError, }; use crate::protocol::{DeserializationError, DirectMessage, EchoBroadcast, NormalBroadcast, RoundId}; @@ -16,18 +17,18 @@ use crate::protocol::{DeserializationError, DirectMessage, EchoBroadcast, Normal pub(crate) struct SerializedSignature(#[serde(with = "SliceLike::")] Box<[u8]>); impl SerializedSignature { - pub fn new(signature: &SP::Signature) -> Result + pub fn new(signature: SP::Signature) -> Result where SP: SessionParameters, { - SP::Format::serialize(signature).map(Self) + SP::WireFormat::serialize(signature).map(Self) } pub fn deserialize(&self) -> Result where SP: SessionParameters, { - SP::Format::deserialize::(&self.0) + SP::WireFormat::deserialize::(&self.0) } } @@ -91,13 +92,13 @@ where { let metadata = MessageMetadata::new(session_id, round_id); let message_with_metadata = MessageWithMetadata { metadata, message }; - let message_bytes = SP::Format::serialize(&message_with_metadata)?; + let message_bytes = SP::WireFormat::serialize(&message_with_metadata)?; let digest = SP::Digest::new_with_prefix(b"SignedMessage").chain_update(message_bytes); let signature = signer .try_sign_digest_with_rng(rng, digest) .map_err(|err| LocalError::new(format!("Failed to sign: {:?}", err)))?; Ok(Self { - signature: SerializedSignature::new::(&signature)?, + signature: SerializedSignature::new::(signature)?, message_with_metadata, }) } @@ -115,7 +116,7 @@ where SP: SessionParameters, { let message_bytes = - SP::Format::serialize(&self.message_with_metadata).map_err(MessageVerificationError::Local)?; + SP::WireFormat::serialize(&self.message_with_metadata).map_err(MessageVerificationError::Local)?; let digest = SP::Digest::new_with_prefix(b"SignedMessage").chain_update(message_bytes); let signature = self .signature @@ -171,6 +172,7 @@ pub struct MessageBundle { } impl MessageBundle { + #[allow(clippy::too_many_arguments)] pub(crate) fn new( rng: &mut impl CryptoRngCore, signer: &SP::Signer, diff --git a/manul/src/session/session.rs b/manul/src/session/session.rs index e0278ce..8928502 100644 --- a/manul/src/session/session.rs +++ b/manul/src/session/session.rs @@ -2,7 +2,6 @@ use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, format, - string::ToString, vec::Vec, }; use core::fmt::Debug; @@ -19,55 +18,20 @@ use super::{ evidence::Evidence, message::{MessageBundle, MessageVerificationError, SignedMessage, VerifiedMessageBundle}, transcript::{SessionOutcome, SessionReport, Transcript}, + wire_format::WireFormat, LocalError, RemoteError, }; use crate::protocol::{ - Artifact, DeserializationError, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, FirstRound, - NormalBroadcast, ObjectSafeRound, ObjectSafeRoundWrapper, Payload, Protocol, ProtocolMessagePart, ReceiveError, - ReceiveErrorType, Round, RoundId, + Artifact, Deserializer, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, FirstRound, NormalBroadcast, + ObjectSafeRound, ObjectSafeRoundWrapper, Payload, Protocol, ProtocolMessagePart, ReceiveError, ReceiveErrorType, + Round, RoundId, Serializer, }; -/// A (de)serializer that will be used for the protocol messages. -pub trait Format { - /// Serializes the given object into a bytestring. - fn serialize(value: T) -> Result, LocalError>; - - /// Tries to deserialize the given bytestring as an object of type `T`. - fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result; -} - -/// [temporary serializer] -#[derive(Debug, Clone, Copy)] -pub struct Serializer; - -impl Serializer { - // TODO: temporary implementation, should be made via `erased_serde` - /// Serializes the given object into a bytestring. - pub fn serialize(&self, value: T) -> Result, LocalError> { - bincode::serde::encode_to_vec(value, bincode::config::standard()) - .map(|vec| vec.into()) - .map_err(|err| LocalError::new(err.to_string())) - } -} - -/// [temporary deserializer] -#[derive(Debug, Clone, Copy)] -pub struct Deserializer; - -impl Deserializer { - // TODO: temporary implementation, should be made via `erased_serde` - /// Tries to deserialize the given bytestring as an object of type `T`. - pub fn deserialize<'de, T: Deserialize<'de>>(&self, bytes: &'de [u8]) -> Result { - bincode::serde::decode_borrowed_from_slice(bytes, bincode::config::standard()) - .map_err(|err| DeserializationError::new(err.to_string())) - } -} - /// A set of types needed to execute a session. /// /// These will be generally determined by the user, depending on what signature type /// is used in the network in which they are running the protocol. -pub trait SessionParameters { +pub trait SessionParameters: 'static { /// The signer type. type Signer: Debug + RandomizedDigestSigner + Keypair; @@ -88,7 +52,7 @@ pub trait SessionParameters { type Signature: Serialize + for<'de> Deserialize<'de>; /// The type used to (de)serialize messages. - type Format: Format; + type WireFormat: WireFormat; } /// A session identifier shared between the parties. @@ -146,6 +110,8 @@ pub struct Session { session_id: SessionId, signer: SP::Signer, verifier: SP::Verifier, + serializer: Serializer, + deserializer: Deserializer, round: Box>, message_destinations: BTreeSet, echo_broadcast: SignedMessage, @@ -170,8 +136,8 @@ pub enum RoundOutcome { impl Session where - P: 'static + Protocol, - SP: 'static + SessionParameters + Debug, + P: Protocol, + SP: SessionParameters + Debug, { /// Initializes a new session. pub fn new( @@ -181,7 +147,7 @@ where inputs: R::Inputs, ) -> Result where - R: FirstRound + Round + 'static, + R: FirstRound + Round, { let verifier = signer.verifying_key(); let first_round = Box::new(ObjectSafeRoundWrapper::new(R::new( @@ -190,22 +156,34 @@ where verifier.clone(), inputs, )?)); - Self::new_for_next_round(rng, session_id, signer, first_round, Transcript::new()) + let serializer = Serializer::new::(); + let deserializer = Deserializer::new::(); + Self::new_for_next_round( + rng, + session_id, + signer, + serializer, + deserializer, + first_round, + Transcript::new(), + ) } fn new_for_next_round( rng: &mut impl CryptoRngCore, session_id: SessionId, signer: SP::Signer, + serializer: Serializer, + deserializer: Deserializer, round: Box>, transcript: Transcript, ) -> Result { let verifier = signer.verifying_key(); - let echo = round.make_echo_broadcast(rng, &Serializer)?; + let echo = round.make_echo_broadcast(rng, &serializer)?; let echo_broadcast = SignedMessage::new::(rng, &signer, &session_id, round.id(), echo)?; - let normal = round.make_normal_broadcast(rng, &Serializer)?; + let normal = round.make_normal_broadcast(rng, &serializer)?; let normal_broadcast = SignedMessage::new::(rng, &signer, &session_id, round.id(), normal)?; let message_destinations = round.message_destinations().clone(); @@ -220,6 +198,8 @@ where session_id, signer, verifier, + serializer, + deserializer, round, echo_broadcast, normal_broadcast, @@ -252,9 +232,9 @@ where rng: &mut impl CryptoRngCore, destination: &SP::Verifier, ) -> Result<(MessageBundle, ProcessedArtifact), LocalError> { - let (direct_message, artifact) = self - .round - .make_direct_message_with_artifact(rng, &Serializer, destination)?; + let (direct_message, artifact) = + self.round + .make_direct_message_with_artifact(rng, &self.serializer, destination)?; let bundle = MessageBundle::new::( rng, @@ -406,7 +386,7 @@ where ) -> ProcessedMessage { let processed = self.round.receive_message( rng, - &Deserializer, + &self.deserializer, message.from(), message.echo_broadcast().clone(), message.normal_broadcast().clone(), @@ -477,7 +457,15 @@ where accum.artifacts, ))); let cached_messages = filter_messages(accum.cached, round.id()); - let session = Session::new_for_next_round(rng, self.session_id, self.signer, round, transcript)?; + let session = Session::new_for_next_round( + rng, + self.session_id, + self.signer, + self.serializer, + self.deserializer, + round, + transcript, + )?; return Ok(RoundOutcome::AnotherRound { session, cached_messages, @@ -506,7 +494,15 @@ where .filter(|message| !transcript.is_banned(message.from())) .collect::>(); - let session = Session::new_for_next_round(rng, self.session_id, self.signer, round, transcript)?; + let session = Session::new_for_next_round( + rng, + self.session_id, + self.signer, + self.serializer, + self.deserializer, + round, + transcript, + )?; RoundOutcome::AnotherRound { cached_messages, session, @@ -775,20 +771,18 @@ fn filter_messages( #[cfg(test)] mod tests { - use alloc::{boxed::Box, collections::BTreeMap, vec::Vec}; + use alloc::{collections::BTreeMap, vec::Vec}; use impls::impls; use serde::{Deserialize, Serialize}; - use super::{ - Deserializer, Format, MessageBundle, ProcessedArtifact, ProcessedMessage, Session, VerifiedMessageBundle, - }; + use super::{MessageBundle, ProcessedArtifact, ProcessedMessage, Session, VerifiedMessageBundle}; use crate::{ protocol::{ - DeserializationError, DirectMessage, EchoBroadcast, LocalError, NormalBroadcast, Protocol, ProtocolError, + Deserializer, DirectMessage, EchoBroadcast, NormalBroadcast, Protocol, ProtocolError, ProtocolValidationError, RoundId, }, - testing::TestSessionParams, + testing::{BinaryFormat, TestSessionParams}, }; #[test] @@ -829,24 +823,7 @@ mod tests { type CorrectnessProof = (); } - struct DummyFormat; - - impl Format for DummyFormat { - fn serialize(_: T) -> Result, LocalError> - where - T: Serialize, - { - unimplemented!() - } - fn deserialize<'de, T>(_: &[u8]) -> Result - where - T: Deserialize<'de>, - { - unimplemented!() - } - } - - type SP = TestSessionParams; + type SP = TestSessionParams; // We need `Session` to be `Send` so that we send a `Session` object to a task // to run the loop there. diff --git a/manul/src/session/wire_format.rs b/manul/src/session/wire_format.rs new file mode 100644 index 0000000..d673d98 --- /dev/null +++ b/manul/src/session/wire_format.rs @@ -0,0 +1,45 @@ +use alloc::{boxed::Box, format}; +use core::fmt::Debug; + +use serde::{Deserialize, Serialize}; + +use crate::protocol::{DeserializationError, LocalError}; + +/* +Why the asymmetry between serialization and deserialization? + +If we had a method returning an object of a type implementing `serde::Serializer`, +we could organize the serialization in the same way as deserialization. +But libraries generally expose `T where &mut T: Serializer`, +and it's tricky to write a similar persistent wrapper as we do for the deserializer +(see https://github.com/fjarri/serde-persistent-deserializer/issues/2). + +So for serialization we have to instead type-erase the value itself and pass it somewhere +where the serializer type is known (`ObjectSafeSerializer::serialize()` impl); +but for the deserialization we instead type-erase the deserializer and pass it somewhere +the type of the target value is known (`Deserializer::deserialize()`). + +One consequence of this is the `'static` requirement for the serialized type, +because we have to put the value in a box; +if we could instead type-erase the serializer, we wouldn't need that. +*/ + +/// A (de)serializer that will be used for the protocol messages. +pub trait WireFormat: 'static + Send + Sync + Debug { + /// Serializes the given object into a bytestring. + fn serialize(value: T) -> Result, LocalError>; + + /// The deserializer type. + type Deserializer<'de>: serde::Deserializer<'de>; + + /// Creates a `serde` deserializer given a bytestring. + fn deserializer(bytes: &[u8]) -> Self::Deserializer<'_>; + + // A helper method for use on the session level when both `WireFormat` and `T` are known at the same point. + + /// Deserializes the given bytestring into `T`. + fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result { + let deserializer = Self::deserializer(bytes); + T::deserialize(deserializer).map_err(|err| DeserializationError::new(format!("Deserialization error: {err:?}"))) + } +} diff --git a/manul/src/testing.rs b/manul/src/testing.rs index 3165961..499f855 100644 --- a/manul/src/testing.rs +++ b/manul/src/testing.rs @@ -15,7 +15,9 @@ The [`run_sync()`] method is helpful to execute a protocol synchronously and col mod identity; mod macros; mod run_sync; +mod wire_format; pub use identity::{TestHasher, TestSessionParams, TestSignature, TestSigner, TestVerifier}; pub use macros::{round_override, RoundOverride, RoundWrapper}; pub use run_sync::run_sync; +pub use wire_format::{BinaryFormat, HumanReadableFormat}; diff --git a/manul/src/testing/identity.rs b/manul/src/testing/identity.rs index 1200980..16a380f 100644 --- a/manul/src/testing/identity.rs +++ b/manul/src/testing/identity.rs @@ -2,7 +2,7 @@ use digest::generic_array::typenum; use rand_core::CryptoRngCore; use serde::{Deserialize, Serialize}; -use crate::session::{Format, SessionParameters}; +use crate::session::{SessionParameters, WireFormat}; /// A simple signer for testing purposes. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] @@ -91,12 +91,12 @@ impl digest::OutputSizeUser for TestHasher { #[derive(Debug, Clone, Copy)] pub struct TestSessionParams(core::marker::PhantomData); -impl SessionParameters for TestSessionParams { +impl SessionParameters for TestSessionParams { type Signer = TestSigner; type Verifier = TestVerifier; type Signature = TestSignature; type Digest = TestHasher; - type Format = F; + type WireFormat = F; } #[cfg(test)] diff --git a/manul/src/testing/macros.rs b/manul/src/testing/macros.rs index d74304b..2b411c5 100644 --- a/manul/src/testing/macros.rs +++ b/manul/src/testing/macros.rs @@ -3,9 +3,9 @@ use alloc::collections::BTreeMap; use rand_core::CryptoRngCore; use crate::protocol::{ - Artifact, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, LocalError, NormalBroadcast, Payload, Round, + Artifact, DirectMessage, EchoBroadcast, FinalizeError, FinalizeOutcome, LocalError, NormalBroadcast, Payload, + Round, Serializer, }; -use crate::session::Serializer; /// A trait defining a wrapper around an existing type implementing [`Round`]. pub trait RoundWrapper: 'static + Sized + Send + Sync { @@ -111,7 +111,7 @@ macro_rules! round_override { fn make_direct_message( &self, rng: &mut impl CryptoRngCore, - serializer: &$crate::session::Serializer, + serializer: &$crate::protocol::Serializer, destination: &Id, ) -> Result<$crate::protocol::DirectMessage, $crate::protocol::LocalError> { >::make_direct_message(self, rng, serializer, destination) @@ -120,7 +120,7 @@ macro_rules! round_override { fn make_direct_message_with_artifact( &self, rng: &mut impl CryptoRngCore, - serializer: &$crate::session::Serializer, + serializer: &$crate::protocol::Serializer, destination: &Id, ) -> Result<($crate::protocol::DirectMessage, Option<$crate::protocol::Artifact>), $crate::protocol::LocalError> { >::make_direct_message_with_artifact(self, rng, serializer, destination) @@ -129,7 +129,7 @@ macro_rules! round_override { fn make_echo_broadcast( &self, rng: &mut impl CryptoRngCore, - serializer: &$crate::session::Serializer, + serializer: &$crate::protocol::Serializer, ) -> Result<$crate::protocol::EchoBroadcast, $crate::protocol::LocalError> { >::make_echo_broadcast(self, rng, serializer) } @@ -137,7 +137,7 @@ macro_rules! round_override { fn make_normal_broadcast( &self, rng: &mut impl CryptoRngCore, - serializer: &$crate::session::Serializer, + serializer: &$crate::protocol::Serializer, ) -> Result<$crate::protocol::NormalBroadcast, $crate::protocol::LocalError> { >::make_normal_broadcast(self, rng, serializer) } @@ -145,7 +145,7 @@ macro_rules! round_override { fn receive_message( &self, rng: &mut impl CryptoRngCore, - deserializer: &$crate::session::Deserializer, + deserializer: &$crate::protocol::Deserializer, from: &Id, echo_broadcast: $crate::protocol::EchoBroadcast, normal_broadcast: $crate::protocol::NormalBroadcast, diff --git a/manul/src/testing/wire_format.rs b/manul/src/testing/wire_format.rs new file mode 100644 index 0000000..ec9e4ae --- /dev/null +++ b/manul/src/testing/wire_format.rs @@ -0,0 +1,69 @@ +use alloc::{boxed::Box, string::ToString}; + +use serde::Serialize; +use serde_persistent_deserializer::{AsTransientDeserializer, PersistentDeserializer}; + +use crate::{protocol::LocalError, session::WireFormat}; + +/// A binary format to use in tests. +#[derive(Debug, Clone, Copy)] +pub struct BinaryFormat; + +/// A wrapper for a Postcard deserializer. +#[allow(missing_debug_implementations)] +pub struct PostcardDeserializer<'de>(postcard::Deserializer<'de, postcard::de_flavors::Slice<'de>>); + +impl<'de> AsTransientDeserializer<'de> for PostcardDeserializer<'de> { + type Error = postcard::Error; + + fn as_transient_deserializer<'a>(&'a mut self) -> impl serde::Deserializer<'de, Error = Self::Error> { + &mut self.0 + } +} + +impl WireFormat for BinaryFormat { + fn serialize(value: T) -> Result, LocalError> { + postcard::to_allocvec(&value) + .map(|vec| vec.into()) + .map_err(|err| LocalError::new(err.to_string())) + } + + type Deserializer<'de> = PersistentDeserializer>; + + fn deserializer(bytes: &[u8]) -> Self::Deserializer<'_> { + let flavor = postcard::de_flavors::Slice::new(bytes); + let deserializer = postcard::Deserializer::from_flavor(flavor); + PersistentDeserializer::new(PostcardDeserializer(deserializer)) + } +} + +/// A human-readable format to use in tests. +#[derive(Debug, Clone, Copy)] +pub struct HumanReadableFormat; + +/// A wrapper for a JSON deserializer. +#[allow(missing_debug_implementations)] +pub struct JSONDeserializer<'de>(serde_json::Deserializer>); + +impl<'de> AsTransientDeserializer<'de> for JSONDeserializer<'de> { + type Error = serde_json::Error; + + fn as_transient_deserializer<'a>(&'a mut self) -> impl serde::Deserializer<'de, Error = Self::Error> { + &mut self.0 + } +} + +impl WireFormat for HumanReadableFormat { + fn serialize(value: T) -> Result, LocalError> { + serde_json::to_vec(&value) + .map(|vec| vec.into()) + .map_err(|err| LocalError::new(err.to_string())) + } + + type Deserializer<'de> = PersistentDeserializer>; + + fn deserializer(bytes: &[u8]) -> Self::Deserializer<'_> { + let deserializer = serde_json::Deserializer::from_slice(bytes); + PersistentDeserializer::new(JSONDeserializer(deserializer)) + } +}