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}