From cdbf457e16d2b386e6eef8232fbac486b8340d97 Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Thu, 23 Nov 2023 11:13:01 +0000 Subject: [PATCH 1/7] New version of round-based --- .github/workflows/readme.yml | 24 + .github/workflows/rust.yml | 44 + .gitignore | 5 +- .travis.yml | 21 + Cargo.lock | 986 ++++++++++++++++++ Cargo.toml | 44 +- LICENSE | 695 +----------- Makefile | 16 + README.md | 44 +- .../random-generation-protocol/Cargo.toml | 24 + .../random-generation-protocol/src/lib.rs | 166 +++ round-based-derive/Cargo.toml | 12 + round-based-derive/src/lib.rs | 169 +++ round-based-tests/Cargo.toml | 27 + round-based-tests/src/lib.rs | 1 + round-based-tests/tests/rounds.rs | 437 ++++++++ round-based/.gitignore | 4 + round-based/Cargo.toml | 41 + round-based/README.md | 1 + round-based/src/_docs.rs | 21 + round-based/src/delivery.rs | 194 ++++ round-based/src/lib.rs | 75 ++ round-based/src/party.rs | 162 +++ round-based/src/rounds_router/mod.rs | 581 +++++++++++ round-based/src/rounds_router/simple_store.rs | 463 ++++++++ round-based/src/rounds_router/store.rs | 132 +++ round-based/src/runtime.rs | 83 ++ round-based/src/simulation.rs | 218 ++++ round-based/tests/derive.rs | 6 + .../tests/derive/compile-fail/wrong_usage.rs | 55 + .../derive/compile-fail/wrong_usage.stderr | 53 + .../derive/compile-pass/correct_usage.rs | 21 + src/async_runtime/mod.rs | 370 ------- src/async_runtime/watcher.rs | 46 - src/containers/broadcast.rs | 187 ---- src/containers/mod.rs | 15 - src/containers/p2p.rs | 190 ---- src/containers/push.rs | 72 -- src/containers/store_err.rs | 28 - src/containers/traits.rs | 42 - src/dev/async_simulation.rs | 193 ---- src/dev/mod.rs | 11 - src/dev/simulation.rs | 278 ----- src/dev/simulation/benchmark.rs | 72 -- src/lib.rs | 86 -- src/sm.rs | 158 --- tests/silly_protocol/mod.rs | 331 ------ tests/silly_protocol/rounds.rs | 146 --- tests/simulate_silly_protocol.rs | 66 -- 49 files changed, 4102 insertions(+), 3014 deletions(-) create mode 100644 .github/workflows/readme.yml create mode 100644 .github/workflows/rust.yml create mode 100644 .travis.yml create mode 100644 Cargo.lock create mode 100644 Makefile create mode 100644 examples/random-generation-protocol/Cargo.toml create mode 100644 examples/random-generation-protocol/src/lib.rs create mode 100644 round-based-derive/Cargo.toml create mode 100644 round-based-derive/src/lib.rs create mode 100644 round-based-tests/Cargo.toml create mode 100644 round-based-tests/src/lib.rs create mode 100644 round-based-tests/tests/rounds.rs create mode 100644 round-based/.gitignore create mode 100644 round-based/Cargo.toml create mode 100644 round-based/README.md create mode 100644 round-based/src/_docs.rs create mode 100644 round-based/src/delivery.rs create mode 100644 round-based/src/lib.rs create mode 100644 round-based/src/party.rs create mode 100644 round-based/src/rounds_router/mod.rs create mode 100644 round-based/src/rounds_router/simple_store.rs create mode 100644 round-based/src/rounds_router/store.rs create mode 100644 round-based/src/runtime.rs create mode 100644 round-based/src/simulation.rs create mode 100644 round-based/tests/derive.rs create mode 100644 round-based/tests/derive/compile-fail/wrong_usage.rs create mode 100644 round-based/tests/derive/compile-fail/wrong_usage.stderr create mode 100644 round-based/tests/derive/compile-pass/correct_usage.rs delete mode 100644 src/async_runtime/mod.rs delete mode 100644 src/async_runtime/watcher.rs delete mode 100644 src/containers/broadcast.rs delete mode 100644 src/containers/mod.rs delete mode 100644 src/containers/p2p.rs delete mode 100644 src/containers/push.rs delete mode 100644 src/containers/store_err.rs delete mode 100644 src/containers/traits.rs delete mode 100644 src/dev/async_simulation.rs delete mode 100644 src/dev/mod.rs delete mode 100644 src/dev/simulation.rs delete mode 100644 src/dev/simulation/benchmark.rs delete mode 100644 src/lib.rs delete mode 100644 src/sm.rs delete mode 100644 tests/silly_protocol/mod.rs delete mode 100644 tests/silly_protocol/rounds.rs delete mode 100644 tests/simulate_silly_protocol.rs diff --git a/.github/workflows/readme.yml b/.github/workflows/readme.yml new file mode 100644 index 0000000..4907af8 --- /dev/null +++ b/.github/workflows/readme.yml @@ -0,0 +1,24 @@ +name: Check README + +on: + pull_request: + branches: [ "*" ] + +env: + CARGO_TERM_COLOR: always + CARGO_NET_GIT_FETCH_WITH_CLI: true + +jobs: + check_readme: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install cargo-hakari + uses: baptiste0928/cargo-install@v1 + with: + crate: cargo-readme + - name: Check that readme matches lib.rs + run: | + cp README.md README-copy.md + make readme + diff README.md README-copy.md \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..3a105a3 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,44 @@ +name: Rust + +on: + pull_request: + branches: [ "*" ] + +env: + CARGO_TERM_COLOR: always + CARGO_NET_GIT_FETCH_WITH_CLI: true + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + - name: Build no features + run: cargo build -p round-based + - name: Build with tokio + run: cargo build -p round-based --features runtime-tokio + - name: Build with all features + run: cargo build -p round-based --all-features + - name: Run tests + run: cargo test --all-features + check-fmt: + runs-on: ubuntu-latest + steps: + - name: Check formatting + run: cargo fmt --all -- --check + check-docs: + runs-on: ubuntu-latest + steps: + - name: Check docs + run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features + clippy: + runs-on: ubuntu-latest + steps: + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + - name: Run clippy + run: cargo clippy --all --lib diff --git a/.gitignore b/.gitignore index b471067..fcd018b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /target -Cargo.lock .idea + +.cargo/ + +.helix/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c9829fd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: rust +cache: cargo +rust: stable +virt: lxd + +before_install: + - rustup component add rustfmt clippy + +script: + - cargo build --verbose --no-default-features --features derive + - cargo test --verbose --no-default-features --features derive + - cargo fmt --all -- --check + - cargo clippy -- -D clippy::all + +deploy: + provider: cargo + token: + secure: "jZHHpJU9TMmwV1tMPxsDk6K+FaLdD8mhG8xsUIDqPb8j4/knPPXNT90Uci0yUHJf6kV5Pls2AUFUEeEawfMRUohADhVCXDK/HHZ/27rplPNJzEntTvSbPg+K8lDSw22f/YZEcAXQMl/u1Ynk9+sUBKKdj9JLkhKgvE5RvroC4KSKDlCBrR0yf8wOgAhFkcm/1PTKRFSWstFJ3XTK5dUoUSJu8jPGYK6yMlP4XmCb4NA7qA9us2/bpSAftqNguW9ovP7mLIO7fDY/hSZbXJ/yi33WcwL/fTkkrmCk9AdY0ciIv+mzSHvuUGvxKfyNAYAqXdvTBj9GxvvGOtkGp9jP2rUiXFyIQULz6LUbZ1rn0UCQECXditYIYJjsSN9NnLrTKQJGTxHYZjcoxFAJ8dfu/WqmAQ1E4q+0ZsnknG30afrnotaPYIegJgM0s0cUZBI9cA/5tdGE+XyR/UiNzI/UnSkICCcokIxXNKNeP21PcVL7+16QstPMwdzjneMW+MX/P3Fq4aiZeaZx2u96XIh0mbsiXdR1ZvAjBk6q9GaKdF6qCl0HLRe55IOaIwB61/n1B4v9iENiyDESM4cp//wvAkKuzEGWhF/9iVglLS+WKpbDcj3KYYcuGyc3rfqbjoZPT3UsmADKbQeE4naapOax/Cj1chWAGH0JhqeFmBrry80=" + on: + tags: true + condition: '"$TRAVIS_TAG" =~ ^v[0-9].+$' diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ac00c2e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,986 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" +dependencies = [ + "block-buffer 0.10.2", + "crypto-common", +] + +[[package]] +name = "educe" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "serde", + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "phantom-type" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f710afd11c9711b04f97ab61bb9747d5a04562fdf0f9f44abc3de92490084982" +dependencies = [ + "educe", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "random-generation-protocol" +version = "0.1.0" +dependencies = [ + "futures", + "generic-array", + "hex", + "rand", + "rand_chacha", + "round-based", + "serde", + "sha2 0.10.1", + "thiserror", + "tokio", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "round-based" +version = "0.2.0" +dependencies = [ + "futures", + "futures-util", + "matches", + "phantom-type", + "round-based-derive", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "trybuild", +] + +[[package]] +name = "round-based-derive" +version = "0.2.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "round-based-tests" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "hex", + "hex-literal", + "matches", + "rand", + "rand_chacha", + "random-generation-protocol", + "round-based", + "rustls", + "sha2 0.9.9", + "thiserror", + "tokio", + "tokio-rustls", + "url", +] + +[[package]] +name = "rustls" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.2", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" +dependencies = [ + "libc", + "mio", + "pin-project-lite", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "trybuild" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a24e67e2b4083a6d0beb5a98e274c3160edfb879d71cd2cd14da93786a93b" +dependencies = [ + "glob", + "once_cell", + "serde", + "serde_json", + "termcolor", + "toml", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "web-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index e343a39..f580479 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,36 +1,8 @@ -[package] -name = "round-based" -version = "0.1.4" -edition = "2018" -authors = ["Denis Varlakov "] -license = "GPL-3.0" -description = "Driver for round-based protocols" -repository = "https://github.com/ZenGo-X/round-based-protocol" -categories = ["asynchronous", "cryptography", "network-programming"] -keywords = ["round-based", "mpc", "protocol"] - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[dependencies] -tokio = { version = "1.0.1", features = ["rt", "sync", "time"], optional = true } -futures = { version = "0.3.9", optional = true } -async-stream = { version = "0.3.0", optional = true } -thiserror = "1.0.23" -serde = { version = "1.0", features = ["derive"] } - -[dev-dependencies] -tokio = { version = "1.0.1", features = ["rt", "sync", "time", "macros"] } -sha2 = "0.9.2" -rand = "0.8.1" - -[features] -default = ["async-runtime"] -async-runtime = ["tokio", "futures", "async-stream"] -# Exposes utils useful for testing -dev = [] - -[[test]] -name = "simulate_silly_protocol" -required-features = ["dev", "async-runtime"] +[workspace] +resolver = "2" +members = [ + "round-based", + "round-based-tests", + "round-based-derive", + "examples/random-generation-protocol", +] diff --git a/LICENSE b/LICENSE index e72bfdd..76a04f4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,21 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. \ No newline at end of file +MIT License + +Copyright (c) 2021 Zengo X + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..697dc91 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +.PHONY: docs docs-open + +docs: + RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --no-deps --all-features + +docs-test: + RUSTDOCFLAGS="--cfg docsrs" cargo +nightly test --doc --all-features + +docs-open: + RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --no-deps --all-features --open + +readme: + cargo readme -i src/lib.rs -r round-based/ -t ../docs/readme.tpl --no-indent-headings \ + | perl -ne 's/(? README.md diff --git a/README.md b/README.md index c63350f..f4aa528 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,38 @@ -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE) [![Docs](https://docs.rs/round-based/badge.svg)](https://docs.rs/round-based) [![Crates io](https://img.shields.io/crates/v/round-based.svg)](https://crates.io/crates/round-based) -# Round-based crate -Round-based is a Rust crate providing utilities for executing generic round-based protocol. -Refer to [documentation](https://docs.rs/round-based) to learn more. +An MPC framework that unifies and simplifies the way of developing and working with +multiparty protocols (e.g. threshold signing, random beacons, etc.). -### Supported Rust Versions -The minimum supported version is 1.45 +## Goals -### License -Round-based is released under the terms of the GPLv3 license. See [LICENSE](LICENSE) for more information. +* Async friendly \ + Async is the most simple and efficient way of doing networking in Rust +* Simple, configurable \ + Protocol can be carried out in a few lines of code: check out examples. +* Independent of networking layer \ + We use abstractions `Stream` and `Sink` to receive and send messages. -### Development Process & Contact -This library is maintained by ZenGo-X. Contributions are highly welcomed! Besides GitHub issues -and PRs, feel free to [reach out](mailto:github@kzencorp.com) by mail or join ZenGo X -[Telegram](https://t.me/joinchat/ET1mddGXRoyCxZ-7) for discussions on code and research. +## Networking + +In order to run an MPC protocol, transport layer needs to be defined. All you have to do is to +implement `Delivery` trait which is basically a stream and a sink for receiving and sending messages. + +Message delivery should meet certain criterias that differ from protocol to protocol (refer to +the documentation of the protocol you're using), but usually they are: + +* Messages should be authenticated \ + Each message should be signed with identity key of the sender. This implies having Public Key + Infrastructure. +* P2P messages should be encrypted \ + Only recipient should be able to learn the content of p2p message +* Broadcast channel should be reliable \ + Some protocols may require broadcast channel to be reliable. Simply saying, when party receives a + broadcast message over reliable channel it should be ensured that everybody else received the same + message. + +## Features + +* `dev` enables development tools such as protocol simulation +* `runtime-tokio` tells that computationally-heavy tasks shall be executed using tokio runtime diff --git a/examples/random-generation-protocol/Cargo.toml b/examples/random-generation-protocol/Cargo.toml new file mode 100644 index 0000000..b7f1a6c --- /dev/null +++ b/examples/random-generation-protocol/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "random-generation-protocol" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +round-based = { path = "../../round-based", features = ["derive"] } + +tokio = { version = "1.15", features = ["rt"] } +futures = "0.3" +rand = "0.8" +sha2 = "0.10" +serde = { version = "1", features = ["derive"] } +generic-array = { version = "0.14", features = ["serde"] } +thiserror = "1" + +[dev-dependencies] +round-based = { path = "../../round-based", features = ["derive", "dev"] } +tokio = { version = "1.15", features = ["macros", "rt"] } +hex = "0.4" +rand_chacha = "0.3" diff --git a/examples/random-generation-protocol/src/lib.rs b/examples/random-generation-protocol/src/lib.rs new file mode 100644 index 0000000..1fe5f51 --- /dev/null +++ b/examples/random-generation-protocol/src/lib.rs @@ -0,0 +1,166 @@ +use futures::SinkExt; +use rand::RngCore; +use serde::{Deserialize, Serialize}; +use sha2::{digest::Output, Digest, Sha256}; +use thiserror::Error; + +use round_based::rounds_router::{ + simple_store::{RoundInput, RoundInputError}, + CompleteRoundError, RoundsRouter, +}; +use round_based::{Delivery, Mpc, MpcParty, MsgId, Outgoing, PartyIndex, ProtocolMessage}; + +#[derive(Clone, Debug, PartialEq, ProtocolMessage, Serialize, Deserialize)] +pub enum Msg { + CommitMsg(CommitMsg), + DecommitMsg(DecommitMsg), +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct CommitMsg { + pub commitment: Output, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DecommitMsg { + pub randomness: [u8; 32], +} + +pub async fn protocol_of_random_generation( + party: M, + i: PartyIndex, + n: u16, + mut rng: R, +) -> Result<[u8; 32], Error> +where + M: Mpc, + R: RngCore, +{ + let MpcParty { delivery, .. } = party.into_party(); + let (incoming, mut outgoing) = delivery.split(); + + // Define rounds + let mut rounds = RoundsRouter::::builder(); + let round1 = rounds.add_round(RoundInput::::broadcast(i, n)); + let round2 = rounds.add_round(RoundInput::::broadcast(i, n)); + let mut rounds = rounds.listen(incoming); + + // --- The Protocol --- + + // 1. Generate local randomness + let mut local_randomness = [0u8; 32]; + rng.fill_bytes(&mut local_randomness); + + // 2. Commit local randomness (broadcast m=sha256(randomness)) + let commitment = Sha256::digest(&local_randomness); + outgoing + .send(Outgoing::broadcast(Msg::CommitMsg(CommitMsg { + commitment, + }))) + .await + .map_err(Error::Round1Send)?; + + // 3. Receive committed randomness from other parties + let commitments = rounds + .complete(round1) + .await + .map_err(Error::Round1Receive)?; + + // 4. Open local randomness + outgoing + .send(Outgoing::broadcast(Msg::DecommitMsg(DecommitMsg { + randomness: local_randomness, + }))) + .await + .map_err(Error::Round2Send)?; + + // 5. Receive opened local randomness from other parties, verify them, and output protocol randomness + let randomness = rounds + .complete(round2) + .await + .map_err(Error::Round2Receive)?; + + let mut guilty_parties = vec![]; + let mut output = local_randomness; + for ((party_i, com_msg_id, commit), (_, decom_msg_id, decommit)) in commitments + .into_iter_indexed() + .zip(randomness.into_iter_indexed()) + { + let commitment_expected = Sha256::digest(&decommit.randomness); + if commit.commitment != commitment_expected { + guilty_parties.push(Blame { + guilty_party: party_i, + commitment_msg: com_msg_id, + decommitment_msg: decom_msg_id, + }); + continue; + } + + output + .iter_mut() + .zip(decommit.randomness) + .for_each(|(x, r)| *x ^= r); + } + + if !guilty_parties.is_empty() { + Err(Error::PartiesOpenedRandomnessDoesntMatchCommitment { guilty_parties }) + } else { + Ok(output) + } +} + +#[derive(Debug, Error)] +pub enum Error { + #[error("send a message at round 1")] + Round1Send(#[source] SendErr), + #[error("receive messages at round 1")] + Round1Receive(#[source] CompleteRoundError), + #[error("send a message at round 2")] + Round2Send(#[source] SendErr), + #[error("receive messages at round 2")] + Round2Receive(#[source] CompleteRoundError), + + #[error("malicious parties: {guilty_parties:?}")] + PartiesOpenedRandomnessDoesntMatchCommitment { guilty_parties: Vec }, +} + +#[derive(Debug)] +pub struct Blame { + pub guilty_party: PartyIndex, + pub commitment_msg: MsgId, + pub decommitment_msg: MsgId, +} + +#[cfg(test)] +mod tests { + use rand::SeedableRng; + use rand_chacha::ChaCha20Rng; + + use round_based::simulation::Simulation; + + use super::{protocol_of_random_generation, Msg}; + + #[tokio::test] + async fn main() { + let n: u16 = 5; + + let mut simulation = Simulation::::new(); + let mut party_output = vec![]; + + for i in 0..n { + let party = simulation.add_party(); + let rng = ChaCha20Rng::from_entropy(); + let output = protocol_of_random_generation(party, i, n, rng); + 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)]); + } + + println!("Output randomness: {}", hex::encode(&output[0])); + } +} diff --git a/round-based-derive/Cargo.toml b/round-based-derive/Cargo.toml new file mode 100644 index 0000000..53b5422 --- /dev/null +++ b/round-based-derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "round-based-derive" +version = "0.2.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = "1.0" +quote = "1.0" +proc-macro2 = "1" diff --git a/round-based-derive/src/lib.rs b/round-based-derive/src/lib.rs new file mode 100644 index 0000000..4b62029 --- /dev/null +++ b/round-based-derive/src/lib.rs @@ -0,0 +1,169 @@ +use proc_macro2::{Span, TokenStream}; +use quote::{quote, quote_spanned}; +use syn::ext::IdentExt; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::{parse_macro_input, Data, DeriveInput, Fields, Generics, Ident, Token, Variant}; + +#[proc_macro_derive(ProtocolMessage, attributes(protocol_message))] +pub fn protocol_message(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let mut root = None; + + for attr in input.attrs { + if !attr.path.is_ident("protocol_message") { + continue; + } + if root.is_some() { + return quote_spanned! { attr.path.span() => compile_error!("#[protocol_message] attribute appears more than once"); }.into(); + } + let tokens = attr.tokens.into(); + root = Some(parse_macro_input!(tokens as RootAttribute)); + } + + let root_path = root + .map(|root| root.path) + .unwrap_or_else(|| Punctuated::from_iter([Ident::new("round_based", Span::call_site())])); + + let enum_data = match input.data { + Data::Enum(e) => e, + Data::Struct(s) => { + return quote_spanned! {s.struct_token.span => compile_error!("only enum may implement ProtocolMessage");}.into() + } + Data::Union(s) => { + return quote_spanned! {s.union_token.span => compile_error!("only enum may implement ProtocolMessage");}.into() + } + }; + + let name = input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let round_method_impl = if !enum_data.variants.is_empty() { + round_method(&name, enum_data.variants.iter()) + } else { + // Special case for empty enum. Empty protocol message is useless, but let it be + quote! { match *self {} } + }; + + let impl_protocol_message = quote! { + impl #impl_generics #root_path::ProtocolMessage for #name #ty_generics #where_clause { + fn round(&self) -> u16 { + #round_method_impl + } + } + }; + + let impl_round_message = round_messages( + &root_path, + &name, + &input.generics, + enum_data.variants.iter(), + ); + + proc_macro::TokenStream::from(quote! { + #impl_protocol_message + #impl_round_message + }) +} + +fn round_method<'v>(enum_name: &Ident, variants: impl Iterator) -> TokenStream { + let match_variants = (0u16..).zip(variants).map(|(i, variant)| { + let variant_name = &variant.ident; + match &variant.fields { + Fields::Unit => quote_spanned! { + variant.ident.span() => + #enum_name::#variant_name => compile_error!("unit variants are not allowed in ProtocolMessage"), + }, + Fields::Named(_) => quote_spanned! { + variant.ident.span() => + #enum_name::#variant_name{..} => compile_error!("named variants are not allowed in ProtocolMessage"), + }, + Fields::Unnamed(unnamed) => if unnamed.unnamed.len() == 1 { + quote_spanned! { + variant.ident.span() => + #enum_name::#variant_name(_) => #i, + } + } else { + quote_spanned! { + variant.ident.span() => + #enum_name::#variant_name(..) => compile_error!("this variant must contain exactly one field to be valid ProtocolMessage"), + } + }, + } + }); + quote! { + match self { + #(#match_variants)* + } + } +} + +fn round_messages<'v>( + root_path: &RootPath, + enum_name: &Ident, + generics: &Generics, + variants: impl Iterator, +) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let impls = (0u16..).zip(variants).map(|(i, variant)| { + let variant_name = &variant.ident; + match &variant.fields { + Fields::Unnamed(unnamed) if unnamed.unnamed.len() == 1 => { + let msg_type = &unnamed.unnamed[0].ty; + quote_spanned! { + variant.ident.span() => + impl #impl_generics #root_path::RoundMessage<#msg_type> for #enum_name #ty_generics #where_clause { + const ROUND: u16 = #i; + fn to_protocol_message(round_message: #msg_type) -> Self { + #enum_name::#variant_name(round_message) + } + fn from_protocol_message(protocol_message: Self) -> Result<#msg_type, Self> { + #[allow(unreachable_patterns)] + match protocol_message { + #enum_name::#variant_name(msg) => Ok(msg), + _ => Err(protocol_message), + } + } + } + } + } + _ => quote! {}, + } + }); + quote! { + #(#impls)* + } +} + +type RootPath = Punctuated; + +#[allow(dead_code)] +struct RootAttribute { + paren: syn::token::Paren, + root: kw::root, + eq: Token![=], + path: RootPath, +} + +impl Parse for RootAttribute { + fn parse(input: ParseStream) -> syn::Result { + let content; + let paren = syn::parenthesized!(content in input); + let root = content.parse::()?; + let eq = content.parse::()?; + let path = RootPath::parse_separated_nonempty_with(&content, Ident::parse_any)?; + let _ = content.parse::()?; + + Ok(Self { + paren, + root, + eq, + path, + }) + } +} + +mod kw { + syn::custom_keyword! { root } +} diff --git a/round-based-tests/Cargo.toml b/round-based-tests/Cargo.toml new file mode 100644 index 0000000..7415b6f --- /dev/null +++ b/round-based-tests/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "round-based-tests" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[dev-dependencies] +tokio = { version = "1", features = ["net", "rt", "macros"] } +tokio-rustls = "0.23" +rustls = "0.20" +anyhow = "1" +rand = "0.8" +sha2 = "0.9" +hex-literal = "0.3" +hex = "*" +futures = "0.3" +rand_chacha = "0.3" +matches = "0.1" +thiserror = "1" +url = "2.2" + +round-based = { path = "../round-based" } +random-generation-protocol = { path = "../examples/random-generation-protocol" } diff --git a/round-based-tests/src/lib.rs b/round-based-tests/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/round-based-tests/src/lib.rs @@ -0,0 +1 @@ + diff --git a/round-based-tests/tests/rounds.rs b/round-based-tests/tests/rounds.rs new file mode 100644 index 0000000..ce799d5 --- /dev/null +++ b/round-based-tests/tests/rounds.rs @@ -0,0 +1,437 @@ +use std::convert::Infallible; + +use futures::{sink, stream, Sink, Stream}; +use hex_literal::hex; +use matches::assert_matches; +use rand::SeedableRng; + +use random_generation_protocol::{ + protocol_of_random_generation, CommitMsg, DecommitMsg, Error, Msg, +}; +use round_based::rounds_router::errors::IoError; +use round_based::rounds_router::{simple_store::RoundInput, CompleteRoundError, RoundsRouter}; +use round_based::{Delivery, Incoming, MessageType, MpcParty, Outgoing}; + +const PARTY0_SEED: [u8; 32] = + hex!("6772d079d5c984b3936a291e36b0d3dc6c474e36ed4afdfc973ef79a431ca870"); +const PARTY1_COMMITMENT: [u8; 32] = + hex!("2a8c585d9a80cb78bc226f4ab35a75c8e5834ff77a83f41cf6c893ea0f3b2aed"); +const PARTY1_RANDOMNESS: [u8; 32] = + hex!("12a595f4893fdb4ab9cc38caeec5f7456acb3002ca58457c5056977ce59136a6"); +const PARTY2_COMMITMENT: [u8; 32] = + hex!("01274ef40aece8aa039587cc05620a19b80a5c93fbfb24a9f8e1b77b7936e47d"); +const PARTY2_RANDOMNESS: [u8; 32] = + hex!("6fc78a926c7eebfad4e98e796cd53b771ac5947b460567c7ea441abb957c89c7"); +const PROTOCOL_OUTPUT: [u8; 32] = + hex!("689a9f02229bdb36521275179676641585c4a3ce7b80ace37f0272a65e89a1c3"); +const PARTY_OVERWRITES: [u8; 32] = + hex!("00aa11bb22cc33dd44ee55ff6677889900aa11bb22cc33dd44ee55ff66778899"); + +#[tokio::test] +async fn random_generation_completes() { + let output = run_protocol([ + Ok::<_, Infallible>(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 1, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY2_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 2, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY1_RANDOMNESS, + }), + }), + Ok(Incoming { + id: 3, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY2_RANDOMNESS, + }), + }), + ]) + .await + .unwrap(); + + assert_eq!(output, PROTOCOL_OUTPUT); +} + +#[tokio::test] +async fn protocol_terminates_with_error_if_party_tries_to_overwrite_message_at_round1() { + let output = run_protocol([ + Ok::<_, Infallible>(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 1, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY_OVERWRITES.into(), + }), + }), + ]) + .await; + + assert_matches!( + output, + Err(Error::Round1Receive(CompleteRoundError::ProcessMessage(_))) + ) +} + +#[tokio::test] +async fn protocol_terminates_with_error_if_party_tries_to_overwrite_message_at_round2() { + let output = run_protocol([ + Ok::<_, Infallible>(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 1, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY2_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 2, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY1_RANDOMNESS, + }), + }), + Ok(Incoming { + id: 3, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY_OVERWRITES, + }), + }), + ]) + .await; + + assert_matches!( + output, + Err(Error::Round2Receive(CompleteRoundError::ProcessMessage(_))) + ) +} + +#[tokio::test] +async fn protocol_terminates_if_received_message_from_unknown_sender_at_round1() { + let output = run_protocol([Ok::<_, Infallible>(Incoming { + id: 0, + sender: 3, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + })]) + .await; + + assert_matches!( + output, + Err(Error::Round1Receive(CompleteRoundError::ProcessMessage(_))) + ) +} + +#[tokio::test] +async fn protocol_ignores_message_that_goes_to_completed_round() { + let output = run_protocol([ + Ok::<_, Infallible>(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 1, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY2_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 2, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY_OVERWRITES.into(), + }), + }), + Ok(Incoming { + id: 3, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY1_RANDOMNESS, + }), + }), + Ok(Incoming { + id: 4, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY2_RANDOMNESS, + }), + }), + ]) + .await + .unwrap(); + + assert_eq!(output, PROTOCOL_OUTPUT); +} + +#[tokio::test] +async fn protocol_ignores_io_error_if_it_is_completed() { + let output = run_protocol([ + Ok(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 1, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY2_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 2, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY1_RANDOMNESS, + }), + }), + Ok(Incoming { + id: 3, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY2_RANDOMNESS, + }), + }), + Err(DummyError), + ]) + .await + .unwrap(); + + assert_eq!(output, PROTOCOL_OUTPUT); +} + +#[tokio::test] +async fn protocol_terminates_with_error_if_io_error_happens_at_round2() { + let output = run_protocol([ + Ok(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 1, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY2_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 2, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY1_RANDOMNESS, + }), + }), + Err(DummyError), + Ok(Incoming { + id: 3, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY2_RANDOMNESS, + }), + }), + ]) + .await; + + assert_matches!(output, Err(Error::Round2Receive(CompleteRoundError::Io(_)))); +} + +#[tokio::test] +async fn protocol_terminates_with_error_if_io_error_happens_at_round1() { + let output = run_protocol([ + Err(DummyError), + Ok(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 1, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY2_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 2, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY1_RANDOMNESS, + }), + }), + Ok(Incoming { + id: 3, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY2_RANDOMNESS, + }), + }), + ]) + .await; + + assert_matches!(output, Err(Error::Round1Receive(CompleteRoundError::Io(_)))); +} + +#[tokio::test] +async fn protocol_terminates_with_error_if_unexpected_eof_happens_at_round2() { + let output = run_protocol([ + Ok::<_, Infallible>(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY1_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 1, + sender: 2, + msg_type: MessageType::Broadcast, + msg: Msg::CommitMsg(CommitMsg { + commitment: PARTY2_COMMITMENT.into(), + }), + }), + Ok(Incoming { + id: 2, + sender: 1, + msg_type: MessageType::Broadcast, + msg: Msg::DecommitMsg(DecommitMsg { + randomness: PARTY1_RANDOMNESS, + }), + }), + ]) + .await; + + assert_matches!( + output, + Err(Error::Round2Receive(CompleteRoundError::Io( + IoError::UnexpectedEof + ))) + ); +} + +#[tokio::test] +async fn all_non_completed_rounds_are_terminated_with_unexpected_eof_error_if_incoming_channel_suddenly_closed( +) { + let mut rounds = RoundsRouter::builder(); + let round1 = rounds.add_round(RoundInput::::new(0, 3, MessageType::P2P)); + let round2 = rounds.add_round(RoundInput::::new(0, 3, MessageType::P2P)); + let mut rounds = rounds.listen(stream::empty::, Infallible>>()); + + assert_matches!( + rounds.complete(round1).await, + Err(CompleteRoundError::Io(IoError::UnexpectedEof)) + ); + assert_matches!( + rounds.complete(round2).await, + Err(CompleteRoundError::Io(IoError::UnexpectedEof)) + ); +} + +async fn run_protocol(incomings: I) -> Result<[u8; 32], Error> +where + I: IntoIterator, E>>, + I::IntoIter: Send + 'static, + E: std::error::Error + Send + Sync + Unpin + 'static, +{ + let rng = rand_chacha::ChaCha8Rng::from_seed(PARTY0_SEED); + + let party = MpcParty::connected(MockedDelivery::new(stream::iter(incomings), sink::drain())); + protocol_of_random_generation(party, 0, 3, rng).await +} + +struct MockedDelivery { + incoming: I, + outgoing: O, +} + +impl MockedDelivery { + pub fn new(incoming: I, outgoing: O) -> Self { + Self { incoming, outgoing } + } +} + +impl Delivery for MockedDelivery +where + I: Stream, IErr>> + Send + Unpin + 'static, + O: Sink, Error = OErr> + Send + Unpin, + IErr: std::error::Error + Send + Sync + 'static, + OErr: std::error::Error + Send + Sync + 'static, +{ + type Send = O; + type Receive = I; + type SendError = OErr; + type ReceiveError = IErr; + + fn split(self) -> (Self::Receive, Self::Send) { + (self.incoming, self.outgoing) + } +} + +#[derive(Debug, thiserror::Error)] +#[error("dummy error")] +struct DummyError; diff --git a/round-based/.gitignore b/round-based/.gitignore new file mode 100644 index 0000000..61e17ef --- /dev/null +++ b/round-based/.gitignore @@ -0,0 +1,4 @@ +/target +Cargo.lock + +.idea/ diff --git a/round-based/Cargo.toml b/round-based/Cargo.toml new file mode 100644 index 0000000..f1c8066 --- /dev/null +++ b/round-based/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "round-based" +version = "0.2.0" +edition = "2021" +license = "MIT OR Apache-2.0" +description = "Driver for MPC protocols" +repository = "https://github.com/ZenGo-X/round-based-protocol" +categories = ["asynchronous", "cryptography", "network-programming"] +keywords = ["round-based", "mpc", "protocol"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +futures-util = { version = "0.3", default-features = false, features = ["sink"] } +phantom-type = "0.3" +tracing = "0.1" +thiserror = "1" + +round-based-derive = { path = "../round-based-derive", optional = true } + +tokio = { version = "1", features = ["rt"], optional = true } +tokio-stream = { version = "0.1", features = ["sync"], optional = true } + +[dev-dependencies] +trybuild = "1" +matches = "0.1" +futures = { version = "0.3", default-features = false } + +[features] +default = [] +dev = ["tokio/sync", "tokio-stream"] +derive = ["round-based-derive"] +runtime-tokio = ["tokio"] + +[[test]] +name = "derive" +required-features = ["derive"] diff --git a/round-based/README.md b/round-based/README.md new file mode 100644 index 0000000..86fa76f --- /dev/null +++ b/round-based/README.md @@ -0,0 +1 @@ +round-based diff --git a/round-based/src/_docs.rs b/round-based/src/_docs.rs new file mode 100644 index 0000000..900603c --- /dev/null +++ b/round-based/src/_docs.rs @@ -0,0 +1,21 @@ +use std::convert::Infallible; + +use phantom_type::PhantomType; + +use crate::{Delivery, Incoming, Outgoing}; + +pub fn fake_delivery() -> impl Delivery { + struct FakeDelivery(PhantomType); + impl Delivery for FakeDelivery { + type Send = futures_util::sink::Drain>; + type Receive = futures_util::stream::Pending, Infallible>>; + + type SendError = Infallible; + type ReceiveError = Infallible; + + fn split(self) -> (Self::Receive, Self::Send) { + (futures_util::stream::pending(), futures_util::sink::drain()) + } + } + FakeDelivery(PhantomType::new()) +} diff --git a/round-based/src/delivery.rs b/round-based/src/delivery.rs new file mode 100644 index 0000000..3fcaa5c --- /dev/null +++ b/round-based/src/delivery.rs @@ -0,0 +1,194 @@ +use std::error::Error; + +use futures_util::{Sink, Stream}; + +/// Networking abstraction +/// +/// Basically, it's pair of channels: [`Stream`] for receiving messages, and [`Sink`] for sending +/// messages to other parties. +pub trait Delivery { + /// Outgoing delivery channel + type Send: Sink, Error = Self::SendError> + Unpin; + /// Incoming delivery channel + type Receive: Stream, Self::ReceiveError>> + Unpin; + /// Error of outgoing delivery channel + type SendError: Error + Send + Sync + 'static; + /// Error of incoming delivery channel + type ReceiveError: Error + Send + Sync + 'static; + /// Returns a pair of incoming and outgoing delivery channels + fn split(self) -> (Self::Receive, Self::Send); +} + +impl Delivery for (I, O) +where + I: Stream, IErr>> + Unpin, + O: Sink, Error = OErr> + Unpin, + IErr: Error + Send + Sync + 'static, + OErr: Error + Send + Sync + 'static, +{ + type Send = O; + type Receive = I; + type SendError = OErr; + type ReceiveError = IErr; + + fn split(self) -> (Self::Receive, Self::Send) { + (self.0, self.1) + } +} + +/// Incoming message +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct Incoming { + /// Index of a message + pub id: MsgId, + /// Index of a party who sent the message + pub sender: PartyIndex, + /// Indicates whether it's a broadcast message (meaning that this message is received by all the + /// parties), or p2p (private message sent by `sender`) + pub msg_type: MessageType, + /// Received message + pub msg: M, +} + +/// Message type (broadcast or p2p) +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum MessageType { + /// Message was broadcasted + Broadcast, + /// P2P message + P2P, +} + +/// Index of party involved in the protocol +pub type PartyIndex = u16; +/// ID of received message +/// +/// Can be used to retrieve extra information about message from delivery layer when needed. +/// E.g. if malicious party is detected, we need a proof that received message was sent by this +/// party, so message id should be used to retrieve signature and original message. +pub type MsgId = u64; + +impl Incoming { + /// Maps `Incoming` to `Incoming` by applying a function to the message body + pub fn map(self, f: F) -> Incoming + where + F: FnOnce(M) -> T, + { + Incoming { + id: self.id, + sender: self.sender, + msg_type: self.msg_type, + msg: f(self.msg), + } + } + + /// Maps `Incoming` to `Result, E>` by applying a function `fn(M) -> Result` + /// to the message body + pub fn try_map(self, f: F) -> Result, E> + where + F: FnOnce(M) -> Result, + { + Ok(Incoming { + id: self.id, + sender: self.sender, + msg_type: self.msg_type, + msg: f(self.msg)?, + }) + } + + /// Converts `&Incoming` to `Incoming<&M>` + pub fn as_ref(&self) -> Incoming<&M> { + Incoming { + id: self.id, + sender: self.sender, + msg_type: self.msg_type, + msg: &self.msg, + } + } + + /// Checks whether it's broadcast message + pub fn is_broadcast(&self) -> bool { + matches!(self.msg_type, MessageType::Broadcast { .. }) + } + + /// Checks whether it's p2p message + pub fn is_p2p(&self) -> bool { + matches!(self.msg_type, MessageType::P2P) + } +} + +/// Outgoing message +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Outgoing { + /// Message destination: either one party (p2p message) or all parties (broadcast message) + pub recipient: MessageDestination, + /// Message being sent + pub msg: M, +} + +impl Outgoing { + /// Constructs an outgoing message addressed to all parties + pub fn broadcast(msg: M) -> Self { + Self { + recipient: MessageDestination::AllParties, + msg, + } + } + + /// Constructs an outgoing message addressed to one party + pub fn p2p(recipient: PartyIndex, msg: M) -> Self { + Self { + recipient: MessageDestination::OneParty(recipient), + msg, + } + } + + /// Maps `Outgoing` to `Outgoing` by applying a function to the message body + pub fn map(self, f: F) -> Outgoing + where + F: FnOnce(M) -> M2, + { + Outgoing { + recipient: self.recipient, + msg: f(self.msg), + } + } + + /// Converts `&Outgoing` to `Outgoing<&M>` + pub fn as_ref(&self) -> Outgoing<&M> { + Outgoing { + recipient: self.recipient, + msg: &self.msg, + } + } + + /// Checks whether it's broadcast message + pub fn is_broadcast(&self) -> bool { + self.recipient.is_broadcast() + } + + /// Checks whether it's p2p message + pub fn is_p2p(&self) -> bool { + self.recipient.is_p2p() + } +} + +/// Destination of an outgoing message +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum MessageDestination { + /// Broadcast message + AllParties, + /// P2P message + OneParty(PartyIndex), +} + +impl MessageDestination { + /// Returns `true` if it's p2p message + pub fn is_p2p(&self) -> bool { + matches!(self, MessageDestination::OneParty(_)) + } + /// Returns `true` if it's broadcast message + pub fn is_broadcast(&self) -> bool { + matches!(self, MessageDestination::AllParties { .. }) + } +} diff --git a/round-based/src/lib.rs b/round-based/src/lib.rs new file mode 100644 index 0000000..6a46a75 --- /dev/null +++ b/round-based/src/lib.rs @@ -0,0 +1,75 @@ +//! [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE) +//! [![Docs](https://docs.rs/round-based/badge.svg)](https://docs.rs/round-based) +//! [![Crates io](https://img.shields.io/crates/v/round-based.svg)](https://crates.io/crates/round-based) +//! +//! An MPC framework that unifies and simplifies the way of developing and working with +//! multiparty protocols (e.g. threshold signing, random beacons, etc.). +//! +//! ## Goals +//! +//! * Async friendly \ +//! Async is the most simple and efficient way of doing networking in Rust +//! * Simple, configurable \ +//! Protocol can be carried out in a few lines of code: check out examples. +//! * Independent of networking layer \ +//! We use abstractions [`Stream`](futures_util::Stream) and [`Sink`](futures_util::Sink) to receive and send messages. +//! +//! ## Networking +//! +//! In order to run an MPC protocol, transport layer needs to be defined. All you have to do is to +//! implement [`Delivery`] trait which is basically a stream and a sink for receiving and sending messages. +//! +//! Message delivery should meet certain criterias that differ from protocol to protocol (refer to +//! the documentation of the protocol you're using), but usually they are: +//! +//! * Messages should be authenticated \ +//! Each message should be signed with identity key of the sender. This implies having Public Key +//! Infrastructure. +//! * P2P messages should be encrypted \ +//! Only recipient should be able to learn the content of p2p message +//! * Broadcast channel should be reliable \ +//! Some protocols may require broadcast channel to be reliable. Simply saying, when party receives a +//! broadcast message over reliable channel it should be ensured that everybody else received the same +//! message. +//! +//! ## Features +//! +//! * `dev` enables development tools such as [protocol simulation](simulation) +//! * `runtime-tokio` enables [tokio]-specific implementation of [async runtime](runtime) + +#![cfg_attr(docsrs, feature(doc_cfg))] +#![deny(missing_docs)] +#![forbid(unused_crate_dependencies)] + +/// Fixes false-positive of `unused_crate_dependencies` lint that only occure in the tests +#[cfg(test)] +mod false_positives { + use futures as _; + use trybuild as _; +} + +mod delivery; +pub mod party; +pub mod rounds_router; +pub mod runtime; + +#[cfg(feature = "dev")] +#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] +pub mod simulation; + +pub use self::delivery::*; +#[doc(no_inline)] +pub use self::{ + party::{Mpc, MpcParty}, + rounds_router::{ProtocolMessage, RoundMessage}, +}; + +#[doc(hidden)] +pub mod _docs; + +/// Derives [`ProtocolMessage`] and [`RoundMessage`] traits +/// +/// See [`ProtocolMessage`] docs for more details +#[cfg(feature = "derive")] +#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] +pub use round_based_derive::ProtocolMessage; diff --git a/round-based/src/party.rs b/round-based/src/party.rs new file mode 100644 index 0000000..ac425bc --- /dev/null +++ b/round-based/src/party.rs @@ -0,0 +1,162 @@ +//! Party of MPC protocol +//! +//! [`MpcParty`] is party of MPC protocol, connected to network, ready to start carrying out the protocol. +//! +//! ```rust +//! use round_based::{Mpc, MpcParty, Delivery, PartyIndex}; +//! +//! # struct KeygenMsg; +//! # struct KeyShare; +//! # struct Error; +//! # type Result = std::result::Result; +//! # async fn doc() -> Result<()> { +//! async fn keygen(party: M, i: PartyIndex, n: u16) -> Result +//! where +//! M: Mpc +//! { +//! // ... +//! # unimplemented!() +//! } +//! async fn connect() -> impl Delivery { +//! // ... +//! # round_based::_docs::fake_delivery() +//! } +//! +//! let delivery = connect().await; +//! let party = MpcParty::connected(delivery); +//! +//! # let (i, n) = (1, 3); +//! let keyshare = keygen(party, i, n).await?; +//! # Ok(()) } +//! ``` + +use std::error::Error; + +use phantom_type::PhantomType; + +use crate::delivery::Delivery; +use crate::runtime::{self, AsyncRuntime}; + +/// Party of MPC protocol (trait) +/// +/// [`MpcParty`] is the only struct that implement this trait. Motivation to have this trait is to fewer amount of +/// generic bounds that are needed to be specified. +/// +/// Typical usage of this trait when implementing MPC protocol: +/// +/// ```rust +/// use round_based::{Mpc, MpcParty, PartyIndex}; +/// +/// # struct Msg; +/// async fn keygen(party: M, i: PartyIndex, n: u16) +/// where +/// M: Mpc +/// { +/// let MpcParty{ delivery, .. } = party.into_party(); +/// // ... +/// } +/// ``` +/// +/// If we didn't have this trait, generics would be less readable: +/// ```rust +/// use round_based::{MpcParty, Delivery, runtime::AsyncRuntime, PartyIndex}; +/// +/// # struct Msg; +/// async fn keygen(party: MpcParty, i: PartyIndex, n: u16) +/// where +/// D: Delivery, +/// R: AsyncRuntime +/// { +/// // ... +/// } +/// ``` +pub trait Mpc: internal::Sealed { + /// MPC message + type ProtocolMessage; + /// Transport layer implementation + type Delivery: Delivery< + Self::ProtocolMessage, + SendError = Self::SendError, + ReceiveError = Self::ReceiveError, + >; + /// Async runtime + type Runtime: AsyncRuntime; + + /// Sending message error + type SendError: Error + Send + Sync + 'static; + /// Receiving message error + type ReceiveError: Error + Send + Sync + 'static; + + /// Converts into [`MpcParty`] + fn into_party(self) -> MpcParty; +} + +mod internal { + pub trait Sealed {} +} + +/// Party of MPC protocol +#[non_exhaustive] +pub struct MpcParty { + /// Defines transport layer + pub delivery: D, + /// Defines how computationally heavy tasks should be handled + pub runtime: R, + _msg: PhantomType, +} + +impl MpcParty +where + M: Send + 'static, + D: Delivery, +{ + /// Party connected to the network + /// + /// Takes the delivery object determining how to deliver/receive other parties' messages + pub fn connected(delivery: D) -> Self { + Self { + delivery, + runtime: Default::default(), + _msg: PhantomType::new(), + } + } +} + +impl MpcParty +where + M: Send + 'static, + D: Delivery, +{ + /// Specifies a [async runtime](runtime) + pub fn set_runtime(self, runtime: R) -> MpcParty + where + R: AsyncRuntime, + { + MpcParty { + delivery: self.delivery, + runtime, + _msg: self._msg, + } + } +} + +impl internal::Sealed for MpcParty {} + +impl Mpc for MpcParty +where + D: Delivery, + D::SendError: Error + Send + Sync + 'static, + D::ReceiveError: Error + Send + Sync + 'static, + R: AsyncRuntime, +{ + type ProtocolMessage = M; + type Delivery = D; + type Runtime = R; + + type SendError = D::SendError; + type ReceiveError = D::ReceiveError; + + fn into_party(self) -> MpcParty { + self + } +} diff --git a/round-based/src/rounds_router/mod.rs b/round-based/src/rounds_router/mod.rs new file mode 100644 index 0000000..8535fe4 --- /dev/null +++ b/round-based/src/rounds_router/mod.rs @@ -0,0 +1,581 @@ +//! Routes incoming MPC messages between rounds +//! +//! [`RoundsRouter`] is an essential building block of MPC protocol, it processes incoming messages, groups +//! them by rounds, and provides convenient API for retrieving received messages at certain round. +//! +//! ## Example +//! +//! ```rust +//! use round_based::{Mpc, MpcParty, ProtocolMessage, Delivery, PartyIndex}; +//! use round_based::rounds_router::{RoundsRouter, simple_store::{RoundInput, RoundMsgs}}; +//! +//! #[derive(ProtocolMessage)] +//! pub enum Msg { +//! Round1(Msg1), +//! Round2(Msg2), +//! } +//! +//! pub struct Msg1 { /* ... */ } +//! pub struct Msg2 { /* ... */ } +//! +//! pub async fn some_mpc_protocol(party: M, i: PartyIndex, n: u16) -> Result +//! where +//! M: Mpc, +//! { +//! let MpcParty{ delivery, .. } = party.into_party(); +//! +//! let (incomings, _outgoings) = delivery.split(); +//! +//! // Build `Rounds` +//! let mut rounds = RoundsRouter::builder(); +//! let round1 = rounds.add_round(RoundInput::::broadcast(i, n)); +//! let round2 = rounds.add_round(RoundInput::::p2p(i, n)); +//! let mut rounds = rounds.listen(incomings); +//! +//! // Receive messages from round 1 +//! let msgs: RoundMsgs = rounds.complete(round1).await?; +//! +//! // ... process received messages +//! +//! // Receive messages from round 2 +//! let msgs = rounds.complete(round2).await?; +//! +//! // ... +//! # todo!() +//! } +//! # type Output = (); +//! # type Error = Box; +//! ``` + +use std::any::Any; +use std::collections::HashMap; +use std::convert::Infallible; +use std::fmt::Debug; +use std::mem; + +use futures_util::{Stream, StreamExt}; +use phantom_type::PhantomType; +use thiserror::Error; +use tracing::{debug, error, trace, trace_span, warn, Span}; + +use crate::Incoming; + +#[doc(inline)] +pub use self::errors::CompleteRoundError; +pub use self::store::*; + +pub mod simple_store; +mod store; + +/// Routes received messages between protocol rounds +/// +/// See [module level](self) documentation to learn more about it. +pub struct RoundsRouter { + incomings: S, + rounds: HashMap + Send>>>, +} + +impl RoundsRouter { + /// Instantiates [`RoundsRouterBuilder`] + pub fn builder() -> RoundsRouterBuilder { + RoundsRouterBuilder::new() + } +} + +impl RoundsRouter +where + M: ProtocolMessage, + S: Stream, E>> + Unpin, +{ + /// Completes specified round + /// + /// Waits until all messages at specified round are received. Returns received + /// messages if round is successfully completed, or error otherwise. + #[inline(always)] + pub async fn complete( + &mut self, + round: Round, + ) -> Result> + where + R: MessagesStore, + M: RoundMessage, + { + let round_number = >::ROUND; + let span = trace_span!("Round", n = round_number); + debug!(parent: &span, "pending round to complete"); + + match self.complete_with_span(&span, round).await { + Ok(output) => { + trace!(parent: &span, "round successfully completed"); + Ok(output) + } + Err(err) => { + error!(parent: &span, %err, "round terminated with error"); + Err(err) + } + } + } + + async fn complete_with_span( + &mut self, + span: &Span, + _round: Round, + ) -> Result> + where + R: MessagesStore, + M: RoundMessage, + { + let pending_round = >::ROUND; + if let Some(output) = self.retrieve_round_output_if_its_completed::() { + return output; + } + + loop { + let incoming = match self.incomings.next().await { + Some(Ok(msg)) => msg, + Some(Err(err)) => return Err(errors::IoError::Io(err).into()), + None => return Err(errors::IoError::UnexpectedEof.into()), + }; + let message_round_n = incoming.msg.round(); + + let message_round = match self.rounds.get_mut(&message_round_n) { + Some(Some(round)) => round, + Some(None) => { + warn!( + parent: span, + n = message_round_n, + "got message for the round that was already completed, ignoring it" + ); + continue; + } + None => { + return Err( + errors::RoundsMisuse::UnregisteredRound { n: message_round_n }.into(), + ) + } + }; + if message_round.needs_more_messages().no() { + warn!( + parent: span, + n = message_round_n, + "received message for the round that was already completed, ignoring it" + ); + continue; + } + message_round.process_message(incoming); + + if pending_round == message_round_n { + if let Some(output) = self.retrieve_round_output_if_its_completed::() { + return output; + } + } + } + } + + fn retrieve_round_output_if_its_completed( + &mut self, + ) -> Option>> + where + R: MessagesStore, + M: RoundMessage, + { + let round_number = >::ROUND; + let round_slot = match self + .rounds + .get_mut(&round_number) + .ok_or(errors::RoundsMisuse::UnregisteredRound { n: round_number }) + { + Ok(slot) => slot, + Err(err) => return Some(Err(err.into())), + }; + let round = match round_slot + .as_mut() + .ok_or(errors::RoundsMisuse::RoundAlreadyCompleted) + { + Ok(round) => round, + Err(err) => return Some(Err(err.into())), + }; + if round.needs_more_messages().no() { + Some(Self::retrieve_round_output::(round_slot)) + } else { + None + } + } + + fn retrieve_round_output( + slot: &mut Option + Send>>, + ) -> Result> + where + R: MessagesStore, + M: RoundMessage, + { + let mut round = slot.take().ok_or(errors::RoundsMisuse::UnregisteredRound { + n: >::ROUND, + })?; + match round.take_output() { + Ok(Ok(any)) => Ok(*any + .downcast::() + .or(Err(CompleteRoundError::from( + errors::Bug::MismatchedOutputType, + )))?), + Ok(Err(any)) => Err(any + .downcast::>() + .or(Err(CompleteRoundError::from( + errors::Bug::MismatchedErrorType, + )))? + .map_io_err(|e| match e {})), + Err(err) => Err(errors::Bug::TakeRoundResult(err).into()), + } + } +} + +/// Builds [`RoundsRouter`] +pub struct RoundsRouterBuilder { + rounds: HashMap + Send>>>, +} + +impl RoundsRouterBuilder +where + M: ProtocolMessage + 'static, +{ + /// Constructs [`RoundsRouterBuilder`] + /// + /// Alias to [`RoundsRouter::builder`] + pub fn new() -> Self { + Self { + rounds: HashMap::new(), + } + } + + /// Registers new round + /// + /// ## Panics + /// Panics if round `R` was already registered + pub fn add_round(&mut self, message_store: R) -> Round + where + R: MessagesStore + Send + 'static, + R::Output: Send, + R::Error: Send, + M: RoundMessage, + { + let overridden_round = self.rounds.insert( + M::ROUND, + Some(Box::new(ProcessRoundMessageImpl::InProgress { + store: message_store, + _ph: PhantomType::new(), + })), + ); + if overridden_round.is_some() { + panic!("round {} is overridden", M::ROUND); + } + Round { + _ph: PhantomType::new(), + } + } + + /// Builds [`RoundsRouter`] + /// + /// Takes a stream of incoming messages which will be routed between registered rounds + pub fn listen(self, incomings: S) -> RoundsRouter + where + S: Stream, E>>, + { + RoundsRouter { + incomings, + rounds: self.rounds, + } + } +} + +/// A round of MPC protocol +/// +/// `Round` can be used to retrieve messages received at this round by calling [`RoundsRouter::complete`]. See +/// [module level](self) documentation to see usage. +pub struct Round { + _ph: PhantomType, +} + +trait ProcessRoundMessage { + type Msg; + + /// Processes round message + /// + /// Before calling this method you must ensure that `.needs_more_messages()` returns `Yes`, + /// otherwise calling this method is unexpected. + fn process_message(&mut self, msg: Incoming); + + /// Indicated whether the store needs more messages + /// + /// If it returns `Yes`, then you need to collect more messages to complete round. If it's `No` + /// then you need to take the round output by calling `.take_output()`. + fn needs_more_messages(&self) -> NeedsMoreMessages; + + /// Tries to obtain round output + /// + /// Can be called once `process_message()` returned `NeedMoreMessages::No`. + /// + /// Returns: + /// * `Ok(Ok(any))` — round is successfully completed, `any` needs to be downcasted to `MessageStore::Output` + /// * `Ok(Err(any))` — round has terminated with an error, `any` needs to be downcasted to `CompleteRoundError` + /// * `Err(err)` — couldn't retrieve the output, see [`TakeOutputError`] + fn take_output(&mut self) -> Result, Box>, TakeOutputError>; +} + +#[derive(Debug, Error)] +enum TakeOutputError { + #[error("output is already taken")] + AlreadyTaken, + #[error("output is not ready yet, more messages are needed")] + NotReady, +} + +enum ProcessRoundMessageImpl> { + InProgress { store: S, _ph: PhantomType }, + Completed(Result>), + Gone, +} + +impl ProcessRoundMessageImpl +where + S: MessagesStore, + M: ProtocolMessage + RoundMessage, +{ + fn _process_message( + store: &mut S, + msg: Incoming, + ) -> Result<(), CompleteRoundError> { + let msg = msg.try_map(M::from_protocol_message).map_err(|msg| { + errors::Bug::MessageFromAnotherRound { + actual_number: msg.round(), + expected_round: M::ROUND, + } + })?; + + store + .add_message(msg) + .map_err(CompleteRoundError::ProcessMessage)?; + Ok(()) + } +} + +impl ProcessRoundMessage for ProcessRoundMessageImpl +where + S: MessagesStore, + M: ProtocolMessage + RoundMessage, +{ + type Msg = M; + + fn process_message(&mut self, msg: Incoming) { + let store = match self { + Self::InProgress { store, .. } => store, + _ => { + return; + } + }; + + match Self::_process_message(store, msg) { + Ok(()) => { + if store.wants_more() { + return; + } + + let store = match mem::replace(self, Self::Gone) { + Self::InProgress { store, .. } => store, + _ => { + *self = Self::Completed(Err(errors::Bug::IncoherentState { + expected: "InProgress", + justification: + "we checked at beginning of the function that `state` is InProgress", + } + .into())); + return; + } + }; + + match store.output() { + Ok(output) => *self = Self::Completed(Ok(output)), + Err(_err) => { + *self = + Self::Completed(Err(errors::ImproperStoreImpl::StoreDidntOutput.into())) + } + } + } + Err(err) => { + *self = Self::Completed(Err(err)); + } + } + } + + fn needs_more_messages(&self) -> NeedsMoreMessages { + match self { + Self::InProgress { .. } => NeedsMoreMessages::Yes, + _ => NeedsMoreMessages::No, + } + } + + fn take_output(&mut self) -> Result, Box>, TakeOutputError> { + match self { + Self::InProgress { .. } => return Err(TakeOutputError::NotReady), + Self::Gone => return Err(TakeOutputError::AlreadyTaken), + _ => (), + } + match mem::replace(self, Self::Gone) { + Self::Completed(Ok(output)) => Ok(Ok(Box::new(output))), + Self::Completed(Err(err)) => Ok(Err(Box::new(err))), + _ => unreachable!("it's checked to be completed"), + } + } +} + +enum NeedsMoreMessages { + Yes, + No, +} + +#[allow(dead_code)] +impl NeedsMoreMessages { + pub fn yes(&self) -> bool { + matches!(self, Self::Yes) + } + pub fn no(&self) -> bool { + matches!(self, Self::No) + } +} + +/// When something goes wrong +pub mod errors { + use thiserror::Error; + + use super::TakeOutputError; + + /// Error indicating that `Rounds` failed to complete certain round + #[derive(Debug, Error)] + pub enum CompleteRoundError { + /// [`MessagesStore`](super::MessagesStore) failed to process this message + #[error("failed to process the message")] + ProcessMessage(#[source] ProcessErr), + /// Receiving next message resulted into i/o error + #[error("receive next message")] + Io( + #[source] + #[from] + IoError, + ), + /// Some implementation specific error + /// + /// Error may be result of improper `MessagesStore` implementation, API misuse, or bug + /// in `Rounds` implementation + #[error("implementation error")] + Other(#[source] OtherError), + } + + /// Error indicating that receiving next message resulted into i/o error + #[derive(Error, Debug)] + pub enum IoError { + /// I/O error + #[error("i/o error")] + Io(#[source] E), + /// Encountered unexpected EOF + #[error("unexpected eof")] + UnexpectedEof, + } + + /// Some implementation specific error + /// + /// Error may be result of improper `MessagesStore` implementation, API misuse, or bug + /// in `Rounds` implementation + #[derive(Error, Debug)] + #[error(transparent)] + pub struct OtherError(OtherReason); + + #[derive(Error, Debug)] + pub(super) enum OtherReason { + #[error("improper `MessagesStore` implementation")] + ImproperStoreImpl(ImproperStoreImpl), + #[error("`Rounds` API misuse")] + RoundsMisuse(RoundsMisuse), + #[error("bug in `Rounds` (please, open a issue)")] + Bug(Bug), + } + + #[derive(Debug, Error)] + pub(super) enum ImproperStoreImpl { + /// Store indicated that it received enough messages but didn't output + /// + /// I.e. [`store.wants_more()`] returned `false`, but `store.output()` returned `Err(_)`. + #[error("store didn't output")] + StoreDidntOutput, + } + + #[derive(Debug, Error)] + pub(super) enum RoundsMisuse { + #[error("round is already completed")] + RoundAlreadyCompleted, + #[error("round {n} is not registered")] + UnregisteredRound { n: u16 }, + } + + #[derive(Debug, Error)] + pub(super) enum Bug { + #[error( + "message originates from another round: we process messages from round \ + {expected_round}, got message from round {actual_number}" + )] + MessageFromAnotherRound { + expected_round: u16, + actual_number: u16, + }, + #[error("state is incoherent, it's expected to be {expected}: {justification}")] + IncoherentState { + expected: &'static str, + justification: &'static str, + }, + #[error("mismatched output type")] + MismatchedOutputType, + #[error("mismatched error type")] + MismatchedErrorType, + #[error("take round result")] + TakeRoundResult(#[source] TakeOutputError), + } + + impl CompleteRoundError { + pub(super) fn map_io_err(self, f: F) -> CompleteRoundError + where + F: FnOnce(IoErr) -> E, + { + match self { + CompleteRoundError::Io(err) => CompleteRoundError::Io(err.map_err(f)), + CompleteRoundError::ProcessMessage(err) => CompleteRoundError::ProcessMessage(err), + CompleteRoundError::Other(err) => CompleteRoundError::Other(err), + } + } + } + + impl IoError { + pub(super) fn map_err(self, f: F) -> IoError + where + F: FnOnce(E) -> B, + { + match self { + IoError::Io(e) => IoError::Io(f(e)), + IoError::UnexpectedEof => IoError::UnexpectedEof, + } + } + } + + macro_rules! impl_from_other_error { + ($($err:ident),+,) => {$( + impl From<$err> for CompleteRoundError { + fn from(err: $err) -> Self { + Self::Other(OtherError(OtherReason::$err(err))) + } + } + )+}; + } + + impl_from_other_error! { + ImproperStoreImpl, + RoundsMisuse, + Bug, + } +} diff --git a/round-based/src/rounds_router/simple_store.rs b/round-based/src/rounds_router/simple_store.rs new file mode 100644 index 0000000..49d0b28 --- /dev/null +++ b/round-based/src/rounds_router/simple_store.rs @@ -0,0 +1,463 @@ +//! Simple implementation of `MessagesStore` + +use std::iter; + +use thiserror::Error; + +use crate::{Incoming, MessageType, MsgId, PartyIndex}; + +use super::MessagesStore; + +/// Simple implementation of [MessagesStore] that waits for all parties to send a message +/// +/// Round is considered complete when the store received a message from every party. Note that the +/// store will ignore all the messages such as `msg.sender == local_party_index`. +/// +/// Once round is complete, it outputs [`RoundMsgs`]. +/// +/// ## Example +/// ```rust +/// # use round_based::rounds_router::{MessagesStore, simple_store::RoundInput}; +/// # use round_based::{Incoming, MessageType}; +/// # fn main() -> Result<(), Box> { +/// let mut input = RoundInput::<&'static str>::broadcast(1, 3); +/// input.add_message(Incoming{ +/// id: 0, +/// sender: 0, +/// msg_type: MessageType::Broadcast, +/// msg: "first party message", +/// })?; +/// input.add_message(Incoming{ +/// id: 1, +/// sender: 2, +/// msg_type: MessageType::Broadcast, +/// msg: "third party message", +/// })?; +/// assert!(!input.wants_more()); +/// +/// let output = input.output().unwrap(); +/// assert_eq!(output.clone().into_vec_without_me(), ["first party message", "third party message"]); +/// assert_eq!( +/// output.clone().into_vec_including_me("my msg"), +/// ["first party message", "my msg", "third party message"] +/// ); +/// # Ok(()) } +/// ``` +#[derive(Debug, Clone)] +pub struct RoundInput { + i: PartyIndex, + n: u16, + messages_ids: Vec, + messages: Vec>, + left_messages: u16, + expected_msg_type: MessageType, +} + +/// List of received messages +#[derive(Debug, Clone)] +pub struct RoundMsgs { + i: PartyIndex, + ids: Vec, + messages: Vec, +} + +impl RoundInput { + /// Constructs new messages store + /// + /// Takes index of local party `i` and amount of parties `n` + /// + /// ## Panics + /// Panics if `n` is less than 2 or `i` is not in the range `[0; n)`. + pub fn new(i: PartyIndex, n: u16, msg_type: MessageType) -> Self { + assert!(n >= 2); + assert!(i < n); + + Self { + i, + n, + messages_ids: vec![0; usize::from(n) - 1], + messages: iter::repeat_with(|| None) + .take(usize::from(n) - 1) + .collect(), + left_messages: n - 1, + expected_msg_type: msg_type, + } + } + + /// Construct a new store for broadcast messages + /// + /// The same as `RoundInput::new(i, n, MessageType::Broadcast)` + pub fn broadcast(i: PartyIndex, n: u16) -> Self { + Self::new(i, n, MessageType::Broadcast) + } + + /// Construct a new store for p2p messages + /// + /// The same as `RoundInput::new(i, n, MessageType::P2P)` + pub fn p2p(i: PartyIndex, n: u16) -> Self { + Self::new(i, n, MessageType::P2P) + } + + fn is_expected_type_of_msg(&self, msg_type: MessageType) -> bool { + self.expected_msg_type == msg_type + } +} + +impl MessagesStore for RoundInput +where + M: 'static, +{ + type Msg = M; + type Output = RoundMsgs; + type Error = RoundInputError; + + fn add_message(&mut self, msg: Incoming) -> Result<(), Self::Error> { + if !self.is_expected_type_of_msg(msg.msg_type) { + return Err(RoundInputError::MismatchedMessageType { + msg_id: msg.id, + expected: self.expected_msg_type, + actual: msg.msg_type, + } + .into()); + } + if msg.sender == self.i { + // Ignore own messages + return Ok(()); + } + + let index = usize::from(if msg.sender < self.i { + msg.sender + } else { + msg.sender - 1 + }); + + match self.messages.get_mut(index) { + Some(vacant @ None) => { + *vacant = Some(msg.msg); + self.messages_ids[index] = msg.id; + self.left_messages -= 1; + Ok(()) + } + Some(Some(_)) => Err(RoundInputError::AttemptToOverwriteReceivedMsg { + msgs_ids: [self.messages_ids[index], msg.id], + sender: msg.sender, + } + .into()), + None => Err(RoundInputError::SenderIndexOutOfRange { + msg_id: msg.id, + sender: msg.sender, + n: self.n, + } + .into()), + } + } + + fn wants_more(&self) -> bool { + self.left_messages > 0 + } + + fn output(self) -> Result { + if self.left_messages > 0 { + Err(self) + } else { + Ok(RoundMsgs { + i: self.i, + ids: self.messages_ids, + messages: self.messages.into_iter().flatten().collect(), + }) + } + } +} + +impl RoundMsgs { + /// Returns vec of `n-1` received messages + /// + /// Messages appear in the list in ascending order of sender index. E.g. for n=4 and local party index i=2, + /// the list would look like: `[{msg from i=0}, {msg from i=1}, {msg from i=3}]`. + pub fn into_vec_without_me(self) -> Vec { + self.messages + } + + /// Returns vec of received messages plus party's own message + /// + /// Similar to `into_vec_without_me`, but inserts `my_msg` at position `i` in resulting list. Thus, i-th + /// message in the list was received from i-th party. + pub fn into_vec_including_me(mut self, my_msg: M) -> Vec { + self.messages.insert(usize::from(self.i), my_msg); + self.messages + } + + /// Returns iterator over messages + pub fn iter(&self) -> impl Iterator { + self.messages.iter() + } + + /// Returns iterator over received messages plus party's own message + /// + /// Similar to [`.iter()`](Self::iter), but inserts `my_msg` at position `i`. Thus, i-th message in the + /// iterator is the message received from party `i`. + pub fn iter_including_me<'m>(&'m self, my_msg: &'m M) -> impl Iterator { + self.messages + .iter() + .take(usize::from(self.i)) + .chain(iter::once(my_msg)) + .chain(self.messages.iter().skip(usize::from(self.i))) + } + + /// Returns iterator over messages with sender indexes + /// + /// Iterator yields `(sender_index, msg_id, message)` + pub fn into_iter_indexed(self) -> impl Iterator { + let parties_indexes = (0..self.i).chain(self.i + 1..); + parties_indexes + .zip(self.ids.into_iter()) + .zip(self.messages) + .map(|((party_ind, msg_id), msg)| (party_ind, msg_id, msg)) + } + + /// Returns iterator over messages with sender indexes + /// + /// Iterator yields `(sender_index, msg_id, &message)` + pub fn iter_indexed(&self) -> impl Iterator { + let parties_indexes = (0..self.i).chain(self.i + 1..); + parties_indexes + .zip(&self.ids) + .zip(&self.messages) + .map(|((party_ind, msg_id), msg)| (party_ind, *msg_id, msg)) + } +} + +/// Error explaining why `RoundInput` wasn't able to process a message +#[derive(Debug, Error)] +pub enum RoundInputError { + /// Party sent two messages in one round + /// + /// `msgs_ids` are ids of conflicting messages + #[error("party {sender} tried to overwrite message")] + AttemptToOverwriteReceivedMsg { + /// IDs of conflicting messages + msgs_ids: [MsgId; 2], + /// Index of party who sent two messages in one round + sender: PartyIndex, + }, + /// Unknown sender + /// + /// This error is thrown when index of sender is not in `[0; n)` where `n` is number of + /// parties involved in the protocol (provided in [`RoundInput::new`]) + #[error("sender index is out of range: sender={sender}, n={n}")] + SenderIndexOutOfRange { + /// Message ID + msg_id: MsgId, + /// Sender index + sender: PartyIndex, + /// Number of parties + n: u16, + }, + /// Received message type doesn't match expectations + /// + /// For instance, this error is returned when it's expected to receive broadcast message, + /// but party sent p2p message instead (which is rough protocol violation). + #[error("expected message {expected:?}, got {actual:?}")] + MismatchedMessageType { + /// Message ID + msg_id: MsgId, + /// Expected type of message + expected: MessageType, + /// Actual type of message + actual: MessageType, + }, +} + +#[cfg(test)] +mod tests { + use matches::assert_matches; + + use crate::rounds_router::store::MessagesStore; + use crate::{Incoming, MessageType}; + + use super::{RoundInput, RoundInputError}; + + #[derive(Debug, Clone, PartialEq)] + pub struct Msg(u16); + + #[test] + fn store_outputs_received_messages() { + let mut store = RoundInput::::new(3, 5, MessageType::P2P); + + let msgs = (0..5) + .map(|s| Incoming { + id: s.into(), + sender: s, + msg_type: MessageType::P2P, + msg: Msg(10 + s), + }) + .filter(|incoming| incoming.sender != 3) + .collect::>(); + + for msg in &msgs { + assert!(store.wants_more()); + store.add_message(msg.clone()).unwrap(); + } + + assert!(!store.wants_more()); + let received = store.output().unwrap(); + + // without me + let msgs: Vec<_> = msgs.into_iter().map(|msg| msg.msg).collect(); + assert_eq!(received.clone().into_vec_without_me(), msgs); + + // including me + let received = received.into_vec_including_me(Msg(13)); + assert_eq!(received[0..3], msgs[0..3]); + assert_eq!(received[3], Msg(13)); + assert_eq!(received[4..5], msgs[3..4]); + } + + #[test] + fn store_returns_error_if_sender_index_is_out_of_range() { + let mut store = RoundInput::new(3, 5, MessageType::P2P); + let error = store + .add_message(Incoming { + id: 0, + sender: 5, + msg_type: MessageType::P2P, + msg: Msg(123), + }) + .unwrap_err(); + assert_matches!( + error, + RoundInputError::SenderIndexOutOfRange { msg_id, sender, n } if msg_id == 0 && sender == 5 && n == 5 + ); + } + + #[test] + fn store_returns_error_if_incoming_msg_overwrites_already_received_one() { + let mut store = RoundInput::new(0, 3, MessageType::P2P); + store + .add_message(Incoming { + id: 0, + sender: 1, + msg_type: MessageType::P2P, + msg: Msg(11), + }) + .unwrap(); + let error = store + .add_message(Incoming { + id: 1, + sender: 1, + msg_type: MessageType::P2P, + msg: Msg(112), + }) + .unwrap_err(); + assert_matches!(error, RoundInputError::AttemptToOverwriteReceivedMsg { msgs_ids, sender } if msgs_ids[0] == 0 && msgs_ids[1] == 1 && sender == 1); + store + .add_message(Incoming { + id: 2, + sender: 2, + msg_type: MessageType::P2P, + msg: Msg(22), + }) + .unwrap(); + + let output = store.output().unwrap().into_vec_without_me(); + assert_eq!(output, [Msg(11), Msg(22)]); + } + + #[test] + fn store_returns_error_if_tried_to_output_before_receiving_enough_messages() { + let mut store = RoundInput::::new(3, 5, MessageType::P2P); + + let msgs = (0..5) + .map(|s| Incoming { + id: s.into(), + sender: s, + msg_type: MessageType::P2P, + msg: Msg(10 + s), + }) + .filter(|incoming| incoming.sender != 3); + + for msg in msgs { + assert!(store.wants_more()); + store = store.output().unwrap_err(); + + store.add_message(msg).unwrap(); + } + + let _ = store.output().unwrap(); + } + + #[test] + fn store_returns_error_if_message_type_mismatched() { + let mut store = RoundInput::::p2p(3, 5); + let err = store + .add_message(Incoming { + id: 0, + sender: 0, + msg_type: MessageType::Broadcast, + msg: Msg(1), + }) + .unwrap_err(); + assert_matches!( + err, + RoundInputError::MismatchedMessageType { + msg_id: 0, + expected: MessageType::P2P, + actual: MessageType::Broadcast + } + ); + + let mut store = RoundInput::::broadcast(3, 5); + let err = store + .add_message(Incoming { + id: 0, + sender: 0, + msg_type: MessageType::P2P, + msg: Msg(1), + }) + .unwrap_err(); + assert_matches!( + err, + RoundInputError::MismatchedMessageType { + msg_id: 0, + expected: MessageType::Broadcast, + actual: MessageType::P2P, + } + ); + for sender in 0u16..5 { + store + .add_message(Incoming { + id: 0, + sender, + msg_type: MessageType::Broadcast, + msg: Msg(1), + }) + .unwrap(); + } + + let mut store = RoundInput::::broadcast(3, 5); + let err = store + .add_message(Incoming { + id: 0, + sender: 0, + msg_type: MessageType::P2P, + msg: Msg(1), + }) + .unwrap_err(); + assert_matches!( + err, + RoundInputError::MismatchedMessageType { + msg_id: 0, + expected: MessageType::Broadcast, + actual, + } if actual == MessageType::P2P + ); + store + .add_message(Incoming { + id: 0, + sender: 0, + msg_type: MessageType::Broadcast, + msg: Msg(1), + }) + .unwrap(); + } +} diff --git a/round-based/src/rounds_router/store.rs b/round-based/src/rounds_router/store.rs new file mode 100644 index 0000000..11ef4c0 --- /dev/null +++ b/round-based/src/rounds_router/store.rs @@ -0,0 +1,132 @@ +use crate::Incoming; + +/// Stores messages received at particular round +/// +/// In MPC protocol, party at every round usually needs to receive up to `n` messages. `MessagesStore` +/// is a container that stores messages, it knows how many messages are expected to be received, +/// and should implement extra measures against malicious parties (e.g. prohibit message overwrite). +/// +/// ## Procedure +/// `MessagesStore` stores received messages. Once enough messages are received, it outputs [`MessagesStore::Output`]. +/// In order to save received messages, [`.add_message(msg)`] is called. Then, [`.wants_more()`] tells whether more +/// messages are needed to be received. If it returned `false`, then output can be retrieved by calling [`.output()`]. +/// +/// [`.add_message(msg)`]: Self::add_message +/// [`.wants_more()`]: Self::wants_more +/// [`.output()`]: Self::output +/// +/// ## Example +/// [`RoundInput`](super::simple_store::RoundInput) is an simple messages store. Refer to its docs to see usage examples. +pub trait MessagesStore: Sized + 'static { + /// Message type + type Msg; + /// Store output (e.g. `Vec<_>` of received messages) + type Output; + /// Store error + type Error; + + /// Adds received message to the store + /// + /// Returns error if message cannot be processed. Usually it means that sender behaves maliciously. + fn add_message(&mut self, msg: Incoming) -> Result<(), Self::Error>; + /// Indicates if store expects more messages to receive + fn wants_more(&self) -> bool; + /// Retrieves store output if enough messages are received + /// + /// Returns `Err(self)` if more message are needed to be received. + /// + /// If store indicated that it needs no more messages (ie `store.wants_more() == false`), then + /// this function must return `Ok(_)`. + fn output(self) -> Result; +} + +/// Message of MPC protocol +/// +/// MPC protocols typically consist of several rounds, each round has differently typed message. +/// `ProtocolMessage` and [`RoundMessage`] traits are used to examine received message: `ProtocolMessage::round` +/// determines which round message belongs to, and then `RoundMessage` trait can be used to retrieve +/// actual round-specific message. +/// +/// You should derive these traits using proc macro (requires `derive` feature): +/// ```rust +/// use round_based::ProtocolMessage; +/// +/// #[derive(ProtocolMessage)] +/// pub enum Message { +/// Round1(Msg1), +/// Round2(Msg2), +/// // ... +/// } +/// +/// pub struct Msg1 { /* ... */ } +/// pub struct Msg2 { /* ... */ } +/// ``` +/// +/// This desugars into: +/// +/// ```rust +/// use round_based::rounds_router::{ProtocolMessage, RoundMessage}; +/// +/// pub enum Message { +/// Round1(Msg1), +/// Round2(Msg2), +/// // ... +/// } +/// +/// pub struct Msg1 { /* ... */ } +/// pub struct Msg2 { /* ... */ } +/// +/// impl ProtocolMessage for Message { +/// fn round(&self) -> u16 { +/// match self { +/// Message::Round1(_) => 1, +/// Message::Round2(_) => 2, +/// // ... +/// } +/// } +/// } +/// impl RoundMessage for Message { +/// const ROUND: u16 = 1; +/// fn to_protocol_message(round_message: Msg1) -> Self { +/// Message::Round1(round_message) +/// } +/// fn from_protocol_message(protocol_message: Self) -> Result { +/// match protocol_message { +/// Message::Round1(msg) => Ok(msg), +/// msg => Err(msg), +/// } +/// } +/// } +/// impl RoundMessage for Message { +/// const ROUND: u16 = 2; +/// fn to_protocol_message(round_message: Msg2) -> Self { +/// Message::Round2(round_message) +/// } +/// fn from_protocol_message(protocol_message: Self) -> Result { +/// match protocol_message { +/// Message::Round2(msg) => Ok(msg), +/// msg => Err(msg), +/// } +/// } +/// } +/// ``` +pub trait ProtocolMessage: Sized { + /// Number of round this message originates from + fn round(&self) -> u16; +} + +/// Round message +/// +/// See [`ProtocolMessage`] trait documentation. +pub trait RoundMessage: ProtocolMessage { + /// Number of the round this message belongs to + const ROUND: u16; + + /// Converts round message into protocol message (never fails) + fn to_protocol_message(round_message: M) -> Self; + /// Extracts round message from protocol message + /// + /// Returns `Err(protocol_message)` if `protocol_message.round() != Self::ROUND`, otherwise + /// returns `Ok(round_message)` + fn from_protocol_message(protocol_message: Self) -> Result; +} diff --git a/round-based/src/runtime.rs b/round-based/src/runtime.rs new file mode 100644 index 0000000..83c64fb --- /dev/null +++ b/round-based/src/runtime.rs @@ -0,0 +1,83 @@ +//! Async runtime abstraction +//! +//! Runtime abstraction allows the MPC protocol to stay runtime-agnostic while being +//! able to use features that are common to any runtime + +/// Async runtime abstraction +/// +/// Abstracts async runtime like [tokio]. Currently only exposes a [yield_now](Self::yield_now) +/// function. +pub trait AsyncRuntime { + /// Future type returned by [yield_now](Self::yield_now) + type YieldNowFuture: core::future::Future + Send + 'static; + + /// Yields the execution back to the runtime + /// + /// If the protocol performs a long computation, it might be better for performance + /// to split it with yield points, so the signle computation does not starve other + /// tasks. + fn yield_now(&self) -> Self::YieldNowFuture; +} + +/// [Tokio](tokio)-specific async runtime +#[cfg(feature = "runtime-tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "runtime-tokio")))] +#[derive(Debug, Default)] +pub struct TokioRuntime; + +#[cfg(feature = "runtime-tokio")] +impl AsyncRuntime for TokioRuntime { + type YieldNowFuture = core::pin::Pin + Send>>; + + fn yield_now(&self) -> Self::YieldNowFuture { + Box::pin(tokio::task::yield_now()) + } +} + +#[doc(inline)] +pub use unknown_runtime::UnknownRuntime; + +/// Default runtime +#[cfg(feature = "runtime-tokio")] +pub type DefaultRuntime = TokioRuntime; +/// Default runtime +#[cfg(not(feature = "runtime-tokio"))] +pub type DefaultRuntime = UnknownRuntime; + +/// Unknown async runtime +pub mod unknown_runtime { + /// Unknown async runtime + /// + /// Tries to implement runtime features using generic futures code. It's better to use + /// runtime-specific implementation. + #[derive(Debug, Default)] + pub struct UnknownRuntime; + + impl super::AsyncRuntime for UnknownRuntime { + type YieldNowFuture = YieldNow; + + fn yield_now(&self) -> Self::YieldNowFuture { + YieldNow(false) + } + } + + /// Future for the `yield_now` function. + pub struct YieldNow(bool); + + impl core::future::Future for YieldNow { + type Output = (); + + fn poll( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + if !self.0 { + self.0 = true; + cx.waker().wake_by_ref(); + core::task::Poll::Pending + } else { + core::task::Poll::Ready(()) + } + } + } +} diff --git a/round-based/src/simulation.rs b/round-based/src/simulation.rs new file mode 100644 index 0000000..829113e --- /dev/null +++ b/round-based/src/simulation.rs @@ -0,0 +1,218 @@ +//! Multiparty protocol simulation +//! +//! [`Simulation`] is an essential developer tool for testing the multiparty protocol locally. +//! It covers most of the boilerplate by mocking networking. +//! +//! ## Example +//! +//! ```rust +//! use round_based::{Mpc, PartyIndex}; +//! use round_based::simulation::Simulation; +//! use futures::future::try_join_all; +//! +//! # type Result = std::result::Result; +//! # type Randomness = [u8; 32]; +//! # type Msg = (); +//! // Any MPC protocol you want to test +//! pub async fn protocol_of_random_generation(party: M, i: PartyIndex, n: u16) -> Result +//! where M: Mpc +//! { +//! // ... +//! # todo!() +//! } +//! +//! async fn test_randomness_generation() { +//! let n = 3; +//! +//! let mut simulation = Simulation::::new(); +//! let mut outputs = vec![]; +//! for i in 0..n { +//! let party = simulation.add_party(); +//! outputs.push(protocol_of_random_generation(party, i, n)); +//! } +//! +//! // Waits each party to complete the protocol +//! let outputs = try_join_all(outputs).await.expect("protocol wasn't completed successfully"); +//! // Asserts that all parties output the same randomness +//! for output in outputs.iter().skip(1) { +//! assert_eq!(&outputs[0], output); +//! } +//! } +//! ``` + +use std::pin::Pin; +use std::sync::atomic::AtomicU64; +use std::sync::Arc; +use std::task::ready; +use std::task::{Context, Poll}; + +use futures_util::{Sink, Stream}; +use tokio::sync::broadcast; +use tokio_stream::wrappers::{errors::BroadcastStreamRecvError, BroadcastStream}; + +use crate::delivery::{Delivery, Incoming, Outgoing}; +use crate::{MessageDestination, MessageType, MpcParty, MsgId, PartyIndex}; + +/// Multiparty protocol simulator +pub struct Simulation { + channel: broadcast::Sender>>, + next_party_idx: PartyIndex, + next_msg_id: Arc, +} + +impl Simulation +where + M: Clone + Send + Unpin + 'static, +{ + /// Instantiates a new simulation + pub fn new() -> Self { + Self::with_capacity(500) + } + + /// Instantiates a new simulation with given capacity + /// + /// `Simulation` stores internally all sent messages. Capacity limits size of the internal buffer. + /// Because of that you might run into error if you choose too small capacity. Choose capacity + /// that can fit all the messages sent by all the parties during entire protocol lifetime. + /// + /// Default capacity is 500 (i.e. if you call `Simulation::new()`) + pub fn with_capacity(capacity: usize) -> Self { + Self { + channel: broadcast::channel(capacity).0, + next_party_idx: 0, + next_msg_id: Default::default(), + } + } + + /// Adds new party to the network + pub fn add_party(&mut self) -> MpcParty> { + MpcParty::connected(self.connect_new_party()) + } + + /// Connects new party to the network + /// + /// Similar to [`.add_party()`](Self::add_party) but returns `MockedDelivery` instead of + /// `MpcParty>` + pub fn connect_new_party(&mut self) -> MockedDelivery { + let local_party_idx = self.next_party_idx; + self.next_party_idx += 1; + + MockedDelivery { + incoming: MockedIncoming { + local_party_idx, + receiver: BroadcastStream::new(self.channel.subscribe()), + }, + outgoing: MockedOutgoing { + local_party_idx, + sender: self.channel.clone(), + next_msg_id: self.next_msg_id.clone(), + }, + } + } +} + +impl Default for Simulation +where + M: Clone + Send + Unpin + 'static, +{ + fn default() -> Self { + Self::new() + } +} + +/// Mocked networking +pub struct MockedDelivery { + incoming: MockedIncoming, + outgoing: MockedOutgoing, +} + +impl Delivery for MockedDelivery +where + M: Clone + Send + Unpin + 'static, +{ + type Send = MockedOutgoing; + type Receive = MockedIncoming; + type SendError = broadcast::error::SendError<()>; + type ReceiveError = BroadcastStreamRecvError; + + fn split(self) -> (Self::Receive, Self::Send) { + (self.incoming, self.outgoing) + } +} + +/// Incoming channel of mocked network +pub struct MockedIncoming { + local_party_idx: PartyIndex, + receiver: BroadcastStream>>, +} + +impl Stream for MockedIncoming +where + M: Clone + Send + 'static, +{ + type Item = Result, BroadcastStreamRecvError>; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + let msg = match ready!(Pin::new(&mut self.receiver).poll_next(cx)) { + Some(Ok(m)) => m, + Some(Err(e)) => return Poll::Ready(Some(Err(e))), + None => return Poll::Ready(None), + }; + if msg.recipient.is_p2p() + && msg.recipient != MessageDestination::OneParty(self.local_party_idx) + { + continue; + } + return Poll::Ready(Some(Ok(msg.msg))); + } + } +} + +/// Outgoing channel of mocked network +pub struct MockedOutgoing { + local_party_idx: PartyIndex, + sender: broadcast::Sender>>, + next_msg_id: Arc, +} + +impl Sink> for MockedOutgoing { + type Error = broadcast::error::SendError<()>; + + fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn start_send(self: Pin<&mut Self>, msg: Outgoing) -> Result<(), Self::Error> { + let msg_type = match msg.recipient { + MessageDestination::AllParties => MessageType::Broadcast, + MessageDestination::OneParty(_) => MessageType::P2P, + }; + self.sender + .send(msg.map(|m| Incoming { + id: self.next_msg_id.next(), + sender: self.local_party_idx, + msg_type, + msg: m, + })) + .map_err(|_| broadcast::error::SendError(()))?; + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } +} + +#[derive(Default)] +struct NextMessageId(AtomicU64); + +impl NextMessageId { + pub fn next(&self) -> MsgId { + self.0.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + } +} diff --git a/round-based/tests/derive.rs b/round-based/tests/derive.rs new file mode 100644 index 0000000..4a9018a --- /dev/null +++ b/round-based/tests/derive.rs @@ -0,0 +1,6 @@ +#[test] +fn compile_test() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/derive/compile-fail/*.rs"); + t.pass("tests/derive/compile-pass/*.rs") +} diff --git a/round-based/tests/derive/compile-fail/wrong_usage.rs b/round-based/tests/derive/compile-fail/wrong_usage.rs new file mode 100644 index 0000000..d081241 --- /dev/null +++ b/round-based/tests/derive/compile-fail/wrong_usage.rs @@ -0,0 +1,55 @@ +use round_based::ProtocolMessage; + +#[derive(ProtocolMessage)] +enum Msg { + // Unnamed variant with single field is the only correct enum variant + // that doesn't contradicts with ProtocolMessage derivation + VariantA(u16), + // Error: You can't have named variants + VariantB { n: u32 }, + // Error: Variant must have exactly 1 field + VariantC(u32, String), + // Error: Variant must have exactly 1 field!! + VariantD(), + // Error: Union variants are not permitted + VariantE, +} + +// Structure cannot implement ProtocolMessage +#[derive(ProtocolMessage)] +struct Msg2 { + some_field: u64, +} + +// Union cannot implement ProtocolMessage +#[derive(ProtocolMessage)] +union Msg3 { + variant: u64, +} + +// protocol_message is repeated twice +#[derive(ProtocolMessage)] +#[protocol_message(root = one)] +#[protocol_message(root = two)] +enum Msg4 { + One(u32), + Two(u16), +} + +// ", blah blah" is not permitted input +#[derive(ProtocolMessage)] +#[protocol_message(root = one, blah blah)] +enum Msg5 { + One(u32), + Two(u16), +} + +// `protocol_message` must not be empty +#[derive(ProtocolMessage)] +#[protocol_message()] +enum Msg6 { + One(u32), + Two(u16), +} + +fn main() {} diff --git a/round-based/tests/derive/compile-fail/wrong_usage.stderr b/round-based/tests/derive/compile-fail/wrong_usage.stderr new file mode 100644 index 0000000..f3d8341 --- /dev/null +++ b/round-based/tests/derive/compile-fail/wrong_usage.stderr @@ -0,0 +1,53 @@ +error: named variants are not allowed in ProtocolMessage + --> tests/derive/compile-fail/wrong_usage.rs:9:5 + | +9 | VariantB { n: u32 }, + | ^^^^^^^^ + +error: this variant must contain exactly one field to be valid ProtocolMessage + --> tests/derive/compile-fail/wrong_usage.rs:11:5 + | +11 | VariantC(u32, String), + | ^^^^^^^^ + +error: this variant must contain exactly one field to be valid ProtocolMessage + --> tests/derive/compile-fail/wrong_usage.rs:13:5 + | +13 | VariantD(), + | ^^^^^^^^ + +error: unit variants are not allowed in ProtocolMessage + --> tests/derive/compile-fail/wrong_usage.rs:15:5 + | +15 | VariantE, + | ^^^^^^^^ + +error: only enum may implement ProtocolMessage + --> tests/derive/compile-fail/wrong_usage.rs:20:1 + | +20 | struct Msg2 { + | ^^^^^^ + +error: only enum may implement ProtocolMessage + --> tests/derive/compile-fail/wrong_usage.rs:26:1 + | +26 | union Msg3 { + | ^^^^^ + +error: #[protocol_message] attribute appears more than once + --> tests/derive/compile-fail/wrong_usage.rs:33:3 + | +33 | #[protocol_message(root = two)] + | ^^^^^^^^^^^^^^^^ + +error: unexpected token + --> tests/derive/compile-fail/wrong_usage.rs:41:30 + | +41 | #[protocol_message(root = one, blah blah)] + | ^ + +error: unexpected end of input, expected `root` + --> tests/derive/compile-fail/wrong_usage.rs:49:20 + | +49 | #[protocol_message()] + | ^ diff --git a/round-based/tests/derive/compile-pass/correct_usage.rs b/round-based/tests/derive/compile-pass/correct_usage.rs new file mode 100644 index 0000000..e4be823 --- /dev/null +++ b/round-based/tests/derive/compile-pass/correct_usage.rs @@ -0,0 +1,21 @@ +use round_based::ProtocolMessage; + +#[derive(ProtocolMessage)] +enum Msg { + VariantA(u16), + VariantB(String), + VariantC((u16, String)), + VariantD(MyStruct), +} +#[derive(ProtocolMessage)] +#[protocol_message(root = round_based)] +enum Msg2 { + VariantA(u16), + VariantB(String), + VariantC((u16, String)), + VariantD(MyStruct), +} + +struct MyStruct(T); + +fn main() {} diff --git a/src/async_runtime/mod.rs b/src/async_runtime/mod.rs deleted file mode 100644 index 8769d8d..0000000 --- a/src/async_runtime/mod.rs +++ /dev/null @@ -1,370 +0,0 @@ -//! Instruments for executing protocol in async environment - -use std::fmt::{self, Debug}; -use std::future::Future; - -use futures::future::{Either, FutureExt}; -use futures::sink::Sink; -use futures::stream::{self, FusedStream, Stream, StreamExt}; -use futures::SinkExt; -use tokio::time::{self, timeout_at}; - -use crate::{IsCritical, Msg, StateMachine}; -use watcher::{BlindWatcher, ProtocolWatcher, When}; - -pub mod watcher; - -/// Executes protocol in async environment using [tokio] backend -/// -/// In the most simple setting, you just provide protocol initial state, stream of incoming -/// messages, and sink for outgoing messages, and you're able to easily execute it: -/// ```no_run -/// # use futures::stream::{self, Stream, FusedStream}; -/// # use futures::sink::{self, Sink, SinkExt}; -/// # use round_based::{Msg, StateMachine, AsyncProtocol}; -/// # struct M; -/// # #[derive(Debug)] struct Error; -/// # impl From for Error { -/// # fn from(_: std::convert::Infallible) -> Error { Error } -/// # } -/// # trait Constructable { fn initial() -> Self; } -/// fn incoming() -> impl Stream, Error>> + FusedStream + Unpin { -/// // ... -/// # stream::pending() -/// } -/// fn outgoing() -> impl Sink, Error=Error> + Unpin { -/// // ... -/// # sink::drain().with(|x| futures::future::ok(x)) -/// } -/// # async fn execute_protocol() -> Result<(), round_based::async_runtime::Error> -/// # where State: StateMachine + Constructable + Send + 'static -/// # { -/// let output: State::Output = AsyncProtocol::new(State::initial(), incoming(), outgoing()) -/// .run().await?; -/// // ... -/// # let _ = output; Ok(()) -/// # } -/// ``` -/// -/// Note that if the protocol has some cryptographical assumptions on transport channel (e.g. messages -/// should be ecrypted, authenticated), then stream and sink must meet these assumptions (e.g. encrypt, -/// authenticate messages) -pub struct AsyncProtocol { - state: Option, - incoming: I, - outgoing: O, - deadline: Option, - current_round: Option, - watcher: W, -} - -impl AsyncProtocol { - /// Constructs new protocol executor from initial state, channels of incoming and outgoing - /// messages - pub fn new(state: SM, incoming: I, outgoing: O) -> Self { - Self { - state: Some(state), - incoming, - outgoing, - deadline: None, - current_round: None, - watcher: BlindWatcher, - } - } -} - -impl AsyncProtocol { - /// Sets new protocol watcher - /// - /// Protocol watcher looks after protocol execution. See list of observable events in - /// [ProtocolWatcher] trait. - /// - /// Default watcher: [BlindWatcher] that does nothing with received events. For development - /// purposes it's convenient to pick [StderrWatcher](watcher::StderrWatcher). - pub fn set_watcher(self, watcher: WR) -> AsyncProtocol { - AsyncProtocol { - state: self.state, - incoming: self.incoming, - outgoing: self.outgoing, - deadline: self.deadline, - current_round: self.current_round, - watcher, - } - } -} - -impl AsyncProtocol -where - SM: StateMachine, - SM::Err: Send, - SM: Send + 'static, - I: Stream, IErr>> + FusedStream + Unpin, - O: Sink> + Unpin, - W: ProtocolWatcher, -{ - /// Executes the protocol - /// - /// Returns protocol output or first occurred critical error - pub async fn run(&mut self) -> Result> { - if self.current_round.is_some() { - return Err(Error::Exhausted); - } - - self.refresh_timer()?; - self.proceed_if_needed().await?; - self.send_outgoing().await?; - self.refresh_timer()?; - - if let Some(result) = self.finish_if_possible() { - return result; - } - - loop { - self.handle_incoming().await?; - self.send_outgoing().await?; - self.refresh_timer()?; - - self.proceed_if_needed().await?; - self.send_outgoing().await?; - self.refresh_timer()?; - - if let Some(result) = self.finish_if_possible() { - return result; - } - } - } - - async fn handle_incoming(&mut self) -> Result<(), Error> { - let state = self.state.as_mut().ok_or(InternalError::MissingState)?; - match Self::enforce_timeout(self.deadline, self.incoming.next()).await { - Ok(Some(Ok(msg))) => match state.handle_incoming(msg) { - Ok(()) => (), - Err(err) if err.is_critical() => return Err(Error::HandleIncoming(err)), - Err(err) => self - .watcher - .caught_non_critical_error(When::HandleIncoming, err), - }, - Ok(Some(Err(err))) => return Err(Error::Recv(err)), - Ok(None) => return Err(Error::RecvEof), - Err(_) => { - let err = state.round_timeout_reached(); - return Err(Error::HandleIncomingTimeout(err)); - } - } - Ok(()) - } - - async fn proceed_if_needed(&mut self) -> Result<(), Error> { - let mut state = self.state.take().ok_or(InternalError::MissingState)?; - if state.wants_to_proceed() { - let (result, s) = tokio::task::spawn_blocking(move || (state.proceed(), state)) - .await - .map_err(Error::ProceedPanicked)?; - state = s; - - match result { - Ok(()) => (), - Err(err) if err.is_critical() => return Err(Error::Proceed(err)), - Err(err) => self.watcher.caught_non_critical_error(When::Proceed, err), - } - } - self.state = Some(state); - Ok(()) - } - - async fn send_outgoing(&mut self) -> Result<(), Error> { - let state = self.state.as_mut().ok_or(InternalError::MissingState)?; - - if !state.message_queue().is_empty() { - let mut msgs = stream::iter(state.message_queue().drain(..).map(Ok)); - self.outgoing - .send_all(&mut msgs) - .await - .map_err(Error::Send)?; - } - - Ok(()) - } - - fn finish_if_possible(&mut self) -> Option>> { - let state = match self.state.as_mut() { - Some(s) => s, - None => return Some(Err(InternalError::MissingState.into())), - }; - if !state.is_finished() { - None - } else { - match state.pick_output() { - Some(Ok(result)) => Some(Ok(result)), - Some(Err(err)) => Some(Err(Error::Finish(err))), - None => Some(Err( - BadStateMachineReason::ProtocolFinishedButNoResult.into() - )), - } - } - } - - fn refresh_timer(&mut self) -> Result<(), Error> { - let state = self.state.as_mut().ok_or(InternalError::MissingState)?; - let round_n = state.current_round(); - if self.current_round != Some(round_n) { - self.current_round = Some(round_n); - self.deadline = match state.round_timeout() { - Some(timeout) => Some(time::Instant::now() + timeout), - None => None, - } - } - - Ok(()) - } - fn enforce_timeout( - deadline: Option, - f: F, - ) -> impl Future> - where - F: Future, - { - match deadline { - Some(deadline) => Either::Right(timeout_at(deadline, f)), - None => Either::Left(f.map(Ok)), - } - } -} - -/// Represents error that can occur while executing protocol -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - /// Receiving next incoming message returned error - Recv(RE), - /// Incoming channel closed (got EOF) - RecvEof, - /// Sending outgoing message resulted in error - Send(SE), - /// [Handling incoming](crate::StateMachine::handle_incoming) message produced critical error - HandleIncoming(E), - /// Round timeout exceed when executor was waiting for new messages from other parties - HandleIncomingTimeout(E), - /// [Proceed method](crate::StateMachine::proceed) panicked - ProceedPanicked(tokio::task::JoinError), - /// State machine [proceeding](crate::StateMachine::proceed) produced critical error - Proceed(E), - /// StateMachine's [pick_output](crate::StateMachine::pick_output) method return error - Finish(E), - /// AsyncProtocol already executed protocol (or at least, tried to) and tired. You need to - /// construct new executor! - Exhausted, - /// Buggy StateMachine implementation - BadStateMachine(BadStateMachineReason), - /// Buggy AsyncProtocol implementation! - /// - /// If you've got this error, please, report bug. - InternalError(InternalError), -} - -impl From for Error { - fn from(reason: BadStateMachineReason) -> Self { - Error::BadStateMachine(reason) - } -} - -impl From for Error { - fn from(err: InternalError) -> Self { - Error::InternalError(err) - } -} - -impl fmt::Display for Error -where - E: fmt::Display, - RE: fmt::Display, - SE: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Recv(err) => { - write!(f, "receive next message: {}", err) - } - Self::RecvEof => { - write!(f, "receive next message: unexpected eof") - } - Self::Send(err) => { - write!(f, "send a message: {}", err) - } - Self::HandleIncoming(err) => { - write!(f, "handle received message: {}", err) - } - Self::HandleIncomingTimeout(err) => { - write!(f, "round timeout reached: {}", err) - } - Self::ProceedPanicked(err) => { - write!(f, "proceed round panicked: {}", err) - } - Self::Proceed(err) => { - write!(f, "round proceed error: {}", err) - } - Self::Finish(err) => { - write!(f, "couldn't finish protocol: {}", err) - } - Self::Exhausted => { - write!(f, "async runtime is exhausted") - } - Self::BadStateMachine(err) => { - write!(f, "buggy state machine implementation: {}", err) - } - Self::InternalError(err) => { - write!(f, "internal error: {:?}", err) - } - } - } -} - -impl std::error::Error for Error -where - E: std::error::Error + 'static, - RE: std::error::Error + 'static, - SE: std::error::Error + 'static, -{ - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Self::Recv(err) => Some(err), - Self::Send(err) => Some(err), - Self::HandleIncoming(err) => Some(err), - Self::HandleIncomingTimeout(err) => Some(err), - Self::ProceedPanicked(err) => Some(err), - Self::Proceed(err) => Some(err), - Self::Finish(err) => Some(err), - Self::RecvEof => None, - Self::Exhausted => None, - Self::BadStateMachine(_) => None, - Self::InternalError(_) => None, - } - } -} - -/// Reason why StateMachine implementation looks buggy -#[derive(Debug)] -#[non_exhaustive] -pub enum BadStateMachineReason { - /// [StateMachine::is_finished](crate::StateMachine::is_finished) returned `true`, - /// but [StateMachine::pick_output](crate::StateMachine::pick_output) returned `None` - ProtocolFinishedButNoResult, -} - -impl fmt::Display for BadStateMachineReason { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::ProtocolFinishedButNoResult => write!( - f, - "couldn't obtain protocol output although it is completed" - ), - } - } -} - -/// Describes internal errors that could occur -#[derive(Debug)] -#[non_exhaustive] -pub enum InternalError { - MissingState, -} diff --git a/src/async_runtime/watcher.rs b/src/async_runtime/watcher.rs deleted file mode 100644 index e228c55..0000000 --- a/src/async_runtime/watcher.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! Mechanism for tracking protocol execution - -use std::fmt::Debug; - -use crate::StateMachine; - -/// Looks after protocol execution in [AsyncProtocol](super::AsyncProtocol) -/// -/// Currently it's only able to see caught non critical errors, API will be expanded (see [#1][issue]). -/// It will be able to track incoming messages, changing round number, etc. -/// -/// [issue]: https://github.com/ZenGo-X/round-based-protocol/issues/1 -pub trait ProtocolWatcher { - /// StateMachine produced a not critical error. Execution continues. - fn caught_non_critical_error(&mut self, when: When, err: SM::Err); -} - -/// Claims at which stage event occurred -#[derive(Debug)] -pub enum When { - HandleIncoming, - Proceed, -} - -/// Watcher that doesn't do anything when event happens -pub struct BlindWatcher; - -impl ProtocolWatcher for BlindWatcher -where - SM: StateMachine, -{ - fn caught_non_critical_error(&mut self, _when: When, _err: SM::Err) {} -} - -/// Watcher that logs non critical error to stderr -pub struct StderrWatcher; - -impl ProtocolWatcher for StderrWatcher -where - SM: StateMachine, - SM::Err: Debug, -{ - fn caught_non_critical_error(&mut self, when: When, err: SM::Err) { - eprintln!("Caught non critical error at {:?}: {:?}", when, err); - } -} diff --git a/src/containers/broadcast.rs b/src/containers/broadcast.rs deleted file mode 100644 index 72c8655..0000000 --- a/src/containers/broadcast.rs +++ /dev/null @@ -1,187 +0,0 @@ -use std::cmp::Ordering; -use std::ops; - -use crate::sm::Msg; - -use super::store_err::StoreErr; -use super::traits::{MessageContainer, MessageStore}; - -/// Received broadcast messages from every protocol participant -#[derive(Debug)] -pub struct BroadcastMsgs { - my_ind: u16, - msgs: Vec, -} - -impl BroadcastMsgs -where - B: 'static, -{ - /// Turns a container into iterator of messages with parties indexes (1 <= i <= n) - pub fn into_iter_indexed(self) -> impl Iterator { - let my_ind = usize::from(self.my_ind); - let ind = move |i| { - if i < my_ind - 1 { - i as u16 + 1 - } else { - i as u16 + 2 - } - }; - self.msgs - .into_iter() - .enumerate() - .map(move |(i, m)| (ind(i), m)) - } - - /// Turns container into vec of `n-1` messages - pub fn into_vec(self) -> Vec { - self.msgs - } - - /// Turns container into vec of `n` messages (where given message lies at index `party_i-1`) - pub fn into_vec_including_me(mut self, me: B) -> Vec { - self.msgs.insert(self.my_ind as usize - 1, me); - self.msgs - } -} - -impl ops::Index for BroadcastMsgs { - type Output = B; - - /// Takes party index i and returns received message (1 <= i <= n) - /// - /// ## Panics - /// Panics if there's no party with index i (or it's your party index) - fn index(&self, index: u16) -> &Self::Output { - match Ord::cmp(&index, &(self.my_ind - 1)) { - Ordering::Less => &self.msgs[usize::from(index)], - Ordering::Greater => &self.msgs[usize::from(index - 1)], - Ordering::Equal => panic!("accessing own broadcasted msg"), - } - } -} - -impl IntoIterator for BroadcastMsgs { - type Item = B; - type IntoIter = as IntoIterator>::IntoIter; - - /// Returns messages in ascending party's index order - fn into_iter(self) -> Self::IntoIter { - self.msgs.into_iter() - } -} - -impl MessageContainer for BroadcastMsgs { - type Store = BroadcastMsgsStore; -} - -/// Receives broadcast messages from every protocol participant -pub struct BroadcastMsgsStore { - party_i: u16, - msgs: Vec>, - msgs_left: usize, -} - -impl BroadcastMsgsStore { - /// Constructs store. Takes this party index and total number of parties. - pub fn new(party_i: u16, parties_n: u16) -> Self { - let parties_n = usize::from(parties_n); - Self { - party_i, - msgs: std::iter::repeat_with(|| None) - .take(parties_n - 1) - .collect(), - msgs_left: parties_n - 1, - } - } - - /// Amount of received messages so far - pub fn messages_received(&self) -> usize { - self.msgs.len() - self.msgs_left - } - /// Total amount of wanted messages (n-1) - pub fn messages_total(&self) -> usize { - self.msgs.len() - } -} - -impl MessageStore for BroadcastMsgsStore { - type M = M; - type Err = StoreErr; - type Output = BroadcastMsgs; - - fn push_msg(&mut self, msg: Msg) -> Result<(), Self::Err> { - if msg.sender == 0 { - return Err(StoreErr::UnknownSender { sender: msg.sender }); - } - if msg.receiver.is_some() { - return Err(StoreErr::ExpectedBroadcast); - } - let party_j = match Ord::cmp(&msg.sender, &self.party_i) { - Ordering::Less => usize::from(msg.sender), - Ordering::Greater => usize::from(msg.sender) - 1, - Ordering::Equal => return Err(StoreErr::ItsFromMe), - }; - let slot = self - .msgs - .get_mut(party_j - 1) - .ok_or(StoreErr::UnknownSender { sender: msg.sender })?; - if slot.is_some() { - return Err(StoreErr::MsgOverwrite); - } - *slot = Some(msg.body); - self.msgs_left -= 1; - - Ok(()) - } - - fn contains_msg_from(&self, sender: u16) -> bool { - let party_j = match Ord::cmp(&sender, &self.party_i) { - Ordering::Less => usize::from(sender), - Ordering::Greater => usize::from(sender) - 1, - Ordering::Equal => return false, - }; - match self.msgs.get(party_j - 1) { - None => false, - Some(None) => false, - Some(Some(_)) => true, - } - } - - fn wants_more(&self) -> bool { - self.msgs_left > 0 - } - - fn finish(self) -> Result { - if self.msgs_left > 0 { - return Err(StoreErr::WantsMoreMessages); - } - Ok(BroadcastMsgs { - my_ind: self.party_i, - msgs: self.msgs.into_iter().map(Option::unwrap).collect(), - }) - } - - fn blame(&self) -> (u16, Vec) { - let ind = |i: u16| -> u16 { - if i < self.party_i - 1 { - i + 1 - } else { - i + 2 - } - }; - let guilty_parties = self - .msgs - .iter() - .enumerate() - .flat_map(|(i, m)| { - if m.is_none() { - Some(ind(i as u16)) - } else { - None - } - }) - .collect(); - (self.msgs_left as u16, guilty_parties) - } -} diff --git a/src/containers/mod.rs b/src/containers/mod.rs deleted file mode 100644 index 880dc30..0000000 --- a/src/containers/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Containers convenient for implementing [StateMachine](crate::StateMachine) trait - -pub mod push; - -mod broadcast; -mod p2p; -mod store_err; -mod traits; - -pub use broadcast::*; -pub use p2p::*; -pub use store_err::*; -pub use traits::*; - -pub type Store = ::Store; diff --git a/src/containers/p2p.rs b/src/containers/p2p.rs deleted file mode 100644 index 6f2d269..0000000 --- a/src/containers/p2p.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::cmp::Ordering; -use std::ops; - -use crate::sm::Msg; - -use super::store_err::StoreErr; -use super::traits::{MessageContainer, MessageStore}; - -/// Received P2P messages from every protocol participant -#[derive(Debug)] -pub struct P2PMsgs { - my_ind: u16, - msgs: Vec, -} - -impl P2PMsgs -where - B: 'static, -{ - /// Turns a container into iterator of messages with parties indexes (1 <= i <= n) - pub fn into_iter_indexed(self) -> impl Iterator { - let my_ind = usize::from(self.my_ind); - let ind = move |i| { - if i < my_ind - 1 { - i as u16 + 1 - } else { - i as u16 + 2 - } - }; - self.msgs - .into_iter() - .enumerate() - .map(move |(i, m)| (ind(i), m)) - } - - /// Turns container into vec of `n-1` messages - pub fn into_vec(self) -> Vec { - self.msgs - } - - /// Turns container into vec of `n` messages (where given message lies at index `party_i-1`) - pub fn into_vec_including_me(mut self, me: B) -> Vec { - self.msgs.insert(self.my_ind as usize - 1, me); - self.msgs - } -} - -impl ops::Index for P2PMsgs { - type Output = B; - - /// Takes party index i and returns received message (1 <= i <= n) - /// - /// ## Panics - /// Panics if there's no party with index i (or it's your party index) - fn index(&self, index: u16) -> &Self::Output { - match Ord::cmp(&index, &(self.my_ind - 1)) { - Ordering::Less => &self.msgs[usize::from(index)], - Ordering::Greater => &self.msgs[usize::from(index - 1)], - Ordering::Equal => panic!("accessing own broadcasted msg"), - } - } -} - -impl IntoIterator for P2PMsgs { - type Item = B; - type IntoIter = as IntoIterator>::IntoIter; - - /// Returns messages in ascending party's index order - fn into_iter(self) -> Self::IntoIter { - self.msgs.into_iter() - } -} - -impl MessageContainer for P2PMsgs { - type Store = P2PMsgsStore; -} - -/// Receives P2P messages from every protocol participant -pub struct P2PMsgsStore { - party_i: u16, - msgs: Vec>, - msgs_left: usize, -} - -impl P2PMsgsStore { - /// Constructs store. Takes this party index and total number of parties. - pub fn new(party_i: u16, parties_n: u16) -> Self { - let parties_n = usize::from(parties_n); - Self { - party_i, - msgs: std::iter::repeat_with(|| None) - .take(parties_n - 1) - .collect(), - msgs_left: parties_n - 1, - } - } - - /// Amount of received messages so far - pub fn messages_received(&self) -> usize { - self.msgs.len() - self.msgs_left - } - /// Total amount of wanted messages (n-1) - pub fn messages_total(&self) -> usize { - self.msgs.len() - } -} - -impl MessageStore for P2PMsgsStore { - type M = M; - type Err = StoreErr; - type Output = P2PMsgs; - - fn push_msg(&mut self, msg: Msg) -> Result<(), Self::Err> { - if msg.sender == 0 { - return Err(StoreErr::UnknownSender { sender: msg.sender }); - } - if msg.receiver.is_none() { - return Err(StoreErr::ExpectedP2P); - } - if msg.receiver != Some(self.party_i) { - return Err(StoreErr::NotForMe); - } - let party_j = match Ord::cmp(&msg.sender, &self.party_i) { - Ordering::Less => usize::from(msg.sender), - Ordering::Greater => usize::from(msg.sender) - 1, - Ordering::Equal => return Err(StoreErr::ItsFromMe), - }; - let slot = self - .msgs - .get_mut(party_j - 1) - .ok_or(StoreErr::UnknownSender { sender: msg.sender })?; - if slot.is_some() { - return Err(StoreErr::MsgOverwrite); - } - *slot = Some(msg.body); - self.msgs_left -= 1; - - Ok(()) - } - - fn contains_msg_from(&self, sender: u16) -> bool { - let party_j = match Ord::cmp(&sender, &self.party_i) { - Ordering::Less => usize::from(sender), - Ordering::Greater => usize::from(sender) - 1, - Ordering::Equal => return false, - }; - match self.msgs.get(party_j - 1) { - None => false, - Some(None) => false, - Some(Some(_)) => true, - } - } - - fn wants_more(&self) -> bool { - self.msgs_left > 0 - } - - fn finish(self) -> Result { - if self.msgs_left > 0 { - return Err(StoreErr::WantsMoreMessages); - } - Ok(P2PMsgs { - my_ind: self.party_i, - msgs: self.msgs.into_iter().map(Option::unwrap).collect(), - }) - } - - fn blame(&self) -> (u16, Vec) { - let ind = |i: u16| -> u16 { - if i < self.party_i - 1 { - i + 1 - } else { - i + 2 - } - }; - let guilty_parties = self - .msgs - .iter() - .enumerate() - .flat_map(|(i, m)| { - if m.is_none() { - Some(ind(i as u16)) - } else { - None - } - }) - .collect(); - (self.msgs_left as u16, guilty_parties) - } -} diff --git a/src/containers/push.rs b/src/containers/push.rs deleted file mode 100644 index b95153f..0000000 --- a/src/containers/push.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Abstraction over pushable collections -//! -//! [Push] trait allows writing functions which are generic over collection and can only append -//! elements to it, preventing from accident accessing/modifying existing data. Along with [PushExt] -//! trait, that provides extra utilities for `Push`able collections, they are convenient for -//! describing protocol in terms of rounds, where every round may send messages by appending -//! them to sending queue. - -/// Collection which can only be appended by 1 element -pub trait Push { - fn push(&mut self, element: T); -} - -impl Push for Vec { - fn push(&mut self, element: T) { - Vec::push(self, element) - } -} - -impl Push for &mut P -where - P: Push, -{ - fn push(&mut self, element: T) { - P::push(self, element) - } -} - -mod private { - pub trait Sealed {} - impl Sealed for P where P: super::Push {} -} - -/// Utilities around [Push]able collections -pub trait PushExt: Push + private::Sealed { - /// Takes a closure and produces a `Push`able object which applies closure to each element - fn gmap(self, f: F) -> Map - where - Self: Sized, - F: FnMut(B) -> T; -} - -impl PushExt for P -where - P: Push, -{ - fn gmap(self, f: F) -> Map - where - Self: Sized, - F: FnMut(B) -> T, - { - Map { pushable: self, f } - } -} - -/// Wraps pushable collection and applies closure `f` to every pushed element -/// -/// This wrapper is created by method [map](PushExt::gmap) on [PushExt] -pub struct Map { - pushable: P, - f: F, -} - -impl Push for Map -where - P: Push, - F: FnMut(B) -> A, -{ - fn push(&mut self, element: B) { - self.pushable.push((self.f)(element)) - } -} diff --git a/src/containers/store_err.rs b/src/containers/store_err.rs deleted file mode 100644 index 1ad9996..0000000 --- a/src/containers/store_err.rs +++ /dev/null @@ -1,28 +0,0 @@ -use thiserror::Error; - -/// Common error type for [MessageStore](super::MessageStore) implementations in this crate -#[derive(Debug, PartialEq, Error)] -#[non_exhaustive] -pub enum StoreErr { - /// Got message which was already received (no matter how similar they are) - #[error("got message which was already received")] - MsgOverwrite, - /// Got message from unknown party - #[error("unknown message sender: {sender}")] - UnknownSender { sender: u16 }, - /// Got broadcast message, whereas P2P message is expected - #[error("unexpected broadcast message (P2P is expected)")] - ExpectedP2P, - /// Got P2P message, whereas broadcast message is expected - #[error("unexpected P2P message (broadcast is expected)")] - ExpectedBroadcast, - /// Got message that addressed to another party (`msg.receiver != me`) - #[error("got message which was addressed to someone else")] - NotForMe, - /// Got message which sent by this party - #[error("got message which was sent by this party")] - ItsFromMe, - /// Called [finish](super::MessageStore::finish), but more messages are wanted - #[error("more messages are expected to receive")] - WantsMoreMessages, -} diff --git a/src/containers/traits.rs b/src/containers/traits.rs deleted file mode 100644 index 20b4abf..0000000 --- a/src/containers/traits.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::sm::Msg; - -/// Message container holding received messages -/// -/// Trait only purpose is to pin [MessageStore] constructing this container -pub trait MessageContainer { - type Store: MessageStore; -} - -/// Accumulates messages received from other parties -/// -/// StateMachine implementations need to handle incoming messages: they need to store messages -/// somewhere until sufficient messages received, incoming messages needs to be pre-validated -/// (haven't we received message from this party at this round? is it a p2p message, as we expected? -/// and so on). MessageStore encapsulates all this boilerplate. -pub trait MessageStore { - /// Message body - type M; - /// Error type - type Err; - /// Resulting messages container holding received messages - type Output; - - /// Pushes received message to store - /// - /// Might result in error if pre-validation failed. However, it does not - /// prevent MessageStore from accepting further messages. - fn push_msg(&mut self, msg: Msg) -> Result<(), Self::Err>; - /// Indicates if store contains message from this party - fn contains_msg_from(&self, sender: u16) -> bool; - /// Indicates whether store needs more messages to receive - fn wants_more(&self) -> bool; - /// Returns resulting messages container - /// - /// Returns error if store needs more messages (see [wants_more](Self::wants_more)). - fn finish(self) -> Result; - /// Retrieve uncooperative parties - /// - /// Returns how many more messages we expected to receive and list of parties who didn't send - /// a message - fn blame(&self) -> (u16, Vec); -} diff --git a/src/dev/async_simulation.rs b/src/dev/async_simulation.rs deleted file mode 100644 index b38ae1a..0000000 --- a/src/dev/async_simulation.rs +++ /dev/null @@ -1,193 +0,0 @@ -use std::fmt::Debug; -use std::iter; -use std::pin::Pin; -use std::task::{Context, Poll}; - -use futures::future::ready; -use futures::sink::Sink; -use futures::stream::{FusedStream, StreamExt}; -use tokio::sync::broadcast; - -use crate::async_runtime::{self, watcher::StderrWatcher, AsyncProtocol}; -use crate::{Msg, StateMachine}; - -/// Emulates running protocol between local parties using [AsyncProtocol](crate::AsyncProtocol) -/// -/// Takes parties (every party is instance of [StateMachine](crate::sm::StateMachine)) and -/// executes protocol between them. -/// -/// Compared to [Simulation], AsyncSimulation requires [tokio] runtime and introduces parallelism, -/// so it's more suitable for writing tests (whereas [Simulation] is more suitable for writing -/// benchmarks). -/// -/// [Simulation]: super::Simulation -/// -/// ## Limitations -/// * Doesn't log process of protocol execution (except for occurring non critical errors). Limited -/// by [ProtocolWatcher](crate::async_runtime::watcher::ProtocolWatcher) API (to be expanded). -/// -/// ## Example -/// ```no_run -/// # use round_based::StateMachine; -/// # use round_based::dev::{AsyncSimulation, AsyncSimulationError}; -/// # trait Builder { fn new(party_i: u16, party_n: u16) -> Self; } -/// # async fn async_simulation() -/// # where Party: StateMachine + Builder + Send + 'static, -/// # Party::MessageBody: Send + Clone + Unpin + 'static, -/// # Party::Err: Send + std::fmt::Debug, -/// # Party::Output: Send, -/// # { -/// let results: Vec> = AsyncSimulation::new() -/// .add_party(Party::new(1, 3)) -/// .add_party(Party::new(2, 3)) -/// .add_party(Party::new(3, 3)) -/// .run() -/// .await; -/// # } -/// ``` -pub struct AsyncSimulation { - tx: broadcast::Sender>, - parties: Vec< - Option< - AsyncProtocol, Outgoing, StderrWatcher>, - >, - >, - exhausted: bool, -} - -impl AsyncSimulation -where - SM: StateMachine + Send + 'static, - SM::MessageBody: Send + Clone + Unpin + 'static, - SM::Err: Send + Debug, - SM::Output: Send, -{ - /// Creates new simulation - pub fn new() -> Self { - let (tx, _) = broadcast::channel(20); - Self { - tx, - parties: vec![], - exhausted: false, - } - } - - /// Adds protocol participant - pub fn add_party(&mut self, party: SM) -> &mut Self { - let rx = self.tx.subscribe(); - - let incoming = incoming(rx, party.party_ind()); - let outgoing = Outgoing { - sender: self.tx.clone(), - }; - let party = AsyncProtocol::new(party, incoming, outgoing).set_watcher(StderrWatcher); - self.parties.push(Some(party)); - self - } - - /// Runs a simulation - /// - /// ## Returns - /// Returns Vec of execution results. Every party is executed independently, simulation - /// will continue until each party finish protocol (either with success or error). - /// - /// It's an error to call this method twice. In this case, - /// `vec![Err(AsyncSimulationError::Exhausted); n]` is returned - pub async fn run(&mut self) -> Vec>> { - if self.exhausted { - return iter::repeat_with(|| Err(AsyncSimulationError::SimulationExhausted)) - .take(self.parties.len()) - .collect(); - } - self.exhausted = true; - - let mut parties = vec![]; - for party in self.parties.drain(..) { - let mut party = party.expect("guaranteed as simulation is not exhausted"); - let h = tokio::spawn(async { (party.run().await, party) }); - parties.push(h) - } - - let mut results = vec![]; - for party in parties { - let (r, party) = match party.await { - Ok((Ok(output), party)) => (Ok(output), Some(party)), - Ok((Err(err), party)) => ( - Err(AsyncSimulationError::ProtocolExecution(err)), - Some(party), - ), - Err(err) => ( - Err(AsyncSimulationError::ProtocolExecutionPanicked(err)), - None, - ), - }; - self.parties.push(party); - results.push(r); - } - results - } -} - -type Incoming = - Pin, broadcast::error::RecvError>> + Send>>; - -fn incoming( - mut rx: broadcast::Receiver>, - me: u16, -) -> Incoming { - let stream = async_stream::stream! { - loop { - let item = rx.recv().await; - yield item - } - }; - let stream = StreamExt::filter(stream, move |m| { - ready(match m { - Ok(m) => m.sender != me && (m.receiver.is_none() || m.receiver == Some(me)), - Err(_) => true, - }) - }); - Box::pin(stream) -} - -struct Outgoing { - sender: broadcast::Sender>, -} - -impl Sink> for Outgoing { - type Error = broadcast::error::SendError>; - - fn poll_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn start_send(self: Pin<&mut Self>, item: Msg) -> Result<(), Self::Error> { - self.sender.send(item).map(|_| ()) - } - - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -/// Possible errors that async simulation can be resulted in -#[non_exhaustive] -#[derive(Debug)] -pub enum AsyncSimulationError { - /// Protocol execution error - ProtocolExecution( - async_runtime::Error< - SM::Err, - broadcast::error::RecvError, - broadcast::error::SendError>, - >, - ), - /// Protocol execution produced a panic - ProtocolExecutionPanicked(tokio::task::JoinError), - /// Simulation ran twice - SimulationExhausted, -} diff --git a/src/dev/mod.rs b/src/dev/mod.rs deleted file mode 100644 index 73b90b1..0000000 --- a/src/dev/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Useful development utils - -#[cfg(feature = "async-runtime")] -#[cfg_attr(docsrs, doc(cfg(feature = "async-runtime")))] -mod async_simulation; -mod simulation; - -#[cfg(feature = "async-runtime")] -#[cfg_attr(docsrs, doc(cfg(feature = "async-runtime")))] -pub use async_simulation::*; -pub use simulation::*; diff --git a/src/dev/simulation.rs b/src/dev/simulation.rs deleted file mode 100644 index bf76ae3..0000000 --- a/src/dev/simulation.rs +++ /dev/null @@ -1,278 +0,0 @@ -use std::fmt::Debug; - -use crate::sm::*; - -mod benchmark; -use benchmark::Benchmark; -pub use benchmark::{BenchmarkResults, Measurements}; - -/// Emulates running protocol between local parties -/// -/// Takes parties (every party is instance of [StateMachine](crate::sm::StateMachine)) and -/// executes protocol between them. It logs whole process (changing state of party, receiving -/// messages, etc.) in stdout. -/// -/// Compared to [AsyncSimulation](super::AsyncSimulation), it's lightweight (doesn't require -/// async runtime), and, more importantly, executes everything in straight order (sequently, without -/// any parallelism). It makes this sumaltion more useful for writing benchmarks that detect -/// performance regression. -/// -/// ## Limitations -/// * No proper error handling. It should attach a context to returning error (like current round, -/// what we was doing when error occurred, etc.). The only way to determine error context is to -/// look at stdout and find out what happened from logs. -/// * Logs everything to stdout. No choice. -/// -/// ## Example -/// ```no_run -/// # use round_based::StateMachine; -/// # use round_based::dev::Simulation; -/// # trait Builder { fn new(party_i: u16, party_n: u16) -> Self; } -/// # fn is_valid(_: &T) -> bool { true } -/// # fn _test() -> Result<(), Party::Err> -/// # where Party: std::fmt::Debug, -/// # Party::Err: std::fmt::Debug, -/// # Party::MessageBody: std::fmt::Debug + Clone, -/// # { -/// let results = Simulation::new() -/// .add_party(Party::new(1, 3)) -/// .add_party(Party::new(2, 3)) -/// .add_party(Party::new(3, 3)) -/// .run()?; -/// assert!(results.into_iter().all(|r| is_valid(&r))); -/// # Ok(()) -/// # } -/// ``` -pub struct Simulation

{ - /// Parties running a protocol - /// - /// Field is exposed mainly to allow examining parties state after simulation is completed. - pub parties: Vec

, - benchmark: Benchmark, -} - -impl

Simulation

{ - /// Creates new simulation - pub fn new() -> Self { - Self { - parties: vec![], - benchmark: Benchmark::disabled(), - } - } - - /// Adds protocol participant - pub fn add_party(&mut self, party: P) -> &mut Self { - self.parties.push(party); - self - } - - /// Enables benchmarks so they can be [retrieved](Simulation::benchmark_results) after simulation - /// is completed - pub fn enable_benchmarks(&mut self, enable: bool) -> &mut Self { - if enable { - self.benchmark = Benchmark::enabled() - } else { - self.benchmark = Benchmark::disabled() - } - self - } - - /// Returns benchmark results if they were [enabled](Simulation::enable_benchmarks) - /// - /// Benchmarks show how much time (in average) [proceed](StateMachine::proceed) method takes for - /// proceeding particular rounds. Benchmarks might help to find out which rounds are cheap to - /// proceed, and which of them are expensive to compute. - pub fn benchmark_results(&self) -> Option<&BenchmarkResults> { - self.benchmark.results() - } -} - -impl

Simulation

-where - P: StateMachine, - P: Debug, - P::Err: Debug, - P::MessageBody: Debug + Clone, -{ - /// Runs a simulation - /// - /// ## Returns - /// Returns either Vec of protocol outputs (one output for each one party) or first - /// occurred critical error. - /// - /// ## Panics - /// * Number of parties is less than 2 - pub fn run(&mut self) -> Result, P::Err> { - assert!(self.parties.len() >= 2, "at least two parties required"); - - let mut parties: Vec<_> = self - .parties - .iter_mut() - .map(|p| Party { state: p }) - .collect(); - - println!("Simulation starts"); - - let mut msgs_pull = vec![]; - - for party in &mut parties { - party.proceed_if_needed(&mut self.benchmark)?; - party.send_outgoing(&mut msgs_pull); - } - - if let Some(results) = finish_if_possible(&mut parties)? { - return Ok(results); - } - - loop { - let msgs_pull_frozen = msgs_pull.split_off(0); - - for party in &mut parties { - party.handle_incoming(&msgs_pull_frozen)?; - party.send_outgoing(&mut msgs_pull); - } - - for party in &mut parties { - party.proceed_if_needed(&mut self.benchmark)?; - party.send_outgoing(&mut msgs_pull); - } - - if let Some(results) = finish_if_possible(&mut parties)? { - return Ok(results); - } - } - } -} - -struct Party<'p, P> { - state: &'p mut P, -} - -impl<'p, P> Party<'p, P> -where - P: StateMachine, - P: Debug, - P::Err: Debug, - P::MessageBody: Debug + Clone, -{ - pub fn proceed_if_needed(&mut self, benchmark: &mut Benchmark) -> Result<(), P::Err> { - if !self.state.wants_to_proceed() { - return Ok(()); - } - - println!("Party {} wants to proceed", self.state.party_ind()); - println!(" - before: {:?}", self.state); - - let round_old = self.state.current_round(); - let stopwatch = benchmark.start(); - match self.state.proceed() { - Ok(()) => (), - Err(err) if err.is_critical() => return Err(err), - Err(err) => { - println!("Non-critical error encountered: {:?}", err); - } - } - let round_new = self.state.current_round(); - let duration = if round_old != round_new { - Some(stopwatch.stop_and_save(round_old)) - } else { - None - }; - - println!(" - after : {:?}", self.state); - println!(" - took : {:?}", duration); - println!(); - - Ok(()) - } - - pub fn send_outgoing(&mut self, msgs_pull: &mut Vec>) { - if !self.state.message_queue().is_empty() { - println!( - "Party {} sends {} message(s)", - self.state.party_ind(), - self.state.message_queue().len() - ); - println!(); - - msgs_pull.append(self.state.message_queue()) - } - } - - pub fn handle_incoming(&mut self, msgs_pull: &[Msg]) -> Result<(), P::Err> { - for msg in msgs_pull { - if Some(self.state.party_ind()) != msg.receiver - && (msg.receiver.is_some() || msg.sender == self.state.party_ind()) - { - continue; - } - println!( - "Party {} got message from={}, broadcast={}: {:?}", - self.state.party_ind(), - msg.sender, - msg.receiver.is_none(), - msg.body, - ); - println!(" - before: {:?}", self.state); - match self.state.handle_incoming(msg.clone()) { - Ok(()) => (), - Err(err) if err.is_critical() => return Err(err), - Err(err) => { - println!("Non-critical error encountered: {:?}", err); - } - } - println!(" - after : {:?}", self.state); - println!(); - } - Ok(()) - } -} - -fn finish_if_possible

(parties: &mut Vec>) -> Result>, P::Err> -where - P: StateMachine, - P: Debug, - P::Err: Debug, - P::MessageBody: Debug + Clone, -{ - let someone_is_finished = parties.iter().any(|p| p.state.is_finished()); - if !someone_is_finished { - return Ok(None); - } - - let everyone_are_finished = parties.iter().all(|p| p.state.is_finished()); - if everyone_are_finished { - let mut results = vec![]; - for party in parties { - results.push( - party - .state - .pick_output() - .expect("is_finished == true, but pick_output == None")?, - ) - } - - println!("Simulation is finished"); - println!(); - - Ok(Some(results)) - } else { - let finished: Vec<_> = parties - .iter() - .filter(|p| p.state.is_finished()) - .map(|p| p.state.party_ind()) - .collect(); - let not_finished: Vec<_> = parties - .iter() - .filter(|p| !p.state.is_finished()) - .map(|p| p.state.party_ind()) - .collect(); - - println!("Warning: some of parties have finished the protocol, but other parties have not"); - println!("Finished parties: {:?}", finished); - println!("Not finished parties: {:?}", not_finished); - println!(); - - Ok(None) - } -} diff --git a/src/dev/simulation/benchmark.rs b/src/dev/simulation/benchmark.rs deleted file mode 100644 index 5d584d9..0000000 --- a/src/dev/simulation/benchmark.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::collections::BTreeMap; -use std::fmt; -use std::time::{Duration, Instant}; - -/// Measures duration of round proceeding -pub struct Benchmark { - results: Option, -} - -impl Benchmark { - pub fn enabled() -> Self { - Self { - results: Some(Default::default()), - } - } - - pub fn disabled() -> Self { - Self { results: None } - } - - pub fn start(&mut self) -> Stopwatch { - Stopwatch { - started_at: Instant::now(), - b: self, - } - } - - fn add_measurement(&mut self, round: u16, time: Duration) { - if let Some(results) = self.results.as_mut() { - let m = results.entry(round).or_insert(Measurements { - n: 0, - total_time: Duration::default(), - }); - m.n += 1; - m.total_time += time; - } - } - - pub fn results(&self) -> Option<&BenchmarkResults> { - self.results.as_ref() - } -} - -pub struct Stopwatch<'a> { - started_at: Instant, - b: &'a mut Benchmark, -} - -impl<'a> Stopwatch<'a> { - pub fn stop_and_save(self, round_n: u16) -> Duration { - let time = Instant::now().duration_since(self.started_at); - self.b.add_measurement(round_n, time); - time - } -} - -/// Benchmark results for every particular round -pub type BenchmarkResults = BTreeMap; - -/// Benchmark results for particular round -/// -/// `n` measurements took in total `total_time` -pub struct Measurements { - pub n: u16, - pub total_time: Duration, -} - -impl fmt::Debug for Measurements { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.total_time / u32::from(self.n)) - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index e63ed62..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! # Round-based protocols execution -//! -//! Crate defines a generic round-based protocol and provides utilities for it. We give -//! formal definition below, but you may have already seen such protocols: most of [MPC] protocols -//! follow round-based communication model. -//! -//! By defining the generic round-based protocol, we can implement generic transport -//! layer for it. See [AsyncProtocol]\: it allows executing the protocol by providing -//! channels of incoming and outgoing messages. -//! -//! [MPC]: https://en.wikipedia.org/wiki/Secure_multi-party_computation -//! -//! ## What is round-based protocol? -//! In round-based protocol we have `n` parties that can send messages to and receive messages -//! from other parties within rounds (number of parties `n` is known prior to starting protocol). -//! -//! At every round party may send a P2P or broadcast message, and it receives all broadcast -//! messages sent by other parties and P2P messages sent directly to it. After -//! party's received enough round messages in this round, it either proceeds (evaluates something on -//! received messages and goes to next round) or finishes the protocol. -//! -//! ## How to define own round-based protocol -//! To define own round-based protocol, you need to implement [StateMachine] trait. I.e. -//! you need to define type of [protocol message](StateMachine::MessageBody) which will be -//! transmitted on wire, determine rules how to -//! [handle incoming message](StateMachine::handle_incoming) and how to -//! [proceed state](StateMachine::proceed), etc. -//! -//! We divide methods in StateMachine on which can block and which can not. Most of MPC protocols -//! rely on computationally expensive math operations, such operations should not be executed -//! in async environment (i.e. on green-thread), that's why the only method which capable of -//! doing expensive operations is [proceed](StateMachine::proceed). -//! -//! ## How to execute round-based protocol -//! To run round-based protocol you need only to provide incoming and outgoing channels. -//! Then you can execute the protocol using [AsyncProtocol]: -//! ```no_run -//! # use futures::stream::{self, Stream, FusedStream}; -//! # use futures::sink::{self, Sink, SinkExt}; -//! # use round_based::{Msg, StateMachine, AsyncProtocol}; -//! # struct M; -//! # #[derive(Debug)] struct Error; -//! # impl From for Error { -//! # fn from(_: std::convert::Infallible) -> Error { Error } -//! # } -//! # trait Constructable { fn initial() -> Self; } -//! fn incoming() -> impl Stream, Error>> + FusedStream + Unpin { -//! // ... -//! # stream::pending() -//! } -//! fn outgoing() -> impl Sink, Error=Error> + Unpin { -//! // ... -//! # sink::drain().with(|x| futures::future::ok(x)) -//! } -//! # async fn execute_protocol() -> Result<(), round_based::async_runtime::Error> -//! # where State: StateMachine + Constructable + Send + 'static -//! # { -//! let output: State::Output = AsyncProtocol::new(State::initial(), incoming(), outgoing()) -//! .run().await?; -//! // ... -//! # let _ = output; Ok(()) -//! # } -//! ``` -//! -//! Usually protocols assume that P2P messages are encrypted and every message is authenticated, in -//! this case underlying sink and stream must meet such requirements. -//! -//! For development purposes, you can also find useful [Simulation](crate::dev::Simulation) and -//! [AsyncSimulation](dev::AsyncSimulation) simulations that can run protocols locally. - -#![cfg_attr(docsrs, feature(doc_cfg))] - -pub mod containers; - -#[cfg(feature = "dev")] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] -pub mod dev; - -mod sm; -pub use sm::*; - -#[cfg(feature = "async-runtime")] -#[cfg_attr(docsrs, doc(cfg(feature = "async-runtime")))] -pub mod async_runtime; -#[cfg(feature = "async-runtime")] -pub use async_runtime::AsyncProtocol; diff --git a/src/sm.rs b/src/sm.rs deleted file mode 100644 index 21f0e67..0000000 --- a/src/sm.rs +++ /dev/null @@ -1,158 +0,0 @@ -use std::time::Duration; - -use serde::{Deserialize, Serialize}; - -/// State machine of party involved in round-based protocol -pub trait StateMachine { - /// Body of transmitting messages - /// - /// Actual type of transmitting messages will be `Msg` (see [Msg struct](Msg)) - type MessageBody; - /// Error type used by StateMachine - /// - /// Errors are divided on critical and not critical to follow different error-handling strategies - /// on appearing one of them. For more details, see method returning `Result`s, e.g. - /// [handle_incoming](Self::handle_incoming) or [proceed](Self::proceed) - type Err: IsCritical; - /// Output of the protocol if it successfully terminates - type Output; - - /// Process received message - /// - /// ## Returns - /// Handling message might result in error, but it doesn't mean that computation should - /// be aborted. Returned error needs to be examined whether it critical or not (by calling - /// [is_critical](IsCritical::is_critical) method). - /// - /// If occurs: - /// * Critical error: protocol must be aborted - /// * Non-critical error: it should be reported, but protocol must continue - /// - /// Example of non-critical error is receiving message which we didn't expect to see. It could - /// be either network lag or bug in implementation or attempt to sabotage the protocol, but - /// protocol might be resistant to this, so it still has a chance to successfully complete. - /// - /// ## Blocking - /// This method should not block or perform expensive computation. E.g. it might do - /// deserialization (if needed) or cheap checks. - fn handle_incoming(&mut self, msg: Msg) -> Result<(), Self::Err>; - - /// Queue of messages to be sent - /// - /// New messages can be appended to queue only as result of calling - /// [proceed](StateMachine::proceed) or [handle_incoming](StateMachine::handle_incoming) methods. - /// - /// Messages can be sent in any order. - fn message_queue(&mut self) -> &mut Vec>; - - /// Indicates whether StateMachine wants to perform some expensive computation - fn wants_to_proceed(&self) -> bool; - - /// Performs some expensive computation - /// - /// If [`StateMachine`] is executed at green thread (in async environment), it will be typically - /// moved to dedicated thread at thread pool before calling `.proceed()` method. - /// - /// ## Returns - /// Returns `Ok(())` if either computation successfully completes or computation was not - /// required (i.e. `self.wants_to_proceed() == false`). - /// - /// If it returns `Err(err)`, then `err` is examined whether it's critical or not (by - /// calling [is_critical](IsCritical::is_critical) method). - /// - /// If occurs: - /// * Critical error: protocol must be aborted - /// * Non-critical error: it should be reported, but protocol must continue - /// - /// For example, in `.proceed()` at verification stage we could find some party trying to - /// sabotage the protocol, but protocol might be designed to be resistant to such attack, so - /// it's not a critical error, but obviously it should be reported. - fn proceed(&mut self) -> Result<(), Self::Err>; - - /// Deadline for a particular round - /// - /// After reaching deadline (if set) [round_timeout_reached](Self::round_timeout_reached) - /// will be called. - /// - /// After proceeding on the next round (increasing [current_round](Self::current_round)), - /// timer will be reset, new timeout will be requested (by calling this method), and new - /// deadline will be set. - fn round_timeout(&self) -> Option; - - /// Method is triggered after reaching [round_timeout](Self::round_timeout) - /// - /// Reaching timeout always aborts computation, no matter what error is returned: critical or not. - fn round_timeout_reached(&mut self) -> Self::Err; - - /// Indicates whether protocol is finished and output can be obtained by calling - /// [pick_output](Self::pick_output) method. - fn is_finished(&self) -> bool; - - /// Obtains protocol output - /// - /// ## Returns - /// * `None`, if protocol is not finished yet - /// i.e. `protocol.is_finished() == false` - /// * `Some(Err(_))`, if protocol terminated with error - /// * `Some(Ok(_))`, if protocol successfully terminated - /// - /// After `Some(_)` has been obtained via this method, StateMachine must be utilized (dropped). - fn pick_output(&mut self) -> Option>; - - /// Sequential number of current round - /// - /// Can be increased by 1 as result of calling either [proceed](StateMachine::proceed) or - /// [handle_incoming](StateMachine::handle_incoming) methods. Changing round number in any other way - /// (or in any other method) might cause strange behaviour. - fn current_round(&self) -> u16; - - /// Total amount of rounds (if known) - fn total_rounds(&self) -> Option; - - /// Index of this party - /// - /// Must be in interval `[1; n]` where `n = self.parties()` - fn party_ind(&self) -> u16; - /// Number of parties involved in computation - fn parties(&self) -> u16; -} - -/// Represent a message transmitting between parties on wire -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct Msg { - /// Index of the sender - /// - /// Lies in range `[1; n]` where `n` is number of parties involved in computation - pub sender: u16, - /// Index of receiver - /// - /// `None` indicates that it's broadcast message. Receiver index, if set, lies in range `[1; n]` - /// where `n` is number of parties involved in computation - pub receiver: Option, - /// Message body - pub body: B, -} - -impl Msg { - /// Applies closure to message body - pub fn map_body(self, f: F) -> Msg - where - F: FnOnce(B) -> T, - { - Msg { - sender: self.sender, - receiver: self.receiver, - body: f(self.body), - } - } -} - -/// Distinguish a critical error from not critical -/// -/// For semantic, see [StateMachine trait](StateMachine) (in particular, -/// [handle_incoming](StateMachine::handle_incoming) and [proceed](StateMachine::proceed) -/// methods) -pub trait IsCritical { - /// Indicates whether an error critical or not - fn is_critical(&self) -> bool; -} diff --git a/tests/silly_protocol/mod.rs b/tests/silly_protocol/mod.rs deleted file mode 100644 index d66144d..0000000 --- a/tests/silly_protocol/mod.rs +++ /dev/null @@ -1,331 +0,0 @@ -use std::fmt; -use std::mem::replace; -use std::time::Duration; - -use rand::{CryptoRng, RngCore}; - -use round_based::containers::{ - push::{Push, PushExt}, - *, -}; -use round_based::{IsCritical, Msg, StateMachine}; - -mod rounds; -pub use rounds::{OutputRandomValue, ProceedError}; -use rounds::{Round0, Round1, Round2}; - -pub struct MultiPartyGenRandom { - round: R, - msgs1: Option>>, - msgs2: Option>>, - msgs_queue: Vec>, - - party_i: u16, - party_n: u16, -} - -impl MultiPartyGenRandom { - pub fn with_fixed_seed( - party_i: u16, - party_n: u16, - seed: u32, - rnd: &mut Rnd, - ) -> Self { - let mut blinding = [0u8; 32]; - rnd.fill_bytes(&mut blinding[..]); - - Self { - party_i, - party_n, - round: R::Round0(Round0 { - is_adversary: false, - my_ind: party_i, - my_seed: seed, - blinding, - }), - msgs1: Some(Round1::expects_messages(party_i, party_n)), - msgs2: Some(Round2::expects_messages(party_i, party_n)), - msgs_queue: vec![], - } - } - - /// Adversary doesn't reveal its seed, so he's the only party who learn output. - pub fn adversary_with_fixed_seed( - party_i: u16, - party_n: u16, - seed: u32, - rnd: &mut Rnd, - ) -> Self { - let mut blinding = [0u8; 32]; - rnd.fill_bytes(&mut blinding[..]); - - Self { - party_i, - party_n, - round: R::Round0(Round0 { - is_adversary: true, - my_ind: party_i, - my_seed: seed, - blinding, - }), - msgs1: Some(Round1::expects_messages(party_i, party_n)), - msgs2: Some(Round2::expects_messages(party_i, party_n)), - msgs_queue: vec![], - } - } - - fn gmap_queue<'a, T, F>(&'a mut self, mut f: F) -> impl Push> + 'a - where - F: FnMut(T) -> M + 'a, - { - (&mut self.msgs_queue).gmap(move |m: Msg| m.map_body(|m| ProtocolMessage(f(m)))) - } - - fn proceed_round(&mut self, may_block: bool) -> Result<()> { - let store1_wants_more = self.msgs1.as_ref().map(|s| s.wants_more()).unwrap_or(false); - let store2_wants_more = self.msgs2.as_ref().map(|s| s.wants_more()).unwrap_or(false); - - let next_state: R; - let try_again: bool = match replace(&mut self.round, R::Gone) { - R::Round0(round) if !round.is_expensive() || may_block => { - next_state = round - .proceed(self.gmap_queue(M::Round1)) - .map(R::Round1) - .map_err(Error::ProceedRound)?; - true - } - s @ R::Round0(_) => { - next_state = s; - false - } - R::Round1(round) if !store1_wants_more && (!round.is_expensive() || may_block) => { - let store = self.msgs1.take().expect("store gone before round complete"); - let msgs = store.finish().map_err(Error::HandleMsg)?; - next_state = round - .proceed(msgs, self.gmap_queue(M::Round2)) - .map(R::Round2) - .map_err(Error::ProceedRound)?; - true - } - s @ R::Round1(_) => { - next_state = s; - false - } - R::Round2(round) if !store2_wants_more && (!round.is_expensive() || may_block) => { - let store = self.msgs2.take().expect("store gone before round complete"); - let msgs = store.finish().map_err(Error::HandleMsg)?; - next_state = round - .proceed(msgs) - .map(R::Finished) - .map_err(Error::ProceedRound)?; - false - } - s @ R::Round2(_) => { - next_state = s; - false - } - s @ R::Finished(_) | s @ R::Gone => { - next_state = s; - false - } - }; - - self.round = next_state; - if try_again { - self.proceed_round(may_block) - } else { - Ok(()) - } - } -} - -impl StateMachine for MultiPartyGenRandom { - type MessageBody = ProtocolMessage; - type Err = Error; - type Output = OutputRandomValue; - - fn handle_incoming(&mut self, msg: Msg) -> Result<()> { - let current_round = self.current_round(); - match msg.body { - ProtocolMessage(M::Round1(m)) => { - let store = self.msgs1.as_mut().ok_or(Error::OutOfOrderMsg { - current_round, - msg_round: 1, - })?; - store - .push_msg(Msg { - sender: msg.sender, - receiver: msg.receiver, - body: m, - }) - .map_err(Error::HandleMsg)?; - self.proceed_round(false) - } - ProtocolMessage(M::Round2(m)) => { - let store = self.msgs2.as_mut().ok_or(Error::OutOfOrderMsg { - current_round, - msg_round: 1, - })?; - store - .push_msg(Msg { - sender: msg.sender, - receiver: msg.receiver, - body: m, - }) - .map_err(Error::HandleMsg)?; - self.proceed_round(false) - } - } - } - - fn message_queue(&mut self) -> &mut Vec> { - &mut self.msgs_queue - } - - fn wants_to_proceed(&self) -> bool { - let store1_wants_more = self.msgs1.as_ref().map(|s| s.wants_more()).unwrap_or(false); - let store2_wants_more = self.msgs2.as_ref().map(|s| s.wants_more()).unwrap_or(false); - - match self.round { - R::Round0(_) => true, - R::Round1(_) => !store1_wants_more, - R::Round2(_) => !store2_wants_more, - R::Finished(_) | R::Gone => false, - } - } - - fn proceed(&mut self) -> Result<()> { - self.proceed_round(true) - } - - fn round_timeout(&self) -> Option { - if matches!(self.round, R::Round2(_)) { - Some(Duration::from_secs(5)) - } else { - None - } - } - - fn round_timeout_reached(&mut self) -> Self::Err { - if !matches!(self.round, R::Round2(_)) { - panic!("no timeout was set") - } - let (_, parties) = self - .msgs2 - .as_ref() - .expect("store is gone, but round is not over yet") - .blame(); - Error::ProceedRound(ProceedError::PartiesDidntRevealItsSeed { party_ind: parties }) - } - - fn is_finished(&self) -> bool { - matches!(self.round, R::Finished(_)) - } - - fn pick_output(&mut self) -> Option> { - match self.round { - R::Finished(_) => (), - R::Gone => return Some(Err(Error::DoublePickResult)), - _ => return None, - } - - match replace(&mut self.round, R::Gone) { - R::Finished(result) => Some(Ok(result)), - _ => unreachable!("guaranteed by match expression above"), - } - } - - fn current_round(&self) -> u16 { - match self.round { - R::Round0(_) => 0, - R::Round1(_) => 1, - R::Round2(_) => 2, - R::Finished(_) | R::Gone => 3, - } - } - - fn total_rounds(&self) -> Option { - Some(2) - } - - fn party_ind(&self) -> u16 { - self.party_i - } - - fn parties(&self) -> u16 { - self.party_n - } -} - -impl fmt::Debug for MultiPartyGenRandom { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let current_round = match &self.round { - R::Round0(_) => "0", - R::Round1(_) => "1", - R::Round2(_) => "2", - R::Finished(_) => "[Finished]", - R::Gone => "[Gone]", - }; - let msgs1 = match self.msgs1.as_ref() { - Some(msgs) => format!("[{}/{}]", msgs.messages_received(), msgs.messages_total()), - None => "[None]".into(), - }; - let msgs2 = match self.msgs2.as_ref() { - Some(msgs) => format!("[{}/{}]", msgs.messages_received(), msgs.messages_total()), - None => "[None]".into(), - }; - write!( - f, - "{{MPCRandom at round={} msgs1={} msgs2={} queue=[len={}]}}", - current_round, - msgs1, - msgs2, - self.msgs_queue.len() - ) - } -} - -// Rounds - -pub enum R { - Round0(Round0), - Round1(Round1), - Round2(Round2), - Finished(OutputRandomValue), - Gone, -} - -// Messages - -/// Protocol message -/// -/// Hides message structure so it could be changed without breaking semver policy. -#[derive(Clone, Debug)] -pub struct ProtocolMessage(M); - -#[derive(Clone, Debug)] -enum M { - Round1(rounds::CommittedSeed), - Round2(rounds::RevealedSeed), -} - -type Result = std::result::Result; - -#[derive(Debug)] -pub enum Error { - /// Protocol error caught at proceeding round - ProceedRound(ProceedError), - /// Received message didn't pass pre-validation - HandleMsg(StoreErr), - /// Received message which we didn't expect to receive (e.g. message from previous round) - OutOfOrderMsg { current_round: u16, msg_round: u16 }, - /// [SillyProtocol::pick_output] called twice - DoublePickResult, -} - -impl IsCritical for Error { - fn is_critical(&self) -> bool { - // Protocol is not resistant to occurring any of errors :( - true - } -} diff --git a/tests/silly_protocol/rounds.rs b/tests/silly_protocol/rounds.rs deleted file mode 100644 index a84deac..0000000 --- a/tests/silly_protocol/rounds.rs +++ /dev/null @@ -1,146 +0,0 @@ -use round_based::containers::push::Push; -use round_based::containers::{self, BroadcastMsgs, Store}; -use round_based::Msg; -use sha2::{Digest, Sha256}; - -#[derive(Debug)] -pub struct Round0 { - pub is_adversary: bool, - pub my_ind: u16, - pub my_seed: u32, - pub blinding: [u8; 32], -} - -impl Round0 { - pub fn proceed(self, mut output: O) -> Result - where - O: Push>, - { - let mut committed_seed = [0u8; 32]; - let hash = Sha256::new() - .chain(self.blinding) - .chain(&self.my_seed.to_be_bytes()[..]) - .finalize(); - committed_seed.copy_from_slice(&hash); - - output.push(Msg { - sender: self.my_ind, - receiver: None, - body: CommittedSeed(committed_seed), - }); - - Ok(Round1 { - is_adversary: self.is_adversary, - my_ind: self.my_ind, - my_seed: self.my_seed, - blinding: self.blinding, - }) - } - pub fn is_expensive(&self) -> bool { - // We assume that computing hash is expensive operation (in real-world, it's not) - true - } -} - -#[derive(Debug)] -pub struct Round1 { - is_adversary: bool, - my_ind: u16, - my_seed: u32, - blinding: [u8; 32], -} - -impl Round1 { - pub fn proceed(self, input: BroadcastMsgs, mut output: O) -> Result - where - O: Push>, - { - if !self.is_adversary { - output.push(Msg { - sender: self.my_ind, - receiver: None, - body: RevealedSeed { - seed: self.my_seed, - blinding: self.blinding, - }, - }); - } - Ok(Round2 { - my_seed: self.my_seed, - committed_seeds: input, - }) - } - pub fn expects_messages(party_i: u16, party_n: u16) -> Store> { - containers::BroadcastMsgsStore::new(party_i, party_n) - } - pub fn is_expensive(&self) -> bool { - // Sending cached message is the cheapest operation - false - } -} - -#[derive(Debug)] -pub struct Round2 { - my_seed: u32, - committed_seeds: BroadcastMsgs, -} - -impl Round2 { - pub fn proceed(self, input: BroadcastMsgs) -> Result { - let mut result = self.my_seed; - let msgs = self - .committed_seeds - .into_iter_indexed() - .zip(input.into_iter()); - - let mut non_cooperative_parties: Vec = vec![]; - for ((i, commit), decommit) in msgs { - let hash = Sha256::new() - .chain(decommit.blinding) - .chain(&decommit.seed.to_be_bytes()[..]) - .finalize(); - if commit.0 != hash.as_ref() { - non_cooperative_parties.push(i) - } else { - result ^= decommit.seed; - } - } - - if !non_cooperative_parties.is_empty() { - Err(ProceedError::PartiesDidntRevealItsSeed { - party_ind: non_cooperative_parties, - }) - } else { - Ok(result) - } - } - pub fn expects_messages(party_i: u16, party_n: u16) -> Store> { - containers::BroadcastMsgsStore::new(party_i, party_n) - } - pub fn is_expensive(&self) -> bool { - // Round involves computing a hash, we assume it's expensive (again, in real-world it's not) - true - } -} - -pub type OutputRandomValue = u32; - -// Messages - -#[derive(Clone, Debug)] -pub struct CommittedSeed([u8; 32]); - -#[derive(Clone, Debug)] -pub struct RevealedSeed { - seed: u32, - blinding: [u8; 32], -} - -// Errors - -type Result = std::result::Result; - -#[derive(Debug, PartialEq)] -pub enum ProceedError { - PartiesDidntRevealItsSeed { party_ind: Vec }, -} diff --git a/tests/simulate_silly_protocol.rs b/tests/simulate_silly_protocol.rs deleted file mode 100644 index 79d9624..0000000 --- a/tests/simulate_silly_protocol.rs +++ /dev/null @@ -1,66 +0,0 @@ -use round_based::async_runtime; -use round_based::dev::{AsyncSimulation, AsyncSimulationError, Simulation}; - -use crate::silly_protocol::{Error, MultiPartyGenRandom, ProceedError}; - -mod silly_protocol; - -#[test] -fn simulate_silly_protocol() { - let mut rnd = rand::thread_rng(); - let mut simulation = Simulation::new(); - simulation - .enable_benchmarks(true) - .add_party(MultiPartyGenRandom::with_fixed_seed(1, 3, 10, &mut rnd)) - .add_party(MultiPartyGenRandom::with_fixed_seed(2, 3, 20, &mut rnd)) - .add_party(MultiPartyGenRandom::with_fixed_seed(3, 3, 30, &mut rnd)); - let result = simulation.run().expect("simulation failed"); - assert_eq!(result, vec![10 ^ 20 ^ 30; 3]); - println!("Benchmarks:"); - println!("{:#?}", simulation.benchmark_results().unwrap()); -} - -#[tokio::test] -async fn async_simulation_of_silly_protocol() { - let mut rnd = rand::thread_rng(); - let results = AsyncSimulation::new() - .add_party(MultiPartyGenRandom::with_fixed_seed(1, 3, 22, &mut rnd)) - .add_party(MultiPartyGenRandom::with_fixed_seed(2, 3, 33, &mut rnd)) - .add_party(MultiPartyGenRandom::with_fixed_seed(3, 3, 44, &mut rnd)) - .run() - .await; - println!("Simulation results: {:?}", results); - let predicate = |x| match x { - &Ok(x) => x == 22 ^ 33 ^ 44, - &Err(_) => false, - }; - assert!(results.iter().all(predicate)) -} - -#[tokio::test] -async fn async_simulation_of_silly_protocol_with_adversary() { - let mut rnd = rand::thread_rng(); - let results = AsyncSimulation::new() - .add_party(MultiPartyGenRandom::with_fixed_seed(1, 3, 43, &mut rnd)) - .add_party(MultiPartyGenRandom::with_fixed_seed(2, 3, 44, &mut rnd)) - .add_party(MultiPartyGenRandom::adversary_with_fixed_seed( - 3, 3, 45, &mut rnd, - )) - .run() - .await; - println!("Simulation results: {:?}", results); - let blamed = |x| match x { - &Err(AsyncSimulationError::ProtocolExecution( - async_runtime::Error::HandleIncomingTimeout(Error::ProceedRound( - ProceedError::PartiesDidntRevealItsSeed { ref party_ind }, - )), - )) => Some(party_ind.clone()), - _ => None, - }; - let predicate = |(i, x)| match i { - 0..=1 => blamed(x) == Some(vec![3]), - 2 => x.is_ok() && *x.as_ref().unwrap() == 43 ^ 44 ^ 45, - _ => unreachable!(), - }; - assert!(results.iter().enumerate().all(predicate)) -} From 531cd6a8e1bb94ee3bfd4390c4a7dd3dc284cc86 Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Thu, 23 Nov 2023 11:13:41 +0000 Subject: [PATCH 2/7] Remove old CI --- .travis.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c9829fd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -language: rust -cache: cargo -rust: stable -virt: lxd - -before_install: - - rustup component add rustfmt clippy - -script: - - cargo build --verbose --no-default-features --features derive - - cargo test --verbose --no-default-features --features derive - - cargo fmt --all -- --check - - cargo clippy -- -D clippy::all - -deploy: - provider: cargo - token: - secure: "jZHHpJU9TMmwV1tMPxsDk6K+FaLdD8mhG8xsUIDqPb8j4/knPPXNT90Uci0yUHJf6kV5Pls2AUFUEeEawfMRUohADhVCXDK/HHZ/27rplPNJzEntTvSbPg+K8lDSw22f/YZEcAXQMl/u1Ynk9+sUBKKdj9JLkhKgvE5RvroC4KSKDlCBrR0yf8wOgAhFkcm/1PTKRFSWstFJ3XTK5dUoUSJu8jPGYK6yMlP4XmCb4NA7qA9us2/bpSAftqNguW9ovP7mLIO7fDY/hSZbXJ/yi33WcwL/fTkkrmCk9AdY0ciIv+mzSHvuUGvxKfyNAYAqXdvTBj9GxvvGOtkGp9jP2rUiXFyIQULz6LUbZ1rn0UCQECXditYIYJjsSN9NnLrTKQJGTxHYZjcoxFAJ8dfu/WqmAQ1E4q+0ZsnknG30afrnotaPYIegJgM0s0cUZBI9cA/5tdGE+XyR/UiNzI/UnSkICCcokIxXNKNeP21PcVL7+16QstPMwdzjneMW+MX/P3Fq4aiZeaZx2u96XIh0mbsiXdR1ZvAjBk6q9GaKdF6qCl0HLRe55IOaIwB61/n1B4v9iENiyDESM4cp//wvAkKuzEGWhF/9iVglLS+WKpbDcj3KYYcuGyc3rfqbjoZPT3UsmADKbQeE4naapOax/Cj1chWAGH0JhqeFmBrry80=" - on: - tags: true - condition: '"$TRAVIS_TAG" =~ ^v[0-9].+$' From 23d9598875bf6fe7a77079a7aade27b48326ebad Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Thu, 23 Nov 2023 11:16:00 +0000 Subject: [PATCH 3/7] Fix CI --- .github/workflows/rust.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3a105a3..87767e3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,16 +27,22 @@ jobs: check-fmt: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 - name: Check formatting run: cargo fmt --all -- --check check-docs: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" - name: Check docs run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features clippy: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 - uses: Swatinem/rust-cache@v2 with: cache-on-failure: "true" From 33cba3326f7de248f8eee70c37051678bc7172a3 Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Thu, 23 Nov 2023 11:21:27 +0000 Subject: [PATCH 4/7] Update readme --- README.md | 2 +- docs/readme.tpl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 docs/readme.tpl diff --git a/README.md b/README.md index f4aa528..b99a79c 100644 --- a/README.md +++ b/README.md @@ -35,4 +35,4 @@ the documentation of the protocol you're using), but usually they are: ## Features * `dev` enables development tools such as protocol simulation -* `runtime-tokio` tells that computationally-heavy tasks shall be executed using tokio runtime +* `runtime-tokio` enables tokio-specific implementation of async runtime diff --git a/docs/readme.tpl b/docs/readme.tpl new file mode 100644 index 0000000..274bb44 --- /dev/null +++ b/docs/readme.tpl @@ -0,0 +1 @@ +{{readme}} From 34392747d23976cbcf8934555718e7d20d4eaea4 Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Thu, 23 Nov 2023 11:23:27 +0000 Subject: [PATCH 5/7] Add codeowners --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..fb4f292 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @survived From f5f00df00f29e7bc455735cda89999d2580b4dc6 Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Thu, 23 Nov 2023 11:53:13 +0000 Subject: [PATCH 6/7] Update license and docs --- LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++ LICENSE => LICENSE-MIT | 2 +- round-based/src/lib.rs | 4 +- round-based/src/runtime.rs | 3 +- 4 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 LICENSE-APACHE rename LICENSE => LICENSE-MIT (97%) diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..7b8a5b2 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE b/LICENSE-MIT similarity index 97% rename from LICENSE rename to LICENSE-MIT index 76a04f4..f889381 100644 --- a/LICENSE +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Zengo X +Copyright (c) 2023 Zengo X Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/round-based/src/lib.rs b/round-based/src/lib.rs index 6a46a75..e69d4cd 100644 --- a/round-based/src/lib.rs +++ b/round-based/src/lib.rs @@ -37,7 +37,7 @@ //! * `dev` enables development tools such as [protocol simulation](simulation) //! * `runtime-tokio` enables [tokio]-specific implementation of [async runtime](runtime) -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg, doc_cfg_hide))] #![deny(missing_docs)] #![forbid(unused_crate_dependencies)] @@ -54,7 +54,6 @@ pub mod rounds_router; pub mod runtime; #[cfg(feature = "dev")] -#[cfg_attr(docsrs, doc(cfg(feature = "dev")))] pub mod simulation; pub use self::delivery::*; @@ -71,5 +70,4 @@ pub mod _docs; /// /// See [`ProtocolMessage`] docs for more details #[cfg(feature = "derive")] -#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] pub use round_based_derive::ProtocolMessage; diff --git a/round-based/src/runtime.rs b/round-based/src/runtime.rs index 83c64fb..2296eb2 100644 --- a/round-based/src/runtime.rs +++ b/round-based/src/runtime.rs @@ -21,7 +21,6 @@ pub trait AsyncRuntime { /// [Tokio](tokio)-specific async runtime #[cfg(feature = "runtime-tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "runtime-tokio")))] #[derive(Debug, Default)] pub struct TokioRuntime; @@ -39,9 +38,11 @@ pub use unknown_runtime::UnknownRuntime; /// Default runtime #[cfg(feature = "runtime-tokio")] +#[cfg_attr(docsrs, doc(cfg(all())))] pub type DefaultRuntime = TokioRuntime; /// Default runtime #[cfg(not(feature = "runtime-tokio"))] +#[cfg_attr(docsrs, doc(cfg(all())))] pub type DefaultRuntime = UnknownRuntime; /// Unknown async runtime From b4898583d38a38b950bb0f31912a181d4cd89fa4 Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Thu, 23 Nov 2023 11:59:41 +0000 Subject: [PATCH 7/7] Add audit workflow --- .github/workflows/audit.yml | 15 + Cargo.lock | 617 +++++++++++++++++++++------------- round-based-derive/Cargo.toml | 7 +- 3 files changed, 398 insertions(+), 241 deletions(-) create mode 100644 .github/workflows/audit.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..08418ea --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,15 @@ +name: Security audit +on: + pull_request: + branches: [ "*" ] + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: rustsec/audit-check@v1.4.1 + with: + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ac00c2e..67fa682 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,26 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "anyhow" -version = "1.0.53" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "autocfg" @@ -14,6 +29,30 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "basic-toml" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f2139706359229bfa8f19142ac1155b4b80beafb7a60471ac5dd109d4a19778" +dependencies = [ + "serde", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -25,30 +64,33 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytes" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.72" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -58,20 +100,21 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] [[package]] name = "crypto-common" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "typenum", ] [[package]] @@ -85,54 +128,53 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.2" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.4", "crypto-common", ] [[package]] name = "educe" -version = "0.4.18" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "enum-ordinalize" -version = "3.1.10" +version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ "num-bigint", "num-traits", "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "futures" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -145,9 +187,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -155,15 +197,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -172,38 +214,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -219,9 +261,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "serde", "typenum", @@ -230,20 +272,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.4" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", "wasi", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hex" @@ -259,99 +307,78 @@ checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" [[package]] name = "idna" -version = "0.2.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.56" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" -version = "0.2.117" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "log" -version = "0.4.14" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.4.1" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] -name = "mio" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ - "winapi", + "adler", ] [[package]] -name = "ntapi" -version = "0.3.6" +name = "mio" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ - "winapi", + "libc", + "wasi", + "windows-sys", ] [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -360,9 +387,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -370,18 +397,27 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.9.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -391,9 +427,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phantom-type" @@ -406,9 +442,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -418,38 +454,37 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.15" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", - "rand_hc", ] [[package]] @@ -464,22 +499,13 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "random-generation-protocol" version = "0.1.0" @@ -491,7 +517,7 @@ dependencies = [ "rand_chacha", "round-based", "serde", - "sha2 0.10.1", + "sha2 0.10.8", "thiserror", "tokio", ] @@ -505,12 +531,26 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys", +] + [[package]] name = "round-based" version = "0.2.0" @@ -533,7 +573,7 @@ version = "0.2.0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -557,59 +597,65 @@ dependencies = [ "url", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustls" -version = "0.20.2" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", - "ring", + "ring 0.16.20", "sct", "webpki", ] [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] name = "serde" -version = "1.0.136" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.78" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -631,20 +677,33 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.1" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.2", + "digest 0.10.7", ] [[package]] name = "slab" -version = "0.4.5" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] [[package]] name = "spin" @@ -652,90 +711,109 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "syn" -version = "1.0.86" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "termcolor" -version = "1.1.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.16.1" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ + "backtrace", "libc", "mio", "pin-project-lite", + "socket2", "tokio-macros", - "winapi", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.7.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "tokio-rustls" -version = "0.23.2" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls", "tokio", @@ -744,9 +822,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -756,34 +834,23 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", "futures-sink", - "log", "pin-project-lite", "tokio", ] -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - [[package]] name = "tracing" -version = "0.1.30" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -791,80 +858,86 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.19" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.22" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] name = "trybuild" -version = "1.0.55" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a24e67e2b4083a6d0beb5a98e274c3160edfb879d71cd2cd14da93786a93b" +checksum = "196a58260a906cedb9bf6d8034b6379d0c11f552416960452f267402ceeddff1" dependencies = [ + "basic-toml", "glob", "once_cell", "serde", + "serde_derive", "serde_json", "termcolor", - "toml", ] [[package]] name = "typenum" -version = "1.13.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "untrusted" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.2.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] @@ -876,15 +949,15 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.79" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -892,24 +965,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.79" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.79" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -917,28 +990,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.79" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.79" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "web-sys" -version = "0.3.56" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" dependencies = [ "js-sys", "wasm-bindgen", @@ -946,12 +1019,12 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -972,9 +1045,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -984,3 +1057,69 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/round-based-derive/Cargo.toml b/round-based-derive/Cargo.toml index 53b5422..0f7ca07 100644 --- a/round-based-derive/Cargo.toml +++ b/round-based-derive/Cargo.toml @@ -2,11 +2,14 @@ name = "round-based-derive" version = "0.2.0" edition = "2021" +license = "MIT OR Apache-2.0" +description = "Proc-macro for deriving `round-based` traits" +repository = "https://github.com/ZenGo-X/round-based-protocol" [lib] proc-macro = true [dependencies] -syn = "1.0" -quote = "1.0" +syn = "1" +quote = "1" proc-macro2 = "1"