round_based/mpc/mod.rs
1//! Party of MPC protocol
2//!
3//! [`MpcParty`] is party of MPC protocol, connected to network, ready to start carrying out the protocol.
4//!
5//! ```rust
6//! use round_based::{Incoming, Outgoing};
7//!
8//! # #[derive(round_based::ProtocolMsg)]
9//! # enum KeygenMsg {}
10//! # struct KeyShare;
11//! # struct Error;
12//! # type Result<T> = std::result::Result<T, Error>;
13//! # async fn doc() -> Result<()> {
14//! async fn keygen<M>(party: M, i: u16, n: u16) -> Result<KeyShare>
15//! where
16//! M: round_based::Mpc<Msg = KeygenMsg>
17//! {
18//! // ...
19//! # unimplemented!()
20//! }
21//! async fn connect() ->
22//! impl futures::Stream<Item = Result<Incoming<KeygenMsg>>>
23//! + futures::Sink<Outgoing<KeygenMsg>, Error = Error>
24//! + Unpin
25//! {
26//! // ...
27//! # round_based::_docs::fake_delivery()
28//! }
29//!
30//! let delivery = connect().await;
31//! let party = round_based::mpc::connected(delivery);
32//!
33//! # let (i, n) = (1, 3);
34//! let keyshare = keygen(party, i, n).await?;
35//! # Ok(()) }
36//! ```
37
38use crate::{
39 Outgoing, PartyIndex,
40 round::{RoundInfo, RoundStore},
41};
42
43pub mod party;
44
45#[doc(no_inline)]
46pub use self::party::{Halves, MpcParty};
47
48/// Abstracts functionalities needed for creating an MPC protocol execution.
49///
50/// An object implementing this trait is accepted as a parameter of a protocol. It is used to
51/// configure the protocol with [`Mpc::add_round`], and then finalized into a protocol executor
52/// with [`Mpc::finish_setup`]
53pub trait Mpc {
54 /// Protocol message
55 type Msg;
56
57 /// A type of a finalized instatiation of a party for this protocol
58 ///
59 /// After being created with [`Mpc::finish_setup`], you can use an object with this type to
60 /// drive the protocol execution using the methods of [`MpcExecution`].
61 type Exec: MpcExecution<Msg = Self::Msg, SendErr = Self::SendErr>;
62 /// Error indicating that sending a message has failed
63 type SendErr;
64
65 /// Registers a round
66 fn add_round<R>(&mut self, round: R) -> <Self::Exec as MpcExecution>::Round<R>
67 where
68 R: RoundStore,
69 Self::Msg: RoundMsg<R::Msg>;
70
71 /// Completes network setup
72 ///
73 /// Once this method is called, no more rounds can be added,
74 /// but the protocol can receive and send messages.
75 fn finish_setup(self) -> Self::Exec;
76}
77
78/// Abstracts functionalities needed for MPC protocol execution
79pub trait MpcExecution {
80 /// Witness that round was registered
81 ///
82 /// It's obtained by registering round in [`Mpc::add_round`], which then can be used to retrieve
83 /// messages from associated round by calling [`MpcExecution::complete`].
84 type Round<R: RoundInfo>;
85
86 /// Protocol message
87 type Msg;
88
89 /// Error indicating that completing a round has failed
90 type CompleteRoundErr<E>;
91 /// Error indicating that sending a message has failed
92 type SendErr;
93
94 /// Returned by [`.send_many()`](Self::send_many)
95 type SendMany: SendMany<Exec = Self, Msg = Self::Msg, SendErr = Self::SendErr>;
96
97 /// Completes the round
98 ///
99 /// Waits until we receive all the messages in the round `R` from other parties. Returns
100 /// received messages.
101 async fn complete<R>(
102 &mut self,
103 round: Self::Round<R>,
104 ) -> Result<R::Output, Self::CompleteRoundErr<R::Error>>
105 where
106 R: RoundInfo,
107 Self::Msg: RoundMsg<R::Msg>;
108
109 /// Sends a message
110 ///
111 /// This method awaits until the message is sent, which might be not the best method to use if you
112 /// need to send many messages at once. If it's the case, prefer using [`.send_many()`](Self::send_many).
113 async fn send(&mut self, msg: Outgoing<Self::Msg>) -> Result<(), Self::SendErr>;
114
115 /// Sends a p2p message to another party
116 ///
117 /// Note: when you send many messages at once (it's most likely the case when you send a p2p message), this method
118 /// is not efficient, prefer using [`.send_many()`](Self::send_many).
119 async fn send_p2p(
120 &mut self,
121 recipient: PartyIndex,
122 msg: Self::Msg,
123 ) -> Result<(), Self::SendErr> {
124 self.send(Outgoing::p2p(recipient, msg)).await
125 }
126
127 /// Sends a message that will be received by all parties
128 ///
129 /// Message will be broadcasted, but not reliably. If you need a reliable broadcast, use
130 /// [`MpcExecution::reliably_broadcast`] method.
131 async fn send_to_all(&mut self, msg: Self::Msg) -> Result<(), Self::SendErr> {
132 self.send(Outgoing::all_parties(msg)).await
133 }
134
135 /// Reliably broadcasts a message
136 ///
137 /// Message will be received by all participants of the protocol. Moreover, when recipient receives a
138 /// message, it will be assured (cryptographically or through other trust assumptions) that all honest
139 /// participants of the protocol received the same message.
140 ///
141 /// It's a responsibility of a message delivery layer to provide the reliable broadcast mechanism. If
142 /// it's not supported, this method returns an error. Note that not every MPC protocol requires the
143 /// reliable broadcast, so it's totally normal to have a message delivery implementation that does
144 /// not support it.
145 async fn reliably_broadcast(&mut self, msg: Self::Msg) -> Result<(), Self::SendErr> {
146 self.send(Outgoing::reliable_broadcast(msg)).await
147 }
148
149 /// Creates a buffer of outgoing messages so they can be sent all at once
150 ///
151 /// When you have many messages that you want to send at once, using [`.send()`](Self::send)
152 /// may be inefficient, as delivery implementation may pause the execution until the message is
153 /// received by the recipient. Use this method to send many messages in a batch.
154 ///
155 /// This method takes ownership of `self` to create the [impl SendMany](SendMany) object. After
156 /// enqueueing all the messages, you need to reclaim `self` back by calling [`SendMany::flush`].
157 fn send_many(self) -> Self::SendMany;
158
159 /// Yields execution back to the async runtime
160 ///
161 /// Used in MPC protocols with many heavy synchronous computations. The protocol implementors
162 /// can manually insert yield points to ease the CPU contention
163 async fn yield_now(&self);
164}
165
166/// Buffer, optimized for sending many messages at once
167///
168/// It's obtained by calling [`MpcExecution::send_many`], which takes ownership of `MpcExecution`. To reclaim
169/// ownership, call [`SendMany::flush`].
170pub trait SendMany {
171 /// MPC executor, returned after successful [`.flush()`](Self::flush)
172 type Exec: MpcExecution<Msg = Self::Msg, SendErr = Self::SendErr>;
173 /// Protocol message
174 type Msg;
175 /// Error indicating that sending a message has failed
176 type SendErr;
177
178 /// Adds a message to the sending queue
179 ///
180 /// Similar to [`MpcExecution::send`], but possibly buffers a message until [`.flush()`](Self::flush) is
181 /// called.
182 ///
183 /// A call to this function may send the message, but this is not guaranteed by the API. To
184 /// flush the sending queue and send all messages, use [`.flush()`](Self::flush).
185 async fn send(&mut self, msg: Outgoing<Self::Msg>) -> Result<(), Self::SendErr>;
186
187 /// Adds a p2p message to the sending queue
188 ///
189 /// Similar to [`MpcExecution::send_p2p`], but possibly buffers a message until [`.flush()`](Self::flush) is
190 /// called.
191 ///
192 /// A call to this function may send the message, but this is not guaranteed by the API. To
193 /// flush the sending queue and send all messages, use [`.flush()`](Self::flush).
194 async fn send_p2p(
195 &mut self,
196 recipient: PartyIndex,
197 msg: Self::Msg,
198 ) -> Result<(), Self::SendErr> {
199 self.send(Outgoing::p2p(recipient, msg)).await
200 }
201
202 /// Adds a broadcast message to the sending queue
203 ///
204 /// Similar to [`MpcExecution::send_to_all`], but possibly buffers a message until [`.flush()`](Self::flush) is
205 /// called.
206 ///
207 /// A call to this function may send the message, but this is not guaranteed by the API. To
208 /// flush the sending queue and send all messages, use [`.flush()`](Self::flush).
209 async fn send_to_all(&mut self, msg: Self::Msg) -> Result<(), Self::SendErr> {
210 self.send(Outgoing::all_parties(msg)).await
211 }
212
213 /// Adds a reliable broadcast message to the sending queue
214 ///
215 /// Similar to [`MpcExecution::reliably_broadcast`], but possibly buffers a message until [`.flush()`](Self::flush) is
216 /// called.
217 ///
218 /// A call to this function may send the message, but this is not guaranteed by the API. To
219 /// flush the sending queue and send all messages, use [`Self::flush`]
220 async fn reliably_broadcast(&mut self, msg: Self::Msg) -> Result<(), Self::SendErr> {
221 self.send(Outgoing::reliable_broadcast(msg)).await
222 }
223
224 /// Flushes internal buffer by sending all messages in the queue
225 async fn flush(self) -> Result<Self::Exec, Self::SendErr>;
226}
227
228/// Alias to `<<M as Mpc>::Exec as MpcExecution>::CompleteRoundErr<E>`
229pub type CompleteRoundErr<M, E> = <<M as Mpc>::Exec as MpcExecution>::CompleteRoundErr<E>;
230
231/// Message of MPC protocol
232///
233/// MPC protocols typically consist of several rounds, each round has differently typed message.
234/// `ProtocolMsg` and [`RoundMsg`] traits are used to examine received message: `ProtocolMsg::round`
235/// determines which round message belongs to, and then `RoundMsg` trait can be used to retrieve
236/// actual round-specific message.
237///
238/// You should derive these traits using proc macro (requires `derive` feature):
239/// ```rust
240/// use round_based::ProtocolMsg;
241///
242/// #[derive(ProtocolMsg)]
243/// pub enum Message {
244/// Round1(Msg1),
245/// Round2(Msg2),
246/// // ...
247/// }
248///
249/// pub struct Msg1 { /* ... */ }
250/// pub struct Msg2 { /* ... */ }
251/// ```
252///
253/// This desugars into:
254///
255/// ```rust
256/// use round_based::{ProtocolMsg, RoundMsg};
257///
258/// pub enum Message {
259/// Round1(Msg1),
260/// Round2(Msg2),
261/// // ...
262/// }
263///
264/// pub struct Msg1 { /* ... */ }
265/// pub struct Msg2 { /* ... */ }
266///
267/// impl ProtocolMsg for Message {
268/// fn round(&self) -> u16 {
269/// match self {
270/// Message::Round1(_) => 1,
271/// Message::Round2(_) => 2,
272/// // ...
273/// }
274/// }
275/// }
276/// impl RoundMsg<Msg1> for Message {
277/// const ROUND: u16 = 1;
278/// fn to_protocol_msg(round_msg: Msg1) -> Self {
279/// Message::Round1(round_msg)
280/// }
281/// fn from_protocol_msg(protocol_msg: Self) -> Result<Msg1, Self> {
282/// match protocol_msg {
283/// Message::Round1(msg) => Ok(msg),
284/// msg => Err(msg),
285/// }
286/// }
287/// }
288/// impl RoundMsg<Msg2> for Message {
289/// const ROUND: u16 = 2;
290/// fn to_protocol_msg(round_msg: Msg2) -> Self {
291/// Message::Round2(round_msg)
292/// }
293/// fn from_protocol_msg(protocol_msg: Self) -> Result<Msg2, Self> {
294/// match protocol_msg {
295/// Message::Round2(msg) => Ok(msg),
296/// msg => Err(msg),
297/// }
298/// }
299/// }
300/// ```
301pub trait ProtocolMsg: Sized {
302 /// Number of the round that this message originates from
303 fn round(&self) -> u16;
304}
305
306/// Round message
307///
308/// See [`ProtocolMsg`] trait documentation.
309pub trait RoundMsg<M>: ProtocolMsg {
310 /// Number of the round this message belongs to
311 const ROUND: u16;
312
313 /// Converts round message into protocol message (never fails)
314 fn to_protocol_msg(round_msg: M) -> Self;
315 /// Extracts round message from protocol message
316 ///
317 /// Returns `Err(protocol_message)` if `protocol_message.round() != Self::ROUND`, otherwise
318 /// returns `Ok(round_message)`
319 fn from_protocol_msg(protocol_msg: Self) -> Result<M, Self>;
320}
321
322/// Construct an [`MpcParty`] that can be used to carry out MPC protocol
323///
324/// Accepts a channels with incoming and outgoing messages.
325///
326/// Alias to [`MpcParty::connected`]
327pub fn connected<M, D>(delivery: D) -> MpcParty<M, D>
328where
329 M: ProtocolMsg + 'static,
330 D: futures_util::Stream<Item = Result<crate::Incoming<M>, D::Error>> + Unpin,
331 D: futures_util::Sink<Outgoing<M>> + Unpin,
332{
333 MpcParty::connected(delivery)
334}
335
336/// Construct an [`MpcParty`] that can be used to carry out MPC protocol
337///
338/// Accepts separately a channel for incoming and a channel for outgoing messages.
339///
340/// Alias to [`MpcParty::connected_halves`]
341pub fn connected_halves<M, In, Out>(incomings: In, outgoings: Out) -> MpcParty<M, Halves<In, Out>>
342where
343 M: ProtocolMsg + 'static,
344 In: futures_util::Stream<Item = Result<crate::Incoming<M>, Out::Error>> + Unpin,
345 Out: futures_util::Sink<Outgoing<M>> + Unpin,
346{
347 MpcParty::connected_halves(incomings, outgoings)
348}