round_based/round/
mod.rs

1//! Primitives that process and collect messages received at certain round
2
3use core::any::Any;
4
5use crate::Incoming;
6
7pub use self::simple_store::{
8    RoundInput, RoundInputError, RoundMsgs, broadcast, p2p, reliable_broadcast,
9};
10
11mod simple_store;
12
13/// Common information about a round
14pub trait RoundInfo: Sized + 'static {
15    /// Message type
16    type Msg;
17    /// Store output (e.g. `Vec<_>` of received messages)
18    type Output;
19    /// Store error
20    type Error: core::error::Error;
21}
22
23/// Stores messages received at particular round
24///
25/// In MPC protocol, party at every round usually needs to receive up to `n` messages. `RoundsStore`
26/// is a container that stores messages, it knows how many messages are expected to be received,
27/// and should implement extra measures against malicious parties (e.g. prohibit message overwrite).
28///
29/// ## Flow
30/// `RoundStore` stores received messages. Once enough messages are received, it outputs [`RoundInfo::Output`].
31/// In order to save received messages, [`.add_message(msg)`] is called. Then, [`.wants_more()`] tells whether more
32/// messages are needed to be received. If it returned `false`, then output can be retrieved by calling [`.output()`].
33///
34/// [`.add_message(msg)`]: Self::add_message
35/// [`.wants_more()`]: Self::wants_more
36/// [`.output()`]: Self::output
37///
38/// ## Example
39/// [`RoundInput`] is an simple messages store. Refer to its docs to see usage examples.
40pub trait RoundStore: RoundInfo {
41    /// Adds received message to the store
42    ///
43    /// Returns error if message cannot be processed. Usually it means that sender behaves maliciously.
44    fn add_message(&mut self, msg: Incoming<Self::Msg>) -> Result<(), Self::Error>;
45    /// Indicates if store expects more messages to receive
46    fn wants_more(&self) -> bool;
47    /// Retrieves store output if enough messages are received
48    ///
49    /// Returns `Err(self)` if more message are needed to be received.
50    ///
51    /// If store indicated that it needs no more messages (ie `store.wants_more() == false`), then
52    /// this function must return `Ok(_)`.
53    fn output(self) -> Result<Self::Output, Self>;
54
55    /// Interface that exposes ability to retrieve generic information about the round store
56    ///
57    /// For reading store properties, it's recommended to use [`RoundStoreExt::read_prop`] method which
58    /// uses this function internally.
59    ///
60    /// When implementing `RoundStore` trait, if you wish to expose no extra information, leave the default
61    /// implementation of this method. If you do want to expose certain properties that will be accessible
62    /// through [`RoundStoreExt::read_prop`], follow this example:
63    ///
64    /// ```rust
65    /// pub struct MyStore { /* ... */ }
66    ///
67    /// #[derive(Debug, PartialEq, Eq)]
68    /// pub struct SomePropertyWeWantToExpose { value: u64 }
69    /// #[derive(Debug, PartialEq, Eq)]
70    /// pub struct AnotherProperty(String);
71    ///
72    /// # type Msg = ();
73    /// # impl round_based::round::RoundInfo for MyStore {
74    /// #    type Msg = Msg;
75    /// #    type Output = Vec<Msg>;
76    /// #    type Error = core::convert::Infallible;
77    /// # }
78    /// impl round_based::round::RoundStore for MyStore {
79    /// #    fn add_message(&mut self, msg: round_based::Incoming<Self::Msg>) -> Result<(), Self::Error> { unimplemented!() }
80    /// #    fn wants_more(&self) -> bool { unimplemented!() }
81    /// #    fn output(self) -> Result<Self::Output, Self> { unimplemented!() }
82    ///     // ...
83    ///
84    ///     fn read_any_prop(&self, property: &mut dyn core::any::Any) {
85    ///         if let Some(p) = property.downcast_mut::<Option<SomePropertyWeWantToExpose>>() {
86    ///             *p = Some(SomePropertyWeWantToExpose { value: 42 })
87    ///         } else if let Some(p) = property.downcast_mut::<Option<AnotherProperty>>() {
88    ///             *p = Some(AnotherProperty("here we return a string".to_owned()))
89    ///         }
90    ///     }
91    /// }
92    ///
93    /// // Which then can be accessed via `.read_prop()` method:
94    /// use round_based::round::RoundStoreExt;
95    /// let store = MyStore { /* ... */ };
96    /// assert_eq!(
97    ///     store.read_prop::<SomePropertyWeWantToExpose>(),
98    ///     Some(SomePropertyWeWantToExpose { value: 42 }),
99    /// );
100    /// assert_eq!(
101    ///     store.read_prop::<AnotherProperty>(),
102    ///     Some(AnotherProperty("here we return a string".to_owned())),
103    /// );
104    /// ```
105    fn read_any_prop(&self, property: &mut dyn Any) {
106        let _ = property;
107    }
108}
109
110/// Extra functionalities defined for any [`RoundStore`]
111pub trait RoundStoreExt: RoundStore {
112    /// Reads a property `P` of the store
113    ///
114    /// Returns `Some(property_value)` if this store exposes property `P`, otherwise returns `None`
115    fn read_prop<P: Any>(&self) -> Option<P>;
116
117    /// Constructs a new store that exposes property `P` with provided value
118    ///
119    /// If store already provides a property `P`, it will be overwritten
120    fn set_prop<P: Clone + 'static>(self, value: P) -> WithProp<P, Self>;
121}
122
123impl<S: RoundStore> RoundStoreExt for S {
124    fn read_prop<P: Any>(&self) -> Option<P> {
125        let mut p: Option<P> = None;
126        self.read_any_prop(&mut p);
127        p
128    }
129
130    fn set_prop<P: Clone + 'static>(self, value: P) -> WithProp<P, Self> {
131        WithProp {
132            prop: value,
133            store: self,
134        }
135    }
136}
137
138/// Returned by [`RoundStoreExt::set_prop`]
139pub struct WithProp<P, S> {
140    prop: P,
141    store: S,
142}
143
144impl<P, S> RoundInfo for WithProp<P, S>
145where
146    S: RoundInfo,
147    P: 'static,
148{
149    type Msg = S::Msg;
150    type Output = S::Output;
151    type Error = S::Error;
152}
153
154impl<P, S> RoundStore for WithProp<P, S>
155where
156    S: RoundStore,
157    P: Clone + 'static,
158{
159    #[inline(always)]
160    fn add_message(&mut self, msg: Incoming<Self::Msg>) -> Result<(), Self::Error> {
161        self.store.add_message(msg)
162    }
163    #[inline(always)]
164    fn wants_more(&self) -> bool {
165        self.store.wants_more()
166    }
167    #[inline(always)]
168    fn output(self) -> Result<Self::Output, Self> {
169        self.store.output().map_err(|store| Self {
170            prop: self.prop,
171            store,
172        })
173    }
174
175    fn read_any_prop(&self, property: &mut dyn Any) {
176        if let Some(p) = property.downcast_mut::<Option<P>>() {
177            *p = Some(self.prop.clone())
178        } else {
179            self.store.read_any_prop(property);
180        }
181    }
182}
183
184/// Properties that may be exposed by [`RoundStore`]
185pub mod props {
186    /// Indicates whether the round requires messages to be reliably broadcasted
187    pub struct RequiresReliableBroadcast(pub bool);
188}