From 52b85934b010bc9363e14d815b6777a6801168e1 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 7 Nov 2024 13:32:22 +0100 Subject: [PATCH] seal: rewrite crate --- single_use_seals/src/lib.rs | 171 ++++++++++++++++++++++++++++-------- 1 file changed, 133 insertions(+), 38 deletions(-) diff --git a/single_use_seals/src/lib.rs b/single_use_seals/src/lib.rs index 3712641f..6b394838 100644 --- a/single_use_seals/src/lib.rs +++ b/single_use_seals/src/lib.rs @@ -19,16 +19,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Coding conventions -#![deny( - non_upper_case_globals, - non_camel_case_types, - non_snake_case, - unused_mut, - unused_imports, - dead_code, - missing_docs -)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] //! # Single-use-seals @@ -113,6 +103,10 @@ //! use core::borrow::Borrow; +use core::convert::Infallible; +use core::error::Error; +use core::fmt::{self, Debug, Display, Formatter}; +use core::marker::PhantomData; /// Trait for proof-of-publication medium on which the seals are defined, /// closed, verified and which can be used for convenience operations related to @@ -132,7 +126,7 @@ use core::borrow::Borrow; /// /// To read more on proof-of-publication please check /// -pub trait Seal: Sized { +pub trait SingleUseSeal: Clone + Debug + Display { /// Single-use seal parameters, which allow to differentiate alternative /// forms of single-use seals from each other. type Params; @@ -140,44 +134,145 @@ pub trait Seal: Sized { /// Seal parameters which the type commits to. const PARAMS: Self::Params; - /// Associated type for the witness produced by the single-use-seal close - /// procedure - type Witness: SealWitness; + /// Message type that is supported by the current single-use-seal. + type Message: Copy; - /// Message type that is supported by the current single-use-seal - type Message; + type PubWitness: PublishedWitness; + type CliWitness: ClientSideWitness; +} + +pub trait ClientSideWitness { + type Seal: SingleUseSeal; + type Error: Clone + Error; + + fn includes_seal(&self, seal: impl Borrow) -> bool; + fn convolve_commit( + &self, + msg: ::Message, + ) -> Result<::Message, Self::Error>; +} + +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] +pub struct NoWitness(PhantomData); +impl ClientSideWitness for NoWitness { + type Seal = Seal; + type Error = Infallible; + + fn includes_seal(&self, _: impl Borrow) -> bool { false } + + fn convolve_commit( + &self, + msg: ::Message, + ) -> Result<::Message, Self::Error> { + Ok(msg) + } +} +pub trait PublishedWitness { /// Publication id that may be used for referencing publication of /// witness data in the medium. By default, set `()`, so [`SealProtocol`] - /// may not implement publication id and related functions - type PublicationId; + /// may not implement publication id and related functions. + type PubId: Copy + Ord + Debug + Display; + + type Error: Clone + Error; + + fn pub_id(&self) -> Self::PubId; + fn includes_seal(&self, seal: impl Borrow) -> bool; + fn verify_commitment(&self, msg: Seal::Message) -> Result<(), Self::Error>; } -/// Seal witness which can verify seal or multiple seals. -pub trait SealWitness> { - /// Message type that is supported by the current single-use-seal - type Message; +/// Seal closing witness. +#[derive(Clone, Copy)] +pub struct SealWitness +where Seal: SingleUseSeal +{ + pub published: Seal::PubWitness, + pub client: Seal::CliWitness, + _phantom: PhantomData, +} - /// Error type that contains reasons of medium access failure - type Error: std::error::Error; +impl SealWitness +where Seal: SingleUseSeal +{ + pub fn includes_seal(&self, seal: impl Borrow) -> bool { + self.published.borrow().includes_seal(seal.borrow()) || + self.client.borrow().includes_seal(seal) + } - /// Verifies that the seal was indeed closed over the message with the - /// provided seal closure witness. - fn verify_seal(&self, seal: impl Borrow, msg: &Self::Message) -> Result<(), Self::Error>; + pub fn verify_seal_closing( + &self, + seal: impl Borrow, + message: Seal::Message, + ) -> Result<(), SealError> { + self.verify_seals_closing([seal], message) + } - /// Performs batch verification of the seals. - /// - /// Default implementation iterates through the seals and calls - /// [`Self::verify_seal`] for each of them, returning `false` on first - /// failure (not verifying the rest of seals). - fn verify_many_seals( + pub fn verify_seals_closing( &self, - seals: impl IntoIterator>, - msg: &Self::Message, - ) -> Result<(), Self::Error> { + seals: impl IntoIterator>, + message: Seal::Message, + ) -> Result<(), SealError> { + // ensure that witness includes all seals for seal in seals { - self.verify_seal(seal, msg)?; + self.includes_seal(seal.borrow()) + .then_some(()) + .ok_or(SealError::NotIncluded(seal.borrow().clone(), self.published.pub_id()))?; + } + // ensure that published witness contains the commitment to the + // f(message), where `f` is defined in the client-side witness + let f_msg = self + .client + .convolve_commit(message) + .map_err(SealError::Client)?; + self.published + .verify_commitment(f_msg) + .map_err(SealError::Published) + } +} + +#[derive(Clone)] +pub enum SealError { + NotIncluded(Seal, >::PubId), + Published(>::Error), + Client(::Error), +} + +impl Debug for SealError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + SealError::NotIncluded(seal, pub_id) => f + .debug_tuple("SealError::NotIncluded") + .field(seal) + .field(pub_id) + .finish(), + SealError::Published(err) => f.debug_tuple("SealError::Published").field(err).finish(), + SealError::Client(err) => f.debug_tuple("SealError::Client(err").field(err).finish(), + } + } +} + +impl Display for SealError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + SealError::NotIncluded(seal, pub_id) => { + write!(f, "seal {seal} is not included in the witness {pub_id}") + } + SealError::Published(err) => Display::fmt(err, f), + SealError::Client(err) => Display::fmt(err, f), + } + } +} + +impl Error for SealError +where + <::PubWitness as PublishedWitness>::Error: 'static, + <::CliWitness as ClientSideWitness>::Error: 'static, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + SealError::NotIncluded(..) => None, + SealError::Published(e) => Some(e), + SealError::Client(e) => Some(e), } - Ok(()) } }