use std::fmt::Debug;
use crate::sm::*;
mod benchmark;
use benchmark::Benchmark;
pub use benchmark::{BenchmarkResults, Measurements};
pub struct Simulation<P> {
pub parties: Vec<P>,
benchmark: Option<Benchmark>,
}
impl<P> Simulation<P> {
pub fn new() -> Self {
Self {
parties: vec![],
benchmark: None,
}
}
pub fn add_party(&mut self, party: P) -> &mut Self {
self.parties.push(party);
self
}
pub fn enable_benchmarks(&mut self, enable: bool) -> &mut Self {
if enable {
self.benchmark = Some(Benchmark::new())
} else {
self.benchmark = None
}
self
}
pub fn benchmark_results(&self) -> Option<&BenchmarkResults> {
self.benchmark.as_ref().map(|b| b.results())
}
}
impl<P> Simulation<P>
where
P: StateMachine,
P: Debug,
P::Err: Debug,
P::MessageBody: Debug,
P::MessageBody: Clone,
{
pub fn run(&mut self) -> Result<Vec<P::Output>, P::Err> {
assert!(self.parties.len() >= 2, "at least two parties required");
println!("Simulation starts");
loop {
let mut msgs: Vec<Msg<P::MessageBody>> = vec![];
for party in &mut self.parties {
if party.wants_to_proceed() {
println!("Party {} wants to proceed", party.party_ind());
println!(" - before: {:?}", party);
let round_old = party.current_round();
let stopwatch = self.benchmark.as_mut().map(|b| b.start());
match party.proceed() {
Ok(()) => (),
Err(err) if err.is_critical() => return Err(err),
Err(err) => {
println!("Non-critical error encountered: {:?}", err);
}
}
let round_new = party.current_round();
let duration = stopwatch
.filter(|_| round_old + 1 == round_new)
.map(|s| s.stop_and_save(round_old));
println!(" - after : {:?}", party);
println!(" - time : {:?}", duration);
}
println!(
"Party {} sends {} message(s)",
party.party_ind(),
party.message_queue().len()
);
msgs.append(party.message_queue())
}
for party in &mut self.parties {
let party_i = party.party_ind();
let msgs = msgs.iter().filter(|m| {
m.sender != party_i && (m.receiver.is_none() || m.receiver == Some(party_i))
});
for msg in msgs {
assert!(
!party.wants_to_proceed(),
"simulation is silly and doesn't expect party \
to wanna proceed at the middle of message handling"
);
println!(
"Party {} got message from={}, broadcast={}: {:?}",
party.party_ind(),
msg.sender,
msg.receiver.is_none(),
msg,
);
println!(" - before: {:?}", party);
match party.handle_incoming(msg.clone()) {
Ok(()) => (),
Err(err) if err.is_critical() => return Err(err),
Err(err) => {
println!("Non-critical error encountered: {:?}", err);
}
}
println!(" - after : {:?}", party);
}
}
let is_finished = self.parties[0].is_finished();
let same_answer_for_all_parties =
self.parties.iter().all(|p| p.is_finished() == is_finished);
assert!(same_answer_for_all_parties);
if is_finished {
let mut results = vec![];
for party in &mut self.parties {
results.push(
party
.pick_output()
.expect("is_finished == true, but pick_output == None")?,
)
}
break Ok(results);
}
}
}
}