1
1
//! Multiparty protocol simulation
2
2
//!
3
- //! [`Simulation`] is an essential developer tool for testing the multiparty protocol locally.
3
+ //! Simulator is an essential developer tool for testing the multiparty protocol locally.
4
4
//! It covers most of the boilerplate by mocking networking.
5
5
//!
6
- //! ## Example
6
+ //! The entry point is either [`run`] or [`run_with_setup`] functions. They take a protocol
7
+ //! defined as an async function, provide simulated networking, carry out the simulation,
8
+ //! and return the result.
9
+ //!
10
+ //! If you need more control over execution, you can use [`Network`] to simulate the networking
11
+ //! and carry out the protocol manually.
12
+ //!
13
+ //! When `state-machine` feature is enabled, [`SimulationSync`] is available which can carry out
14
+ //! protocols defined as a state machine.
7
15
//!
8
- //! ```rust
16
+ //! ## Example
17
+ //! ```rust,no_run
18
+ //! # #[tokio::main(flavor = "current_thread")]
19
+ //! # async fn main() {
9
20
//! use round_based::{Mpc, PartyIndex};
10
- //! use round_based::simulation::Simulation;
11
- //! use futures::future::try_join_all;
12
21
//!
13
22
//! # type Result<T, E = ()> = std::result::Result<T, E>;
14
23
//! # type Randomness = [u8; 32];
15
24
//! # type Msg = ();
16
25
//! // Any MPC protocol you want to test
17
- //! pub async fn protocol_of_random_generation<M>(party: M, i: PartyIndex, n: u16) -> Result<Randomness>
18
- //! where M: Mpc<ProtocolMessage = Msg>
26
+ //! pub async fn protocol_of_random_generation<M>(
27
+ //! party: M,
28
+ //! i: PartyIndex,
29
+ //! n: u16
30
+ //! ) -> Result<Randomness>
31
+ //! where
32
+ //! M: Mpc<ProtocolMessage = Msg>
19
33
//! {
20
34
//! // ...
21
35
//! # todo!()
22
36
//! }
23
37
//!
24
- //! async fn test_randomness_generation() {
25
- //! let n = 3;
26
- //!
27
- //! let mut simulation = Simulation::<Msg>::new();
28
- //! let mut outputs = vec![];
29
- //! for i in 0..n {
30
- //! let party = simulation.add_party();
31
- //! outputs.push(protocol_of_random_generation(party, i, n));
32
- //! }
33
- //!
34
- //! // Waits each party to complete the protocol
35
- //! let outputs = try_join_all(outputs).await.expect("protocol wasn't completed successfully");
36
- //! // Asserts that all parties output the same randomness
37
- //! for output in outputs.iter().skip(1) {
38
- //! assert_eq!(&outputs[0], output);
39
- //! }
40
- //! }
38
+ //! let n = 3;
39
+ //!
40
+ //! let output = round_based::simulation::run(
41
+ //! n,
42
+ //! |i, party| protocol_of_random_generation(party, i, n),
43
+ //! )
44
+ //! .await
45
+ //! // unwrap `Result`s
46
+ //! .expect_success()
47
+ //! // check that all parties produced the same response
48
+ //! .expect_same();
49
+ //!
50
+ //! println!("Output randomness: {}", hex::encode(output));
51
+ //! # }
41
52
//! ```
42
53
43
54
mod sim_async;
@@ -47,3 +58,102 @@ mod sim_sync;
47
58
pub use sim_async:: * ;
48
59
#[ cfg( feature = "state-machine" ) ]
49
60
pub use sim_sync:: * ;
61
+
62
+ /// Result of the simulation
63
+ pub struct SimResult < T > ( pub alloc:: vec:: Vec < T > ) ;
64
+
65
+ impl < T , E > SimResult < Result < T , E > >
66
+ where
67
+ E : core:: fmt:: Debug ,
68
+ {
69
+ /// Unwraps `Result<T, E>` produced by each party
70
+ ///
71
+ /// Panics if at least one of the parties returned `Err(_)`. In this case,
72
+ /// a verbose error message will shown specifying which of the parties returned
73
+ /// an error.
74
+ pub fn expect_success ( self ) -> SimResult < T > {
75
+ let mut oks = alloc:: vec:: Vec :: with_capacity ( self . 0 . len ( ) ) ;
76
+ let mut errs = alloc:: vec:: Vec :: with_capacity ( self . 0 . len ( ) ) ;
77
+
78
+ for ( res, i) in self . 0 . into_iter ( ) . zip ( 0u16 ..) {
79
+ match res {
80
+ Ok ( res) => oks. push ( res) ,
81
+ Err ( res) => errs. push ( ( i, res) ) ,
82
+ }
83
+ }
84
+
85
+ if !errs. is_empty ( ) {
86
+ let mut msg = alloc:: format!(
87
+ "Simulation output didn't match expectations.\n \
88
+ Expected: all parties succeed\n \
89
+ Actual : {success} parties succeeded, {failed} parties returned an error\n \
90
+ Failures:\n ",
91
+ success = oks. len( ) ,
92
+ failed = errs. len( ) ,
93
+ ) ;
94
+
95
+ for ( i, err) in errs {
96
+ msg += & alloc:: format!( "- Party {i}: {err:?}\n " ) ;
97
+ }
98
+
99
+ panic ! ( "{msg}" ) ;
100
+ }
101
+
102
+ SimResult ( oks)
103
+ }
104
+ }
105
+
106
+ impl < T > SimResult < T >
107
+ where
108
+ T : PartialEq + core:: fmt:: Debug ,
109
+ {
110
+ /// Checks that outputs of all parties are equally the same
111
+ ///
112
+ /// Returns the output on success (all the outputs are checked to be the same), otherwise
113
+ /// panics with a verbose error message.
114
+ ///
115
+ /// Panics if simulation contained zero parties.
116
+ pub fn expect_same ( mut self ) -> T {
117
+ let Some ( first) = self . 0 . get ( 0 ) else {
118
+ panic ! ( "simulation contained zero parties" ) ;
119
+ } ;
120
+
121
+ if !self . 0 [ 1 ..] . iter ( ) . all ( |i| i == first) {
122
+ let mut msg = alloc:: format!(
123
+ "Simulation output didn't match expectations.\n \
124
+ Expected: all parties return the same output\n \
125
+ Actual : some of the parties returned a different output\n \
126
+ Outputs :\n "
127
+ ) ;
128
+
129
+ for ( i, res) in self . 0 . iter ( ) . enumerate ( ) {
130
+ msg += & alloc:: format!( "- Party {i}: {res:?}" ) ;
131
+ }
132
+
133
+ panic ! ( "{msg}" )
134
+ }
135
+
136
+ self . 0
137
+ . pop ( )
138
+ . expect ( "we checked that the list contains at least one element" )
139
+ }
140
+ }
141
+
142
+ impl < T > SimResult < T > {
143
+ /// Deconstructs the simulation result returning inner list of results
144
+ pub fn into_vec ( self ) -> alloc:: vec:: Vec < T > {
145
+ self . 0
146
+ }
147
+ }
148
+
149
+ impl < T > From < alloc:: vec:: Vec < T > > for SimResult < T > {
150
+ fn from ( list : alloc:: vec:: Vec < T > ) -> Self {
151
+ Self ( list)
152
+ }
153
+ }
154
+
155
+ impl < T > From < SimResult < T > > for alloc:: vec:: Vec < T > {
156
+ fn from ( res : SimResult < T > ) -> Self {
157
+ res. 0
158
+ }
159
+ }
0 commit comments