Skip to content

Commit

Permalink
Merge pull request #14 from LFDT-Lockness/impr-sim
Browse files Browse the repository at this point in the history
Improve simulation UX
  • Loading branch information
survived authored Dec 4, 2024
2 parents a7f14b2 + f6f44d3 commit 100b267
Show file tree
Hide file tree
Showing 24 changed files with 1,340 additions and 679 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ jobs:
- name: cargo check
run: cargo check -p round-based --all-features

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: "true"
- name: cargo test
run: cargo test --all-features

check-fmt:
runs-on: ubuntu-latest
steps:
Expand Down
54 changes: 25 additions & 29 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ the documentation of the protocol you're using), but usually they are:

## Features

* `dev` enables development tools such as protocol simulation
* `sim` enables protocol execution simulation, see `sim` module
* `sim-async` enables protocol execution simulation with tokio runtime, see `sim::async_env`
module
* `state-machine` provides ability to carry out the protocol, defined as async function, via Sync
API, see `state_machine` module
* `derive` is needed to use `ProtocolMessage` proc macro
* `runtime-tokio` enables tokio-specific implementation of async runtime

## Join us in Discord!
Expand Down
8 changes: 2 additions & 6 deletions examples/random-generation-protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,15 @@ rand_core = { version = "0.6", default-features = false }
sha2 = { version = "0.10", default-features = false }
serde = { version = "1", default-features = false, features = ["derive"] }

displaydoc = { version = "0.2", default-features = false }
thiserror = { version = "1", optional = true }
thiserror = { version = "2", default-features = false }

# We don't use it directy, but we need to enable `serde` feature
generic-array = { version = "0.14", features = ["serde"] }

[dev-dependencies]
round-based = { path = "../../round-based", features = ["derive", "dev", "state-machine"] }
round-based = { path = "../../round-based", features = ["derive", "sim", "state-machine"] }
tokio = { version = "1.15", features = ["macros", "rt"] }
futures = "0.3"
hex = "0.4"
rand_dev = "0.1"
rand = "0.8"

[features]
std = ["thiserror"]
88 changes: 35 additions & 53 deletions examples/random-generation-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
#![no_std]
#![forbid(unused_crate_dependencies, missing_docs)]

#[cfg(any(feature = "std", test))]
#[cfg(test)]
extern crate std;

extern crate alloc;

mod _unused_deps {
// We don't use it directy, but we need to enable `serde` feature
// We don't use it directly, but we need to enable `serde` feature
use generic_array as _;
}

Expand Down Expand Up @@ -132,28 +132,23 @@ where
}

/// Protocol error
#[derive(Debug, displaydoc::Display)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
#[derive(Debug, thiserror::Error)]
pub enum Error<RecvErr, SendErr> {
/// Couldn't send a message in the first round
#[displaydoc("send a message at round 1")]
Round1Send(#[cfg_attr(feature = "std", source)] SendErr),
#[error("send a message at round 1")]
Round1Send(#[source] SendErr),
/// Couldn't receive a message in the first round
#[displaydoc("receive messages at round 1")]
Round1Receive(
#[cfg_attr(feature = "std", source)] CompleteRoundError<RoundInputError, RecvErr>,
),
#[error("receive messages at round 1")]
Round1Receive(#[source] CompleteRoundError<RoundInputError, RecvErr>),
/// Couldn't send a message in the second round
#[displaydoc("send a message at round 2")]
Round2Send(#[cfg_attr(feature = "std", source)] SendErr),
#[error("send a message at round 2")]
Round2Send(#[source] SendErr),
/// Couldn't receive a message in the second round
#[displaydoc("receive messages at round 2")]
Round2Receive(
#[cfg_attr(feature = "std", source)] CompleteRoundError<RoundInputError, RecvErr>,
),
#[error("receive messages at round 2")]
Round2Receive(#[source] CompleteRoundError<RoundInputError, RecvErr>),

/// Some of the parties cheated
#[displaydoc("malicious parties: {guilty_parties:?}")]
#[error("malicious parties: {guilty_parties:?}")]
PartiesOpenedRandomnessDoesntMatchCommitment {
/// List of cheated parties
guilty_parties: Vec<Blame>,
Expand All @@ -173,56 +168,43 @@ pub struct Blame {

#[cfg(test)]
mod tests {
use alloc::{vec, vec::Vec};

use rand::Rng;
use round_based::simulation::Simulation;
use sha2::{Digest, Sha256};

use super::{protocol_of_random_generation, Msg};
use super::protocol_of_random_generation;

#[tokio::test]
async fn simulation_async() {
#[test]
fn simulation() {
let mut rng = rand_dev::DevRng::new();

let n: u16 = 5;

let mut simulation = Simulation::<Msg>::new();
let mut party_output = vec![];

for i in 0..n {
let party = simulation.add_party();
let output = protocol_of_random_generation(party, i, n, rng.fork());
party_output.push(output);
}

let output = futures::future::try_join_all(party_output).await.unwrap();

// Assert that all parties outputed the same randomness
for i in 1..n {
assert_eq!(output[0], output[usize::from(i)]);
}
let randomness = round_based::sim::run_with_setup(
core::iter::repeat_with(|| rng.fork()).take(n.into()),
|i, party, rng| protocol_of_random_generation(party, i, n, rng),
)
.unwrap()
.expect_ok()
.expect_eq();

std::println!("Output randomness: {}", hex::encode(output[0]));
std::println!("Output randomness: {}", hex::encode(randomness));
}

#[test]
fn simulation_sync() {
#[tokio::test]
async fn simulation_async() {
let mut rng = rand_dev::DevRng::new();

let simulation = round_based::simulation::SimulationSync::from_async_fn(5, |i, party| {
protocol_of_random_generation(party, i, 5, rng.fork())
});
let n: u16 = 5;

let outputs = simulation
.run()
.unwrap()
.into_iter()
.collect::<Result<Vec<_>, _>>()
.unwrap();
for output_i in &outputs {
assert_eq!(*output_i, outputs[0]);
}
let randomness = round_based::sim::async_env::run_with_setup(
core::iter::repeat_with(|| rng.fork()).take(n.into()),
|i, party, rng| protocol_of_random_generation(party, i, n, rng),
)
.await
.expect_ok()
.expect_eq();

std::println!("Output randomness: {}", hex::encode(randomness));
}

// Emulate the protocol using the state machine interface
Expand Down
31 changes: 31 additions & 0 deletions round-based/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
## v0.4.0
* BREAKING: Improve ergonomics of protocol simulation, which is used for writing tests [#14]
* Remove `dev` feature, it's replaced with `sim` and `sim-async`
* `round_based::simulation` module is renamed into `round_based::sim`
* `round_based::simulation::Simulation` is renamed and moved to `round_based::sim::async_env::Network`
* Other async simulated network related types are moved to `round_based::sim::async_env`
* Added convenient `round_based::sim::{run, run_with_setup}` which make simulation very ergonomic
* Simulation outputs `round_based::sim::SimResult`, which has convenient most-common methods:
* `.expect_ok()` that unwraps all results, and if any party returned an error, panics with a verbose
error message
* `.expect_eq()` that checks that all outputs are equally the same
* When `sim-async` feature is enabled, you can use `round_based::sim::async_env::{run, run_with_setup, ...}`,
but typically you don't want to use them
* `round_based::simulation::SimulationSync` has been renamed to `round_based::sim::Simulation`
* Use `core::error::Error` trait which is now always implemented for all errors regardless whether `std` feature
is enabled or not [#14]
* Update `thiserror` dependency to v2
* BREAKING: remove `std` feature, as the crate is fully no_std now

Migration guidelines:
* Replace `dev` feature with `sim`
* Instead of using `round_based::simulation::Simulation` from previous version, use
`round_based::simulation::{run, run_with_setup}`
* Take advantage of `SimResult::{expect_ok, expect_eq}` to reduce amount of the code
in your tests
* Remove `std` feature, if it was explicitly enabled

Other than simulation, there are no breaking changes in this release.

[#14]: https://github.com/LFDT-Lockness/round-based/pull/14

## v0.3.2
* Update links in crate settings, update readme [#11]

Expand Down
Loading

0 comments on commit 100b267

Please sign in to comment.