diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0b4d57e9cb..da4667b837 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -284,7 +284,7 @@ jobs: needs: - k8s-peers - build-tests - # - build-tests-webrtc + - build-tests-webrtc runs-on: ubuntu-20.04 container: image: minaprotocol/mina-daemon:3.0.0-dc6bf78-focal-devnet @@ -296,6 +296,7 @@ jobs: # TODO: remove when replayer supports identify KEEP_CONNECTION_WITH_UNKNOWN_STREAM: true OPENMINA_SCENARIO_SEEDS: ${{ needs.k8s-peers.outputs.peers }} + REPLAYER_MULTIADDR: "/dns4/1.k8.openmina.com/tcp/31968/p2p/12D3KooWPayQEdprqY2m3biReUUybA5LoULpJE7YWu6wetEKKELv" BPF_ALIAS: /coda/0.0.1/29936104443aaf264a7f0192ac64b1c7173198c1ed404c1bcff5e562e05eb7f6-0.0.0.0 strategy: matrix: @@ -309,6 +310,7 @@ jobs: - connection_discovery_rust_as_seed - connection_discovery_rust_to_ocaml_via_seed - connection_discovery_rust_to_ocaml + - webrtc_p2p_signaling # - webrtc_single_node # - webrtc_multi_node fail-fast: false @@ -331,6 +333,12 @@ jobs: with: pattern: tests* merge-multiple: true + + - name: Download tests + uses: actions/download-artifact@v4 + with: + pattern: tests-webrtc* + merge-multiple: true - name: Setup permissions run: | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0a0a0ef62a..39bda33630 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ jobs: sudo apt install -y protobuf-compiler - uses: actions-rs/toolchain@v1 with: - toolchain: 1.79 + toolchain: 1.81 components: rustfmt, clippy default: true - uses: actions-rs/cargo@v1 @@ -36,4 +36,4 @@ jobs: name: clippy with: token: ${{ secrets.GITHUB_TOKEN }} - args: --all-targets -- -D warnings + args: --all-targets -- -D warnings --allow clippy::mutable_key_type --allow clippy::result_unit_err diff --git a/CHANGELOG.md b/CHANGELOG.md index ab72a6c775..a81831826e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.10.3] - 2024-10-16 + +### Added + +- Support for name resolution in peer addresses. +- Block producer docker compose setup. +- WASM file containing the webnode is now included in the frontend image. +- Logging output to the filesystem in addition to stdout. +- Webnode: peer discovery and p2p based signaling. + ## [0.10.0] - 2024-10-10 ### Added @@ -279,7 +289,8 @@ First public release. - Alpha version of the node which can connect and syncup to the berkeleynet network, and keep applying new blocks to maintain consensus state and ledger up to date. - Web-based frontend for the node. -[Unreleased]: https://github.com/openmina/openmina/compare/v0.10.0...develop +[Unreleased]: https://github.com/openmina/openmina/compare/v0.10.3...develop +[0.10.3]: https://github.com/openmina/openmina/releases/tag/v0.10.0...v0.10.3 [0.10.0]: https://github.com/openmina/openmina/releases/tag/v0.9.0...v0.10.0 [0.9.0]: https://github.com/openmina/openmina/releases/tag/v0.8.14...v0.9.0 [0.8.14]: https://github.com/openmina/openmina/releases/tag/v0.8.13...v0.8.14 diff --git a/Cargo.lock b/Cargo.lock index 1bc2815932..c2b248a5f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1006,7 +1006,7 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "cli" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "bytes", @@ -1177,6 +1177,15 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.3" @@ -1213,12 +1222,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -2379,7 +2385,7 @@ dependencies = [ [[package]] name = "hash-tool" -version = "0.10.0" +version = "0.10.3" dependencies = [ "bs58 0.5.0", "hex", @@ -2980,7 +2986,7 @@ dependencies = [ [[package]] name = "ledger-tool" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "mina-curves", @@ -3306,7 +3312,7 @@ dependencies = [ [[package]] name = "libp2p-rpc-behaviour" -version = "0.10.0" +version = "0.10.3" dependencies = [ "libp2p", "log", @@ -3691,7 +3697,7 @@ dependencies = [ [[package]] name = "mina-transport" -version = "0.10.0" +version = "0.10.3" dependencies = [ "blake2", "hex", @@ -3702,7 +3708,7 @@ dependencies = [ [[package]] name = "mina-tree" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "ark-ec", @@ -4042,7 +4048,7 @@ dependencies = [ [[package]] name = "node" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "ark-ff", @@ -4388,7 +4394,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openmina-bootstrap-sandbox" -version = "0.10.0" +version = "0.10.3" dependencies = [ "base64 0.21.7", "binprot", @@ -4413,7 +4419,7 @@ dependencies = [ [[package]] name = "openmina-core" -version = "0.10.0" +version = "0.10.3" dependencies = [ "ark-ff", "binprot", @@ -4442,7 +4448,7 @@ dependencies = [ [[package]] name = "openmina-fuzzer" -version = "0.10.0" +version = "0.10.3" dependencies = [ "lazy_static", "rand", @@ -4453,7 +4459,7 @@ dependencies = [ [[package]] name = "openmina-gossipsub-sandbox" -version = "0.10.0" +version = "0.10.3" dependencies = [ "bs58 0.5.0", "env_logger", @@ -4467,7 +4473,7 @@ dependencies = [ [[package]] name = "openmina-macros" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "openmina-core", @@ -4480,7 +4486,7 @@ dependencies = [ [[package]] name = "openmina-node-account" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "argon2", @@ -4501,7 +4507,7 @@ dependencies = [ [[package]] name = "openmina-node-common" -version = "0.10.0" +version = "0.10.3" dependencies = [ "ark-ff", "gloo-timers", @@ -4522,6 +4528,7 @@ dependencies = [ "thiserror", "tokio", "tracing", + "tracing-appender", "tracing-subscriber", "tracing-wasm", "vrf", @@ -4531,7 +4538,7 @@ dependencies = [ [[package]] name = "openmina-node-invariants" -version = "0.10.0" +version = "0.10.3" dependencies = [ "documented", "lazy_static", @@ -4545,9 +4552,10 @@ dependencies = [ [[package]] name = "openmina-node-native" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", + "bs58 0.4.0", "bytes", "derive_more", "getrandom", @@ -4581,7 +4589,7 @@ dependencies = [ [[package]] name = "openmina-node-testing" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "axum", @@ -4625,7 +4633,7 @@ dependencies = [ [[package]] name = "openmina-node-web" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "bytes", @@ -4651,7 +4659,7 @@ dependencies = [ [[package]] name = "openmina-producer-dashboard" -version = "0.10.0" +version = "0.10.3" dependencies = [ "bincode", "clap 4.5.2", @@ -4743,8 +4751,9 @@ dependencies = [ [[package]] name = "p2p" -version = "0.10.0" +version = "0.10.3" dependencies = [ + "aes-gcm 0.10.3", "anyhow", "binprot", "binprot_derive", @@ -4801,12 +4810,13 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webrtc", + "x25519-dalek", "zeroize", ] [[package]] name = "p2p-testing" -version = "0.10.0" +version = "0.10.3" dependencies = [ "derive_more", "futures", @@ -5622,7 +5632,7 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "replay_dynamic_effects" -version = "0.10.0" +version = "0.10.3" dependencies = [ "node", "openmina-node-invariants", @@ -5977,7 +5987,7 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salsa-simple" -version = "0.10.0" +version = "0.10.3" dependencies = [ "generic-array", "hex", @@ -6379,7 +6389,7 @@ dependencies = [ [[package]] name = "snark" -version = "0.10.0" +version = "0.10.3" dependencies = [ "ark-ec", "ark-ff", @@ -7157,6 +7167,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.27" @@ -7556,7 +7578,7 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "vrf" -version = "0.10.0" +version = "0.10.3" dependencies = [ "anyhow", "ark-ec", @@ -8260,9 +8282,9 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", "rand_core", diff --git a/Dockerfile b/Dockerfile index 994e211bc1..ff8c93024b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,3 @@ -ARG MINA_SNARK_WORKER_TAG=0.0.9 - FROM rust:buster AS build RUN apt-get update && apt-get install -y protobuf-compiler && apt-get clean RUN rustup default 1.80 && rustup component add rustfmt @@ -12,8 +10,6 @@ RUN cargo build --release --features scenario-generators --bin openmina-node-tes RUN git clone --depth 1 https://github.com/openmina/circuit-blobs.git \ && rm -rf circuit-blobs/berkeley_rc1 circuit-blobs/*/tests -FROM openmina/mina-snark-worker-prover:${MINA_SNARK_WORKER_TAG} AS prover - FROM debian:buster RUN apt-get update && apt-get install -y libjemalloc2 libssl1.1 libpq5 curl jq procps && apt-get clean COPY --from=build /openmina/cli/bin/snark-worker /usr/local/bin/ @@ -21,5 +17,4 @@ COPY --from=build /openmina/target/release/openmina /usr/local/bin/ COPY --from=build /openmina/target/release/openmina-node-testing /usr/local/bin/ RUN mkdir -p /usr/local/lib/openmina/circuit-blobs COPY --from=build /openmina/circuit-blobs/ /usr/local/lib/openmina/circuit-blobs/ -COPY --from=prover /usr/local/bin/mina /usr/local/bin ENTRYPOINT [ "openmina" ] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 9a425f3de5..8b02c3ef8b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cli" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" @@ -47,7 +47,7 @@ redux = { workspace = true, features=["serializable_callbacks"] } tempfile = "3.8.0" [features] -default = ["p2p-libp2p"] +default = ["p2p-libp2p", "p2p-webrtc"] unsafe-signal-handlers = [] p2p-libp2p = ["openmina-node-native/p2p-libp2p"] p2p-webrtc = ["openmina-node-native/p2p-webrtc"] diff --git a/cli/replay_dynamic_effects/Cargo.toml b/cli/replay_dynamic_effects/Cargo.toml index 973f78644d..13566b9dbd 100644 --- a/cli/replay_dynamic_effects/Cargo.toml +++ b/cli/replay_dynamic_effects/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "replay_dynamic_effects" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/cli/src/commands/node/mod.rs b/cli/src/commands/node/mod.rs index 07f1482e41..436e600f63 100644 --- a/cli/src/commands/node/mod.rs +++ b/cli/src/commands/node/mod.rs @@ -120,7 +120,10 @@ pub struct Node { impl Node { pub fn run(self) -> anyhow::Result<()> { - tracing::initialize(self.verbosity); + let work_dir = shellexpand::full(&self.work_dir).unwrap().into_owned(); + + let _guard = + tracing::initialize_with_filesystem_output(self.verbosity, work_dir.clone().into()); rayon::ThreadPoolBuilder::new() .num_threads(num_cpus::get().max(2) - 1) @@ -228,7 +231,6 @@ impl Node { node_builder.snarker(sec_key, self.snarker_fee, self.snarker_strategy); } - let work_dir = shellexpand::full(&self.work_dir).unwrap().into_owned(); openmina_core::set_work_dir(work_dir.clone().into()); node_builder diff --git a/core/Cargo.toml b/core/Cargo.toml index 550e1551e6..db9dfc798e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-core" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/core/src/network.rs b/core/src/network.rs index a9373e1839..6b84f4cbe4 100644 --- a/core/src/network.rs +++ b/core/src/network.rs @@ -44,9 +44,11 @@ impl NetworkConfig { pub fn global() -> &'static Self { CONFIG.get_or_init(|| { let config = Self::default_config(); - eprintln!( - "WARNING: no network config initialized, using default config: {}", - config.name + crate::warn!( + crate::log::system_time(); + kind = "network", + message = "no network config initialized, using default config", + config = config.name, ); config }) @@ -172,12 +174,12 @@ pub mod devnet { pub fn default_peers() -> Vec<&'static str> { vec![ - // "/dns4/seed-1.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWAdgYL6hv18M3iDBdaK1dRygPivSfAfBNDzie6YqydVbs", - // "/dns4/seed-2.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWLjs54xHzVmMmGYb7W5RVibqbwD1co7M2ZMfPgPm7iAag", - // "/dns4/seed-3.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWEiGVAFC7curXWXiGZyMWnZK9h8BKr88U8D5PKV3dXciv", - "/ip4/34.45.167.81/tcp/10003/p2p/12D3KooWAdgYL6hv18M3iDBdaK1dRygPivSfAfBNDzie6YqydVbs", - "/ip4/34.28.194.121/tcp/10003/p2p/12D3KooWLjs54xHzVmMmGYb7W5RVibqbwD1co7M2ZMfPgPm7iAag", - "/ip4/34.44.189.148/tcp/10003/p2p/12D3KooWEiGVAFC7curXWXiGZyMWnZK9h8BKr88U8D5PKV3dXciv", + "/dns4/seed-1.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWAdgYL6hv18M3iDBdaK1dRygPivSfAfBNDzie6YqydVbs", + "/dns4/seed-2.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWLjs54xHzVmMmGYb7W5RVibqbwD1co7M2ZMfPgPm7iAag", + "/dns4/seed-3.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWEiGVAFC7curXWXiGZyMWnZK9h8BKr88U8D5PKV3dXciv", + // "/ip4/34.45.167.81/tcp/10003/p2p/12D3KooWAdgYL6hv18M3iDBdaK1dRygPivSfAfBNDzie6YqydVbs", + // "/ip4/34.28.194.121/tcp/10003/p2p/12D3KooWLjs54xHzVmMmGYb7W5RVibqbwD1co7M2ZMfPgPm7iAag", + // "/ip4/34.44.189.148/tcp/10003/p2p/12D3KooWEiGVAFC7curXWXiGZyMWnZK9h8BKr88U8D5PKV3dXciv", ] } } diff --git a/docker-compose.block-producer.yml b/docker-compose.block-producer.yml new file mode 100644 index 0000000000..7efe51b338 --- /dev/null +++ b/docker-compose.block-producer.yml @@ -0,0 +1,21 @@ +version: '3.8' + +services: + openmina-node: + image: openmina/openmina:${OPENMINA_TAG:-latest} + entrypoint: > + sh -c 'openmina node --producer-key /root/.openmina/producer-key ${COINBASE_RECEIVER:+--coinbase-receiver $COINBASE_RECEIVER}' + ports: + - "3000:3000" + volumes: + - ./openmina-workdir:/root/.openmina:rw + environment: + MINA_PRIVKEY_PASS: "${MINA_PRIVKEY_PASS:-}" + COINBASE_RECEIVER: "${COINBASE_RECEIVER:-}" + + frontend: + image: openmina/frontend:${OPENMINA_FRONTEND_TAG:-latest} + environment: + OPENMINA_FRONTEND_ENVIRONMENT: compose-producer + ports: + - "8070:80" diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index b3b67113ec..cddffc3dbc 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -8,8 +8,6 @@ services: command: [ "node" ] ports: - "3000:3000" - environment: - - MINA_SNARK_WORKER_TAG=0.0.9 networks: - app-network diff --git a/docker-compose.local.producers.yml b/docker-compose.local.producers.yml index 40b46eca97..756661f5fc 100644 --- a/docker-compose.local.producers.yml +++ b/docker-compose.local.producers.yml @@ -1,7 +1,7 @@ services: local-producer-cluster: container_name: local-producer-cluster - image: openmina/openmina:0.10.0 + image: openmina/openmina:0.10.3 environment: - RUST_BACKTRACE=1 entrypoint: ["openmina-node-testing", "scenarios-generate", "--name", "simulation-small-forever-real-time"] @@ -12,8 +12,8 @@ services: frontend: container_name: frontend - image: openmina/frontend:0.10.0 + image: openmina/frontend:0.10.3 environment: - OPENMINA_FRONTEND_ENVIRONMENT: block_producers + OPENMINA_FRONTEND_ENVIRONMENT: block-producers ports: - "8070:80" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 7b76f56179..4409cf8d7d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,6 @@ services: command: [ "node" ] ports: - "3000:3000" - environment: - - MINA_SNARK_WORKER_TAG=0.0.9 frontend: image: openmina/frontend:${OPENMINA_FRONTEND_TAG:-latest} diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 27ceaa7416..e993ee8550 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -10,6 +10,11 @@ RUN npm prune --production RUN echo "---------- USING APACHE ----------" FROM httpd:2.4 + +RUN apt-get update && \ + apt-get install -y curl && \ + rm -rf /var/lib/apt/lists/* + COPY --from=BUILD_IMAGE /app/dist/frontend /usr/local/apache2/htdocs/ COPY --from=BUILD_IMAGE /app/httpd.conf /usr/local/apache2/conf/httpd.conf diff --git a/frontend/angular.json b/frontend/angular.json index ca9c830cd4..8f916ba697 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -94,24 +94,6 @@ } ], "outputHashing": "all" - }, - "compose": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.compose.ts" - } - ], - "outputHashing": "all" - }, - "block_producers": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.block_producers.ts" - } - ], - "outputHashing": "all" } }, "defaultConfiguration": "production" diff --git a/frontend/docker/startup.sh b/frontend/docker/startup.sh index e7a97a4115..7f167362f2 100644 --- a/frontend/docker/startup.sh +++ b/frontend/docker/startup.sh @@ -1,12 +1,126 @@ #!/bin/bash +OPENMINA_BASE_URL="https://github.com/openmina" + +replace_signaling_url() { + if [ -n "$OPENMINA_SIGNALING_URL" ]; then + HTTPD_CONF="/usr/local/apache2/conf/httpd.conf" + SIGNALING_URL="http://localhost:3000/mina/webrtc/signal" + + echo "Replacing signaling URL in $HTTPD_CONF..." + + sed -i "s|$SIGNALING_URL|$OPENMINA_SIGNALING_URL|g" "$HTTPD_CONF" + + if [[ $? -ne 0 ]]; then + echo "Failed to replace the signaling URL, exiting." + exit 1 + else + echo "Successfully replaced signaling URL with '$OPENMINA_SIGNALING_URL' in $HTTPD_CONF" + fi + else + echo "OPENMINA_SIGNALING_URL is not set. No replacement performed." + fi +} + +download_circuit_files() { + CIRCUITS_BASE_URL="$OPENMINA_BASE_URL/circuit-blobs/releases/download" + CIRCUITS_VERSION="3.0.1devnet" + + DEVNET_CIRCUIT_FILES=( + "step-step-proving-key-blockchain-snark-step-0-55f640777b6486a6fd3fdbc3fcffcc60_gates.json" + "step-step-proving-key-blockchain-snark-step-0-55f640777b6486a6fd3fdbc3fcffcc60_internal_vars.bin" + "step-step-proving-key-blockchain-snark-step-0-55f640777b6486a6fd3fdbc3fcffcc60_rows_rev.bin" + "step-step-proving-key-transaction-snark-merge-1-ba1d52dfdc2dd4d2e61f6c66ff2a5b2f_gates.json" + "step-step-proving-key-transaction-snark-merge-1-ba1d52dfdc2dd4d2e61f6c66ff2a5b2f_internal_vars.bin" + "step-step-proving-key-transaction-snark-merge-1-ba1d52dfdc2dd4d2e61f6c66ff2a5b2f_rows_rev.bin" + "step-step-proving-key-transaction-snark-opt_signed-3-9eefed16953d2bfa78a257adece02d47_gates.json" + "step-step-proving-key-transaction-snark-opt_signed-3-9eefed16953d2bfa78a257adece02d47_internal_vars.bin" + "step-step-proving-key-transaction-snark-opt_signed-3-9eefed16953d2bfa78a257adece02d47_rows_rev.bin" + "step-step-proving-key-transaction-snark-opt_signed-opt_signed-2-48925e6a97197028e1a7c1ecec09021d_gates.json" + "step-step-proving-key-transaction-snark-opt_signed-opt_signed-2-48925e6a97197028e1a7c1ecec09021d_internal_vars.bin" + "step-step-proving-key-transaction-snark-opt_signed-opt_signed-2-48925e6a97197028e1a7c1ecec09021d_rows_rev.bin" + "step-step-proving-key-transaction-snark-proved-4-0cafcbc6dffccddbc82f8c2519c16341_gates.json" + "step-step-proving-key-transaction-snark-proved-4-0cafcbc6dffccddbc82f8c2519c16341_internal_vars.bin" + "step-step-proving-key-transaction-snark-proved-4-0cafcbc6dffccddbc82f8c2519c16341_rows_rev.bin" + "step-step-proving-key-transaction-snark-transaction-0-c33ec5211c07928c87e850a63c6a2079_gates.json" + "step-step-proving-key-transaction-snark-transaction-0-c33ec5211c07928c87e850a63c6a2079_internal_vars.bin" + "step-step-proving-key-transaction-snark-transaction-0-c33ec5211c07928c87e850a63c6a2079_rows_rev.bin" + "wrap-wrap-proving-key-blockchain-snark-bbecaf158ca543ec8ac9e7144400e669_gates.json" + "wrap-wrap-proving-key-blockchain-snark-bbecaf158ca543ec8ac9e7144400e669_internal_vars.bin" + "wrap-wrap-proving-key-blockchain-snark-bbecaf158ca543ec8ac9e7144400e669_rows_rev.bin" + "wrap-wrap-proving-key-transaction-snark-b9a01295c8cc9bda6d12142a581cd305_gates.json" + "wrap-wrap-proving-key-transaction-snark-b9a01295c8cc9bda6d12142a581cd305_internal_vars.bin" + "wrap-wrap-proving-key-transaction-snark-b9a01295c8cc9bda6d12142a581cd305_rows_rev.bin" + ) + DOWNLOAD_DIR="/usr/local/apache2/htdocs/assets/webnode/circuit-blobs/$CIRCUITS_VERSION" + + mkdir -p "$DOWNLOAD_DIR" + + for FILE in "${DEVNET_CIRCUIT_FILES[@]}"; do + if [[ -f "$DOWNLOAD_DIR/$FILE" ]]; then + echo "$FILE already exists in $DOWNLOAD_DIR, skipping download." + else + echo "Downloading $FILE to $DOWNLOAD_DIR..." + curl -s -L --retry 3 --retry-delay 5 -o "$DOWNLOAD_DIR/$FILE" "$CIRCUITS_BASE_URL/$CIRCUITS_VERSION/$FILE" + if [[ $? -ne 0 ]]; then + echo "Failed to download $FILE after 3 attempts, exiting." + exit 1 + else + echo "$FILE downloaded successfully to $DOWNLOAD_DIR" + fi + fi + done +} + +download_wasm_files() { + if [ -z "$OPENMINA_WASM_VERSION" ]; then + echo "Error: OPENMINA_WASM_VERSION is not set. Exiting." + exit 1 + fi + + WASM_URL="$OPENMINA_BASE_URL/openmina/releases/download/$OPENMINA_WASM_VERSION/openmina-$OPENMINA_WASM_VERSION-webnode-wasm.tar.gz" + TARGET_DIR="/usr/local/apache2/htdocs/assets/webnode/pkg" + + mkdir -p "$TARGET_DIR" + + echo "Downloading WASM files from $WASM_URL..." + curl -s -L --retry 3 --retry-delay 5 -o "/tmp/openmina-$OPENMINA_WASM_VERSION-webnode-wasm.tar.gz" "$WASM_URL" + + if [[ $? -ne 0 ]]; then + echo "Failed to download the WASM file after 3 attempts, exiting." + exit 1 + else + echo "WASM file downloaded successfully. Extracting to $TARGET_DIR..." + + tar -xzf "/tmp/openmina-$OPENMINA_WASM_VERSION-webnode-wasm.tar.gz" -C "$TARGET_DIR" + + # Check if the extraction was successful + if [[ $? -ne 0 ]]; then + echo "Failed to extract the WASM file, exiting." + exit 1 + else + echo "WASM files extracted successfully to $TARGET_DIR" + fi + fi + + rm "/tmp/openmina-$OPENMINA_WASM_VERSION-webnode-wasm.tar.gz" +} + if [ -n "$OPENMINA_FRONTEND_ENVIRONMENT" ]; then echo "Using environment: $OPENMINA_FRONTEND_ENVIRONMENT" cp -f /usr/local/apache2/htdocs/assets/environments/"$OPENMINA_FRONTEND_ENVIRONMENT".js \ /usr/local/apache2/htdocs/assets/environments/env.js + + if [ "$OPENMINA_FRONTEND_ENVIRONMENT" = "webnode" ]; then + echo "Environment is 'webnode'. Downloading circuit and WASM files..." + download_wasm_files + download_circuit_files + fi else echo "No environment specified. Using default." fi +replace_signaling_url + echo "Starting Apache..." exec "$@" diff --git a/frontend/httpd.conf b/frontend/httpd.conf index d904dceeca..f82a328e46 100644 --- a/frontend/httpd.conf +++ b/frontend/httpd.conf @@ -147,7 +147,7 @@ LoadModule proxy_http_module modules/mod_proxy_http.so #LoadModule proxy_scgi_module modules/mod_proxy_scgi.so #LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so #LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so -#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so +LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so #LoadModule proxy_ajp_module modules/mod_proxy_ajp.so #LoadModule proxy_balancer_module modules/mod_proxy_balancer.so #LoadModule proxy_express_module modules/mod_proxy_express.so @@ -158,7 +158,7 @@ LoadModule proxy_http_module modules/mod_proxy_http.so #LoadModule session_dbd_module modules/mod_session_dbd.so #LoadModule slotmem_shm_module modules/mod_slotmem_shm.so #LoadModule slotmem_plain_module modules/mod_slotmem_plain.so -#LoadModule ssl_module modules/mod_ssl.so +LoadModule ssl_module modules/mod_ssl.so #LoadModule optional_hook_export_module modules/mod_optional_hook_export.so #LoadModule optional_hook_import_module modules/mod_optional_hook_import.so #LoadModule optional_fn_import_module modules/mod_optional_fn_import.so @@ -202,7 +202,8 @@ LoadModule rewrite_module modules/mod_rewrite.so RewriteEngine On # Exclude /openmina-node from the general rewrite rules - RewriteCond %{REQUEST_URI} ^/openmina-node/ [NC] + RewriteCond %{REQUEST_URI} ^/openmina-node/ [NC,OR] + RewriteCond %{REQUEST_URI} ^/mina/webrtc/signal [NC] RewriteRule ^ - [L] # If an existing asset or directory is requested go to it as it is @@ -214,11 +215,18 @@ LoadModule rewrite_module modules/mod_rewrite.so RewriteRule ^ /index.html +SSLProxyEngine On + ProxyPass http://openmina-node:3000 ProxyPassReverse http://openmina-node:3000 + + ProxyPass https://staging-devnet-openmina-bp-0.minaprotocol.network/mina/webrtc/signal + ProxyPassReverse https://staging-devnet-openmina-bp-0.minaprotocol.network/mina/webrtc/signal + + # # If you wish httpd to run as a different user or group, you must run diff --git a/frontend/package.json b/frontend/package.json index 1439464d75..e7062c29a8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,8 @@ "watch": "ng build --watch --configuration development", "tests": "npx cypress open --config baseUrl=http://localhost:4200", "tests:headless": "npx cypress run --headless --config baseUrl=http://localhost:4200", - "docker": "npm run build:prod && docker buildx build --platform linux/amd64 -t openmina/frontend:latest . && docker push openmina/frontend:latest" + "docker": "npm run build:prod && docker buildx build --platform linux/amd64 -t openmina/frontend:latest . && docker push openmina/frontend:latest", + "start:bundle": "npx http-server dist/frontend -p 4200" }, "private": true, "dependencies": { diff --git a/frontend/src/app/app.reducer.ts b/frontend/src/app/app.reducer.ts index ef95ddfb37..dc6a5ecf1e 100644 --- a/frontend/src/app/app.reducer.ts +++ b/frontend/src/app/app.reducer.ts @@ -40,12 +40,12 @@ export const appReducer = createReducer( })), on(AppActions.toggleMenuOpening, (state) => ({ ...state, menu: { ...state.menu, open: !state.menu.open } })), on(AppActions.addNode, (state, { node }) => { - const customNodes = localStorage.getItem('custom_nodes') ? JSON.parse(localStorage.getItem('custom_nodes')) : []; + const customNodes = JSON.parse(localStorage.getItem('custom_nodes') ?? '[]'); localStorage.setItem('custom_nodes', JSON.stringify([node, ...customNodes])); return { ...state, nodes: [node, ...state.nodes] }; }), on(AppActions.deleteNode, (state, { node }) => { - const customNodes = localStorage.getItem('custom_nodes') ? JSON.parse(localStorage.getItem('custom_nodes')) : []; + const customNodes = JSON.parse(localStorage.getItem('custom_nodes') ?? '[]'); localStorage.setItem('custom_nodes', JSON.stringify(customNodes.filter((n: MinaNode) => n.name !== node.name))); const nodes = state.nodes.filter(n => n.name !== node.name); return { ...state, nodes, activeNode: state.activeNode?.name === node.name ? nodes[0] : state.activeNode }; diff --git a/frontend/src/app/app.routing.ts b/frontend/src/app/app.routing.ts index 7918f7fd26..1232bef8b5 100644 --- a/frontend/src/app/app.routing.ts +++ b/frontend/src/app/app.routing.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core'; -import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; -import { getFirstFeature } from '@shared/constants/config'; +import { NoPreloading, PreloadAllModules, RouterModule, Routes } from '@angular/router'; +import { CONFIG, getFirstFeature } from '@shared/constants/config'; const APP_TITLE: string = 'Open Mina'; @@ -10,7 +10,6 @@ export const NETWORK_TITLE: string = APP_TITLE + ' - Network'; export const NODES_TITLE: string = APP_TITLE + ' - Nodes'; export const STATE_TITLE: string = APP_TITLE + ' - State'; export const SNARKS_TITLE: string = APP_TITLE + ' - Snarks'; -export const TESTING_TOOL_TITLE: string = APP_TITLE + ' - Testing Tool'; export const BLOCK_PRODUCTION_TITLE: string = APP_TITLE + ' - Block Production'; export const MEMPOOL_TITLE: string = APP_TITLE + ' - Mempool'; export const BENCHMARKS_TITLE: string = APP_TITLE + ' - Benchmarks'; @@ -48,11 +47,6 @@ const routes: Routes = [ loadChildren: () => import('@snarks/snarks.module').then(m => m.SnarksModule), title: SNARKS_TITLE, }, - { - path: 'testing-tool', - loadChildren: () => import('@testing-tool/testing-tool.module').then(m => m.TestingToolModule), - title: TESTING_TOOL_TITLE, - }, { path: 'block-production', loadChildren: () => import('@block-production/block-production.module').then(m => m.BlockProductionModule), @@ -79,7 +73,7 @@ const routes: Routes = [ imports: [ RouterModule.forRoot(routes, { // enableTracing: true, - preloadingStrategy: PreloadAllModules, + preloadingStrategy: CONFIG.configs.some(c => c.isWebNode) ? NoPreloading : PreloadAllModules, onSameUrlNavigation: 'ignore', initialNavigation: 'enabledNonBlocking', }), diff --git a/frontend/src/app/app.service.ts b/frontend/src/app/app.service.ts index 700a0a3f69..7090d81d17 100644 --- a/frontend/src/app/app.service.ts +++ b/frontend/src/app/app.service.ts @@ -23,7 +23,7 @@ export class AppService { getNodes(): Observable { return of([ ...CONFIG.configs, - ...(localStorage.getItem('custom_nodes') ? JSON.parse(localStorage.getItem('custom_nodes')) : []), + ...JSON.parse(localStorage.getItem('custom_nodes') ?? '[]'), ]); } @@ -46,16 +46,16 @@ export class AppService { } private getStatus(data: NodeDetailsResponse): AppNodeStatus { - if (data.transition_frontier.sync.phase === 'Bootstrap') { - return AppNodeStatus.BOOTSTRAP; + switch (data.transition_frontier.sync.phase) { + case 'Bootstrap': + return AppNodeStatus.BOOTSTRAP; + case 'Catchup': + return AppNodeStatus.CATCHUP; + case 'Synced': + return AppNodeStatus.SYNCED; + default: + return AppNodeStatus.PENDING; } - if (data.transition_frontier.sync.phase === 'Catchup') { - return AppNodeStatus.CATCHUP; - } - if (data.transition_frontier.sync.phase === 'Synced') { - return AppNodeStatus.SYNCED; - } - return AppNodeStatus.PENDING; } } diff --git a/frontend/src/app/core/services/rust.service.ts b/frontend/src/app/core/services/rust.service.ts index 3379eda818..6f428c7436 100644 --- a/frontend/src/app/core/services/rust.service.ts +++ b/frontend/src/app/core/services/rust.service.ts @@ -37,6 +37,12 @@ export class RustService { } post(path: string, body: B): Observable { + if (this.node.isWebNode) { + return this.postToWebNode(path, body).pipe(map((response: any) => { + // console.log(path, response); + return response; + })); + } return this.http.post(this.URL + path, body); } @@ -56,6 +62,21 @@ export class RustService { return this.webNodeService.sync$; case '/stats/block_producer': return this.webNodeService.blockProducerStats$; + case '/transaction-pool': + return this.webNodeService.transactionPool$; + case '/accounts': + return this.webNodeService.accounts$; + case '/best-chain-user-commands': + return this.webNodeService.bestChainUserCommands$; + default: + throw new Error(`Web node doesn't support "${path}" path!`); + } + } + + private postToWebNode(path: string, body: B): Observable { + switch (path) { + case '/send-payment': + return this.webNodeService.sendPayment$(body); default: throw new Error(`Web node doesn't support "${path}" path!`); } diff --git a/frontend/src/app/core/services/web-node.service.ts b/frontend/src/app/core/services/web-node.service.ts index e595e5d00a..f70f24ec19 100644 --- a/frontend/src/app/core/services/web-node.service.ts +++ b/frontend/src/app/core/services/web-node.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject, filter, from, fromEvent, map, Observable, of, switchMap, tap } from 'rxjs'; +import { BehaviorSubject, filter, from, fromEvent, map, merge, Observable, of, switchMap, tap } from 'rxjs'; import base from 'base-x'; import { any, log } from '@openmina/shared'; -import { CONFIG } from '@shared/constants/config'; +import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root', @@ -11,9 +11,9 @@ export class WebNodeService { private readonly backendSubject$: BehaviorSubject = new BehaviorSubject(null); private backend: any; - webNodeState: string = 'notLoaded'; + private webNodeKeyPair: { publicKey: string, privateKey: string }; - constructor() { + constructor(private http: HttpClient) { const basex = base('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); any(window)['bs58btc'] = { encode: (buffer: Uint8Array | number[]) => 'z' + basex.encode(buffer), @@ -22,19 +22,25 @@ export class WebNodeService { } loadWasm$(): Observable { - if ((window as any).webnode) { - return of(void 0); - } - return fromEvent(window, 'webNodeLoaded').pipe(map(() => void 0)); + console.log('---LOADING WEBNODE---'); + return merge( + of(any(window).webnode).pipe(filter(Boolean)), + fromEvent(window, 'webNodeLoaded'), + ).pipe( + switchMap(() => this.http.get<{ publicKey: string, privateKey: string }>('assets/webnode/web-node-secrets.json')), + tap(data => this.webNodeKeyPair = data), + map(() => void 0), + ); } startWasm$(): Observable { - return of((window as any).webnode) + console.log('---STARTING WEBNODE---'); + return of(any(window).webnode) .pipe( switchMap((wasm: any) => from(wasm.default('assets/webnode/pkg/openmina_node_web_bg.wasm')).pipe(map(() => wasm))), switchMap((wasm) => { console.log(wasm); - return from(wasm.run(CONFIG.webNodeKey)); + return from(wasm.run(this.webNodeKeyPair.privateKey)); }), tap((jsHandle: any) => { this.backend = jsHandle; @@ -47,6 +53,10 @@ export class WebNodeService { ); } + get webNodeKeys(): { publicKey: string, privateKey: string } { + return this.webNodeKeyPair; + } + get status$(): Observable { return this.backendSubject$.asObservable().pipe( filter(Boolean), @@ -81,4 +91,32 @@ export class WebNodeService { switchMap(handle => from((handle as any).stats().sync())), ); } + + get accounts$(): Observable { + return this.backendSubject$.asObservable().pipe( + filter(Boolean), + switchMap(handle => from((handle as any).ledger().latest().accounts().all())), + ); + } + + get bestChainUserCommands$(): Observable { + return this.backendSubject$.asObservable().pipe( + filter(Boolean), + switchMap(handle => from((handle as any).transition_frontier().best_chain().user_commands())), + ); + } + + sendPayment$(payment: any): Observable { + return this.backendSubject$.asObservable().pipe( + filter(Boolean), + switchMap(handle => from((handle as any).transaction_pool().inject().payment(payment))), + ); + } + + get transactionPool$(): Observable { + return this.backendSubject$.asObservable().pipe( + filter(Boolean), + switchMap(handle => from((handle as any).transaction_pool().get())), + ); + } } diff --git a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets-zk.service.ts b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets-zk.service.ts index 5bcbf1dc98..2c425391df 100644 --- a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets-zk.service.ts +++ b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets-zk.service.ts @@ -15,7 +15,6 @@ export class BenchmarksWalletsZkService { readonly updates$ = this.updates.asObservable(); - loadO1js(): void { this.loadScript(); } diff --git a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.html b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.html index d81ef32cf1..57ae83e92c 100644 --- a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.html +++ b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.html @@ -1,7 +1,8 @@
- -
+ +
diff --git a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.ts b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.ts index 39b36dc31f..a09f7ed0ef 100644 --- a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.ts +++ b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.ts @@ -4,6 +4,7 @@ import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; import { AppSelectors } from '@app/app.state'; import { filter, skip } from 'rxjs'; import { BenchmarksWalletsZkService } from '@benchmarks/wallets/benchmarks-wallets-zk.service'; +import { MinaNode } from '@shared/types/core/environment/mina-env.type'; @Component({ selector: 'mina-benchmarks-wallets', @@ -14,7 +15,9 @@ import { BenchmarksWalletsZkService } from '@benchmarks/wallets/benchmarks-walle }) export class BenchmarksWalletsComponent extends StoreDispatcher implements OnInit, OnDestroy { - constructor(private zkService: BenchmarksWalletsZkService) {super();} + isWebNode: boolean = false; + + constructor(private zkService: BenchmarksWalletsZkService) { super(); } ngOnInit(): void { this.zkService.loadO1js(); @@ -23,6 +26,10 @@ export class BenchmarksWalletsComponent extends StoreDispatcher implements OnIni } private listenToActiveNodeChange(): void { + this.select(AppSelectors.activeNode, (node: MinaNode) => { + this.isWebNode = node.isWebNode; + this.detect(); + }, filter(Boolean)); this.select(AppSelectors.activeNode, () => { this.dispatch(BenchmarksWalletsGetWallets, { initialRequest: true }); }, filter(Boolean), skip(1)); diff --git a/frontend/src/app/features/dashboard/dashboard-block-height/dashboard-block-height.component.html b/frontend/src/app/features/dashboard/dashboard-block-height/dashboard-block-height.component.html deleted file mode 100644 index 5280328859..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-block-height/dashboard-block-height.component.html +++ /dev/null @@ -1,17 +0,0 @@ -
- straighten - Block Height -
-
- - -
diff --git a/frontend/src/app/features/dashboard/dashboard-block-height/dashboard-block-height.component.scss b/frontend/src/app/features/dashboard/dashboard-block-height/dashboard-block-height.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/app/features/dashboard/dashboard-block-height/dashboard-block-height.component.ts b/frontend/src/app/features/dashboard/dashboard-block-height/dashboard-block-height.component.ts deleted file mode 100644 index c65c25a1ba..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-block-height/dashboard-block-height.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; - -@Component({ - selector: 'mina-dashboard-block-height', - templateUrl: './dashboard-block-height.component.html', - styleUrls: ['./dashboard-block-height.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class DashboardBlockHeightComponent { - -} diff --git a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts index 5ef7aaf2cb..bd29e3cb04 100644 --- a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts @@ -35,10 +35,23 @@ export class DashboardBlocksSyncComponent extends StoreDispatcher implements OnI private listenToNodesChanges(): void { this.select(selectDashboardNodesAndPeers, ([nodes, peers]: [NodesOverviewNode[], DashboardPeer[]]) => { - this.extractNodesData(nodes); - this.extractPeersData(peers); + if (nodes.length === 0) { + this.fetched = undefined; + this.fetchedPercentage = '-'; + this.applied = undefined; + this.appliedPercentage = undefined; + this.root = undefined; + this.rootText = PENDING; + this.bestTipBlock = undefined; + this.bestTipBlockSyncedText = PENDING; + this.targetBlock = undefined; + this.syncProgress = undefined; + } else { + this.extractNodesData(nodes); + this.extractPeersData(peers); + } this.detect(); - }, filter(n => n[0].length > 0)); + }); } private extractPeersData(peers: DashboardPeer[]): void { diff --git a/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.ts b/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.ts index 9e405a67fc..bb4d2ea011 100644 --- a/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.ts @@ -17,7 +17,7 @@ import { } from '@shared/types/nodes/dashboard/nodes-overview-ledger.type'; import { filter } from 'rxjs'; import { NodesOverviewNode } from '@shared/types/nodes/dashboard/nodes-overview-node.type'; -import { ONE_BILLION, ONE_MILLION, ONE_THOUSAND, SecDurationConfig } from '@openmina/shared'; +import { ONE_MILLION, SecDurationConfig } from '@openmina/shared'; import { Overlay, OverlayRef } from '@angular/cdk/overlay'; import { TemplatePortal } from '@angular/cdk/portal'; import { DashboardRpcStats } from '@shared/types/dashboard/dashboard-rpc-stats.type'; @@ -67,8 +67,6 @@ const initialStaged: NodesOverviewRootStagedLedgerStep = { }) export class DashboardLedgerComponent extends StoreDispatcher implements OnInit, OnDestroy { - protected readonly NodesOverviewLedgerStepState = NodesOverviewLedgerStepState; - ledgers: NodesOverviewLedger = { stakingEpoch: initialSnarked, nextEpoch: initialSnarked, @@ -102,38 +100,59 @@ export class DashboardLedgerComponent extends StoreDispatcher implements OnInit, private listenToNodesChanges(): void { this.select(selectDashboardNodesAndRpcStats, ([nodes, rpcStats]: [NodesOverviewNode[], DashboardRpcStats]) => { - this.ledgers = nodes[0].ledgers; - - const getConfig = (state: NodesOverviewLedgerStepState): SecDurationConfig => - state === NodesOverviewLedgerStepState.LOADING ? this.undefinedConfig : this.emptyConfig; - - this.configMap = { - stakingEpoch: getConfig(this.ledgers.stakingEpoch.state), - nextEpoch: getConfig(this.ledgers.nextEpoch.state), - rootSnarked: getConfig(this.ledgers.rootSnarked.state), - rootStaged: getConfig(this.ledgers.rootStaged.state), - }; - this.setProgressTime(); - this.stakingProgress = rpcStats.stakingLedger?.fetched / rpcStats.stakingLedger?.estimation * 100 || 0; - this.nextProgress = rpcStats.nextLedger?.fetched / rpcStats.nextLedger?.estimation * 100 || 0; - this.rootSnarkedProgress = rpcStats.rootLedger?.fetched / rpcStats.rootLedger?.estimation * 100 || 0; - this.rootStagedProgress = this.ledgers.rootStaged.staged.fetchPartsEnd ? 50 : 0; - - if (this.ledgers.stakingEpoch.state === NodesOverviewLedgerStepState.SUCCESS) { - this.stakingProgress = 100; - } - if (this.ledgers.nextEpoch.state === NodesOverviewLedgerStepState.SUCCESS) { - this.nextProgress = 100; - } - if (this.ledgers.rootSnarked.state === NodesOverviewLedgerStepState.SUCCESS) { - this.rootSnarkedProgress = 100; + if (nodes.length === 0) { + this.ledgers = { + stakingEpoch: initialSnarked, + nextEpoch: initialSnarked, + rootSnarked: initialSnarked, + rootStaged: initialStaged, + }; + this.configMap = { + stakingEpoch: this.emptyConfig, + nextEpoch: this.emptyConfig, + rootSnarked: this.emptyConfig, + rootStaged: this.emptyConfig, + }; + this.progress = undefined; + this.stakingProgress = 0; + this.nextProgress = 0; + this.rootSnarkedProgress = 0; + this.rootStagedProgress = 0; + this.totalProgress = 0; + } else { + this.ledgers = nodes[0].ledgers; + + const getConfig = (state: NodesOverviewLedgerStepState): SecDurationConfig => + state === NodesOverviewLedgerStepState.LOADING ? this.undefinedConfig : this.emptyConfig; + + this.configMap = { + stakingEpoch: getConfig(this.ledgers.stakingEpoch.state), + nextEpoch: getConfig(this.ledgers.nextEpoch.state), + rootSnarked: getConfig(this.ledgers.rootSnarked.state), + rootStaged: getConfig(this.ledgers.rootStaged.state), + }; + this.setProgressTime(); + this.stakingProgress = rpcStats.stakingLedger?.fetched / rpcStats.stakingLedger?.estimation * 100 || 0; + this.nextProgress = rpcStats.nextLedger?.fetched / rpcStats.nextLedger?.estimation * 100 || 0; + this.rootSnarkedProgress = rpcStats.rootLedger?.fetched / rpcStats.rootLedger?.estimation * 100 || 0; + this.rootStagedProgress = this.ledgers.rootStaged.staged.fetchPartsEnd ? 50 : 0; + + if (this.ledgers.stakingEpoch.state === NodesOverviewLedgerStepState.SUCCESS) { + this.stakingProgress = 100; + } + if (this.ledgers.nextEpoch.state === NodesOverviewLedgerStepState.SUCCESS) { + this.nextProgress = 100; + } + if (this.ledgers.rootSnarked.state === NodesOverviewLedgerStepState.SUCCESS) { + this.rootSnarkedProgress = 100; + } + if (this.ledgers.rootStaged.state === NodesOverviewLedgerStepState.SUCCESS) { + this.rootStagedProgress = 100; + } + this.totalProgress = (this.stakingProgress + this.nextProgress + this.rootSnarkedProgress + this.rootStagedProgress) / 4; } - if (this.ledgers.rootStaged.state === NodesOverviewLedgerStepState.SUCCESS) { - this.rootStagedProgress = 100; - } - this.totalProgress = (this.stakingProgress + this.nextProgress + this.rootSnarkedProgress + this.rootStagedProgress) / 4; this.detect(); - }, filter(n => n[0].length > 0)); + }); } show(event: MouseEvent, start: number, end: number): void { diff --git a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts index 662790b3fa..3c8b63e204 100644 --- a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts @@ -30,6 +30,4 @@ export class DashboardNetworkComponent extends StoreDispatcher implements OnInit this.detect(); }, skip(1)); } - - protected readonly NodesOverviewLedgerStepState = NodesOverviewLedgerStepState; } diff --git a/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.html b/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.html deleted file mode 100644 index ac9871723c..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.html +++ /dev/null @@ -1,24 +0,0 @@ -
- margin - Node Status -
-
- -
-
- Reconstructing Mina Short History – 290 Blocks - info -
-
- -
- Applying Block 96 / 290 -
-
diff --git a/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.scss b/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.scss deleted file mode 100644 index bc69dad011..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.scss +++ /dev/null @@ -1,14 +0,0 @@ -@import 'openmina'; - -.bg-surface { - height: 128px; - - .bar { - height: 32px; - border-left: 2px solid $base-container; - - &.done { - border-left: 2px solid $aware-primary; - } - } -} diff --git a/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.ts b/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.ts deleted file mode 100644 index eca415c3de..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-node/dashboard-node.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, HostListener, ViewChild } from '@angular/core'; -import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; - -@Component({ - selector: 'mina-dashboard-node', - templateUrl: './dashboard-node.component.html', - styleUrls: ['./dashboard-node.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class DashboardNodeComponent extends StoreDispatcher implements AfterViewInit { - - barWidth: number = 0; - - @ViewChild('barsWrapper') parent: ElementRef; - - @HostListener('window:resize') onResize(): void { - this.barWidth = this.parent.nativeElement.offsetWidth / 290; - } - - ngAfterViewInit(): void { - this.onResize(); - this.detect(); - } -} diff --git a/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.ts b/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.ts index 78704f454c..1b67a896bc 100644 --- a/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.ts @@ -39,7 +39,7 @@ export class DashboardPeersMinimalTableComponent extends MinaTableRustWrapper { this.table.rows = peers; this.table.detect(); - }, filter(peers => peers.length > 0)); + }); } protected override onRowClick(row: DashboardPeer): void { diff --git a/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.html b/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.html deleted file mode 100644 index 5c6ca89977..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - circle - {{ row.status }} - - {{ row.datetime }} - {{ row.address ?? '-' | truncateMid: 20: 0 }} - {{ row.globalSlot ?? '-' }} - {{ row.height ?? '-' }} - {{ row.bestTipDatetime }} - - - - diff --git a/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.scss b/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.scss deleted file mode 100644 index feea4c1932..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import 'openmina'; - -.mina-icon { - font-variation-settings: 'FILL' 1, 'wght' 400 !important; -} - -.Connected { - color: $success-primary; -} - -.Connecting { - color: $base-primary; -} diff --git a/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.ts b/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.ts deleted file mode 100644 index 42c4283b7f..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-peers-table/dashboard-peers-table.component.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { MinaTableRustWrapper } from '@shared/base-classes/mina-table-rust-wrapper.class'; -import { TableColumnList } from '@openmina/shared'; -import { DashboardPeer, DashboardPeerStatus } from '@shared/types/dashboard/dashboard.peer'; -import { filter } from 'rxjs'; -import { selectDashboardPeers, selectDashboardPeersSort } from '@dashboard/dashboard.state'; -import { DashboardPeersSort } from '@dashboard/dashboard.actions'; - -@Component({ - selector: 'mina-dashboard-peers-table', - templateUrl: './dashboard-peers-table.component.html', - styleUrls: ['./dashboard-peers-table.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - host: { class: 'flex-column h-100' }, -}) -export class DashboardPeersTableComponent extends MinaTableRustWrapper { - - protected readonly DashboardPeerStatus = DashboardPeerStatus; - protected readonly tableHeads: TableColumnList = [ - { name: 'peer ID', sort: 'peerId' }, - { name: 'status' }, - { name: 'datetime', sort: 'timestamp' }, - { name: 'address' }, - { name: 'global slot', sort: 'globalSlot' }, - { name: 'height' }, - { name: 'best tip datetime', sort: 'bestTipTimestamp' }, - { name: 'best tip', sort: 'bestTip' }, - ]; - - override async ngOnInit(): Promise { - await super.ngOnInit(); - this.listenToPeersChanges(); - } - - protected override setupTable(): void { - this.table.gridTemplateColumns = [140, 140, 165, 150, 110, 100, 165, 190]; - this.table.minWidth = 1160; - this.table.sortClz = DashboardPeersSort; - this.table.sortSelector = selectDashboardPeersSort; - this.table.trackByFn = (_: number, row: DashboardPeer) => row.peerId + row.status + row.bestTip + row.timestamp; - } - - private listenToPeersChanges(): void { - this.select(selectDashboardPeers, (peers: DashboardPeer[]) => { - this.table.rows = peers; - this.table.detect(); - }, filter(peers => peers.length > 0)); - } - - protected override onRowClick(row: DashboardPeer): void { - } -} diff --git a/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.html b/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.html deleted file mode 100644 index 56b1a99243..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.html +++ /dev/null @@ -1,21 +0,0 @@ -
-language - Peer Discovery -
-
-
- - - -
-
- -
-
diff --git a/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.scss b/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.scss deleted file mode 100644 index 4659c0176b..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -.bg-surface { - border-top-right-radius: 8px; - border-top-left-radius: 8px; -} - -.table-zone { - height: calc(100% - 128px); -} diff --git a/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.ts b/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.ts deleted file mode 100644 index d68520e623..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-peers/dashboard-peers.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; -import { selectDashboardPeersStats } from '@dashboard/dashboard.state'; -import { DashboardPeersStats } from '@shared/types/dashboard/dashboard-peers-stats.type'; -import { skip } from 'rxjs'; - -@Component({ - selector: 'mina-dashboard-peers', - templateUrl: './dashboard-peers.component.html', - styleUrls: ['./dashboard-peers.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - host: { class: 'flex-column' }, -}) -export class DashboardPeersComponent extends StoreDispatcher implements OnInit { - - stats: DashboardPeersStats; - - ngOnInit(): void { - this.listenToPeersChanges(); - } - - private listenToPeersChanges(): void { - this.select(selectDashboardPeersStats, (stats: DashboardPeersStats) => { - this.stats = stats; - }, skip(1)); - } -} diff --git a/frontend/src/app/features/dashboard/dashboard-received/dashboard-received.component.html b/frontend/src/app/features/dashboard/dashboard-received/dashboard-received.component.html deleted file mode 100644 index 0cc08c8aca..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-received/dashboard-received.component.html +++ /dev/null @@ -1,17 +0,0 @@ -
- download - Received -
-
- - - -
diff --git a/frontend/src/app/features/dashboard/dashboard-received/dashboard-received.component.scss b/frontend/src/app/features/dashboard/dashboard-received/dashboard-received.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/app/features/dashboard/dashboard-received/dashboard-received.component.ts b/frontend/src/app/features/dashboard/dashboard-received/dashboard-received.component.ts deleted file mode 100644 index 9bacf25158..0000000000 --- a/frontend/src/app/features/dashboard/dashboard-received/dashboard-received.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; - -@Component({ - selector: 'mina-dashboard-received', - templateUrl: './dashboard-received.component.html', - styleUrls: ['./dashboard-received.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class DashboardReceivedComponent { - -} diff --git a/frontend/src/app/features/dashboard/dashboard-transition-frontier/dashboard-transition-frontier.component.ts b/frontend/src/app/features/dashboard/dashboard-transition-frontier/dashboard-transition-frontier.component.ts index e81c5b406d..7af4da1482 100644 --- a/frontend/src/app/features/dashboard/dashboard-transition-frontier/dashboard-transition-frontier.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-transition-frontier/dashboard-transition-frontier.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; import { selectDashboardNodes } from '@dashboard/dashboard.state'; import { NodesOverviewNode, NodesOverviewNodeKindType } from '@shared/types/nodes/dashboard/nodes-overview-node.type'; -import { filter } from 'rxjs'; @Component({ selector: 'mina-dashboard-transition-frontier', @@ -21,8 +20,8 @@ export class DashboardTransitionFrontierComponent extends StoreDispatcher implem private listenToNodesChanges(): void { this.select(selectDashboardNodes, (nodes: NodesOverviewNode[]) => { - this.loading = nodes[0].kind !== NodesOverviewNodeKindType.SYNCED; + this.loading = !nodes.length || nodes[0].kind !== NodesOverviewNodeKindType.SYNCED; this.detect(); - }, filter(n => n.length > 0)); + }); } } diff --git a/frontend/src/app/features/dashboard/dashboard.component.html b/frontend/src/app/features/dashboard/dashboard.component.html index 70f248e5de..97e54aa457 100644 --- a/frontend/src/app/features/dashboard/dashboard.component.html +++ b/frontend/src/app/features/dashboard/dashboard.component.html @@ -1,11 +1,4 @@ - - - -
- - -
diff --git a/frontend/src/app/features/dashboard/dashboard.module.ts b/frontend/src/app/features/dashboard/dashboard.module.ts index 4eaa8de27d..e06adfdf47 100644 --- a/frontend/src/app/features/dashboard/dashboard.module.ts +++ b/frontend/src/app/features/dashboard/dashboard.module.ts @@ -1,13 +1,8 @@ import { NgModule } from '@angular/core'; import { DashboardRouting } from '@dashboard/dashboard.routing'; -import { DashboardPeersComponent } from '@dashboard/dashboard-peers/dashboard-peers.component'; -import { DashboardNodeComponent } from '@dashboard/dashboard-node/dashboard-node.component'; import { DashboardComponent } from '@dashboard/dashboard.component'; import { SharedModule } from '@shared/shared.module'; -import { DashboardPeersTableComponent } from '@dashboard/dashboard-peers-table/dashboard-peers-table.component'; -import { DashboardBlockHeightComponent } from '@dashboard/dashboard-block-height/dashboard-block-height.component'; -import { DashboardReceivedComponent } from '@dashboard/dashboard-received/dashboard-received.component'; import { EffectsModule } from '@ngrx/effects'; import { DashboardEffects } from '@dashboard/dashboard.effects'; import { LoadingSpinnerComponent } from '@shared/loading-spinner/loading-spinner.component'; @@ -26,11 +21,6 @@ import { @NgModule({ declarations: [ DashboardComponent, - DashboardPeersComponent, - DashboardNodeComponent, - DashboardPeersTableComponent, - DashboardBlockHeightComponent, - DashboardReceivedComponent, DashboardNetworkComponent, DashboardLedgerComponent, DashboardTransitionFrontierComponent, diff --git a/frontend/src/app/features/dashboard/dashboard.reducer.ts b/frontend/src/app/features/dashboard/dashboard.reducer.ts index b369ca0f07..7ee9016523 100644 --- a/frontend/src/app/features/dashboard/dashboard.reducer.ts +++ b/frontend/src/app/features/dashboard/dashboard.reducer.ts @@ -1,6 +1,6 @@ import { DashboardState } from '@dashboard/dashboard.state'; import { - DASHBOARD_CLOSE, + DASHBOARD_CLOSE, DASHBOARD_GET_DATA, DASHBOARD_GET_DATA_SUCCESS, DASHBOARD_PEERS_SORT, DashboardActions, @@ -38,6 +38,15 @@ const initialState: DashboardState = { export function dashboardReducer(state: DashboardState = initialState, action: DashboardActions): DashboardState { switch (action.type) { + case DASHBOARD_GET_DATA: { + if (action.payload?.force) { + return { + ...initialState, + peersSort: state.peersSort, + }; + } + return state; + } case DASHBOARD_PEERS_SORT: { return { ...state, diff --git a/frontend/src/app/layout/node-picker/node-picker.component.html b/frontend/src/app/layout/node-picker/node-picker.component.html index d6bfd60424..2cc67c4f92 100644 --- a/frontend/src/app/layout/node-picker/node-picker.component.html +++ b/frontend/src/app/layout/node-picker/node-picker.component.html @@ -12,13 +12,14 @@
-
+ (click)="deleteCustomNode(node); $event.stopImmediatePropagation()"> delete
diff --git a/frontend/src/app/layout/node-picker/node-picker.component.scss b/frontend/src/app/layout/node-picker/node-picker.component.scss index 32ae70a14f..73e471b098 100644 --- a/frontend/src/app/layout/node-picker/node-picker.component.scss +++ b/frontend/src/app/layout/node-picker/node-picker.component.scss @@ -32,6 +32,10 @@ } } } + + &.custom .names { + width: calc(100% - 20px - 20px); + } } } diff --git a/frontend/src/app/layout/node-picker/node-picker.component.ts b/frontend/src/app/layout/node-picker/node-picker.component.ts index 1568e2fbf1..edc489da45 100644 --- a/frontend/src/app/layout/node-picker/node-picker.component.ts +++ b/frontend/src/app/layout/node-picker/node-picker.component.ts @@ -5,8 +5,6 @@ import { untilDestroyed } from '@ngneat/until-destroy'; import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; import { AppActions } from '@app/app.actions'; import { animate, state, style, transition, trigger } from '@angular/animations'; -import { WebNodeService } from '@core/services/web-node.service'; -import { Router } from '@angular/router'; import { CONFIG } from '@shared/constants/config'; @Component({ @@ -42,9 +40,8 @@ export class NodePickerComponent extends StoreDispatcher implements AfterViewIni @ViewChild('searchNode') searchInput: ElementRef; readonly canAddNodes: boolean = CONFIG.globalConfig?.canAddNodes; - constructor(private elementRef: ElementRef, - private webNodeService: WebNodeService, - private router: Router) { super(); } + + constructor(private elementRef: ElementRef) { super(); } ngAfterViewInit(): void { this.listenToNodeSearch(); @@ -80,9 +77,6 @@ export class NodePickerComponent extends StoreDispatcher implements AfterViewIni if (node !== this.activeNode) { this.dispatch2(AppActions.changeActiveNode({ node })); } - if (node.isWebNode) { - this.webNodeService.webNodeState = 'loading'; - } } addNode(event: MouseEvent): void { @@ -92,6 +86,7 @@ export class NodePickerComponent extends StoreDispatcher implements AfterViewIni } deleteCustomNode(node: MinaNode): void { + this.filteredNodes = this.filteredNodes.filter(n => n !== node); this.dispatch2(AppActions.deleteNode({ node })); } } diff --git a/frontend/src/app/shared/constants/config.ts b/frontend/src/app/shared/constants/config.ts index 6c27b2e5e9..56f87c5709 100644 --- a/frontend/src/app/shared/constants/config.ts +++ b/frontend/src/app/shared/constants/config.ts @@ -6,7 +6,7 @@ export const CONFIG: Readonly = { ...environment, globalConfig: { ...environment.globalConfig, - graphQL: getURL(environment.globalConfig.graphQL), + graphQL: getURL(environment.globalConfig?.graphQL), }, configs: environment.configs.map((config) => ({ ...config, diff --git a/frontend/src/app/shared/types/core/environment/mina-env.type.ts b/frontend/src/app/shared/types/core/environment/mina-env.type.ts index 7d6cb73d38..c1e529f71e 100644 --- a/frontend/src/app/shared/types/core/environment/mina-env.type.ts +++ b/frontend/src/app/shared/types/core/environment/mina-env.type.ts @@ -2,7 +2,6 @@ export interface MinaEnv { production: boolean; configs: MinaNode[]; identifier?: string; - webNodeKey?: string; hideToolbar?: boolean; hideNodeStats?: boolean; globalConfig?: { diff --git a/frontend/src/assets/environments/block_producers.js b/frontend/src/assets/environments/block-producers.js similarity index 76% rename from frontend/src/assets/environments/block_producers.js rename to frontend/src/assets/environments/block-producers.js index bbb420c066..b11ed49bd1 100644 --- a/frontend/src/assets/environments/block_producers.js +++ b/frontend/src/assets/environments/block-producers.js @@ -1,3 +1,8 @@ +/** + * This configuration is used with the demo block producers inside a private network. + * https://github.com/openmina/openmina/blob/main/docs/producer-demo.md#launch + */ + export default { production: true, globalConfig: { diff --git a/frontend/src/environments/environment.compose.ts b/frontend/src/assets/environments/compose-producer.js similarity index 56% rename from frontend/src/environments/environment.compose.ts rename to frontend/src/assets/environments/compose-producer.js index c3daac10e4..e2d02972df 100644 --- a/frontend/src/environments/environment.compose.ts +++ b/frontend/src/assets/environments/compose-producer.js @@ -1,18 +1,21 @@ -import { MinaEnv } from '@shared/types/core/environment/mina-env.type'; +/** + * This configuration is used for lunching devnet rust nodes and user's own node to produce block. All inside a docker container. + * Todo: github documentation link + */ -export const environment: Readonly = { +export default { production: true, identifier: 'Running in Docker', globalConfig: { features: { dashboard: [], + 'block-production': ['won-slots'], nodes: ['overview', 'live', 'bootstrap'], state: ['actions'], - network: ['node-dht', 'graph-overview', 'bootstrap-stats'], snarks: ['scan-state'], - benchmarks: ['wallets'], }, canAddNodes: true, + graphQL: '/openmina-node/graphql', }, configs: [ { @@ -21,4 +24,3 @@ export const environment: Readonly = { }, ], }; - diff --git a/frontend/src/assets/environments/compose.js b/frontend/src/assets/environments/compose.js index 0f9f93ab6f..81f626e202 100644 --- a/frontend/src/assets/environments/compose.js +++ b/frontend/src/assets/environments/compose.js @@ -1,3 +1,8 @@ +/** + * This configuration is used for lunching devnet rust nodes inside a docker container. + * https://github.com/openmina/openmina?tab=readme-ov-file#how-to-launch-the-node-with-docker-compose + */ + export default { production: true, identifier: 'Running in Docker', @@ -6,9 +11,7 @@ export default { dashboard: [], nodes: ['overview', 'live', 'bootstrap'], state: ['actions'], - network: ['node-dht', 'graph-overview', 'bootstrap-stats'], snarks: ['scan-state'], - benchmarks: ['wallets'], }, canAddNodes: true, graphQL: '/openmina-node/graphql', diff --git a/frontend/src/assets/environments/env.js b/frontend/src/assets/environments/env.js index 72bf751ba3..50bb30fc11 100644 --- a/frontend/src/assets/environments/env.js +++ b/frontend/src/assets/environments/env.js @@ -1,6 +1,6 @@ /** - * This file is imported in the environment.prod.ts file - * The content of this file is replaced from a docker command + * This file is imported in the environment.prod.ts file in frontend/src/index.html + * The content of this file is replaced from a docker command (frontend/docker/startup.sh) * => 1 Docker image = multiple environments */ export default { diff --git a/frontend/src/assets/environments/producer.js b/frontend/src/assets/environments/producer.js index 03ea87447c..99b7d06c05 100644 --- a/frontend/src/assets/environments/producer.js +++ b/frontend/src/assets/environments/producer.js @@ -1,3 +1,9 @@ +/** + * This configuration is used for the producer dashboard that are based on archive node. + * Currently, there is no pipeline that this file is linked to, therefore it is not used anywhere right now. + * Check frontend/src/environments/environment.producer.ts file. + */ + export default { production: true, hideNodeStats: true, diff --git a/frontend/src/assets/environments/staging.js b/frontend/src/assets/environments/staging.js index 6c49290a6b..e9b184156f 100644 --- a/frontend/src/assets/environments/staging.js +++ b/frontend/src/assets/environments/staging.js @@ -1,3 +1,7 @@ +/** + * This configuration is used for the staging environment. + */ + export default { production: true, globalConfig: { diff --git a/frontend/src/assets/environments/webnode.js b/frontend/src/assets/environments/webnode.js new file mode 100644 index 0000000000..d755b2248b --- /dev/null +++ b/frontend/src/assets/environments/webnode.js @@ -0,0 +1,22 @@ +/** + * This configuration is used for the staging-webnode environment. + */ + +export default { + production: true, + globalConfig: { + features: { + 'dashboard': [], + 'block-production': ['won-slots'], + 'mempool': [], + 'benchmarks': ['wallets'], + }, + canAddNodes: false, + }, + configs: [ + { + name: 'Web Node', + isWebNode: true, + }, + ], +}; diff --git a/frontend/src/assets/webnode/.gitignore b/frontend/src/assets/webnode/.gitignore index aa054e2d62..f735b09de9 100644 --- a/frontend/src/assets/webnode/.gitignore +++ b/frontend/src/assets/webnode/.gitignore @@ -1,3 +1,4 @@ #.gitignore circuit-blobs pkg +web-node-secrets.json diff --git a/frontend/src/environments/environment.block_producers.ts b/frontend/src/environments/environment.block_producers.ts deleted file mode 100644 index 8f9a1f4dfe..0000000000 --- a/frontend/src/environments/environment.block_producers.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MinaEnv } from '@shared/types/core/environment/mina-env.type'; - -export const environment: Readonly = { - production: true, - globalConfig: { - features: { - 'block-production': ['won-slots'], - 'mempool': [], - 'benchmarks': ['wallets'], - 'snarks': ['scan-state', 'work-pool'], - }, - canAddNodes: true, - graphQL: 'http://localhost:11010/graphql', - }, - configs: [ - { - name: 'Producer 11010', - url: 'http://localhost:11010', - }, - { - name: 'Producer 11012', - url: 'http://localhost:11012', - }, - { - name: 'Producer 11014', - url: 'http://localhost:11014', - }, - ], -}; - diff --git a/frontend/src/environments/environment.local.ts b/frontend/src/environments/environment.local.ts index f90f0edc62..5df4f2a787 100644 --- a/frontend/src/environments/environment.local.ts +++ b/frontend/src/environments/environment.local.ts @@ -1,3 +1,7 @@ +/** + * This file is used for local starting of the app without any development intentions. + */ + import { MinaEnv } from '@shared/types/core/environment/mina-env.type'; export const environment: Readonly = { diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index 1aa4df76b7..5f8c81e001 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -3,7 +3,6 @@ import { MinaEnv } from '@shared/types/core/environment/mina-env.type'; export const environment: Readonly = { production: false, identifier: 'Dev FE', - webNodeKey: '', globalConfig: { features: { dashboard: [], @@ -11,7 +10,6 @@ export const environment: Readonly = { state: ['actions'], network: ['messages', 'connections', 'blocks', 'topology', 'node-dht', 'graph-overview', 'bootstrap-stats'], snarks: ['scan-state', 'work-pool'], - 'testing-tool': ['scenarios'], resources: ['memory'], 'block-production': ['won-slots'], mempool: [], diff --git a/frontend/src/index.html b/frontend/src/index.html index 26abb901d1..4bb32fa14c 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -29,17 +29,17 @@ diff --git a/fuzzer/Cargo.toml b/fuzzer/Cargo.toml index f34008228a..5efffc3863 100644 --- a/fuzzer/Cargo.toml +++ b/fuzzer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-fuzzer" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/ledger/Cargo.toml b/ledger/Cargo.toml index 1097cc8ee3..05d398007f 100644 --- a/ledger/Cargo.toml +++ b/ledger/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mina-tree" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/ledger/src/proofs/circuit_blobs.rs b/ledger/src/proofs/circuit_blobs.rs index 23ec9c21a0..ed89c42852 100644 --- a/ledger/src/proofs/circuit_blobs.rs +++ b/ledger/src/proofs/circuit_blobs.rs @@ -87,10 +87,13 @@ pub fn fetch(filename: &impl AsRef) -> std::io::Result> { return std::fs::read(path); } - eprintln!( - "circuit-blobs '{}' not found locally, so fetching it...", - filename.as_ref().to_str().unwrap() + openmina_core::info!( + openmina_core::log::system_time(); + kind = "ledger proofs", + message = "circuit-blobs not found locally, so fetching it...", + filename = filename.as_ref().to_str().unwrap(), ); + let base_dir = home_base_dir.expect("$HOME env not set!"); let bytes = reqwest::blocking::get(git_release_url(filename)) @@ -101,7 +104,12 @@ pub fn fetch(filename: &impl AsRef) -> std::io::Result> { // cache it to home dir. let cache_path = base_dir.join(filename); - eprintln!("caching circuit-blobs to {}", cache_path.to_str().unwrap()); + openmina_core::info!( + openmina_core::log::system_time(); + kind = "ledger proofs", + message = "caching circuit-blobs", + path = cache_path.to_str().unwrap(), + ); let _ = std::fs::create_dir_all(cache_path.parent().unwrap()); let _ = std::fs::write(cache_path, &bytes); diff --git a/ledger/src/proofs/verification.rs b/ledger/src/proofs/verification.rs index cf23f67216..3e219a578f 100644 --- a/ledger/src/proofs/verification.rs +++ b/ledger/src/proofs/verification.rs @@ -807,13 +807,17 @@ pub fn verify_zkapp( let ok = accum_check && verified; - eprintln!("verify_zkapp OK={:?}", ok); + openmina_core::info!(openmina_core::log::system_time(); message = format!("verify_zkapp OK={ok:?}")); #[cfg(not(test))] if !ok { if let Err(e) = dump_zkapp_verification(verification_key, zkapp_statement, sideloaded_proof) { - eprintln!("Failed to dump zkapp verification: {:?}", e); + openmina_core::error!( + openmina_core::log::system_time(); + message = "Failed to dump zkapp verification", + error = format!("{e:?}") + ); } } diff --git a/ledger/src/transaction_pool.rs b/ledger/src/transaction_pool.rs index cda188513b..9e180fe63c 100644 --- a/ledger/src/transaction_pool.rs +++ b/ledger/src/transaction_pool.rs @@ -1422,7 +1422,9 @@ impl IndexedPool { } } } else { - eprintln!("Sender queue is malformed"); + openmina_core::warn!( + openmina_core::log::system_time(); + kind = "transaction pool", message = "Sender queue is malformed"); all_by_sender.remove(&sender); } @@ -1484,7 +1486,9 @@ impl IndexedPool { } } } else { - eprintln!("Sender queue is malformed"); + openmina_core::warn!( + openmina_core::log::system_time(); + kind = "transaction pool", message = "Sender queue is malformed"); self.all_by_sender.remove(&sender); } @@ -1683,10 +1687,12 @@ impl TransactionPool { .collect::>(); if !dropped_locally_generated.is_empty() { - eprintln!( - "Dropped locally generated commands $cmds from pool when transition frontier was recreated. {:?}", - dropped_locally_generated - ) + openmina_core::info!( + openmina_core::log::system_time(); + kind = "transaction pool", + message = "Dropped locally generated commands $cmds from pool when transition frontier was recreated.", + dropped = format!("{dropped_locally_generated:?}") + ); } Ok(()) diff --git a/ledger/src/zkapps/zkapp_logic.rs b/ledger/src/zkapps/zkapp_logic.rs index bde54a57dd..74c7c6fe04 100644 --- a/ledger/src/zkapps/zkapp_logic.rs +++ b/ledger/src/zkapps/zkapp_logic.rs @@ -41,11 +41,14 @@ pub enum ZkAppCommandElt { ZkAppCommandCommitment(crate::ReceiptChainHash), } -fn assert_(_b: Z::Bool) -> Result<(), String> { +fn assert_(b: Z::Bool, s: &str) -> Result<(), String> { // Used only for circuit generation (add constraints) // https://github.com/MinaProtocol/mina/blob/e44ddfe1ca54b3855e1ed336d89f6230d35aeb8c/src/lib/transaction_logic/zkapp_command_logic.ml#L929 - // TODO: In non-witness generation, we raise an exception + if let Boolean::False = b.as_boolean() { + return Err(s.to_string()); + } + Ok(()) } @@ -345,8 +348,8 @@ where let is_empty_call_forest = local_state.stack_frame.calls().is_empty(w); match is_start { IsStart::Compute(_) => (), - IsStart::Yes(_) => assert_::(is_empty_call_forest)?, - IsStart::No => assert_::(is_empty_call_forest.neg())?, + IsStart::Yes(_) => assert_::(is_empty_call_forest, "is_empty_call_forest")?, + IsStart::No => assert_::(is_empty_call_forest.neg(), "is_empty_call_forest.neg()")?, }; match is_start { IsStart::Yes(_) => Z::Bool::true_(), @@ -566,16 +569,14 @@ where w, ) }; - assert_::(Z::Bool::equal( - proof_verifies, - account_update.is_proved(), - w, - ))?; - assert_::(Z::Bool::equal( - signature_verifies, - account_update.is_signed(), - w, - ))?; + assert_::( + Z::Bool::equal(proof_verifies, account_update.is_proved(), w), + "not proved", + )?; + assert_::( + Z::Bool::equal(signature_verifies, account_update.is_signed(), w), + "not signed", + )?; Z::LocalState::add_check( local_state, @@ -650,11 +651,14 @@ where SetOrKeep::Keep => a.get().timing.clone(), } }); - assert_::(Z::GlobalSlotSpan::greater_than( - &timing.to_record().vesting_period, - &SlotSpan::zero(), - w, - ))?; + assert_::( + Z::GlobalSlotSpan::greater_than( + &timing.to_record().vesting_period, + &SlotSpan::zero(), + w, + ), + "vesting_period zero", + )?; a.get_mut().timing = timing; ((), ()) }; @@ -1184,7 +1188,7 @@ where Z::SignedAmount::is_non_neg(&local_delta), w, ); - assert_::(Z::Bool::or(is_start2.neg(), first, w))?; + assert_::(Z::Bool::or(is_start2.neg(), first, w), "is_start2 or first")?; let (new_local_fee_excess, overflow) = Z::SignedAmount::add_flagged(&local_state.excess, &local_delta, w); // We decompose this way because of OCaml evaluation order diff --git a/macros/Cargo.toml b/macros/Cargo.toml index fa55f22393..d12689c862 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-macros" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" authors = [ "Alexander Koptelov " ] diff --git a/mina-p2p-messages/benches/gossip-binprot.rs b/mina-p2p-messages/benches/gossip-binprot.rs index b757ce9206..d0e9eec8f8 100644 --- a/mina-p2p-messages/benches/gossip-binprot.rs +++ b/mina-p2p-messages/benches/gossip-binprot.rs @@ -1,3 +1,4 @@ +#![allow(unexpected_cfgs)] #![cfg(benchmarks)] #![feature(test)] diff --git a/mina-p2p-messages/tests/rpc-read.rs b/mina-p2p-messages/tests/rpc-read.rs index 31be00fc0f..64e3939165 100644 --- a/mina-p2p-messages/tests/rpc-read.rs +++ b/mina-p2p-messages/tests/rpc-read.rs @@ -158,7 +158,7 @@ fn debugger_to_wire() { "v1/rpc/get-transition-knowledge", "v1/rpc/get-ancestry", ] { - for_all_with_path(&PathBuf::from(d).join("response"), |encoded, path| { + for_all_with_path(PathBuf::from(d).join("response"), |encoded, path| { let mut p = &encoded[1..]; let tag = BinprotTag::binprot_read(&mut p).unwrap().to_string_lossy(); let ver = Ver::binprot_read(&mut p).unwrap(); diff --git a/node/Cargo.toml b/node/Cargo.toml index 7cf1e0795a..7006e7c8e9 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/node/account/Cargo.toml b/node/account/Cargo.toml index fb1541ef25..93209b1421 100644 --- a/node/account/Cargo.toml +++ b/node/account/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-node-account" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/node/common/Cargo.toml b/node/common/Cargo.toml index 9d347d5e47..ffe0ba3262 100644 --- a/node/common/Cargo.toml +++ b/node/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-node-common" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" @@ -34,6 +34,7 @@ tracing-wasm = "0.2" [target.'cfg(not(target_family = "wasm"))'.dependencies] redux = { workspace = true, features=["serializable_callbacks"] } tracing-subscriber = { version = "0.3.17", features = ["json", "env-filter"] } +tracing-appender = "0.2.3" libp2p-identity = { version = "=0.2.7", features = ["ed25519", "rand", "serde"] } [features] diff --git a/node/common/src/service/block_producer/mod.rs b/node/common/src/service/block_producer/mod.rs index 054a953509..afb8288b79 100644 --- a/node/common/src/service/block_producer/mod.rs +++ b/node/common/src/service/block_producer/mod.rs @@ -120,7 +120,9 @@ impl node::service::BlockProducerService for crate::NodeService { // IMPORTANT: Make sure that `input` here is a copy from before `prove` is called, we don't // want to leak the private key. if let Err(error) = dump_failed_block_proof_input(block_hash.clone(), input) { - eprintln!("ERROR when dumping failed block proof inputs: {}", error); + openmina_core::error!( + openmina_core::log::system_time(); + message = "Failure when dumping failed block proof inputs", error = format!("{error}")); } } let _ = tx.send(BlockProducerEvent::BlockProve(block_hash, res).into()); @@ -137,7 +139,11 @@ fn dump_failed_block_proof_input( .join(format!("failed_block_proof_input_{block_hash}.binprot")) .to_string_lossy() .to_string(); - println!("Dumping failed block proof to {filename}"); + openmina_core::warn!( + openmina_core::log::system_time(); + message = "Dumping failed block proof.", + filename = filename + ); std::fs::create_dir_all(&debug_dir)?; let mut file = std::fs::File::create(&filename)?; input.binprot_write(&mut file)?; diff --git a/node/common/src/service/p2p.rs b/node/common/src/service/p2p.rs index 10f1efffdd..d0093ccc0b 100644 --- a/node/common/src/service/p2p.rs +++ b/node/common/src/service/p2p.rs @@ -3,7 +3,11 @@ use std::collections::BTreeMap; use node::{ core::channels::mpsc, event_source::Event, - p2p::{connection::outgoing::P2pConnectionOutgoingInitOpts, PeerId}, + p2p::{ + connection::outgoing::P2pConnectionOutgoingInitOpts, + identity::{EncryptableType, PublicKey}, + PeerId, + }, }; use rand::prelude::*; #[cfg(feature = "p2p-libp2p")] @@ -34,6 +38,23 @@ impl webrtc::P2pServiceWebrtc for NodeService { fn peers(&mut self) -> &mut BTreeMap { &mut self.p2p.webrtc.peers } + + fn encrypt( + &mut self, + other_pk: &PublicKey, + message: &T, + ) -> Result { + let rng = &mut self.rng; + self.p2p.sec_key.encrypt(other_pk, rng, message) + } + + fn decrypt( + &mut self, + other_pk: &PublicKey, + encrypted: &T::Encrypted, + ) -> Result { + self.p2p.sec_key.decrypt(other_pk, encrypted) + } } impl webrtc_with_libp2p::P2pServiceWebrtcWithLibp2p for NodeService { diff --git a/node/common/src/service/rpc/state.rs b/node/common/src/service/rpc/state.rs index 5fec6f009e..16e4949976 100644 --- a/node/common/src/service/rpc/state.rs +++ b/node/common/src/service/rpc/state.rs @@ -23,6 +23,16 @@ impl State { #[cfg(target_family = "wasm")] #[cfg_attr(target_family = "wasm", wasm_bindgen)] impl State { + pub async fn get(&self, filter: String) -> JsValue { + let res = self + .sender + .oneshot_request::(RpcRequest::StateGet(Some(filter))) + .await + .and_then(|v| v.ok()); + res.map(|res| JsValue::from_serde(&res).unwrap_or_default()) + .unwrap_or_default() + } + pub async fn peers(&self) -> JsValue { let res = self .sender diff --git a/node/common/src/tracing.rs b/node/common/src/tracing.rs index 3729a1a59c..64e426bbce 100644 --- a/node/common/src/tracing.rs +++ b/node/common/src/tracing.rs @@ -2,8 +2,9 @@ pub use tracing::Level; #[cfg(not(target_family = "wasm"))] mod native { - use std::fmt::Result; - use tracing::{field::Visit, Level}; + use std::{fmt::Result, path::PathBuf}; + use tracing::{field::Visit, level_filters::LevelFilter, Level}; + use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{ field::{RecordFields, VisitOutput}, fmt::{ @@ -11,6 +12,8 @@ mod native { time::FormatTime, FormatFields, }, + layer::SubscriberExt, + Layer, }; #[allow(unused)] @@ -68,11 +71,11 @@ mod native { pub fn initialize(max_log_level: Level) { let builder = tracing_subscriber::FmtSubscriber::builder() - .with_max_level(max_log_level) - .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stdout())) - .with_test_writer() + .with_max_level(max_log_level) + .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stdout())) + .with_test_writer(); //.with_timer(ReduxTimer) - ; + if max_log_level != Level::TRACE { let subscriber = builder.fmt_fields(TracingFieldFormatter).finish(); tracing::subscriber::set_global_default(subscriber) @@ -82,6 +85,34 @@ mod native { } .expect("global subscriber should be configurable"); } + + pub fn initialize_with_filesystem_output( + max_log_level: Level, + log_output_dir: PathBuf, + ) -> WorkerGuard { + let file_appender = tracing_appender::rolling::daily(log_output_dir, "openmina.log"); + let (file_writer, file_guard) = tracing_appender::non_blocking(file_appender); + let level_filter = LevelFilter::from_level(max_log_level); + + let file_layer = tracing_subscriber::fmt::layer() + .with_writer(file_writer) + .with_ansi(false) + .with_filter(level_filter); + + let stdout_layer = tracing_subscriber::fmt::layer() + .with_writer(std::io::stdout) + .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stdout())) + .with_filter(level_filter); + + let subscriber = tracing_subscriber::Registry::default() + .with(file_layer) + .with(stdout_layer); + + tracing::subscriber::set_global_default(subscriber) + .expect("Failed to set global subscriber"); + + file_guard + } } #[cfg(target_family = "wasm")] @@ -97,6 +128,6 @@ mod web { } #[cfg(not(target_family = "wasm"))] -pub use native::initialize; +pub use native::{initialize, initialize_with_filesystem_output}; #[cfg(target_family = "wasm")] pub use web::initialize; diff --git a/node/invariants/Cargo.toml b/node/invariants/Cargo.toml index 3636c94ea0..599225b0bb 100644 --- a/node/invariants/Cargo.toml +++ b/node/invariants/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-node-invariants" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/node/native/Cargo.toml b/node/native/Cargo.toml index 30d3451d49..a52a5ccfc5 100644 --- a/node/native/Cargo.toml +++ b/node/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-node-native" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" @@ -10,6 +10,7 @@ rand = "0.8" serde = "1.0.158" serde_json = "1.0.94" derive_more = "0.99.17" +bs58 = { version = "0.4" } rayon = "1.5" tokio = { version = "1.26.0", features = ["process", "macros"] } reqwest = { version = "0.11.24", features = ["blocking", "json"] } diff --git a/node/native/src/http_server.rs b/node/native/src/http_server.rs index e195a169ba..e62ca24f79 100644 --- a/node/native/src/http_server.rs +++ b/node/native/src/http_server.rs @@ -39,45 +39,64 @@ pub async fn run(port: u16, rpc_sender: RpcSender) { use super::rpc::RpcP2pConnectionIncomingResponse; + let handle = |sender: RpcSender, offer: Box| async move { + let mut rx = sender + .multishot_request( + 2, + RpcRequest::P2pConnectionIncoming(P2pConnectionIncomingInitOpts { + peer_id: PeerId::from_public_key(offer.identity_pub_key.clone()), + signaling: IncomingSignalingMethod::Http, + offer, + }), + ) + .await; + + match rx.recv().await { + Some(RpcP2pConnectionIncomingResponse::Answer(answer)) => { + let status = match &answer { + P2pConnectionResponse::Accepted(_) => StatusCode::OK, + P2pConnectionResponse::Rejected(reason) => match reason.is_bad() { + false => StatusCode::OK, + true => StatusCode::BAD_REQUEST, + }, + P2pConnectionResponse::SignalDecryptionFailed => StatusCode::BAD_REQUEST, + P2pConnectionResponse::InternalError => StatusCode::INTERNAL_SERVER_ERROR, + }; + with_json_reply(&answer, status) + } + _ => { + let resp = P2pConnectionResponse::internal_error_str(); + with_json_reply(&resp, StatusCode::INTERNAL_SERVER_ERROR) + } + } + }; + let rpc_sender_clone = rpc_sender.clone(); - warp::path!("mina" / "webrtc" / "signal") - .and(warp::post()) - .and(warp::filters::body::json()) - .then(move |offer: Box| { + let get = warp::path!("mina" / "webrtc" / "signal" / String) + .and(warp::get()) + .then(move |offer: String| { let rpc_sender_clone = rpc_sender_clone.clone(); async move { - let mut rx = rpc_sender_clone - .multishot_request( - 2, - RpcRequest::P2pConnectionIncoming(P2pConnectionIncomingInitOpts { - peer_id: PeerId::from_public_key(offer.identity_pub_key.clone()), - signaling: IncomingSignalingMethod::Http, - offer, - }), - ) - .await; - - match rx.recv().await { - Some(RpcP2pConnectionIncomingResponse::Answer(answer)) => { - let status = match &answer { - P2pConnectionResponse::Accepted(_) => StatusCode::OK, - P2pConnectionResponse::Rejected(reason) => match reason.is_bad() { - false => StatusCode::OK, - true => StatusCode::BAD_REQUEST, - }, - P2pConnectionResponse::InternalError => { - StatusCode::INTERNAL_SERVER_ERROR - } - }; - with_json_reply(&answer, status) - } - _ => { - let resp = P2pConnectionResponse::internal_error_str(); - with_json_reply(&resp, StatusCode::INTERNAL_SERVER_ERROR) - } + let decode_res = Err(()).or_else(move |_| { + let json = bs58::decode(&offer).into_vec().or(Err(()))?; + serde_json::from_slice(&json).or(Err(())) + }); + match decode_res { + Err(()) => with_json_reply( + &P2pConnectionResponse::SignalDecryptionFailed, + StatusCode::BAD_REQUEST, + ), + Ok(offer) => handle(rpc_sender_clone.clone(), offer).await, } } - }) + }); + + let rpc_sender_clone = rpc_sender.clone(); + let post = warp::path!("mina" / "webrtc" / "signal") + .and(warp::post()) + .and(warp::filters::body::json()) + .then(move |offer: Box| handle(rpc_sender_clone.clone(), offer)); + get.or(post) }; // TODO(binier): make endpoint only accessible locally. diff --git a/node/native/src/node/builder.rs b/node/native/src/node/builder.rs index 2607a17334..2f26611d83 100644 --- a/node/native/src/node/builder.rs +++ b/node/native/src/node/builder.rs @@ -271,6 +271,17 @@ impl NodeBuilder { self.initial_peers }; + let initial_peers = initial_peers + .into_iter() + .filter_map(|opts| match opts { + P2pConnectionOutgoingInitOpts::LibP2P(mut opts) => { + opts.host = opts.host.resolve()?; + Some(P2pConnectionOutgoingInitOpts::LibP2P(opts)) + } + x => Some(x), + }) + .collect(); + let srs = self.verifier_srs.unwrap_or_else(get_srs); let block_verifier_index = self .block_verifier_index diff --git a/node/src/action.rs b/node/src/action.rs index 0669809e9e..8d8ee5e0e5 100644 --- a/node/src/action.rs +++ b/node/src/action.rs @@ -1,3 +1,4 @@ +use p2p::P2pEffectfulAction; use serde::{Deserialize, Serialize}; pub type ActionWithMeta = redux::ActionWithMeta; @@ -33,6 +34,7 @@ pub enum Action { EventSource(EventSourceAction), P2p(P2pAction), + P2pEffectful(P2pEffectfulAction), P2pCallbacks(P2pCallbacksAction), Ledger(LedgerAction), @@ -76,6 +78,10 @@ impl redux::EnablingCondition for Action { .ready() .map_or(false, |p2p| other.is_enabled(p2p, time)), }, + Action::P2pEffectful(a) => state + .p2p + .ready() + .map_or(false, |state| a.is_enabled(state, time)), Action::Ledger(a) => a.is_enabled(state, time), Action::Snark(a) => a.is_enabled(&state.snark, time), Action::Consensus(a) => a.is_enabled(state, time), diff --git a/node/src/action_kind.rs b/node/src/action_kind.rs index ff2b74ec88..f50049e58a 100644 --- a/node/src/action_kind.rs +++ b/node/src/action_kind.rs @@ -28,6 +28,10 @@ use crate::p2p::channels::best_tip::P2pChannelsBestTipAction; use crate::p2p::channels::best_tip_effectful::P2pChannelsBestTipEffectfulAction; use crate::p2p::channels::rpc::P2pChannelsRpcAction; use crate::p2p::channels::rpc_effectful::P2pChannelsRpcEffectfulAction; +use crate::p2p::channels::signaling::discovery::P2pChannelsSignalingDiscoveryAction; +use crate::p2p::channels::signaling::discovery_effectful::P2pChannelsSignalingDiscoveryEffectfulAction; +use crate::p2p::channels::signaling::exchange::P2pChannelsSignalingExchangeAction; +use crate::p2p::channels::signaling::exchange_effectful::P2pChannelsSignalingExchangeEffectfulAction; use crate::p2p::channels::snark::P2pChannelsSnarkAction; use crate::p2p::channels::snark_effectful::P2pChannelsSnarkEffectfulAction; use crate::p2p::channels::snark_job_commitment::P2pChannelsSnarkJobCommitmentAction; @@ -49,8 +53,9 @@ use crate::p2p::disconnection_effectful::P2pDisconnectionEffectfulAction; use crate::p2p::identify::P2pIdentifyAction; use crate::p2p::network::identify::stream::P2pNetworkIdentifyStreamAction; use crate::p2p::network::identify::stream_effectful::P2pNetworkIdentifyStreamEffectfulAction; -use crate::p2p::network::identify::P2pNetworkIdentifyAction; +use crate::p2p::network::identify::{P2pNetworkIdentifyAction, P2pNetworkIdentifyEffectfulAction}; use crate::p2p::network::kad::bootstrap::P2pNetworkKadBootstrapAction; +use crate::p2p::network::kad::kad_effectful::P2pNetworkKadEffectfulAction; use crate::p2p::network::kad::request::P2pNetworkKadRequestAction; use crate::p2p::network::kad::stream::P2pNetworkKademliaStreamAction; use crate::p2p::network::kad::{P2pNetworkKadAction, P2pNetworkKademliaAction}; @@ -64,9 +69,9 @@ use crate::p2p::network::scheduler::P2pNetworkSchedulerAction; use crate::p2p::network::scheduler_effectful::P2pNetworkSchedulerEffectfulAction; use crate::p2p::network::select::P2pNetworkSelectAction; use crate::p2p::network::yamux::P2pNetworkYamuxAction; -use crate::p2p::network::P2pNetworkAction; +use crate::p2p::network::{P2pNetworkAction, P2pNetworkEffectfulAction}; use crate::p2p::peer::P2pPeerAction; -use crate::p2p::{P2pAction, P2pInitializeAction}; +use crate::p2p::{P2pAction, P2pEffectfulAction, P2pInitializeAction}; use crate::rpc::RpcAction; use crate::snark::block_verify::SnarkBlockVerifyAction; use crate::snark::block_verify_effectful::SnarkBlockVerifyEffectfulAction; @@ -208,6 +213,41 @@ pub enum ActionKind { P2pChannelsRpcEffectfulInit, P2pChannelsRpcEffectfulRequestSend, P2pChannelsRpcEffectfulResponseSend, + P2pChannelsSignalingDiscoveryAnswerDecrypted, + P2pChannelsSignalingDiscoveryAnswerReceived, + P2pChannelsSignalingDiscoveryAnswerSend, + P2pChannelsSignalingDiscoveryDiscoveredAccept, + P2pChannelsSignalingDiscoveryDiscoveredAcceptReceived, + P2pChannelsSignalingDiscoveryDiscoveredReceived, + P2pChannelsSignalingDiscoveryDiscoveredReject, + P2pChannelsSignalingDiscoveryDiscoveredRejectReceived, + P2pChannelsSignalingDiscoveryDiscoveredSend, + P2pChannelsSignalingDiscoveryDiscoveryRequestReceived, + P2pChannelsSignalingDiscoveryDiscoveryRequestSend, + P2pChannelsSignalingDiscoveryInit, + P2pChannelsSignalingDiscoveryPending, + P2pChannelsSignalingDiscoveryReady, + P2pChannelsSignalingDiscoveryRequestReceived, + P2pChannelsSignalingDiscoveryRequestSend, + P2pChannelsSignalingDiscoveryEffectfulAnswerDecrypt, + P2pChannelsSignalingDiscoveryEffectfulInit, + P2pChannelsSignalingDiscoveryEffectfulMessageSend, + P2pChannelsSignalingDiscoveryEffectfulOfferEncryptAndSend, + P2pChannelsSignalingExchangeAnswerReceived, + P2pChannelsSignalingExchangeAnswerSend, + P2pChannelsSignalingExchangeInit, + P2pChannelsSignalingExchangeOfferDecryptError, + P2pChannelsSignalingExchangeOfferDecryptSuccess, + P2pChannelsSignalingExchangeOfferReceived, + P2pChannelsSignalingExchangeOfferSend, + P2pChannelsSignalingExchangePending, + P2pChannelsSignalingExchangeReady, + P2pChannelsSignalingExchangeRequestReceived, + P2pChannelsSignalingExchangeRequestSend, + P2pChannelsSignalingExchangeEffectfulAnswerEncryptAndSend, + P2pChannelsSignalingExchangeEffectfulInit, + P2pChannelsSignalingExchangeEffectfulMessageSend, + P2pChannelsSignalingExchangeEffectfulOfferDecrypt, P2pChannelsSnarkInit, P2pChannelsSnarkLibp2pBroadcast, P2pChannelsSnarkLibp2pReceived, @@ -304,6 +344,7 @@ pub enum ActionKind { P2pDisconnectionFinish, P2pDisconnectionInit, P2pDisconnectionEffectfulInit, + P2pEffectfulInitialize, P2pIdentifyNewRequest, P2pIdentifyUpdatePeerInformation, P2pInitializeInitialize, @@ -313,9 +354,13 @@ pub enum ActionKind { P2pNetworkIdentifyStreamPrune, P2pNetworkIdentifyStreamRemoteClose, P2pNetworkIdentifyStreamEffectfulSendIdentify, + P2pNetworkKadBootstrapAppendRequest, P2pNetworkKadBootstrapCreateRequests, + P2pNetworkKadBootstrapFinalizeRequests, P2pNetworkKadBootstrapRequestDone, P2pNetworkKadBootstrapRequestError, + P2pNetworkKadEffectfulDiscovered, + P2pNetworkKadEffectfulMakeRequest, P2pNetworkKadRequestError, P2pNetworkKadRequestMuxReady, P2pNetworkKadRequestNew, @@ -380,6 +425,7 @@ pub enum ActionKind { P2pNetworkSchedulerDisconnect, P2pNetworkSchedulerDisconnected, P2pNetworkSchedulerError, + P2pNetworkSchedulerIncomingConnectionIsReady, P2pNetworkSchedulerIncomingDataDidReceive, P2pNetworkSchedulerIncomingDataIsReady, P2pNetworkSchedulerIncomingDidAccept, @@ -611,7 +657,7 @@ pub enum ActionKind { } impl ActionKind { - pub const COUNT: u16 = 504; + pub const COUNT: u16 = 545; } impl std::fmt::Display for ActionKind { @@ -626,6 +672,7 @@ impl ActionKindGet for Action { Self::CheckTimeouts(a) => a.kind(), Self::EventSource(a) => a.kind(), Self::P2p(a) => a.kind(), + Self::P2pEffectful(a) => a.kind(), Self::P2pCallbacks(a) => a.kind(), Self::Ledger(a) => a.kind(), Self::Snark(a) => a.kind(), @@ -665,18 +712,27 @@ impl ActionKindGet for P2pAction { match self { Self::Initialization(a) => a.kind(), Self::Connection(a) => a.kind(), - Self::ConnectionEffectful(a) => a.kind(), Self::Disconnection(a) => a.kind(), - Self::DisconnectionEffectful(a) => a.kind(), Self::Identify(a) => a.kind(), Self::Channels(a) => a.kind(), - Self::ChannelsEffectful(a) => a.kind(), Self::Peer(a) => a.kind(), Self::Network(a) => a.kind(), } } } +impl ActionKindGet for P2pEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Channels(a) => a.kind(), + Self::Connection(a) => a.kind(), + Self::Disconnection(a) => a.kind(), + Self::Network(a) => a.kind(), + Self::Initialize => ActionKind::P2pEffectfulInitialize, + } + } +} + impl ActionKindGet for P2pCallbacksAction { fn kind(&self) -> ActionKind { match self { @@ -990,15 +1046,6 @@ impl ActionKindGet for P2pConnectionAction { } } -impl ActionKindGet for P2pConnectionEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Outgoing(a) => a.kind(), - Self::Incoming(a) => a.kind(), - } - } -} - impl ActionKindGet for P2pDisconnectionAction { fn kind(&self) -> ActionKind { match self { @@ -1008,14 +1055,6 @@ impl ActionKindGet for P2pDisconnectionAction { } } -impl ActionKindGet for P2pDisconnectionEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Init { .. } => ActionKind::P2pDisconnectionEffectfulInit, - } - } -} - impl ActionKindGet for P2pIdentifyAction { fn kind(&self) -> ActionKind { match self { @@ -1029,6 +1068,8 @@ impl ActionKindGet for P2pChannelsAction { fn kind(&self) -> ActionKind { match self { Self::MessageReceived(a) => a.kind(), + Self::SignalingDiscovery(a) => a.kind(), + Self::SignalingExchange(a) => a.kind(), Self::BestTip(a) => a.kind(), Self::Transaction(a) => a.kind(), Self::Snark(a) => a.kind(), @@ -1039,9 +1080,38 @@ impl ActionKindGet for P2pChannelsAction { } } +impl ActionKindGet for P2pPeerAction { + fn kind(&self) -> ActionKind { + match self { + Self::Discovered { .. } => ActionKind::P2pPeerDiscovered, + Self::Ready { .. } => ActionKind::P2pPeerReady, + Self::BestTipUpdate { .. } => ActionKind::P2pPeerBestTipUpdate, + Self::Remove { .. } => ActionKind::P2pPeerRemove, + } + } +} + +impl ActionKindGet for P2pNetworkAction { + fn kind(&self) -> ActionKind { + match self { + Self::Scheduler(a) => a.kind(), + Self::Pnet(a) => a.kind(), + Self::Select(a) => a.kind(), + Self::Noise(a) => a.kind(), + Self::Yamux(a) => a.kind(), + Self::Identify(a) => a.kind(), + Self::Kad(a) => a.kind(), + Self::Pubsub(a) => a.kind(), + Self::Rpc(a) => a.kind(), + } + } +} + impl ActionKindGet for P2pChannelsEffectfulAction { fn kind(&self) -> ActionKind { match self { + Self::SignalingDiscovery(a) => a.kind(), + Self::SignalingExchange(a) => a.kind(), Self::BestTip(a) => a.kind(), Self::Rpc(a) => a.kind(), Self::Snark(a) => a.kind(), @@ -1052,32 +1122,31 @@ impl ActionKindGet for P2pChannelsEffectfulAction { } } -impl ActionKindGet for P2pPeerAction { +impl ActionKindGet for P2pConnectionEffectfulAction { fn kind(&self) -> ActionKind { match self { - Self::Discovered { .. } => ActionKind::P2pPeerDiscovered, - Self::Ready { .. } => ActionKind::P2pPeerReady, - Self::BestTipUpdate { .. } => ActionKind::P2pPeerBestTipUpdate, - Self::Remove { .. } => ActionKind::P2pPeerRemove, + Self::Outgoing(a) => a.kind(), + Self::Incoming(a) => a.kind(), } } } -impl ActionKindGet for P2pNetworkAction { +impl ActionKindGet for P2pDisconnectionEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pDisconnectionEffectfulInit, + } + } +} + +impl ActionKindGet for P2pNetworkEffectfulAction { fn kind(&self) -> ActionKind { match self { Self::Scheduler(a) => a.kind(), - Self::SchedulerEffectful(a) => a.kind(), Self::Pnet(a) => a.kind(), - Self::PnetEffectful(a) => a.kind(), - Self::Select(a) => a.kind(), - Self::Noise(a) => a.kind(), - Self::Yamux(a) => a.kind(), + Self::Pubsub(a) => a.kind(), Self::Identify(a) => a.kind(), Self::Kad(a) => a.kind(), - Self::Pubsub(a) => a.kind(), - Self::PubsubEffectful(a) => a.kind(), - Self::Rpc(a) => a.kind(), } } } @@ -1368,29 +1437,72 @@ impl ActionKindGet for P2pConnectionIncomingAction { } } -impl ActionKindGet for P2pConnectionOutgoingEffectfulAction { +impl ActionKindGet for P2pChannelsMessageReceivedAction { fn kind(&self) -> ActionKind { - match self { - Self::RandomInit => ActionKind::P2pConnectionOutgoingEffectfulRandomInit, - Self::Init { .. } => ActionKind::P2pConnectionOutgoingEffectfulInit, - Self::OfferSend { .. } => ActionKind::P2pConnectionOutgoingEffectfulOfferSend, - Self::AnswerSet { .. } => ActionKind::P2pConnectionOutgoingEffectfulAnswerSet, - } + ActionKind::P2pChannelsMessageReceived } } -impl ActionKindGet for P2pConnectionIncomingEffectfulAction { +impl ActionKindGet for P2pChannelsSignalingDiscoveryAction { fn kind(&self) -> ActionKind { match self { - Self::Init { .. } => ActionKind::P2pConnectionIncomingEffectfulInit, - Self::AnswerSend { .. } => ActionKind::P2pConnectionIncomingEffectfulAnswerSend, + Self::Init { .. } => ActionKind::P2pChannelsSignalingDiscoveryInit, + Self::Pending { .. } => ActionKind::P2pChannelsSignalingDiscoveryPending, + Self::Ready { .. } => ActionKind::P2pChannelsSignalingDiscoveryReady, + Self::RequestSend { .. } => ActionKind::P2pChannelsSignalingDiscoveryRequestSend, + Self::DiscoveryRequestReceived { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryDiscoveryRequestReceived + } + Self::DiscoveredSend { .. } => ActionKind::P2pChannelsSignalingDiscoveryDiscoveredSend, + Self::DiscoveredRejectReceived { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryDiscoveredRejectReceived + } + Self::DiscoveredAcceptReceived { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryDiscoveredAcceptReceived + } + Self::AnswerSend { .. } => ActionKind::P2pChannelsSignalingDiscoveryAnswerSend, + Self::RequestReceived { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryRequestReceived + } + Self::DiscoveryRequestSend { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryDiscoveryRequestSend + } + Self::DiscoveredReceived { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryDiscoveredReceived + } + Self::DiscoveredReject { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryDiscoveredReject + } + Self::DiscoveredAccept { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryDiscoveredAccept + } + Self::AnswerReceived { .. } => ActionKind::P2pChannelsSignalingDiscoveryAnswerReceived, + Self::AnswerDecrypted { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryAnswerDecrypted + } } } } -impl ActionKindGet for P2pChannelsMessageReceivedAction { +impl ActionKindGet for P2pChannelsSignalingExchangeAction { fn kind(&self) -> ActionKind { - ActionKind::P2pChannelsMessageReceived + match self { + Self::Init { .. } => ActionKind::P2pChannelsSignalingExchangeInit, + Self::Pending { .. } => ActionKind::P2pChannelsSignalingExchangePending, + Self::Ready { .. } => ActionKind::P2pChannelsSignalingExchangeReady, + Self::RequestSend { .. } => ActionKind::P2pChannelsSignalingExchangeRequestSend, + Self::OfferReceived { .. } => ActionKind::P2pChannelsSignalingExchangeOfferReceived, + Self::OfferDecryptError { .. } => { + ActionKind::P2pChannelsSignalingExchangeOfferDecryptError + } + Self::OfferDecryptSuccess { .. } => { + ActionKind::P2pChannelsSignalingExchangeOfferDecryptSuccess + } + Self::AnswerSend { .. } => ActionKind::P2pChannelsSignalingExchangeAnswerSend, + Self::RequestReceived { .. } => ActionKind::P2pChannelsSignalingExchangeRequestReceived, + Self::OfferSend { .. } => ActionKind::P2pChannelsSignalingExchangeOfferSend, + Self::AnswerReceived { .. } => ActionKind::P2pChannelsSignalingExchangeAnswerReceived, + } } } @@ -1504,78 +1616,6 @@ impl ActionKindGet for P2pChannelsStreamingRpcAction { } } -impl ActionKindGet for P2pChannelsBestTipEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Init { .. } => ActionKind::P2pChannelsBestTipEffectfulInit, - Self::RequestSend { .. } => ActionKind::P2pChannelsBestTipEffectfulRequestSend, - Self::ResponseSend { .. } => ActionKind::P2pChannelsBestTipEffectfulResponseSend, - } - } -} - -impl ActionKindGet for P2pChannelsRpcEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Init { .. } => ActionKind::P2pChannelsRpcEffectfulInit, - Self::RequestSend { .. } => ActionKind::P2pChannelsRpcEffectfulRequestSend, - Self::ResponseSend { .. } => ActionKind::P2pChannelsRpcEffectfulResponseSend, - } - } -} - -impl ActionKindGet for P2pChannelsSnarkEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Init { .. } => ActionKind::P2pChannelsSnarkEffectfulInit, - Self::RequestSend { .. } => ActionKind::P2pChannelsSnarkEffectfulRequestSend, - Self::ResponseSend { .. } => ActionKind::P2pChannelsSnarkEffectfulResponseSend, - } - } -} - -impl ActionKindGet for P2pChannelsSnarkJobCommitmentEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Init { .. } => ActionKind::P2pChannelsSnarkJobCommitmentEffectfulInit, - Self::RequestSend { .. } => { - ActionKind::P2pChannelsSnarkJobCommitmentEffectfulRequestSend - } - Self::ResponseSend { .. } => { - ActionKind::P2pChannelsSnarkJobCommitmentEffectfulResponseSend - } - } - } -} - -impl ActionKindGet for P2pChannelsStreamingRpcEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Init { .. } => ActionKind::P2pChannelsStreamingRpcEffectfulInit, - Self::RequestSend { .. } => ActionKind::P2pChannelsStreamingRpcEffectfulRequestSend, - Self::ResponseNextPartGet { .. } => { - ActionKind::P2pChannelsStreamingRpcEffectfulResponseNextPartGet - } - Self::ResponseSendInit { .. } => { - ActionKind::P2pChannelsStreamingRpcEffectfulResponseSendInit - } - Self::ResponsePartSend { .. } => { - ActionKind::P2pChannelsStreamingRpcEffectfulResponsePartSend - } - } - } -} - -impl ActionKindGet for P2pChannelsTransactionEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Init { .. } => ActionKind::P2pChannelsTransactionEffectfulInit, - Self::RequestSend { .. } => ActionKind::P2pChannelsTransactionEffectfulRequestSend, - Self::ResponseSend { .. } => ActionKind::P2pChannelsTransactionEffectfulResponseSend, - } - } -} - impl ActionKindGet for P2pNetworkSchedulerAction { fn kind(&self) -> ActionKind { match self { @@ -1583,6 +1623,9 @@ impl ActionKindGet for P2pNetworkSchedulerAction { Self::InterfaceExpired { .. } => ActionKind::P2pNetworkSchedulerInterfaceExpired, Self::ListenerReady { .. } => ActionKind::P2pNetworkSchedulerListenerReady, Self::ListenerError { .. } => ActionKind::P2pNetworkSchedulerListenerError, + Self::IncomingConnectionIsReady { .. } => { + ActionKind::P2pNetworkSchedulerIncomingConnectionIsReady + } Self::IncomingDidAccept { .. } => ActionKind::P2pNetworkSchedulerIncomingDidAccept, Self::IncomingDataIsReady { .. } => ActionKind::P2pNetworkSchedulerIncomingDataIsReady, Self::OutgoingConnect { .. } => ActionKind::P2pNetworkSchedulerOutgoingConnect, @@ -1603,33 +1646,6 @@ impl ActionKindGet for P2pNetworkSchedulerAction { } } -impl ActionKindGet for P2pNetworkSchedulerEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::InterfaceDetected { .. } => { - ActionKind::P2pNetworkSchedulerEffectfulInterfaceDetected - } - Self::IncomingConnectionIsReady { .. } => { - ActionKind::P2pNetworkSchedulerEffectfulIncomingConnectionIsReady - } - Self::IncomingDidAccept { .. } => { - ActionKind::P2pNetworkSchedulerEffectfulIncomingDidAccept - } - Self::OutgoingConnect { .. } => ActionKind::P2pNetworkSchedulerEffectfulOutgoingConnect, - Self::OutgoingDidConnect { .. } => { - ActionKind::P2pNetworkSchedulerEffectfulOutgoingDidConnect - } - Self::IncomingDataIsReady { .. } => { - ActionKind::P2pNetworkSchedulerEffectfulIncomingDataIsReady - } - Self::NoiseSelectDone { .. } => ActionKind::P2pNetworkSchedulerEffectfulNoiseSelectDone, - Self::SelectError { .. } => ActionKind::P2pNetworkSchedulerEffectfulSelectError, - Self::Disconnect { .. } => ActionKind::P2pNetworkSchedulerEffectfulDisconnect, - Self::Error { .. } => ActionKind::P2pNetworkSchedulerEffectfulError, - } - } -} - impl ActionKindGet for P2pNetworkPnetAction { fn kind(&self) -> ActionKind { match self { @@ -1641,15 +1657,6 @@ impl ActionKindGet for P2pNetworkPnetAction { } } -impl ActionKindGet for P2pNetworkPnetEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::OutgoingData { .. } => ActionKind::P2pNetworkPnetEffectfulOutgoingData, - Self::SetupNonce { .. } => ActionKind::P2pNetworkPnetEffectfulSetupNonce, - } - } -} - impl ActionKindGet for P2pNetworkSelectAction { fn kind(&self) -> ActionKind { match self { @@ -1702,7 +1709,6 @@ impl ActionKindGet for P2pNetworkIdentifyAction { fn kind(&self) -> ActionKind { match self { Self::Stream(a) => a.kind(), - Self::StreamEffectful(a) => a.kind(), } } } @@ -1737,15 +1743,6 @@ impl ActionKindGet for P2pNetworkPubsubAction { } } -impl ActionKindGet for P2pNetworkPubsubEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::Sign { .. } => ActionKind::P2pNetworkPubsubEffectfulSign, - Self::IncomingData { .. } => ActionKind::P2pNetworkPubsubEffectfulIncomingData, - } - } -} - impl ActionKindGet for P2pNetworkRpcAction { fn kind(&self) -> ActionKind { match self { @@ -1761,6 +1758,194 @@ impl ActionKindGet for P2pNetworkRpcAction { } } +impl ActionKindGet for P2pChannelsSignalingDiscoveryEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pChannelsSignalingDiscoveryEffectfulInit, + Self::MessageSend { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryEffectfulMessageSend + } + Self::OfferEncryptAndSend { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryEffectfulOfferEncryptAndSend + } + Self::AnswerDecrypt { .. } => { + ActionKind::P2pChannelsSignalingDiscoveryEffectfulAnswerDecrypt + } + } + } +} + +impl ActionKindGet for P2pChannelsSignalingExchangeEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pChannelsSignalingExchangeEffectfulInit, + Self::MessageSend { .. } => { + ActionKind::P2pChannelsSignalingExchangeEffectfulMessageSend + } + Self::OfferDecrypt { .. } => { + ActionKind::P2pChannelsSignalingExchangeEffectfulOfferDecrypt + } + Self::AnswerEncryptAndSend { .. } => { + ActionKind::P2pChannelsSignalingExchangeEffectfulAnswerEncryptAndSend + } + } + } +} + +impl ActionKindGet for P2pChannelsBestTipEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pChannelsBestTipEffectfulInit, + Self::RequestSend { .. } => ActionKind::P2pChannelsBestTipEffectfulRequestSend, + Self::ResponseSend { .. } => ActionKind::P2pChannelsBestTipEffectfulResponseSend, + } + } +} + +impl ActionKindGet for P2pChannelsRpcEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pChannelsRpcEffectfulInit, + Self::RequestSend { .. } => ActionKind::P2pChannelsRpcEffectfulRequestSend, + Self::ResponseSend { .. } => ActionKind::P2pChannelsRpcEffectfulResponseSend, + } + } +} + +impl ActionKindGet for P2pChannelsSnarkEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pChannelsSnarkEffectfulInit, + Self::RequestSend { .. } => ActionKind::P2pChannelsSnarkEffectfulRequestSend, + Self::ResponseSend { .. } => ActionKind::P2pChannelsSnarkEffectfulResponseSend, + } + } +} + +impl ActionKindGet for P2pChannelsSnarkJobCommitmentEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pChannelsSnarkJobCommitmentEffectfulInit, + Self::RequestSend { .. } => { + ActionKind::P2pChannelsSnarkJobCommitmentEffectfulRequestSend + } + Self::ResponseSend { .. } => { + ActionKind::P2pChannelsSnarkJobCommitmentEffectfulResponseSend + } + } + } +} + +impl ActionKindGet for P2pChannelsStreamingRpcEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pChannelsStreamingRpcEffectfulInit, + Self::RequestSend { .. } => ActionKind::P2pChannelsStreamingRpcEffectfulRequestSend, + Self::ResponseNextPartGet { .. } => { + ActionKind::P2pChannelsStreamingRpcEffectfulResponseNextPartGet + } + Self::ResponseSendInit { .. } => { + ActionKind::P2pChannelsStreamingRpcEffectfulResponseSendInit + } + Self::ResponsePartSend { .. } => { + ActionKind::P2pChannelsStreamingRpcEffectfulResponsePartSend + } + } + } +} + +impl ActionKindGet for P2pChannelsTransactionEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pChannelsTransactionEffectfulInit, + Self::RequestSend { .. } => ActionKind::P2pChannelsTransactionEffectfulRequestSend, + Self::ResponseSend { .. } => ActionKind::P2pChannelsTransactionEffectfulResponseSend, + } + } +} + +impl ActionKindGet for P2pConnectionOutgoingEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::RandomInit => ActionKind::P2pConnectionOutgoingEffectfulRandomInit, + Self::Init { .. } => ActionKind::P2pConnectionOutgoingEffectfulInit, + Self::OfferSend { .. } => ActionKind::P2pConnectionOutgoingEffectfulOfferSend, + Self::AnswerSet { .. } => ActionKind::P2pConnectionOutgoingEffectfulAnswerSet, + } + } +} + +impl ActionKindGet for P2pConnectionIncomingEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Init { .. } => ActionKind::P2pConnectionIncomingEffectfulInit, + Self::AnswerSend { .. } => ActionKind::P2pConnectionIncomingEffectfulAnswerSend, + } + } +} + +impl ActionKindGet for P2pNetworkSchedulerEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::InterfaceDetected { .. } => { + ActionKind::P2pNetworkSchedulerEffectfulInterfaceDetected + } + Self::IncomingConnectionIsReady { .. } => { + ActionKind::P2pNetworkSchedulerEffectfulIncomingConnectionIsReady + } + Self::IncomingDidAccept { .. } => { + ActionKind::P2pNetworkSchedulerEffectfulIncomingDidAccept + } + Self::OutgoingConnect { .. } => ActionKind::P2pNetworkSchedulerEffectfulOutgoingConnect, + Self::OutgoingDidConnect { .. } => { + ActionKind::P2pNetworkSchedulerEffectfulOutgoingDidConnect + } + Self::IncomingDataIsReady { .. } => { + ActionKind::P2pNetworkSchedulerEffectfulIncomingDataIsReady + } + Self::NoiseSelectDone { .. } => ActionKind::P2pNetworkSchedulerEffectfulNoiseSelectDone, + Self::SelectError { .. } => ActionKind::P2pNetworkSchedulerEffectfulSelectError, + Self::Disconnect { .. } => ActionKind::P2pNetworkSchedulerEffectfulDisconnect, + Self::Error { .. } => ActionKind::P2pNetworkSchedulerEffectfulError, + } + } +} + +impl ActionKindGet for P2pNetworkPnetEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::OutgoingData { .. } => ActionKind::P2pNetworkPnetEffectfulOutgoingData, + Self::SetupNonce { .. } => ActionKind::P2pNetworkPnetEffectfulSetupNonce, + } + } +} + +impl ActionKindGet for P2pNetworkPubsubEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Sign { .. } => ActionKind::P2pNetworkPubsubEffectfulSign, + Self::IncomingData { .. } => ActionKind::P2pNetworkPubsubEffectfulIncomingData, + } + } +} + +impl ActionKindGet for P2pNetworkIdentifyEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Stream(a) => a.kind(), + } + } +} + +impl ActionKindGet for P2pNetworkKadEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::Discovered { .. } => ActionKind::P2pNetworkKadEffectfulDiscovered, + Self::MakeRequest { .. } => ActionKind::P2pNetworkKadEffectfulMakeRequest, + } + } +} + impl ActionKindGet for TransitionFrontierSyncLedgerAction { fn kind(&self) -> ActionKind { match self { @@ -1784,14 +1969,6 @@ impl ActionKindGet for P2pNetworkIdentifyStreamAction { } } -impl ActionKindGet for P2pNetworkIdentifyStreamEffectfulAction { - fn kind(&self) -> ActionKind { - match self { - Self::SendIdentify { .. } => ActionKind::P2pNetworkIdentifyStreamEffectfulSendIdentify, - } - } -} - impl ActionKindGet for P2pNetworkKademliaAction { fn kind(&self) -> ActionKind { match self { @@ -1812,6 +1989,8 @@ impl ActionKindGet for P2pNetworkKadBootstrapAction { fn kind(&self) -> ActionKind { match self { Self::CreateRequests => ActionKind::P2pNetworkKadBootstrapCreateRequests, + Self::AppendRequest { .. } => ActionKind::P2pNetworkKadBootstrapAppendRequest, + Self::FinalizeRequests => ActionKind::P2pNetworkKadBootstrapFinalizeRequests, Self::RequestDone { .. } => ActionKind::P2pNetworkKadBootstrapRequestDone, Self::RequestError { .. } => ActionKind::P2pNetworkKadBootstrapRequestError, } @@ -1851,6 +2030,14 @@ impl ActionKindGet for P2pNetworkKademliaStreamAction { } } +impl ActionKindGet for P2pNetworkIdentifyStreamEffectfulAction { + fn kind(&self) -> ActionKind { + match self { + Self::SendIdentify { .. } => ActionKind::P2pNetworkIdentifyStreamEffectfulSendIdentify, + } + } +} + impl ActionKindGet for TransitionFrontierSyncLedgerSnarkedAction { fn kind(&self) -> ActionKind { match self { diff --git a/node/src/effects.rs b/node/src/effects.rs index bc6aa6a205..895f7e3339 100644 --- a/node/src/effects.rs +++ b/node/src/effects.rs @@ -70,7 +70,7 @@ pub fn effects(store: &mut Store, action: ActionWithMeta) { Action::TransitionFrontier(action) => { transition_frontier_effects(store, meta.with_action(action)); } - Action::P2p(action) => { + Action::P2pEffectful(action) => { node_p2p_effects(store, meta.with_action(action)); } Action::Ledger(action) => { @@ -95,6 +95,9 @@ pub fn effects(store: &mut Store, action: ActionWithMeta) { Action::P2pCallbacks(_) => { // Handled by reducer } + Action::P2p(_) => { + // Handled by reducer + } } } diff --git a/node/src/event_source/event_source_effects.rs b/node/src/event_source/event_source_effects.rs index b7f129c627..8f3c544e1b 100644 --- a/node/src/event_source/event_source_effects.rs +++ b/node/src/event_source/event_source_effects.rs @@ -1,7 +1,8 @@ +use p2p::channels::signaling::discovery::P2pChannelsSignalingDiscoveryAction; +use p2p::channels::signaling::exchange::P2pChannelsSignalingExchangeAction; use p2p::channels::snark::P2pChannelsSnarkAction; use p2p::channels::streaming_rpc::P2pChannelsStreamingRpcAction; use p2p::channels::transaction::P2pChannelsTransactionAction; -use p2p::P2pNetworkSchedulerEffectfulAction; use snark::user_command_verify::{SnarkUserCommandVerifyAction, SnarkUserCommandVerifyError}; use crate::action::CheckTimeoutsAction; @@ -74,11 +75,9 @@ pub fn event_source_effects(store: &mut Store, action: EventSourc .dispatch(P2pNetworkSchedulerAction::ListenerError { listener, error }); } MioEvent::IncomingConnectionIsReady { listener } => { - store.dispatch( - P2pNetworkSchedulerEffectfulAction::IncomingConnectionIsReady { - listener, - }, - ); + store.dispatch(P2pNetworkSchedulerAction::IncomingConnectionIsReady { + listener, + }); } MioEvent::IncomingConnectionDidAccept(addr, result) => { store.dispatch(P2pNetworkSchedulerAction::IncomingDidAccept { @@ -161,6 +160,12 @@ pub fn event_source_effects(store: &mut Store, action: EventSourc error: P2pConnectionErrorResponse::Rejected(reason), }); } + P2pConnectionResponse::SignalDecryptionFailed => { + store.dispatch(P2pConnectionOutgoingAction::AnswerRecvError { + peer_id, + error: P2pConnectionErrorResponse::SignalDecryptionFailed, + }); + } P2pConnectionResponse::InternalError => { store.dispatch(P2pConnectionOutgoingAction::AnswerRecvError { peer_id, @@ -197,8 +202,17 @@ pub fn event_source_effects(store: &mut Store, action: EventSourc openmina_core::log::warn!(meta.time(); kind = "P2pChannelEvent::Opened", peer_id = peer_id.to_string(), error = err); // TODO(binier): dispatch error action. } - // TODO(binier): maybe dispatch success and then ready. Ok(_) => match chan_id { + ChannelId::SignalingDiscovery => { + store.dispatch(P2pChannelsSignalingDiscoveryAction::Ready { + peer_id, + }); + } + ChannelId::SignalingExchange => { + store.dispatch(P2pChannelsSignalingExchangeAction::Ready { + peer_id, + }); + } ChannelId::BestTipPropagation => { store.dispatch(P2pChannelsBestTipAction::Ready { peer_id }); } diff --git a/node/src/logger/logger_effects.rs b/node/src/logger/logger_effects.rs index 6a4623b79b..7f7a7d939a 100644 --- a/node/src/logger/logger_effects.rs +++ b/node/src/logger/logger_effects.rs @@ -65,15 +65,12 @@ pub fn logger_effects(store: &Store, action: ActionWithMetaRef<'_ P2pConnectionAction::Outgoing(action) => action.action_event(&context), P2pConnectionAction::Incoming(action) => action.action_event(&context), }, - P2pAction::ConnectionEffectful(action) => match action { - P2pConnectionEffectfulAction::Outgoing(action) => action.action_event(&context), - P2pConnectionEffectfulAction::Incoming(action) => action.action_event(&context), - }, P2pAction::Disconnection(action) => action.action_event(&context), - P2pAction::DisconnectionEffectful(action) => action.action_event(&context), P2pAction::Identify(action) => action.action_event(&context), P2pAction::Channels(action) => match action { P2pChannelsAction::MessageReceived(action) => action.action_event(&context), + P2pChannelsAction::SignalingDiscovery(action) => action.action_event(&context), + P2pChannelsAction::SignalingExchange(action) => action.action_event(&context), P2pChannelsAction::BestTip(action) => action.action_event(&context), P2pChannelsAction::Transaction(action) => action.action_event(&context), P2pChannelsAction::Snark(action) => action.action_event(&context), @@ -81,16 +78,6 @@ pub fn logger_effects(store: &Store, action: ActionWithMetaRef<'_ P2pChannelsAction::Rpc(action) => action.action_event(&context), P2pChannelsAction::StreamingRpc(action) => action.action_event(&context), }, - P2pAction::ChannelsEffectful(action) => match action { - P2pChannelsEffectfulAction::BestTip(action) => action.action_event(&context), - P2pChannelsEffectfulAction::Rpc(action) => action.action_event(&context), - P2pChannelsEffectfulAction::StreamingRpc(action) => action.action_event(&context), - P2pChannelsEffectfulAction::SnarkJobCommitment(action) => { - action.action_event(&context) - } - P2pChannelsEffectfulAction::Snark(action) => action.action_event(&context), - P2pChannelsEffectfulAction::Transaction(action) => action.action_event(&context), - }, P2pAction::Peer(action) => action.action_event(&context), P2pAction::Network(action) => match action { P2pNetworkAction::Scheduler(action) => match action { @@ -108,19 +95,41 @@ pub fn logger_effects(store: &Store, action: ActionWithMetaRef<'_ } action => action.action_event(&context), }, - P2pNetworkAction::SchedulerEffectful(action) => action.action_event(&context), P2pNetworkAction::Pnet(action) => action.action_event(&context), - P2pNetworkAction::PnetEffectful(action) => action.action_event(&context), P2pNetworkAction::Select(action) => action.action_event(&context), P2pNetworkAction::Noise(action) => action.action_event(&context), P2pNetworkAction::Yamux(action) => action.action_event(&context), P2pNetworkAction::Rpc(action) => action.action_event(&context), P2pNetworkAction::Kad(action) => action.action_event(&context), P2pNetworkAction::Pubsub(action) => action.action_event(&context), - P2pNetworkAction::PubsubEffectful(action) => action.action_event(&context), P2pNetworkAction::Identify(action) => action.action_event(&context), }, }, + Action::P2pEffectful(action) => match action { + p2p::P2pEffectfulAction::Channels(action) => match action { + P2pChannelsEffectfulAction::SignalingDiscovery(action) => { + action.action_event(&context) + } + P2pChannelsEffectfulAction::SignalingExchange(action) => { + action.action_event(&context) + } + P2pChannelsEffectfulAction::BestTip(action) => action.action_event(&context), + P2pChannelsEffectfulAction::Rpc(action) => action.action_event(&context), + P2pChannelsEffectfulAction::StreamingRpc(action) => action.action_event(&context), + P2pChannelsEffectfulAction::SnarkJobCommitment(action) => { + action.action_event(&context) + } + P2pChannelsEffectfulAction::Snark(action) => action.action_event(&context), + P2pChannelsEffectfulAction::Transaction(action) => action.action_event(&context), + }, + p2p::P2pEffectfulAction::Connection(action) => match action { + P2pConnectionEffectfulAction::Outgoing(action) => action.action_event(&context), + P2pConnectionEffectfulAction::Incoming(action) => action.action_event(&context), + }, + p2p::P2pEffectfulAction::Disconnection(action) => action.action_event(&context), + p2p::P2pEffectfulAction::Network(action) => action.action_event(&context), + p2p::P2pEffectfulAction::Initialize => {} + }, Action::ExternalSnarkWorker(action) => action.action_event(&context), Action::SnarkPool(action) => action.action_event(&context), Action::Snark(SnarkAction::WorkVerify(a)) => a.action_event(&context), diff --git a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs index fcabd0d449..2b24054fba 100644 --- a/node/src/p2p/callbacks/p2p_callbacks_reducer.rs +++ b/node/src/p2p/callbacks/p2p_callbacks_reducer.rs @@ -45,12 +45,16 @@ impl crate::State { P2pCallbacksAction::P2pChannelsRpcReady { peer_id } => { let peer_id = *peer_id; - dispatcher.push(P2pChannelsRpcAction::RequestSend { - peer_id, - id: 0, - request: Box::new(P2pRpcRequest::BestTipWithProof), - on_init: None, - }); + if state.p2p.get_peer(&peer_id).map_or(false, |p| p.is_libp2p) { + // for webrtc peers, we don't need to send this rpc, as we + // will receive current best tip in best tip channel anyways. + dispatcher.push(P2pChannelsRpcAction::RequestSend { + peer_id, + id: 0, + request: Box::new(P2pRpcRequest::BestTipWithProof), + on_init: None, + }); + } dispatcher.push(TransitionFrontierSyncLedgerSnarkedAction::PeersQuery); dispatcher.push(TransitionFrontierSyncLedgerStagedAction::PartsPeerFetchInit); @@ -283,7 +287,9 @@ impl crate::State { let response = None.or_else(|| { let best_tip = best_chain.last()?; let mut chain_iter = best_chain.iter(); - let root_block = chain_iter.next()?; + let root_block = chain_iter.next(); + // when our best tip is genesis block. + let root_block = root_block.unwrap_or(best_tip); // TODO(binier): cache body hashes let Ok(body_hashes) = chain_iter .map(|b| b.header().protocol_state.body.try_hash()) diff --git a/node/src/p2p/channels/mod.rs b/node/src/p2p/channels/mod.rs index 471c7e6640..74cb3c3e70 100644 --- a/node/src/p2p/channels/mod.rs +++ b/node/src/p2p/channels/mod.rs @@ -2,6 +2,7 @@ pub use ::p2p::channels::*; pub mod best_tip; pub mod rpc; +pub mod signaling; pub mod snark; pub mod snark_job_commitment; pub mod streaming_rpc; diff --git a/node/src/p2p/channels/signaling/mod.rs b/node/src/p2p/channels/signaling/mod.rs new file mode 100644 index 0000000000..77951eb36c --- /dev/null +++ b/node/src/p2p/channels/signaling/mod.rs @@ -0,0 +1,4 @@ +pub use ::p2p::channels::signaling::*; + +mod p2p_channels_signaling_discovery_actions; +mod p2p_channels_signaling_exchange_actions; diff --git a/node/src/p2p/channels/signaling/p2p_channels_signaling_discovery_actions.rs b/node/src/p2p/channels/signaling/p2p_channels_signaling_discovery_actions.rs new file mode 100644 index 0000000000..2540e8e534 --- /dev/null +++ b/node/src/p2p/channels/signaling/p2p_channels_signaling_discovery_actions.rs @@ -0,0 +1,7 @@ +use super::discovery::*; + +impl redux::EnablingCondition for P2pChannelsSignalingDiscoveryAction { + fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool { + state.p2p.is_enabled(self, time) + } +} diff --git a/node/src/p2p/channels/signaling/p2p_channels_signaling_exchange_actions.rs b/node/src/p2p/channels/signaling/p2p_channels_signaling_exchange_actions.rs new file mode 100644 index 0000000000..0861667ea3 --- /dev/null +++ b/node/src/p2p/channels/signaling/p2p_channels_signaling_exchange_actions.rs @@ -0,0 +1,7 @@ +use super::exchange::*; + +impl redux::EnablingCondition for P2pChannelsSignalingExchangeAction { + fn is_enabled(&self, state: &crate::State, time: redux::Timestamp) -> bool { + state.p2p.is_enabled(self, time) + } +} diff --git a/node/src/p2p/mod.rs b/node/src/p2p/mod.rs index aac743bfb1..06d72936dc 100644 --- a/node/src/p2p/mod.rs +++ b/node/src/p2p/mod.rs @@ -1,10 +1,18 @@ pub use ::p2p::*; -use p2p::channels::{ - best_tip_effectful::P2pChannelsBestTipEffectfulAction, - rpc_effectful::P2pChannelsRpcEffectfulAction, snark_effectful::P2pChannelsSnarkEffectfulAction, - snark_job_commitment_effectful::P2pChannelsSnarkJobCommitmentEffectfulAction, - streaming_rpc_effectful::P2pChannelsStreamingRpcEffectfulAction, - transaction_effectful::P2pChannelsTransactionEffectfulAction, +use p2p::{ + channels::{ + best_tip_effectful::P2pChannelsBestTipEffectfulAction, + rpc_effectful::P2pChannelsRpcEffectfulAction, + signaling::{ + discovery_effectful::P2pChannelsSignalingDiscoveryEffectfulAction, + exchange_effectful::P2pChannelsSignalingExchangeEffectfulAction, + }, + snark_effectful::P2pChannelsSnarkEffectfulAction, + snark_job_commitment_effectful::P2pChannelsSnarkJobCommitmentEffectfulAction, + streaming_rpc_effectful::P2pChannelsStreamingRpcEffectfulAction, + transaction_effectful::P2pChannelsTransactionEffectfulAction, + }, + network::identify::stream_effectful::P2pNetworkIdentifyStreamEffectfulAction, }; pub mod channels; @@ -81,6 +89,13 @@ macro_rules! impl_into_global_action { } } }; + (effectful $a:ty) => { + impl From<$a> for crate::Action { + fn from(value: $a) -> Self { + Self::P2pEffectful(value.into()) + } + } + }; } impl_into_global_action!(P2pInitializeAction); @@ -94,9 +109,10 @@ impl_into_global_action!(disconnection::P2pDisconnectionAction); impl_into_global_action!(network::P2pNetworkSchedulerAction); impl_into_global_action!(network::kad::P2pNetworkKademliaAction); impl_into_global_action!(network::pubsub::P2pNetworkPubsubAction); -impl_into_global_action!(network::pubsub::P2pNetworkPubsubEffectfulAction); impl_into_global_action!(channels::P2pChannelsMessageReceivedAction); +impl_into_global_action!(channels::signaling::discovery::P2pChannelsSignalingDiscoveryAction); +impl_into_global_action!(channels::signaling::exchange::P2pChannelsSignalingExchangeAction); impl_into_global_action!(channels::best_tip::P2pChannelsBestTipAction); impl_into_global_action!(channels::transaction::P2pChannelsTransactionAction); impl_into_global_action!(channels::snark::P2pChannelsSnarkAction); @@ -110,24 +126,29 @@ impl_into_global_action!(p2p::P2pNetworkKadBootstrapAction); impl_into_global_action!(p2p::P2pNetworkYamuxAction); impl_into_global_action!(p2p::peer::P2pPeerAction); impl_into_global_action!(p2p::network::identify::stream::P2pNetworkIdentifyStreamAction); -impl_into_global_action!( - p2p::network::identify::stream_effectful::P2pNetworkIdentifyStreamEffectfulAction -); impl_into_global_action!(p2p::identify::P2pIdentifyAction); impl_into_global_action!(p2p::P2pNetworkSelectAction); impl_into_global_action!(p2p::P2pNetworkPnetAction); impl_into_global_action!(p2p::P2pNetworkNoiseAction); impl_into_global_action!(p2p::P2pNetworkRpcAction); -impl_into_global_action!(p2p::P2pNetworkSchedulerEffectfulAction); -impl_into_global_action!(p2p::P2pNetworkPnetEffectfulAction); -impl_into_global_action!(connection::incoming_effectful::P2pConnectionIncomingEffectfulAction); -impl_into_global_action!(connection::outgoing_effectful::P2pConnectionOutgoingEffectfulAction); -impl_into_global_action!(p2p::disconnection_effectful::P2pDisconnectionEffectfulAction); -impl_into_global_action!(P2pChannelsBestTipEffectfulAction); -impl_into_global_action!(P2pChannelsStreamingRpcEffectfulAction); -impl_into_global_action!(P2pChannelsTransactionEffectfulAction); -impl_into_global_action!(P2pChannelsSnarkJobCommitmentEffectfulAction); -impl_into_global_action!(P2pChannelsRpcEffectfulAction); -impl_into_global_action!(P2pChannelsSnarkEffectfulAction); + +impl_into_global_action!(effectful network::kad_effectful::P2pNetworkKadEffectfulAction); +impl_into_global_action!(effectful p2p::P2pNetworkSchedulerEffectfulAction); +impl_into_global_action!(effectful p2p::P2pNetworkPnetEffectfulAction); +impl_into_global_action!(effectful connection::incoming_effectful::P2pConnectionIncomingEffectfulAction); +impl_into_global_action!(effectful connection::outgoing_effectful::P2pConnectionOutgoingEffectfulAction); +impl_into_global_action!(effectful p2p::disconnection_effectful::P2pDisconnectionEffectfulAction); +impl_into_global_action!( + effectful P2pChannelsSignalingDiscoveryEffectfulAction +); +impl_into_global_action!(effectful P2pChannelsSignalingExchangeEffectfulAction); +impl_into_global_action!(effectful P2pChannelsBestTipEffectfulAction); +impl_into_global_action!(effectful P2pChannelsStreamingRpcEffectfulAction); +impl_into_global_action!(effectful P2pChannelsTransactionEffectfulAction); +impl_into_global_action!(effectful P2pChannelsSnarkJobCommitmentEffectfulAction); +impl_into_global_action!(effectful P2pChannelsRpcEffectfulAction); +impl_into_global_action!(effectful P2pChannelsSnarkEffectfulAction); +impl_into_global_action!(effectful network::pubsub::P2pNetworkPubsubEffectfulAction); +impl_into_global_action!(effectful P2pNetworkIdentifyStreamEffectfulAction); impl p2p::P2pActionTrait for crate::Action {} diff --git a/node/src/p2p/p2p_effects.rs b/node/src/p2p/p2p_effects.rs index d6fb9e0146..18ef2dd06d 100644 --- a/node/src/p2p/p2p_effects.rs +++ b/node/src/p2p/p2p_effects.rs @@ -1,43 +1,21 @@ -use super::P2pActionWithMeta; -use crate::{P2pAction, Service, Store}; -use p2p::{ - channels::P2pChannelsEffectfulAction, connection::P2pConnectionEffectfulAction, - P2pInitializeAction, -}; +use crate::{Service, Store}; +use p2p::P2pEffectfulAction; +use redux::ActionWithMeta; -pub fn node_p2p_effects(store: &mut Store, action: P2pActionWithMeta) { +pub fn node_p2p_effects( + store: &mut Store, + action: ActionWithMeta, +) { let (action, meta) = action.split(); match action { - P2pAction::Initialization(P2pInitializeAction::Initialize { .. }) => { + P2pEffectfulAction::Initialize => + { #[cfg(feature = "p2p-libp2p")] if store.state().p2p.ready().is_some() { store.service().start_mio(); } } - P2pAction::DisconnectionEffectful(action) => action.effects(&meta, store), - P2pAction::ChannelsEffectful(action) => match action { - P2pChannelsEffectfulAction::BestTip(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::Transaction(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::StreamingRpc(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::Snark(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::Rpc(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::SnarkJobCommitment(action) => action.effects(&meta, store), - }, - P2pAction::Network(_action) => { - #[cfg(feature = "p2p-libp2p")] - _action.effects(&meta, store); - } - P2pAction::ConnectionEffectful(action) => match action { - P2pConnectionEffectfulAction::Outgoing(action) => action.effects(&meta, store), - P2pConnectionEffectfulAction::Incoming(action) => action.effects(&meta, store), - }, - P2pAction::Peer(_) - | P2pAction::Channels(_) - | P2pAction::Connection(_) - | P2pAction::Disconnection(_) - | P2pAction::Identify(_) => { - // handled by reducer - } + action => action.effects(meta, store), } } diff --git a/node/src/reducer.rs b/node/src/reducer.rs index dc838a265a..33c2b8c78f 100644 --- a/node/src/reducer.rs +++ b/node/src/reducer.rs @@ -1,5 +1,5 @@ use openmina_core::{bug_condition, error, Substate}; -use p2p::{P2pAction, P2pInitializeAction, P2pState}; +use p2p::{P2pAction, P2pEffectfulAction, P2pInitializeAction, P2pState}; use crate::{Action, ActionWithMeta, EventSourceAction, P2p, State}; @@ -21,12 +21,12 @@ pub fn reducer( } Action::EventSource(EventSourceAction::NewEvent { .. }) => {} Action::EventSource(_) => {} - Action::P2p(a) => match a { P2pAction::Initialization(P2pInitializeAction::Initialize { chain_id }) => { if let Err(err) = state.p2p.initialize(chain_id) { error!(meta.time(); summary = "error initializing p2p", error = display(err)); } + dispatcher.push(P2pEffectfulAction::Initialize); } action => match &mut state.p2p { P2p::Pending(_) => { @@ -36,7 +36,7 @@ pub fn reducer( let time = meta.time(); let result = p2p::P2pState::reducer( Substate::new(state, dispatcher), - meta.with_action(action), + meta.with_action(action.clone()), ); if let Err(error) = result { @@ -45,6 +45,7 @@ pub fn reducer( } }, }, + Action::P2pEffectful(_) => {} Action::Ledger(a) => { state.ledger.reducer(meta.with_action(a)); } diff --git a/node/src/rpc/mod.rs b/node/src/rpc/mod.rs index 71492b05c9..8f307b2097 100644 --- a/node/src/rpc/mod.rs +++ b/node/src/rpc/mod.rs @@ -587,7 +587,7 @@ pub mod discovery { } Ok(RpcDiscoveryRoutingTable { - this_key: value.this_key.clone(), + this_key: value.this_key, buckets, }) } @@ -643,8 +643,8 @@ pub mod discovery { Ok(RpcEntry { peer_id: value.peer_id, libp2p: value.peer_id.try_into()?, - key: value.key.clone(), - dist: this_key - &value.key, + key: value.key, + dist: this_key - value.key, addrs: value.addresses().clone(), connection: value.connection, }) diff --git a/node/src/rpc/rpc_effects.rs b/node/src/rpc/rpc_effects.rs index ea15efaf89..04cb8d1a95 100644 --- a/node/src/rpc/rpc_effects.rs +++ b/node/src/rpc/rpc_effects.rs @@ -293,8 +293,11 @@ pub fn rpc_effects(store: &mut Store, action: RpcActionWithMeta) RpcAction::P2pConnectionIncomingRespond { rpc_id, response } => { let error = match &response { P2pConnectionResponse::Accepted(_) => None, - P2pConnectionResponse::InternalError => Some("RemoteInternalError".to_owned()), P2pConnectionResponse::Rejected(reason) => Some(format!("Rejected({:?})", reason)), + P2pConnectionResponse::SignalDecryptionFailed => { + Some("RemoteSignalDecryptionFailed".to_owned()) + } + P2pConnectionResponse::InternalError => Some("RemoteInternalError".to_owned()), }; let _ = store .service diff --git a/node/testing/Cargo.toml b/node/testing/Cargo.toml index fe9c1946ff..9d18e50017 100644 --- a/node/testing/Cargo.toml +++ b/node/testing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-node-testing" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/node/testing/src/hosts.rs b/node/testing/src/hosts.rs new file mode 100644 index 0000000000..dd62637f42 --- /dev/null +++ b/node/testing/src/hosts.rs @@ -0,0 +1,28 @@ +/// This should be the only place where environment variables are converted to addresses. +/// +use std::{env, str::FromStr}; + +use node::p2p::connection::outgoing::P2pConnectionOutgoingInitOpts; + +use crate::scenario::ListenerNode; + +pub fn replayer() -> P2pConnectionOutgoingInitOpts { + // "/dns4/1.k8.openmina.com/tcp/31968/p2p/12D3KooWPayQEdprqY2m3biReUUybA5LoULpJE7YWu6wetEKKELv", + + let multiaddr = env::var("REPLAYER_MULTIADDR") + .expect("must set variable `REPLAYER_MULTIADDR`") + .parse::() + .expect("`REPLAYER_MULTIADDR` must be a valid multiaddress"); + (&multiaddr).try_into().expect("must be valid init opts") +} + +pub fn devnet() -> Vec { + let seeds = std::env::var("OPENMINA_SCENARIO_SEEDS") + .unwrap_or_else(|_| node::p2p::DEVNET_SEEDS.join(" ")); + seeds + .split_whitespace() + .map(P2pConnectionOutgoingInitOpts::from_str) + .filter_map(Result::ok) + .map(Into::into) + .collect() +} diff --git a/node/testing/src/lib.rs b/node/testing/src/lib.rs index ed89903daa..f2fb794863 100644 --- a/node/testing/src/lib.rs +++ b/node/testing/src/lib.rs @@ -12,6 +12,7 @@ pub mod scenarios; pub mod service; pub mod simulator; +pub mod hosts; pub mod network_debugger; mod server; diff --git a/node/testing/src/node/ocaml/mod.rs b/node/testing/src/node/ocaml/mod.rs index 3266b36a33..a6a8e58804 100644 --- a/node/testing/src/node/ocaml/mod.rs +++ b/node/testing/src/node/ocaml/mod.rs @@ -106,7 +106,7 @@ impl OcamlNode { cmd.arg("daemon"); cmd.arg("--config-dir").arg(&config_dir); - cmd.arg("--libp2p-keypair").arg(&Self::privkey_path(dir)); + cmd.arg("--libp2p-keypair").arg(Self::privkey_path(dir)); cmd.args(["--external-ip", "127.0.0.1"]) .args(["--external-port", &config.libp2p_port.to_string()]) .args(["--client-port", &config.client_port.to_string()]) diff --git a/node/testing/src/scenarios/mod.rs b/node/testing/src/scenarios/mod.rs index 7fccead6ef..867a1f7464 100644 --- a/node/testing/src/scenarios/mod.rs +++ b/node/testing/src/scenarios/mod.rs @@ -2,6 +2,7 @@ //! Initial Joining: //! * Ensure new nodes can discover peers and establish initial connections. //! * Test how nodes handle scenarios when they are overwhelmed with too many connections or data requests. +//! //! TODO(vlad9486): //! Reconnection: Validate that nodes can reconnect after both intentional and unintentional disconnections. //! Handling Latency: Nodes should remain connected and synchronize even under high latency conditions. @@ -17,6 +18,7 @@ pub mod p2p; mod driver; pub use driver::*; +use p2p::signaling::P2pSignaling; pub use crate::cluster::runner::*; @@ -65,6 +67,7 @@ pub enum Scenarios { SimulationSmall(SimulationSmall), SimulationSmallForeverRealTime(SimulationSmallForeverRealTime), P2pReceiveBlock(P2pReceiveBlock), + P2pSignaling(P2pSignaling), MultiNodePubsubPropagateBlock(MultiNodePubsubPropagateBlock), RecordReplayBootstrap(RecordReplayBootstrap), RecordReplayBlockProduction(RecordReplayBlockProduction), @@ -89,6 +92,7 @@ impl Scenarios { Self::SimulationSmall(_) => true, Self::SimulationSmallForeverRealTime(_) => true, Self::MultiNodePubsubPropagateBlock(_) => true, // in progress + Self::P2pSignaling(_) => cfg!(feature = "p2p-webrtc"), _ => false, } } @@ -147,6 +151,7 @@ impl Scenarios { Self::SimulationSmall(_) => SimulationSmall::DOCS, Self::SimulationSmallForeverRealTime(_) => SimulationSmallForeverRealTime::DOCS, Self::P2pReceiveBlock(_) => P2pReceiveBlock::DOCS, + Self::P2pSignaling(_) => P2pSignaling::DOCS, Self::MultiNodePubsubPropagateBlock(_) => MultiNodePubsubPropagateBlock::DOCS, Self::RecordReplayBootstrap(_) => RecordReplayBootstrap::DOCS, Self::RecordReplayBlockProduction(_) => RecordReplayBlockProduction::DOCS, @@ -183,6 +188,7 @@ impl Scenarios { Self::SimulationSmall(v) => v.run(runner).await, Self::SimulationSmallForeverRealTime(v) => v.run(runner).await, Self::P2pReceiveBlock(v) => v.run(runner).await, + Self::P2pSignaling(v) => v.run(runner).await, Self::MultiNodePubsubPropagateBlock(v) => v.run(runner).await, Self::RecordReplayBootstrap(v) => v.run(runner).await, Self::RecordReplayBlockProduction(v) => v.run(runner).await, diff --git a/node/testing/src/scenarios/p2p/mod.rs b/node/testing/src/scenarios/p2p/mod.rs index c12f2ec263..a4000cd92a 100644 --- a/node/testing/src/scenarios/p2p/mod.rs +++ b/node/testing/src/scenarios/p2p/mod.rs @@ -3,3 +3,4 @@ pub mod basic_incoming_connections; pub mod basic_outgoing_connections; pub mod kademlia; pub mod pubsub; +pub mod signaling; diff --git a/node/testing/src/scenarios/p2p/pubsub.rs b/node/testing/src/scenarios/p2p/pubsub.rs index cf28de1dec..1e2d1604c3 100644 --- a/node/testing/src/scenarios/p2p/pubsub.rs +++ b/node/testing/src/scenarios/p2p/pubsub.rs @@ -1,8 +1,8 @@ use std::time::Duration; use crate::{ + hosts, node::RustNodeTestingConfig, - scenario::ListenerNode, scenarios::{ClusterRunner, Driver}, }; @@ -16,7 +16,7 @@ impl P2pReceiveBlock { // make sure it will not ask initial peers .ask_initial_peers_interval(Duration::from_secs(3600)) .max_peers(1) - .initial_peers(vec![ListenerNode::Custom("/ip4/34.135.63.47/tcp/10001/p2p/12D3KooWLjs54xHzVmMmGYb7W5RVibqbwD1co7M2ZMfPgPm7iAag".parse().unwrap())]); + .initial_peers(vec![hosts::devnet()[0].clone()]); let retransmitter_openmina_node = runner.add_rust_node(config); let retransmitter_peer_id = runner .node(retransmitter_openmina_node) diff --git a/node/testing/src/scenarios/p2p/signaling.rs b/node/testing/src/scenarios/p2p/signaling.rs new file mode 100644 index 0000000000..5124238d6a --- /dev/null +++ b/node/testing/src/scenarios/p2p/signaling.rs @@ -0,0 +1,60 @@ +use std::{collections::BTreeSet, time::Duration}; + +use node::{ + p2p::{P2pPeerAction, PeerId}, + Action, P2pAction, +}; + +use crate::{ + node::RustNodeTestingConfig, + scenarios::{ClusterRunner, DynEffectsData, RunCfg}, +}; + +/// Makes sure that when using WebRTC only nodes, peers can discover +/// each other and connect to each other via p2p signaling. +#[derive(documented::Documented, Default, Clone, Copy)] +pub struct P2pSignaling; + +impl P2pSignaling { + pub async fn run(self, mut runner: ClusterRunner<'_>) { + const NODES_N: usize = 4; + + let seed_config = RustNodeTestingConfig::devnet_default(); + let seed = runner.add_rust_node(seed_config.clone()); + + let node_config = seed_config.initial_peers(vec![seed.into()]); + let _node_1 = runner.add_rust_node(node_config.clone()); + let _node_2 = runner.add_rust_node(node_config.clone()); + let _node_3 = runner.add_rust_node(node_config.clone()); + + let node_peers: [_; NODES_N] = std::array::from_fn(|_| BTreeSet::::new()); + let node_peers = DynEffectsData::new(node_peers); + + runner + .run( + RunCfg::default() + .timeout(Duration::from_secs(60)) + .advance_time(1..=100) + .action_handler(move |node_id, _state, _, action| { + if action.action().kind().to_string().contains("Signaling") { + let me = _state.p2p.my_id(); + let me_pk = me.to_public_key().unwrap(); + dbg!((me, me_pk, action.action())); + } + match action.action() { + Action::P2p(P2pAction::Peer(P2pPeerAction::Ready { + peer_id, .. + })) => { + node_peers.inner()[node_id.index()].insert(*peer_id); + dbg!(node_peers.inner()) + .iter() + .all(|v| v.len() == NODES_N - 1) + } + _ => false, + } + }), + ) + .await + .expect("peers didn't discover each other"); + } +} diff --git a/node/testing/src/scenarios/record_replay/bootstrap.rs b/node/testing/src/scenarios/record_replay/bootstrap.rs index 06a4359a05..d6becca307 100644 --- a/node/testing/src/scenarios/record_replay/bootstrap.rs +++ b/node/testing/src/scenarios/record_replay/bootstrap.rs @@ -1,9 +1,10 @@ use std::time::Duration; -use node::{p2p::connection::outgoing::P2pConnectionOutgoingInitOpts, ActionKind}; +use node::ActionKind; use openmina_node_native::replay_state_with_input_actions; use crate::{ + hosts, node::{Recorder, RustNodeTestingConfig, TestPeerId}, scenarios::{ClusterRunner, RunCfg, RunCfgAdvanceTime}, }; @@ -15,17 +16,7 @@ pub struct RecordReplayBootstrap; impl RecordReplayBootstrap { pub async fn run(self, mut runner: ClusterRunner<'_>) { - let seeds_var = std::env::var("OPENMINA_SCENARIO_SEEDS"); - let seeds = seeds_var.as_ref().map_or_else( - |_| node::p2p::DEVNET_SEEDS.to_vec(), - |val| val.split_whitespace().collect(), - ); - - let initial_peers = seeds - .iter() - .map(|s| s.parse::().unwrap()) - .map(Into::into) - .collect::>(); + let initial_peers = hosts::devnet(); let node_id = runner.add_rust_node(RustNodeTestingConfig { initial_time: redux::Timestamp::global_now(), diff --git a/node/testing/src/scenarios/solo_node/basic_connectivity_accept_incoming.rs b/node/testing/src/scenarios/solo_node/basic_connectivity_accept_incoming.rs index c7d0088ac2..55bc34059f 100644 --- a/node/testing/src/scenarios/solo_node/basic_connectivity_accept_incoming.rs +++ b/node/testing/src/scenarios/solo_node/basic_connectivity_accept_incoming.rs @@ -7,6 +7,7 @@ use node::p2p::{connection::outgoing::P2pConnectionOutgoingInitOpts, PeerId}; use rand::Rng; use crate::{ + hosts, node::{DaemonJson, OcamlNodeTestingConfig, RustNodeTestingConfig}, scenario::{ListenerNode, ScenarioStep}, scenarios::ClusterRunner, @@ -28,21 +29,10 @@ impl SoloNodeBasicConnectivityAcceptIncoming { const STEPS: usize = 6_000; const STEP_DELAY: Duration = Duration::from_millis(200); - let seeds_var = std::env::var("OPENMINA_SCENARIO_SEEDS"); - let seeds = seeds_var.as_ref().map_or_else( - |_| node::p2p::DEVNET_SEEDS.to_vec(), - |val| val.split_whitespace().collect(), - ); - - let initial_peers = seeds - .iter() - .map(|s| s.parse::().unwrap()) - .map(|maddr| P2pConnectionOutgoingInitOpts::try_from(&maddr).unwrap()) - .map(ListenerNode::from) - .collect::>(); + let initial_peers = hosts::devnet(); eprintln!("set max peers per node: {MAX_PEERS_PER_NODE}"); - for seed in seeds { - eprintln!("add initial peer: {seed}"); + for seed in &initial_peers { + eprintln!("add initial peer: {seed:?}"); } let config = RustNodeTestingConfig::devnet_default() .ask_initial_peers_interval(Duration::from_secs(3600)) diff --git a/node/testing/src/scenarios/solo_node/basic_connectivity_initial_joining.rs b/node/testing/src/scenarios/solo_node/basic_connectivity_initial_joining.rs index 92a1e7e20d..b04cd24ac4 100644 --- a/node/testing/src/scenarios/solo_node/basic_connectivity_initial_joining.rs +++ b/node/testing/src/scenarios/solo_node/basic_connectivity_initial_joining.rs @@ -7,6 +7,7 @@ use libp2p::Multiaddr; use node::p2p::connection::outgoing::P2pConnectionOutgoingInitOpts; use crate::{ + hosts, node::RustNodeTestingConfig, scenario::{ListenerNode, ScenarioStep}, scenarios::ClusterRunner, @@ -28,21 +29,10 @@ impl SoloNodeBasicConnectivityInitialJoining { const STEPS: usize = 3_000; const STEP_DELAY: Duration = Duration::from_millis(200); - let seeds_var = std::env::var("OPENMINA_SCENARIO_SEEDS"); - let seeds = seeds_var.as_ref().map_or_else( - |_| node::p2p::DEVNET_SEEDS.to_vec(), - |val| val.split_whitespace().collect(), - ); - - let initial_peers = seeds - .iter() - .map(|s| s.parse::().unwrap()) - .map(|maddr| P2pConnectionOutgoingInitOpts::try_from(&maddr).unwrap()) - .map(ListenerNode::from) - .collect::>(); + let initial_peers = hosts::devnet(); eprintln!("set max peers per node: {MAX_PEERS_PER_NODE}"); - for seed in seeds { - eprintln!("add initial peer: {seed}"); + for seed in &initial_peers { + eprintln!("add initial peer: {seed:?}"); } let config = RustNodeTestingConfig::devnet_default() .ask_initial_peers_interval(Duration::from_secs(3600)) diff --git a/node/testing/src/scenarios/solo_node/bootstrap.rs b/node/testing/src/scenarios/solo_node/bootstrap.rs index 2b42d60597..858decf4b3 100644 --- a/node/testing/src/scenarios/solo_node/bootstrap.rs +++ b/node/testing/src/scenarios/solo_node/bootstrap.rs @@ -1,12 +1,10 @@ use std::time::Duration; -use node::{ - p2p::connection::outgoing::P2pConnectionOutgoingInitOpts, - transition_frontier::sync::TransitionFrontierSyncState, -}; +use node::transition_frontier::sync::TransitionFrontierSyncState; use tokio::time::Instant; use crate::{ + hosts, node::RustNodeTestingConfig, scenario::{ListenerNode, ScenarioStep}, scenarios::ClusterRunner, @@ -27,16 +25,12 @@ impl SoloNodeBootstrap { const TIMEOUT: Duration = Duration::from_secs(60 * 40); - const REPLAYER_1: & str = - "/ip4/135.181.217.23/tcp/31968/p2p/12D3KooWPayQEdprqY2m3biReUUybA5LoULpJE7YWu6wetEKKELv"; - let replayer = (&REPLAYER_1.parse::().unwrap()) - .try_into() - .unwrap(); + let replayer = hosts::replayer(); - let node_id = - runner.add_rust_node(RustNodeTestingConfig::devnet_default().initial_peers(vec![ - ListenerNode::Custom(P2pConnectionOutgoingInitOpts::LibP2P(replayer)), - ])); + let node_id = runner.add_rust_node( + RustNodeTestingConfig::devnet_default() + .initial_peers(vec![ListenerNode::Custom(replayer)]), + ); eprintln!("launch Openmina node with default configuration, id: {node_id}"); let mut timeout = TIMEOUT; diff --git a/node/testing/src/scenarios/solo_node/sync_root_snarked_ledger.rs b/node/testing/src/scenarios/solo_node/sync_root_snarked_ledger.rs index f814737f52..c8a79ec755 100644 --- a/node/testing/src/scenarios/solo_node/sync_root_snarked_ledger.rs +++ b/node/testing/src/scenarios/solo_node/sync_root_snarked_ledger.rs @@ -37,9 +37,9 @@ impl SoloNodeSyncRootSnarkedLedger { eprintln!("launch Openmina node with default configuration, id: {node_id}"); const REPLAYER_1: &str = - "/ip4/65.109.110.75/tcp/18302/p2p/12D3KooWD8jSyPFXNdAcMBHyHjRBcK1AW9t3xvnpfCFSRKMweVKi"; + "/dns4/web-node-1/tcp/18302/p2p/12D3KooWD8jSyPFXNdAcMBHyHjRBcK1AW9t3xvnpfCFSRKMweVKi"; const REPLAYER_2: &str = - "/ip4/65.109.110.75/tcp/18303/p2p/12D3KooWBxbfeaxGHxdxP3U5jRKpNK5wQmbjKywGJEqTCNpVPxqk"; + "/dns4/web-node-1/tcp/18303/p2p/12D3KooWBxbfeaxGHxdxP3U5jRKpNK5wQmbjKywGJEqTCNpVPxqk"; // Initiate connection to 2 replayers. runner diff --git a/node/testing/src/service/mod.rs b/node/testing/src/service/mod.rs index 65bd6ea09b..4880bed8d5 100644 --- a/node/testing/src/service/mod.rs +++ b/node/testing/src/service/mod.rs @@ -359,6 +359,22 @@ impl P2pServiceWebrtc for NodeTestingService { fn incoming_init(&mut self, peer_id: PeerId, offer: webrtc::Offer) { P2pServiceWebrtc::incoming_init(&mut self.real, peer_id, offer) } + + fn encrypt( + &mut self, + other_pk: &node::p2p::identity::PublicKey, + message: &T, + ) -> Result { + self.real.encrypt(other_pk, message) + } + + fn decrypt( + &mut self, + other_pub_key: &node::p2p::identity::PublicKey, + encrypted: &T::Encrypted, + ) -> Result { + self.real.decrypt(other_pub_key, encrypted) + } } impl P2pServiceWebrtcWithLibp2p for NodeTestingService { diff --git a/node/testing/tests/p2p_signaling.rs b/node/testing/tests/p2p_signaling.rs new file mode 100644 index 0000000000..ce17eac88c --- /dev/null +++ b/node/testing/tests/p2p_signaling.rs @@ -0,0 +1,7 @@ +#![cfg(feature = "p2p-webrtc")] + +use openmina_node_testing::scenarios::p2p::signaling::P2pSignaling; + +mod common; + +scenario_test!(p2p_signaling, P2pSignaling, P2pSignaling, true); diff --git a/node/web/Cargo.toml b/node/web/Cargo.toml index 9813455cae..270c634aa5 100644 --- a/node/web/Cargo.toml +++ b/node/web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-node-web" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/node/web/src/lib.rs b/node/web/src/lib.rs index 2fc576ae16..5d8c0a5f89 100644 --- a/node/web/src/lib.rs +++ b/node/web/src/lib.rs @@ -70,7 +70,6 @@ async fn setup_node( } node_builder - .p2p_no_discovery() .p2p_custom_task_spawner(P2pTaskRemoteSpawner {}) .unwrap(); node_builder.gather_stats(); diff --git a/node/web/src/node/builder.rs b/node/web/src/node/builder.rs index d272a0027b..1d851f6462 100644 --- a/node/web/src/node/builder.rs +++ b/node/web/src/node/builder.rs @@ -274,8 +274,7 @@ impl NodeBuilder { } fn default_peers() -> Vec { - ["/2cBFzmUmkYgMUrxdv5S2Udyv8eiuhokAFS4WnYfHiAJLWoQ3yL9/http/webrtc3.webnode.openmina.com/3000", - "/2aevrztkwUrbPDP85ZZHkNYM6qha2GYT9sUwPhttKAKzqQdF4nV/http/localhost/3000"] + ["/2cBFzmUmkYgMUrxdv5S2Udyv8eiuhokAFS4WnYfHiAJLWoQ3yL9/https/webrtc3.webnode.openmina.com/443"] .into_iter() .map(|s| s.parse().unwrap()) .collect() diff --git a/p2p/Cargo.toml b/p2p/Cargo.toml index 179d8aa67e..db3d6960b3 100644 --- a/p2p/Cargo.toml +++ b/p2p/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "p2p" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" @@ -23,8 +23,9 @@ cfg-if = "1.0.0" url = "2.3.1" multihash = "0.18.1" sha2 = "0.10.6" -# ecies-ed25519 = "0.5.1" ed25519-dalek = { version = "2.1.1", features = ["serde"] } +x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } +aes-gcm = "0.10.3" faster-stun = { version = "1.0.1", optional = true } reqwest = { version = "0.11.22", optional = true } unsigned-varint = { version = "0.8.0" } diff --git a/p2p/libp2p-rpc-behaviour/Cargo.toml b/p2p/libp2p-rpc-behaviour/Cargo.toml index 25f2c202b9..a0da01f587 100644 --- a/p2p/libp2p-rpc-behaviour/Cargo.toml +++ b/p2p/libp2p-rpc-behaviour/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-rpc-behaviour" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/p2p/src/channels/best_tip/p2p_channels_best_tip_reducer.rs b/p2p/src/channels/best_tip/p2p_channels_best_tip_reducer.rs index 2a64f7797f..c4bdf794d5 100644 --- a/p2p/src/channels/best_tip/p2p_channels_best_tip_reducer.rs +++ b/p2p/src/channels/best_tip/p2p_channels_best_tip_reducer.rs @@ -12,7 +12,7 @@ impl P2pChannelsBestTipState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pChannelsBestTipAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -90,10 +90,7 @@ impl P2pChannelsBestTipState { *last_received = Some(best_tip.clone()); let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pPeerAction::BestTipUpdate { - peer_id, - best_tip: best_tip.clone(), - }); + dispatcher.push(P2pPeerAction::BestTipUpdate { peer_id, best_tip }); dispatcher.push(P2pChannelsBestTipAction::RequestSend { peer_id }); Ok(()) } @@ -115,7 +112,7 @@ impl P2pChannelsBestTipState { .callbacks .on_p2p_channels_best_tip_request_received { - dispatcher.push_callback(callback.clone(), *peer_id); + dispatcher.push_callback(callback.clone(), peer_id); } Ok(()) } @@ -141,7 +138,7 @@ impl P2pChannelsBestTipState { if !is_libp2p { dispatcher.push(P2pChannelsBestTipEffectfulAction::ResponseSend { peer_id, - best_tip: best_tip.clone(), + best_tip, }); return Ok(()); } diff --git a/p2p/src/channels/best_tip_effectful/p2p_channels_best_tip_effectful_actions.rs b/p2p/src/channels/best_tip_effectful/p2p_channels_best_tip_effectful_actions.rs index 73d3cd5a1b..efb08ca2fa 100644 --- a/p2p/src/channels/best_tip_effectful/p2p_channels_best_tip_effectful_actions.rs +++ b/p2p/src/channels/best_tip_effectful/p2p_channels_best_tip_effectful_actions.rs @@ -24,8 +24,8 @@ impl redux::EnablingCondition for P2pChannelsBestTipEffectfulAction { } } -impl From for crate::P2pAction { - fn from(action: P2pChannelsBestTipEffectfulAction) -> crate::P2pAction { - crate::P2pAction::ChannelsEffectful(P2pChannelsEffectfulAction::BestTip(action)) +impl From for crate::P2pEffectfulAction { + fn from(action: P2pChannelsBestTipEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Channels(P2pChannelsEffectfulAction::BestTip(action)) } } diff --git a/p2p/src/channels/mod.rs b/p2p/src/channels/mod.rs index 27d3d84c4b..78b35cf3b3 100644 --- a/p2p/src/channels/mod.rs +++ b/p2p/src/channels/mod.rs @@ -2,6 +2,7 @@ pub mod best_tip; pub mod best_tip_effectful; pub mod rpc; pub mod rpc_effectful; +pub mod signaling; pub mod snark; pub mod snark_effectful; pub mod snark_job_commitment; @@ -26,6 +27,8 @@ use binprot::{BinProtRead, BinProtWrite}; use binprot_derive::{BinProtRead, BinProtWrite}; use derive_more::From; use serde::{Deserialize, Serialize}; +use signaling::discovery::SignalingDiscoveryChannelMsg; +use signaling::exchange::SignalingExchangeChannelMsg; use strum_macros::EnumIter; use self::best_tip::BestTipPropagationChannelMsg; @@ -38,12 +41,14 @@ use self::transaction::TransactionPropagationChannelMsg; #[derive(Serialize, Deserialize, EnumIter, Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Copy)] #[repr(u8)] pub enum ChannelId { - BestTipPropagation = 2, - TransactionPropagation = 3, - SnarkPropagation = 4, - SnarkJobCommitmentPropagation = 5, - Rpc = 100, - StreamingRpc = 101, + SignalingDiscovery = 1, + SignalingExchange = 2, + BestTipPropagation = 3, + TransactionPropagation = 4, + SnarkPropagation = 5, + SnarkJobCommitmentPropagation = 6, + Rpc = 7, + StreamingRpc = 8, } impl ChannelId { @@ -59,6 +64,8 @@ impl ChannelId { pub fn name(self) -> &'static str { match self { + Self::SignalingDiscovery => "signaling/discovery", + Self::SignalingExchange => "signaling/exchange", Self::BestTipPropagation => "best_tip/propagation", Self::TransactionPropagation => "transaction/propagation", Self::SnarkPropagation => "snark/propagation", @@ -70,6 +77,8 @@ impl ChannelId { pub fn supported_by_libp2p(self) -> bool { match self { + Self::SignalingDiscovery => false, + Self::SignalingExchange => false, Self::BestTipPropagation => true, Self::TransactionPropagation => true, Self::SnarkPropagation => true, @@ -81,6 +90,9 @@ impl ChannelId { pub fn max_msg_size(self) -> usize { match self { + // TODO(binier): measure signaling message sizes + Self::SignalingDiscovery => 16 * 1024, // 16KB + Self::SignalingExchange => 16 * 1024, // 16KB // TODO(binier): reduce this value once we change message for best tip // propagation to just propagating consensus state with block hash. Self::BestTipPropagation => 32 * 1024 * 1024, // 32MB @@ -122,6 +134,8 @@ impl MsgId { #[derive(BinProtWrite, BinProtRead, Serialize, Deserialize, From, Debug, Clone)] pub enum ChannelMsg { + SignalingDiscovery(SignalingDiscoveryChannelMsg), + SignalingExchange(SignalingExchangeChannelMsg), BestTipPropagation(BestTipPropagationChannelMsg), TransactionPropagation(TransactionPropagationChannelMsg), SnarkPropagation(SnarkPropagationChannelMsg), @@ -133,6 +147,8 @@ pub enum ChannelMsg { impl ChannelMsg { pub fn channel_id(&self) -> ChannelId { match self { + Self::SignalingDiscovery(_) => ChannelId::SignalingDiscovery, + Self::SignalingExchange(_) => ChannelId::SignalingExchange, Self::BestTipPropagation(_) => ChannelId::BestTipPropagation, Self::TransactionPropagation(_) => ChannelId::TransactionPropagation, Self::SnarkPropagation(_) => ChannelId::SnarkPropagation, @@ -147,6 +163,8 @@ impl ChannelMsg { W: std::io::Write, { match self { + Self::SignalingDiscovery(v) => v.binprot_write(w), + Self::SignalingExchange(v) => v.binprot_write(w), Self::BestTipPropagation(v) => v.binprot_write(w), Self::TransactionPropagation(v) => v.binprot_write(w), Self::SnarkPropagation(v) => v.binprot_write(w), @@ -162,6 +180,12 @@ impl ChannelMsg { R: std::io::Read + ?Sized, { match id { + ChannelId::SignalingDiscovery => { + SignalingDiscoveryChannelMsg::binprot_read(r).map(|v| v.into()) + } + ChannelId::SignalingExchange => { + SignalingExchangeChannelMsg::binprot_read(r).map(|v| v.into()) + } ChannelId::BestTipPropagation => { BestTipPropagationChannelMsg::binprot_read(r).map(|v| v.into()) } @@ -194,6 +218,16 @@ impl crate::P2pState { // exhaustive matching so that we don't miss any channels. for id in self.config.enabled_channels.iter().copied() { match id { + ChannelId::SignalingDiscovery => { + dispatcher.push( + signaling::discovery::P2pChannelsSignalingDiscoveryAction::Init { peer_id }, + ); + } + ChannelId::SignalingExchange => { + dispatcher.push( + signaling::exchange::P2pChannelsSignalingExchangeAction::Init { peer_id }, + ); + } ChannelId::BestTipPropagation => { dispatcher.push(best_tip::P2pChannelsBestTipAction::Init { peer_id }); } diff --git a/p2p/src/channels/p2p_channels_actions.rs b/p2p/src/channels/p2p_channels_actions.rs index 30f598ae35..30e9ec7065 100644 --- a/p2p/src/channels/p2p_channels_actions.rs +++ b/p2p/src/channels/p2p_channels_actions.rs @@ -4,20 +4,32 @@ use serde::{Deserialize, Serialize}; use crate::{P2pState, PeerId}; use super::{ - best_tip::P2pChannelsBestTipAction, best_tip_effectful::P2pChannelsBestTipEffectfulAction, - rpc::P2pChannelsRpcAction, rpc_effectful::P2pChannelsRpcEffectfulAction, - snark::P2pChannelsSnarkAction, snark_effectful::P2pChannelsSnarkEffectfulAction, + best_tip::P2pChannelsBestTipAction, + best_tip_effectful::P2pChannelsBestTipEffectfulAction, + rpc::P2pChannelsRpcAction, + rpc_effectful::P2pChannelsRpcEffectfulAction, + signaling::{ + discovery::P2pChannelsSignalingDiscoveryAction, + discovery_effectful::P2pChannelsSignalingDiscoveryEffectfulAction, + exchange::P2pChannelsSignalingExchangeAction, + exchange_effectful::P2pChannelsSignalingExchangeEffectfulAction, + }, + snark::P2pChannelsSnarkAction, + snark_effectful::P2pChannelsSnarkEffectfulAction, snark_job_commitment::P2pChannelsSnarkJobCommitmentAction, snark_job_commitment_effectful::P2pChannelsSnarkJobCommitmentEffectfulAction, streaming_rpc::P2pChannelsStreamingRpcAction, streaming_rpc_effectful::P2pChannelsStreamingRpcEffectfulAction, transaction::P2pChannelsTransactionAction, - transaction_effectful::P2pChannelsTransactionEffectfulAction, ChannelMsg, + transaction_effectful::P2pChannelsTransactionEffectfulAction, + ChannelMsg, }; #[derive(Serialize, Deserialize, Debug, Clone, openmina_core::ActionEvent)] pub enum P2pChannelsAction { MessageReceived(P2pChannelsMessageReceivedAction), + SignalingDiscovery(P2pChannelsSignalingDiscoveryAction), + SignalingExchange(P2pChannelsSignalingExchangeAction), BestTip(P2pChannelsBestTipAction), Transaction(P2pChannelsTransactionAction), Snark(P2pChannelsSnarkAction), @@ -28,6 +40,8 @@ pub enum P2pChannelsAction { #[derive(Serialize, Deserialize, Debug, Clone, openmina_core::ActionEvent)] pub enum P2pChannelsEffectfulAction { + SignalingDiscovery(P2pChannelsSignalingDiscoveryEffectfulAction), + SignalingExchange(P2pChannelsSignalingExchangeEffectfulAction), BestTip(P2pChannelsBestTipEffectfulAction), Rpc(P2pChannelsRpcEffectfulAction), Snark(P2pChannelsSnarkEffectfulAction), @@ -40,6 +54,8 @@ impl P2pChannelsAction { pub fn peer_id(&self) -> Option<&PeerId> { match self { Self::MessageReceived(v) => Some(&v.peer_id), + Self::SignalingDiscovery(v) => Some(v.peer_id()), + Self::SignalingExchange(v) => Some(v.peer_id()), Self::BestTip(v) => Some(v.peer_id()), Self::Transaction(v) => v.peer_id(), Self::Snark(v) => v.peer_id(), @@ -54,6 +70,8 @@ impl redux::EnablingCondition for P2pChannelsAction { fn is_enabled(&self, state: &crate::P2pState, time: redux::Timestamp) -> bool { match self { P2pChannelsAction::MessageReceived(a) => a.is_enabled(state, time), + P2pChannelsAction::SignalingDiscovery(a) => a.is_enabled(state, time), + P2pChannelsAction::SignalingExchange(a) => a.is_enabled(state, time), P2pChannelsAction::Transaction(a) => a.is_enabled(state, time), P2pChannelsAction::BestTip(a) => a.is_enabled(state, time), P2pChannelsAction::Snark(a) => a.is_enabled(state, time), @@ -67,6 +85,8 @@ impl redux::EnablingCondition for P2pChannelsAction { impl redux::EnablingCondition for P2pChannelsEffectfulAction { fn is_enabled(&self, state: &crate::P2pState, time: redux::Timestamp) -> bool { match self { + P2pChannelsEffectfulAction::SignalingDiscovery(a) => a.is_enabled(state, time), + P2pChannelsEffectfulAction::SignalingExchange(a) => a.is_enabled(state, time), P2pChannelsEffectfulAction::BestTip(a) => a.is_enabled(state, time), P2pChannelsEffectfulAction::Transaction(a) => a.is_enabled(state, time), P2pChannelsEffectfulAction::StreamingRpc(a) => a.is_enabled(state, time), diff --git a/p2p/src/channels/p2p_channels_reducer.rs b/p2p/src/channels/p2p_channels_reducer.rs index 7bdc200507..624f7e428d 100644 --- a/p2p/src/channels/p2p_channels_reducer.rs +++ b/p2p/src/channels/p2p_channels_reducer.rs @@ -1,6 +1,16 @@ use super::{ best_tip::{BestTipPropagationChannelMsg, P2pChannelsBestTipAction, P2pChannelsBestTipState}, rpc::{P2pChannelsRpcAction, P2pChannelsRpcState, RpcChannelMsg}, + signaling::{ + discovery::{ + P2pChannelsSignalingDiscoveryAction, P2pChannelsSignalingDiscoveryState, + SignalingDiscoveryChannelMsg, + }, + exchange::{ + P2pChannelsSignalingExchangeAction, P2pChannelsSignalingExchangeState, + SignalingExchangeChannelMsg, + }, + }, snark::{P2pChannelsSnarkAction, P2pChannelsSnarkState, SnarkPropagationChannelMsg}, snark_job_commitment::{ P2pChannelsSnarkJobCommitmentAction, P2pChannelsSnarkJobCommitmentState, @@ -24,7 +34,7 @@ use redux::{ActionWithMeta, Dispatcher}; impl P2pChannelsState { pub fn reducer( state_context: Substate, - action: ActionWithMeta<&P2pChannelsAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -37,6 +47,12 @@ impl P2pChannelsState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); Self::dispatch_message(meta.with_action(action), dispatcher, state) } + P2pChannelsAction::SignalingDiscovery(action) => { + P2pChannelsSignalingDiscoveryState::reducer(state_context, meta.with_action(action)) + } + P2pChannelsAction::SignalingExchange(action) => { + P2pChannelsSignalingExchangeState::reducer(state_context, meta.with_action(action)) + } P2pChannelsAction::BestTip(action) => { P2pChannelsBestTipState::reducer(state_context, meta.with_action(action)) } @@ -59,7 +75,7 @@ impl P2pChannelsState { } fn dispatch_message( - action: ActionWithMeta<&P2pChannelsMessageReceivedAction>, + action: ActionWithMeta, dispatcher: &mut Dispatcher, state: &State, ) -> Result<(), String> @@ -75,7 +91,56 @@ impl P2pChannelsState { let mut is_enabled = |action: Action| dispatcher.push_if_enabled(action, state, time); - let was_expected = match *action.message.clone() { + let was_expected = match *action.message { + ChannelMsg::SignalingDiscovery(msg) => match msg { + SignalingDiscoveryChannelMsg::GetNext => is_enabled( + P2pChannelsSignalingDiscoveryAction::RequestReceived { peer_id }.into(), + ), + SignalingDiscoveryChannelMsg::Discover => is_enabled( + P2pChannelsSignalingDiscoveryAction::DiscoveryRequestReceived { peer_id } + .into(), + ), + SignalingDiscoveryChannelMsg::Discovered { target_public_key } => is_enabled( + P2pChannelsSignalingDiscoveryAction::DiscoveredReceived { + peer_id, + target_public_key, + } + .into(), + ), + SignalingDiscoveryChannelMsg::DiscoveredReject => is_enabled( + P2pChannelsSignalingDiscoveryAction::DiscoveredRejectReceived { peer_id } + .into(), + ), + SignalingDiscoveryChannelMsg::DiscoveredAccept(offer) => is_enabled( + P2pChannelsSignalingDiscoveryAction::DiscoveredAcceptReceived { + peer_id, + offer, + } + .into(), + ), + SignalingDiscoveryChannelMsg::Answer(answer) => is_enabled( + P2pChannelsSignalingDiscoveryAction::AnswerReceived { peer_id, answer }.into(), + ), + }, + ChannelMsg::SignalingExchange(msg) => match msg { + SignalingExchangeChannelMsg::GetNext => is_enabled( + P2pChannelsSignalingExchangeAction::RequestReceived { peer_id }.into(), + ), + SignalingExchangeChannelMsg::OfferToYou { + offerer_pub_key, + offer, + } => is_enabled( + P2pChannelsSignalingExchangeAction::OfferReceived { + peer_id, + offerer_pub_key, + offer, + } + .into(), + ), + SignalingExchangeChannelMsg::Answer(answer) => is_enabled( + P2pChannelsSignalingExchangeAction::AnswerReceived { peer_id, answer }.into(), + ), + }, ChannelMsg::BestTipPropagation(msg) => match msg { BestTipPropagationChannelMsg::GetNext => { is_enabled(P2pChannelsBestTipAction::RequestReceived { peer_id }.into()) @@ -201,6 +266,7 @@ impl P2pChannelsState { }; if !was_expected { + // dbg!(&action.message); let reason = P2pDisconnectionReason::P2pChannelMsgUnexpected(chain_id); dispatcher.push(P2pDisconnectionAction::Init { peer_id, reason }); } diff --git a/p2p/src/channels/p2p_channels_service.rs b/p2p/src/channels/p2p_channels_service.rs index 1c34914d5a..38a6eae8d7 100644 --- a/p2p/src/channels/p2p_channels_service.rs +++ b/p2p/src/channels/p2p_channels_service.rs @@ -1,8 +1,21 @@ -use crate::PeerId; +use crate::{ + identity::{EncryptableType, PublicKey}, + PeerId, +}; use super::{ChannelId, ChannelMsg, MsgId}; pub trait P2pChannelsService: redux::Service { fn channel_open(&mut self, peer_id: PeerId, id: ChannelId); fn channel_send(&mut self, peer_id: PeerId, msg_id: MsgId, msg: ChannelMsg); + fn encrypt( + &mut self, + other_pk: &PublicKey, + message: &T, + ) -> Result; + fn decrypt( + &mut self, + other_pk: &PublicKey, + encrypted: &T::Encrypted, + ) -> Result; } diff --git a/p2p/src/channels/p2p_channels_state.rs b/p2p/src/channels/p2p_channels_state.rs index 4134672f67..30df02cd9d 100644 --- a/p2p/src/channels/p2p_channels_state.rs +++ b/p2p/src/channels/p2p_channels_state.rs @@ -5,6 +5,10 @@ use serde::{Deserialize, Serialize}; use super::{ best_tip::P2pChannelsBestTipState, rpc::{P2pChannelsRpcState, P2pRpcId}, + signaling::{ + discovery::P2pChannelsSignalingDiscoveryState, exchange::P2pChannelsSignalingExchangeState, + P2pChannelsSignalingState, + }, snark::P2pChannelsSnarkState, snark_job_commitment::P2pChannelsSnarkJobCommitmentState, streaming_rpc::P2pChannelsStreamingRpcState, @@ -14,6 +18,7 @@ use super::{ #[derive(Serialize, Deserialize, Debug, Clone)] pub struct P2pChannelsState { + pub signaling: P2pChannelsSignalingState, pub best_tip: P2pChannelsBestTipState, pub transaction: P2pChannelsTransactionState, pub snark: P2pChannelsSnarkState, @@ -27,6 +32,16 @@ pub struct P2pChannelsState { impl P2pChannelsState { pub fn new(enabled_channels: &BTreeSet) -> Self { Self { + signaling: P2pChannelsSignalingState { + discovery: match enabled_channels.contains(&ChannelId::SignalingDiscovery) { + false => P2pChannelsSignalingDiscoveryState::Disabled, + true => P2pChannelsSignalingDiscoveryState::Enabled, + }, + exchange: match enabled_channels.contains(&ChannelId::SignalingExchange) { + false => P2pChannelsSignalingExchangeState::Disabled, + true => P2pChannelsSignalingExchangeState::Enabled, + }, + }, best_tip: match enabled_channels.contains(&ChannelId::BestTipPropagation) { false => P2pChannelsBestTipState::Disabled, true => P2pChannelsBestTipState::Enabled, @@ -73,6 +88,8 @@ impl P2pChannelsState { impl P2pChannelsState { pub fn is_channel_ready(&self, chan_id: ChannelId) -> bool { match chan_id { + ChannelId::SignalingDiscovery => self.signaling.discovery.is_ready(), + ChannelId::SignalingExchange => self.signaling.exchange.is_ready(), ChannelId::BestTipPropagation => self.best_tip.is_ready(), ChannelId::TransactionPropagation => self.transaction.is_ready(), ChannelId::SnarkPropagation => self.snark.is_ready(), diff --git a/p2p/src/channels/rpc/p2p_channels_rpc_reducer.rs b/p2p/src/channels/rpc/p2p_channels_rpc_reducer.rs index c0dbd8a156..04d4fa5cfe 100644 --- a/p2p/src/channels/rpc/p2p_channels_rpc_reducer.rs +++ b/p2p/src/channels/rpc/p2p_channels_rpc_reducer.rs @@ -17,7 +17,7 @@ impl P2pChannelsRpcState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pChannelsRpcAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -83,7 +83,7 @@ impl P2pChannelsRpcState { *next_local_rpc_id += 1; *local = P2pRpcLocalState::Requested { time: meta.time(), - id: *id, + id, request: request.clone(), }; @@ -92,7 +92,7 @@ impl P2pChannelsRpcState { #[cfg(feature = "p2p-libp2p")] if is_libp2p { if let Some((query, data)) = - super::libp2p::internal_request_into_libp2p(*request.clone(), *id) + super::libp2p::internal_request_into_libp2p(*request.clone(), id) { dispatcher.push(P2pNetworkRpcAction::OutgoingQuery { peer_id, @@ -101,7 +101,7 @@ impl P2pChannelsRpcState { }); } if let Some(on_init) = on_init { - dispatcher.push_callback(on_init.clone(), (peer_id, *id, *request.clone())); + dispatcher.push_callback(on_init, (peer_id, id, *request)); } return Ok(()); @@ -109,9 +109,9 @@ impl P2pChannelsRpcState { dispatcher.push(P2pChannelsRpcEffectfulAction::RequestSend { peer_id, - id: *id, - request: request.clone(), - on_init: on_init.clone(), + id, + request, + on_init, }); Ok(()) } @@ -120,7 +120,7 @@ impl P2pChannelsRpcState { let p2p_state: &P2pState = state.substate()?; if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_rpc_timeout { - dispatcher.push_callback(callback.clone(), (peer_id, *id)); + dispatcher.push_callback(callback.clone(), (peer_id, id)); } Ok(()) @@ -163,8 +163,7 @@ impl P2pChannelsRpcState { } if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_rpc_response_received { - dispatcher - .push_callback(callback.clone(), (peer_id, *rpc_id, response.clone())); + dispatcher.push_callback(callback.clone(), (peer_id, rpc_id, response)); } Ok(()) } @@ -180,8 +179,8 @@ impl P2pChannelsRpcState { .pending_requests .push_back(P2pRpcRemotePendingRequestState { time: meta.time(), - id: *id, - request: (**request).clone(), + id, + request: *request.clone(), is_pending: false, }); @@ -189,7 +188,7 @@ impl P2pChannelsRpcState { let p2p_state: &P2pState = state.substate()?; if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_rpc_request_received { - dispatcher.push_callback(callback.clone(), (peer_id, *id, request.clone())); + dispatcher.push_callback(callback.clone(), (peer_id, id, request)); } Ok(()) } @@ -201,7 +200,7 @@ impl P2pChannelsRpcState { ); return Ok(()); }; - if let Some(req) = remote.pending_requests.iter_mut().find(|r| r.id == *id) { + if let Some(req) = remote.pending_requests.iter_mut().find(|r| r.id == id) { req.is_pending = true; } Ok(()) @@ -215,7 +214,7 @@ impl P2pChannelsRpcState { return Ok(()); }; - if let Some(pos) = remote.pending_requests.iter().position(|r| r.id == *id) { + if let Some(pos) = remote.pending_requests.iter().position(|r| r.id == id) { remote.pending_requests.remove(pos); remote.last_responded = meta.time(); } @@ -226,7 +225,7 @@ impl P2pChannelsRpcState { if is_libp2p { if let Some(response) = response { if let Some((response, data)) = - super::libp2p::internal_response_into_libp2p(*response.clone(), *id) + super::libp2p::internal_response_into_libp2p(*response, id) { dispatcher.push(P2pNetworkRpcAction::OutgoingResponse { peer_id, @@ -241,8 +240,8 @@ impl P2pChannelsRpcState { dispatcher.push(P2pChannelsRpcEffectfulAction::ResponseSend { peer_id, - id: *id, - response: response.clone(), + id, + response, }); Ok(()) } diff --git a/p2p/src/channels/rpc_effectful/p2p_channels_rpc_effectful_actions.rs b/p2p/src/channels/rpc_effectful/p2p_channels_rpc_effectful_actions.rs index 14c7d8b638..57fb90b0f8 100644 --- a/p2p/src/channels/rpc_effectful/p2p_channels_rpc_effectful_actions.rs +++ b/p2p/src/channels/rpc_effectful/p2p_channels_rpc_effectful_actions.rs @@ -35,8 +35,8 @@ impl redux::EnablingCondition for P2pChannelsRpcEffectfulAction { } } -impl From for crate::P2pAction { - fn from(a: P2pChannelsRpcEffectfulAction) -> crate::P2pAction { - crate::P2pAction::ChannelsEffectful(P2pChannelsEffectfulAction::Rpc(a)) +impl From for crate::P2pEffectfulAction { + fn from(a: P2pChannelsRpcEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Channels(P2pChannelsEffectfulAction::Rpc(a)) } } diff --git a/p2p/src/channels/signaling/discovery/mod.rs b/p2p/src/channels/signaling/discovery/mod.rs new file mode 100644 index 0000000000..112a35c2c3 --- /dev/null +++ b/p2p/src/channels/signaling/discovery/mod.rs @@ -0,0 +1,33 @@ +mod p2p_channels_signaling_discovery_state; +pub use p2p_channels_signaling_discovery_state::*; + +mod p2p_channels_signaling_discovery_actions; +pub use p2p_channels_signaling_discovery_actions::*; + +mod p2p_channels_signaling_discovery_reducer; + +use binprot_derive::{BinProtRead, BinProtWrite}; +use serde::{Deserialize, Serialize}; + +use crate::{ + identity::PublicKey, + webrtc::{EncryptedAnswer, EncryptedOffer}, +}; + +#[derive(BinProtWrite, BinProtRead, Serialize, Deserialize, Debug, Clone)] +pub enum SignalingDiscoveryChannelMsg { + /// Get next request for connecting 2 peers to each other. + GetNext, + /// Dialer is asking relayer to find available connected peer + /// and start signaling with it. + Discover, + /// Relayer found available connected peer and ready to facilitate + /// signaling. + Discovered { target_public_key: PublicKey }, + /// Dialer rejected target peer. + DiscoveredReject, + /// Dialer accepted target peer and wants to initiate signaling. + DiscoveredAccept(EncryptedOffer), + /// Relayed answer Answer to dialer to relay, if you aren't dialer. + Answer(Option), +} diff --git a/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_actions.rs b/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_actions.rs new file mode 100644 index 0000000000..66f0a3b0b1 --- /dev/null +++ b/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_actions.rs @@ -0,0 +1,277 @@ +use openmina_core::ActionEvent; +use serde::{Deserialize, Serialize}; + +use crate::{ + channels::P2pChannelsAction, + connection::P2pConnectionResponse, + identity::PublicKey, + webrtc::{EncryptedAnswer, EncryptedOffer, Offer}, + P2pState, PeerId, +}; + +use super::{P2pChannelsSignalingDiscoveryState, SignalingDiscoveryState}; + +#[derive(Debug, Clone, Serialize, Deserialize, ActionEvent)] +#[action_event(fields(display(peer_id)))] +pub enum P2pChannelsSignalingDiscoveryAction { + /// Initialize channel. + Init { + peer_id: PeerId, + }, + Pending { + peer_id: PeerId, + }, + /// Channel is ready. + Ready { + peer_id: PeerId, + }, + /// Send request to get next peer discovery request from peer. + RequestSend { + peer_id: PeerId, + }, + DiscoveryRequestReceived { + peer_id: PeerId, + }, + DiscoveredSend { + peer_id: PeerId, + target_public_key: PublicKey, + }, + DiscoveredRejectReceived { + peer_id: PeerId, + }, + DiscoveredAcceptReceived { + peer_id: PeerId, + offer: EncryptedOffer, + }, + AnswerSend { + peer_id: PeerId, + answer: Option, + }, + /// Received request to get next peer discovery request from us. + RequestReceived { + peer_id: PeerId, + }, + DiscoveryRequestSend { + peer_id: PeerId, + }, + DiscoveredReceived { + peer_id: PeerId, + target_public_key: PublicKey, + }, + DiscoveredReject { + peer_id: PeerId, + }, + DiscoveredAccept { + peer_id: PeerId, + offer: Box, + }, + AnswerReceived { + peer_id: PeerId, + answer: Option, + }, + AnswerDecrypted { + peer_id: PeerId, + answer: P2pConnectionResponse, + }, +} + +impl P2pChannelsSignalingDiscoveryAction { + pub fn peer_id(&self) -> &PeerId { + match self { + Self::Init { peer_id } + | Self::Pending { peer_id } + | Self::Ready { peer_id } + | Self::RequestSend { peer_id } + | Self::DiscoveryRequestReceived { peer_id } + | Self::DiscoveredSend { peer_id, .. } + | Self::DiscoveredRejectReceived { peer_id } + | Self::DiscoveredAcceptReceived { peer_id, .. } + | Self::AnswerSend { peer_id, .. } + | Self::RequestReceived { peer_id } + | Self::DiscoveryRequestSend { peer_id, .. } + | Self::DiscoveredReceived { peer_id, .. } + | Self::DiscoveredReject { peer_id, .. } + | Self::DiscoveredAccept { peer_id, .. } + | Self::AnswerReceived { peer_id, .. } + | Self::AnswerDecrypted { peer_id, .. } => peer_id, + } + } +} + +impl redux::EnablingCondition for P2pChannelsSignalingDiscoveryAction { + fn is_enabled(&self, state: &P2pState, now: redux::Timestamp) -> bool { + match self { + P2pChannelsSignalingDiscoveryAction::Init { peer_id } => { + state.get_ready_peer(peer_id).map_or(false, |p| { + matches!( + &p.channels.signaling.discovery, + P2pChannelsSignalingDiscoveryState::Enabled + ) + }) + } + P2pChannelsSignalingDiscoveryAction::Pending { peer_id } => { + state.get_ready_peer(peer_id).map_or(false, |p| { + matches!( + &p.channels.signaling.discovery, + P2pChannelsSignalingDiscoveryState::Init { .. } + ) + }) + } + P2pChannelsSignalingDiscoveryAction::Ready { peer_id } => { + state.get_ready_peer(peer_id).map_or(false, |p| { + matches!( + &p.channels.signaling.discovery, + P2pChannelsSignalingDiscoveryState::Pending { .. } + ) + }) + } + P2pChannelsSignalingDiscoveryAction::RequestSend { peer_id } => { + state.get_ready_peer(peer_id).map_or(false, |p| { + match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { local, .. } => { + match local { + SignalingDiscoveryState::WaitingForRequest { .. } => true, + SignalingDiscoveryState::DiscoveredRejected { time, .. } + | SignalingDiscoveryState::Answered { time, .. } => { + // Allow one discovery request per minute. + // TODO(binier): make configurable + now.checked_sub(*time) + .map_or(false, |dur| dur.as_secs() >= 60) + } + _ => false, + } + } + _ => false, + } + }) + } + P2pChannelsSignalingDiscoveryAction::DiscoveryRequestReceived { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { local, .. } => { + matches!(local, SignalingDiscoveryState::Requested { .. }) + } + _ => false, + }), + P2pChannelsSignalingDiscoveryAction::DiscoveredSend { + peer_id, + target_public_key, + .. + } => { + let target_peer_id = target_public_key.peer_id(); + let has_peer_requested_discovery = + state.get_ready_peer(peer_id).map_or(false, |p| { + match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { local, .. } => { + matches!(local, SignalingDiscoveryState::DiscoveryRequested { .. }) + } + _ => false, + } + }); + let target_peer_already_discovering_them = + state.get_ready_peer(&target_peer_id).map_or(false, |p| { + p.channels.signaling.sent_discovered_peer_id() == Some(*peer_id) + }); + has_peer_requested_discovery + && !target_peer_already_discovering_them + && state.ready_peers_iter().all(|(_, p)| { + p.channels.signaling.sent_discovered_peer_id() != Some(target_peer_id) + }) + } + P2pChannelsSignalingDiscoveryAction::DiscoveredRejectReceived { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { local, .. } => { + matches!(local, SignalingDiscoveryState::Discovered { .. }) + } + _ => false, + }), + P2pChannelsSignalingDiscoveryAction::DiscoveredAcceptReceived { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { local, .. } => { + matches!(local, SignalingDiscoveryState::Discovered { .. }) + } + _ => false, + }), + P2pChannelsSignalingDiscoveryAction::AnswerSend { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { local, .. } => { + matches!(local, SignalingDiscoveryState::DiscoveredAccepted { .. }) + } + _ => false, + }), + P2pChannelsSignalingDiscoveryAction::RequestReceived { peer_id } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { remote, .. } => matches!( + remote, + SignalingDiscoveryState::WaitingForRequest { .. } + | SignalingDiscoveryState::DiscoveredRejected { .. } + | SignalingDiscoveryState::Answered { .. } + ), + _ => false, + }), + // TODO(binier): constrain interval between these requests + // to handle malicious peers. + P2pChannelsSignalingDiscoveryAction::DiscoveryRequestSend { peer_id, .. } => { + !state.already_has_min_peers() + && state.get_ready_peer(peer_id).map_or(false, |p| { + match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { remote, .. } => { + matches!(remote, SignalingDiscoveryState::Requested { .. }) + } + _ => false, + } + }) + } + P2pChannelsSignalingDiscoveryAction::DiscoveredReceived { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { remote, .. } => { + matches!(remote, SignalingDiscoveryState::DiscoveryRequested { .. }) + } + _ => false, + }), + P2pChannelsSignalingDiscoveryAction::DiscoveredReject { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { remote, .. } => { + matches!(remote, SignalingDiscoveryState::Discovered { .. }) + } + _ => false, + }), + P2pChannelsSignalingDiscoveryAction::DiscoveredAccept { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { remote, .. } => { + matches!(remote, SignalingDiscoveryState::Discovered { .. }) + } + _ => false, + }), + P2pChannelsSignalingDiscoveryAction::AnswerReceived { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { remote, .. } => { + matches!(remote, SignalingDiscoveryState::DiscoveredAccepted { .. }) + } + _ => false, + }), + P2pChannelsSignalingDiscoveryAction::AnswerDecrypted { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.discovery { + P2pChannelsSignalingDiscoveryState::Ready { remote, .. } => { + matches!(remote, SignalingDiscoveryState::DiscoveredAccepted { .. }) + } + _ => false, + }), + } + } +} + +impl From for crate::P2pAction { + fn from(action: P2pChannelsSignalingDiscoveryAction) -> Self { + Self::Channels(P2pChannelsAction::SignalingDiscovery(action)) + } +} diff --git a/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_reducer.rs b/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_reducer.rs new file mode 100644 index 0000000000..b934647c95 --- /dev/null +++ b/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_reducer.rs @@ -0,0 +1,431 @@ +use openmina_core::{bug_condition, Substate}; +use redux::ActionWithMeta; + +use crate::{ + channels::signaling::{ + discovery_effectful::P2pChannelsSignalingDiscoveryEffectfulAction, + exchange::P2pChannelsSignalingExchangeAction, + }, + connection::{ + outgoing::{P2pConnectionOutgoingAction, P2pConnectionOutgoingInitOpts}, + P2pConnectionErrorResponse, P2pConnectionResponse, + }, + webrtc::SignalingMethod, + P2pState, +}; + +use super::{ + P2pChannelsSignalingDiscoveryAction, P2pChannelsSignalingDiscoveryState, + SignalingDiscoveryChannelMsg, SignalingDiscoveryState, +}; + +impl P2pChannelsSignalingDiscoveryState { + /// Substate is accessed + pub fn reducer( + mut state_context: Substate, + action: ActionWithMeta, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let (action, meta) = action.split(); + let p2p_state = state_context.get_substate_mut()?; + let peer_id = *action.peer_id(); + let state = &mut p2p_state + .get_ready_peer_mut(&peer_id) + .ok_or_else(|| format!("Peer state not found for: {action:?}"))? + .channels + .signaling + .discovery; + + match action { + P2pChannelsSignalingDiscoveryAction::Init { .. } => { + *state = Self::Init { time: meta.time() }; + + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(P2pChannelsSignalingDiscoveryEffectfulAction::Init { peer_id }); + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::Pending { .. } => { + *state = Self::Pending { time: meta.time() }; + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::Ready { .. } => { + *state = Self::Ready { + time: meta.time(), + local: SignalingDiscoveryState::WaitingForRequest { time: meta.time() }, + remote: SignalingDiscoveryState::WaitingForRequest { time: meta.time() }, + }; + + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(P2pChannelsSignalingDiscoveryAction::RequestSend { peer_id }); + + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::RequestSend { .. } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::RequestSend`, state: {state:?}", + ); + return Ok(()); + }; + *local = SignalingDiscoveryState::Requested { time: meta.time() }; + + let dispatcher = state_context.into_dispatcher(); + let message = SignalingDiscoveryChannelMsg::GetNext; + dispatcher.push(P2pChannelsSignalingDiscoveryEffectfulAction::MessageSend { + peer_id, + message, + }); + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::DiscoveryRequestReceived { .. } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveryRequestReceived`, state: {state:?}", + ); + return Ok(()); + }; + + *local = SignalingDiscoveryState::DiscoveryRequested { time: meta.time() }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let state: &P2pState = state.substate()?; + state.webrtc_discovery_respond_with_availble_peers(dispatcher, meta.time()); + + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::DiscoveredSend { + target_public_key, .. + } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredSend`, state: {state:?}", + ); + return Ok(()); + }; + + *local = SignalingDiscoveryState::Discovered { + time: meta.time(), + target_public_key: target_public_key.clone(), + }; + + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(P2pChannelsSignalingDiscoveryEffectfulAction::MessageSend { + peer_id, + message: SignalingDiscoveryChannelMsg::Discovered { + target_public_key: target_public_key.clone(), + }, + }); + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::DiscoveredRejectReceived { .. } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredRejectReceived`, state: {state:?}", + ); + return Ok(()); + }; + + let target_public_key = match local { + SignalingDiscoveryState::Discovered { + target_public_key, .. + } => target_public_key.clone(), + state => { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredRejectReceived`, state: {state:?}", + ); + return Ok(()); + } + }; + + *local = SignalingDiscoveryState::DiscoveredRejected { + time: meta.time(), + target_public_key, + }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let state: &P2pState = state.substate()?; + state.webrtc_discovery_respond_with_availble_peers(dispatcher, meta.time()); + + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::DiscoveredAcceptReceived { offer, .. } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredAcceptReceived`, state: {state:?}", + ); + return Ok(()); + }; + + let target_public_key = match local { + SignalingDiscoveryState::Discovered { + target_public_key, .. + } => target_public_key.clone(), + state => { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredAcceptReceived`, state: {state:?}", + ); + return Ok(()); + } + }; + + *local = SignalingDiscoveryState::DiscoveredAccepted { + time: meta.time(), + target_public_key: target_public_key.clone(), + }; + + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(P2pChannelsSignalingExchangeAction::OfferSend { + peer_id: target_public_key.peer_id(), + offerer_pub_key: peer_id.to_public_key().unwrap(), + offer: offer.clone(), + }); + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::AnswerSend { answer, .. } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::AnswerSend`, state: {state:?}", + ); + return Ok(()); + }; + + *local = SignalingDiscoveryState::Answered { time: meta.time() }; + + let dispatcher = state_context.into_dispatcher(); + let message = SignalingDiscoveryChannelMsg::Answer(answer.clone()); + dispatcher.push(P2pChannelsSignalingDiscoveryEffectfulAction::MessageSend { + peer_id, + message, + }); + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::RequestReceived { .. } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::RequestReceived`, state: {state:?}", + ); + return Ok(()); + }; + + *remote = SignalingDiscoveryState::Requested { time: meta.time() }; + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::DiscoveryRequestSend { .. } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveryRequestSend`, state: {state:?}", + ); + return Ok(()); + }; + + *remote = SignalingDiscoveryState::DiscoveryRequested { time: meta.time() }; + let dispatcher = state_context.into_dispatcher(); + let message = SignalingDiscoveryChannelMsg::Discover; + dispatcher.push(P2pChannelsSignalingDiscoveryEffectfulAction::MessageSend { + peer_id, + message, + }); + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::DiscoveredReceived { + target_public_key, .. + } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredReceived`, state: {state:?}", + ); + return Ok(()); + }; + + *remote = SignalingDiscoveryState::Discovered { + time: meta.time(), + target_public_key: target_public_key.clone(), + }; + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let state: &P2pState = state.substate()?; + let action = P2pConnectionOutgoingAction::Init { + opts: P2pConnectionOutgoingInitOpts::WebRTC { + peer_id: target_public_key.peer_id(), + signaling: SignalingMethod::P2p { + relay_peer_id: peer_id, + }, + }, + rpc_id: None, + }; + let accepted = redux::EnablingCondition::is_enabled(&action, state, meta.time()); + if accepted { + dispatcher.push(action); + } else { + dispatcher + .push(P2pChannelsSignalingDiscoveryAction::DiscoveredReject { peer_id }); + } + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::DiscoveredReject { .. } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredReject`, state: {state:?}", + ); + return Ok(()); + }; + + let target_public_key = match remote { + SignalingDiscoveryState::Discovered { + target_public_key, .. + } => target_public_key.clone(), + state => { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredReject`, state: {state:?}", + ); + return Ok(()); + } + }; + + *remote = SignalingDiscoveryState::DiscoveredRejected { + time: meta.time(), + target_public_key, + }; + let dispatcher = state_context.into_dispatcher(); + let message = SignalingDiscoveryChannelMsg::DiscoveredReject; + dispatcher.push(P2pChannelsSignalingDiscoveryEffectfulAction::MessageSend { + peer_id, + message, + }); + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::DiscoveredAccept { offer, .. } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredAccept`, state: {state:?}", + ); + return Ok(()); + }; + + let target_public_key = match remote { + SignalingDiscoveryState::Discovered { + target_public_key, .. + } => target_public_key.clone(), + state => { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::DiscoveredAccept`, state: {state:?}", + ); + return Ok(()); + } + }; + + *remote = SignalingDiscoveryState::DiscoveredAccepted { + time: meta.time(), + target_public_key: target_public_key.clone(), + }; + let dispatcher = state_context.into_dispatcher(); + // TODO(binier): this action might not be enabled, in + // which case we sshould be rejecting discovered peer. + dispatcher.push(P2pConnectionOutgoingAction::OfferSendSuccess { + peer_id: target_public_key.peer_id(), + }); + dispatcher.push( + P2pChannelsSignalingDiscoveryEffectfulAction::OfferEncryptAndSend { + peer_id, + pub_key: target_public_key, + offer: offer.clone(), + }, + ); + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::AnswerReceived { answer, .. } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::AnswerReceived`, state: {state:?}", + ); + return Ok(()); + }; + + let target_public_key = match remote { + SignalingDiscoveryState::DiscoveredAccepted { + target_public_key, .. + } => target_public_key.clone(), + state => { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::AnswerReceived`, state: {state:?}", + ); + return Ok(()); + } + }; + + let dispatcher = state_context.into_dispatcher(); + match answer { + // TODO(binier): custom error + None => dispatcher.push(P2pConnectionOutgoingAction::AnswerRecvError { + peer_id: target_public_key.peer_id(), + error: P2pConnectionErrorResponse::InternalError, + }), + Some(answer) => dispatcher.push( + P2pChannelsSignalingDiscoveryEffectfulAction::AnswerDecrypt { + peer_id, + pub_key: target_public_key, + answer: answer.clone(), + }, + ), + } + Ok(()) + } + P2pChannelsSignalingDiscoveryAction::AnswerDecrypted { answer, .. } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::AnswerDecrypted`, state: {state:?}", + ); + return Ok(()); + }; + + let target_public_key = match remote { + SignalingDiscoveryState::DiscoveredAccepted { + target_public_key, .. + } => target_public_key.clone(), + state => { + bug_condition!( + "Invalid state for `P2pChannelsSignalingDiscoveryAction::AnswerDecrypted`, state: {state:?}", + ); + return Ok(()); + } + }; + + *remote = SignalingDiscoveryState::Answered { time: meta.time() }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + match answer { + P2pConnectionResponse::Accepted(answer) => { + dispatcher.push(P2pConnectionOutgoingAction::AnswerRecvSuccess { + peer_id: target_public_key.peer_id(), + answer: answer.clone(), + }) + } + P2pConnectionResponse::Rejected(reason) => { + dispatcher.push(P2pConnectionOutgoingAction::AnswerRecvError { + peer_id: target_public_key.peer_id(), + error: P2pConnectionErrorResponse::Rejected(reason), + }) + } + P2pConnectionResponse::SignalDecryptionFailed => { + dispatcher.push(P2pConnectionOutgoingAction::AnswerRecvError { + peer_id: target_public_key.peer_id(), + error: P2pConnectionErrorResponse::SignalDecryptionFailed, + }) + } + P2pConnectionResponse::InternalError => { + dispatcher.push(P2pConnectionOutgoingAction::AnswerRecvError { + peer_id: target_public_key.peer_id(), + error: P2pConnectionErrorResponse::InternalError, + }) + } + } + + let state: &P2pState = state.substate()?; + state.webrtc_discovery_respond_with_availble_peers(dispatcher, meta.time()); + Ok(()) + } + } + } +} diff --git a/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_state.rs b/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_state.rs new file mode 100644 index 0000000000..06ba5de293 --- /dev/null +++ b/p2p/src/channels/signaling/discovery/p2p_channels_signaling_discovery_state.rs @@ -0,0 +1,57 @@ +use serde::{Deserialize, Serialize}; + +use crate::identity::PublicKey; + +#[allow(clippy::large_enum_variant)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum P2pChannelsSignalingDiscoveryState { + Disabled, + Enabled, + Init { + time: redux::Timestamp, + }, + Pending { + time: redux::Timestamp, + }, + Ready { + time: redux::Timestamp, + /// We are the requestors here. + local: SignalingDiscoveryState, + /// We are the responders here. + remote: SignalingDiscoveryState, + }, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum SignalingDiscoveryState { + WaitingForRequest { + time: redux::Timestamp, + }, + Requested { + time: redux::Timestamp, + }, + DiscoveryRequested { + time: redux::Timestamp, + }, + Discovered { + time: redux::Timestamp, + target_public_key: PublicKey, + }, + DiscoveredRejected { + time: redux::Timestamp, + target_public_key: PublicKey, + }, + DiscoveredAccepted { + time: redux::Timestamp, + target_public_key: PublicKey, + }, + Answered { + time: redux::Timestamp, + }, +} + +impl P2pChannelsSignalingDiscoveryState { + pub fn is_ready(&self) -> bool { + matches!(self, Self::Ready { .. }) + } +} diff --git a/p2p/src/channels/signaling/discovery_effectful/mod.rs b/p2p/src/channels/signaling/discovery_effectful/mod.rs new file mode 100644 index 0000000000..ae681677f3 --- /dev/null +++ b/p2p/src/channels/signaling/discovery_effectful/mod.rs @@ -0,0 +1,4 @@ +mod p2p_channels_signaling_discovery_effectful_actions; +pub use p2p_channels_signaling_discovery_effectful_actions::P2pChannelsSignalingDiscoveryEffectfulAction; + +mod p2p_channels_signaling_discovery_effectful_effects; diff --git a/p2p/src/channels/signaling/discovery_effectful/p2p_channels_signaling_discovery_effectful_actions.rs b/p2p/src/channels/signaling/discovery_effectful/p2p_channels_signaling_discovery_effectful_actions.rs new file mode 100644 index 0000000000..20e133e489 --- /dev/null +++ b/p2p/src/channels/signaling/discovery_effectful/p2p_channels_signaling_discovery_effectful_actions.rs @@ -0,0 +1,44 @@ +use openmina_core::ActionEvent; +use serde::{Deserialize, Serialize}; + +use crate::{ + channels::{signaling::discovery::SignalingDiscoveryChannelMsg, P2pChannelsEffectfulAction}, + connection::Offer, + identity::PublicKey, + webrtc::EncryptedAnswer, + P2pState, PeerId, +}; + +#[derive(Debug, Clone, Serialize, Deserialize, ActionEvent)] +#[action_event(fields(display(peer_id)))] +pub enum P2pChannelsSignalingDiscoveryEffectfulAction { + Init { + peer_id: PeerId, + }, + MessageSend { + peer_id: PeerId, + message: SignalingDiscoveryChannelMsg, + }, + OfferEncryptAndSend { + peer_id: PeerId, + pub_key: PublicKey, + offer: Box, + }, + AnswerDecrypt { + peer_id: PeerId, + pub_key: PublicKey, + answer: EncryptedAnswer, + }, +} + +impl redux::EnablingCondition for P2pChannelsSignalingDiscoveryEffectfulAction { + fn is_enabled(&self, _state: &P2pState, _time: redux::Timestamp) -> bool { + true + } +} + +impl From for crate::P2pEffectfulAction { + fn from(action: P2pChannelsSignalingDiscoveryEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Channels(P2pChannelsEffectfulAction::SignalingDiscovery(action)) + } +} diff --git a/p2p/src/channels/signaling/discovery_effectful/p2p_channels_signaling_discovery_effectful_effects.rs b/p2p/src/channels/signaling/discovery_effectful/p2p_channels_signaling_discovery_effectful_effects.rs new file mode 100644 index 0000000000..b95e028cfd --- /dev/null +++ b/p2p/src/channels/signaling/discovery_effectful/p2p_channels_signaling_discovery_effectful_effects.rs @@ -0,0 +1,75 @@ +use redux::ActionMeta; + +use crate::{ + channels::{ + signaling::discovery::{P2pChannelsSignalingDiscoveryAction, SignalingDiscoveryChannelMsg}, + ChannelId, MsgId, + }, + connection::P2pConnectionResponse, + P2pChannelsService, +}; + +use super::P2pChannelsSignalingDiscoveryEffectfulAction; + +impl P2pChannelsSignalingDiscoveryEffectfulAction { + pub fn effects(self, _meta: &ActionMeta, store: &mut Store) + where + Store: crate::P2pStore, + Store::Service: P2pChannelsService, + { + match self { + P2pChannelsSignalingDiscoveryEffectfulAction::Init { peer_id } => { + store + .service() + .channel_open(peer_id, ChannelId::SignalingDiscovery); + store.dispatch(P2pChannelsSignalingDiscoveryAction::Pending { peer_id }); + } + P2pChannelsSignalingDiscoveryEffectfulAction::MessageSend { peer_id, message } => { + message_send(store.service(), peer_id, message); + } + P2pChannelsSignalingDiscoveryEffectfulAction::OfferEncryptAndSend { + peer_id, + pub_key, + offer, + } => match store.service().encrypt(&pub_key, &*offer) { + Err(()) => { + // todo!("Failed to encrypt webrtc offer. Handle it.") + } + Ok(offer) => { + let message = SignalingDiscoveryChannelMsg::DiscoveredAccept(offer); + message_send(store.service(), peer_id, message); + } + }, + P2pChannelsSignalingDiscoveryEffectfulAction::AnswerDecrypt { + peer_id, + pub_key, + answer, + } => { + match store + .service() + .decrypt::(&pub_key, &answer) + { + Err(()) => { + store.dispatch(P2pChannelsSignalingDiscoveryAction::AnswerDecrypted { + peer_id, + answer: P2pConnectionResponse::SignalDecryptionFailed, + }); + } + Ok(answer) => { + store.dispatch(P2pChannelsSignalingDiscoveryAction::AnswerDecrypted { + peer_id, + answer, + }); + } + } + } + } + } +} + +fn message_send(service: &mut S, peer_id: crate::PeerId, message: SignalingDiscoveryChannelMsg) +where + S: P2pChannelsService, +{ + service.channel_send(peer_id, MsgId::first(), message.into()) +} diff --git a/p2p/src/channels/signaling/exchange/mod.rs b/p2p/src/channels/signaling/exchange/mod.rs new file mode 100644 index 0000000000..223eaa27d5 --- /dev/null +++ b/p2p/src/channels/signaling/exchange/mod.rs @@ -0,0 +1,28 @@ +mod p2p_channels_signaling_exchange_state; +pub use p2p_channels_signaling_exchange_state::*; + +mod p2p_channels_signaling_exchange_actions; +pub use p2p_channels_signaling_exchange_actions::*; + +mod p2p_channels_signaling_exchange_reducer; + +use binprot_derive::{BinProtRead, BinProtWrite}; +use serde::{Deserialize, Serialize}; + +use crate::{ + identity::PublicKey, + webrtc::{EncryptedAnswer, EncryptedOffer}, +}; + +#[derive(BinProtWrite, BinProtRead, Serialize, Deserialize, Debug, Clone)] +pub enum SignalingExchangeChannelMsg { + /// Get next incoming offer to us. + GetNext, + /// Relayed offer from dialer to you. + OfferToYou { + offerer_pub_key: PublicKey, + offer: EncryptedOffer, + }, + /// Answer to dialer to relay, if you aren't dialer. + Answer(Option), +} diff --git a/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_actions.rs b/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_actions.rs new file mode 100644 index 0000000000..93d64d4f6d --- /dev/null +++ b/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_actions.rs @@ -0,0 +1,187 @@ +use openmina_core::ActionEvent; +use serde::{Deserialize, Serialize}; + +use crate::{ + channels::P2pChannelsAction, + connection::P2pConnectionResponse, + identity::PublicKey, + webrtc::{EncryptedAnswer, EncryptedOffer, Offer}, + P2pState, PeerId, +}; + +use super::{P2pChannelsSignalingExchangeState, SignalingExchangeState}; + +#[derive(Debug, Clone, Serialize, Deserialize, ActionEvent)] +#[action_event(fields(display(peer_id)))] +pub enum P2pChannelsSignalingExchangeAction { + /// Initialize channel. + Init { + peer_id: PeerId, + }, + Pending { + peer_id: PeerId, + }, + /// Channel is ready. + Ready { + peer_id: PeerId, + }, + /// Send request to get next offer/incoming connection from peer. + RequestSend { + peer_id: PeerId, + }, + OfferReceived { + peer_id: PeerId, + offerer_pub_key: PublicKey, + offer: EncryptedOffer, + }, + OfferDecryptError { + peer_id: PeerId, + }, + OfferDecryptSuccess { + peer_id: PeerId, + offer: Offer, + }, + AnswerSend { + peer_id: PeerId, + answer: P2pConnectionResponse, + }, + /// Received request to get next offer/incoming connection from peer. + RequestReceived { + peer_id: PeerId, + }, + OfferSend { + peer_id: PeerId, + offerer_pub_key: PublicKey, + offer: EncryptedOffer, + }, + AnswerReceived { + peer_id: PeerId, + answer: Option, + }, +} + +impl P2pChannelsSignalingExchangeAction { + pub fn peer_id(&self) -> &PeerId { + match self { + Self::Init { peer_id } + | Self::Pending { peer_id } + | Self::Ready { peer_id } + | Self::RequestSend { peer_id } + | Self::OfferReceived { peer_id, .. } + | Self::OfferDecryptError { peer_id, .. } + | Self::OfferDecryptSuccess { peer_id, .. } + | Self::AnswerSend { peer_id, .. } + | Self::RequestReceived { peer_id } + | Self::OfferSend { peer_id, .. } + | Self::AnswerReceived { peer_id, .. } => peer_id, + } + } +} + +impl redux::EnablingCondition for P2pChannelsSignalingExchangeAction { + fn is_enabled(&self, state: &P2pState, _time: redux::Timestamp) -> bool { + match self { + P2pChannelsSignalingExchangeAction::Init { peer_id } => { + state.get_ready_peer(peer_id).map_or(false, |p| { + matches!( + &p.channels.signaling.exchange, + P2pChannelsSignalingExchangeState::Enabled + ) + }) + } + P2pChannelsSignalingExchangeAction::Pending { peer_id } => { + state.get_ready_peer(peer_id).map_or(false, |p| { + matches!( + &p.channels.signaling.exchange, + P2pChannelsSignalingExchangeState::Init { .. } + ) + }) + } + P2pChannelsSignalingExchangeAction::Ready { peer_id } => { + state.get_ready_peer(peer_id).map_or(false, |p| { + matches!( + &p.channels.signaling.exchange, + P2pChannelsSignalingExchangeState::Pending { .. } + ) + }) + } + P2pChannelsSignalingExchangeAction::RequestSend { peer_id } => { + !state.already_has_max_peers() + && state.get_ready_peer(peer_id).map_or(false, |p| { + match &p.channels.signaling.exchange { + P2pChannelsSignalingExchangeState::Ready { local, .. } => matches!( + local, + SignalingExchangeState::WaitingForRequest { .. } + | SignalingExchangeState::Answered { .. }, + ), + _ => false, + } + }) + } + P2pChannelsSignalingExchangeAction::OfferReceived { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.exchange { + P2pChannelsSignalingExchangeState::Ready { local, .. } => { + matches!(local, SignalingExchangeState::Requested { .. }) + } + _ => false, + }), + P2pChannelsSignalingExchangeAction::OfferDecryptError { peer_id } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.exchange { + P2pChannelsSignalingExchangeState::Ready { local, .. } => { + matches!(local, SignalingExchangeState::Offered { .. }) + } + _ => false, + }), + P2pChannelsSignalingExchangeAction::OfferDecryptSuccess { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.exchange { + P2pChannelsSignalingExchangeState::Ready { local, .. } => { + matches!(local, SignalingExchangeState::Offered { .. }) + } + _ => false, + }), + P2pChannelsSignalingExchangeAction::AnswerSend { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.exchange { + P2pChannelsSignalingExchangeState::Ready { local, .. } => { + matches!(local, SignalingExchangeState::Offered { .. }) + } + _ => false, + }), + P2pChannelsSignalingExchangeAction::RequestReceived { peer_id } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.exchange { + P2pChannelsSignalingExchangeState::Ready { remote, .. } => matches!( + remote, + SignalingExchangeState::WaitingForRequest { .. } + | SignalingExchangeState::Answered { .. } + ), + _ => false, + }), + P2pChannelsSignalingExchangeAction::OfferSend { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.exchange { + P2pChannelsSignalingExchangeState::Ready { remote, .. } => { + matches!(remote, SignalingExchangeState::Requested { .. }) + } + _ => false, + }), + P2pChannelsSignalingExchangeAction::AnswerReceived { peer_id, .. } => state + .get_ready_peer(peer_id) + .map_or(false, |p| match &p.channels.signaling.exchange { + P2pChannelsSignalingExchangeState::Ready { remote, .. } => { + matches!(remote, SignalingExchangeState::Offered { .. }) + } + _ => false, + }), + } + } +} + +impl From for crate::P2pAction { + fn from(action: P2pChannelsSignalingExchangeAction) -> Self { + Self::Channels(P2pChannelsAction::SignalingExchange(action)) + } +} diff --git a/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_reducer.rs b/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_reducer.rs new file mode 100644 index 0000000000..737d6e2e78 --- /dev/null +++ b/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_reducer.rs @@ -0,0 +1,247 @@ +use openmina_core::{bug_condition, Substate}; +use redux::ActionWithMeta; + +use crate::{ + channels::signaling::{ + discovery::P2pChannelsSignalingDiscoveryAction, + exchange_effectful::P2pChannelsSignalingExchangeEffectfulAction, + }, + connection::{ + incoming::{ + IncomingSignalingMethod, P2pConnectionIncomingAction, P2pConnectionIncomingInitOpts, + }, + P2pConnectionResponse, + }, + P2pState, +}; + +use super::{ + P2pChannelsSignalingExchangeAction, P2pChannelsSignalingExchangeState, + SignalingExchangeChannelMsg, SignalingExchangeState, +}; + +impl P2pChannelsSignalingExchangeState { + /// Substate is accessed + pub fn reducer( + mut state_context: Substate, + action: ActionWithMeta, + ) -> Result<(), String> + where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let (action, meta) = action.split(); + let p2p_state = state_context.get_substate_mut()?; + let peer_id = *action.peer_id(); + let state = &mut p2p_state + .get_ready_peer_mut(&peer_id) + .ok_or_else(|| format!("Peer state not found for: {action:?}"))? + .channels + .signaling + .exchange; + + match action { + P2pChannelsSignalingExchangeAction::Init { .. } => { + *state = Self::Init { time: meta.time() }; + + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(P2pChannelsSignalingExchangeEffectfulAction::Init { peer_id }); + Ok(()) + } + P2pChannelsSignalingExchangeAction::Pending { .. } => { + *state = Self::Pending { time: meta.time() }; + Ok(()) + } + P2pChannelsSignalingExchangeAction::Ready { .. } => { + *state = Self::Ready { + time: meta.time(), + local: SignalingExchangeState::WaitingForRequest { time: meta.time() }, + remote: SignalingExchangeState::WaitingForRequest { time: meta.time() }, + }; + + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(P2pChannelsSignalingExchangeAction::RequestSend { peer_id }); + + Ok(()) + } + P2pChannelsSignalingExchangeAction::RequestSend { .. } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingExchangeAction::RequestSend`, state: {state:?}", + ); + return Ok(()); + }; + *local = SignalingExchangeState::Requested { time: meta.time() }; + + let dispatcher = state_context.into_dispatcher(); + let message = SignalingExchangeChannelMsg::GetNext; + dispatcher.push(P2pChannelsSignalingExchangeEffectfulAction::MessageSend { + peer_id, + message, + }); + Ok(()) + } + P2pChannelsSignalingExchangeAction::OfferReceived { + offer, + offerer_pub_key, + .. + } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingExchangeAction::OfferReceived`, state: {state:?}", + ); + return Ok(()); + }; + + *local = SignalingExchangeState::Offered { + time: meta.time(), + offerer_pub_key: offerer_pub_key.clone(), + }; + + let dispatcher = state_context.into_dispatcher(); + let offer = offer.clone(); + dispatcher.push(P2pChannelsSignalingExchangeEffectfulAction::OfferDecrypt { + peer_id, + pub_key: offerer_pub_key.clone(), + offer, + }); + Ok(()) + } + P2pChannelsSignalingExchangeAction::OfferDecryptError { .. } => { + let dispatcher = state_context.into_dispatcher(); + let answer = P2pConnectionResponse::SignalDecryptionFailed; + dispatcher.push(P2pChannelsSignalingExchangeAction::AnswerSend { peer_id, answer }); + Ok(()) + } + P2pChannelsSignalingExchangeAction::OfferDecryptSuccess { offer, .. } => { + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let state: &P2pState = state.substate()?; + let opts = P2pConnectionIncomingInitOpts { + peer_id: offer.identity_pub_key.peer_id(), + signaling: IncomingSignalingMethod::P2p { + relay_peer_id: peer_id, + }, + offer: offer.clone().into(), + }; + match state.incoming_accept(opts.peer_id, &opts.offer) { + Ok(_) => { + dispatcher.push(P2pConnectionIncomingAction::Init { opts, rpc_id: None }); + } + Err(reason) => { + let answer = P2pConnectionResponse::Rejected(reason); + dispatcher.push(P2pChannelsSignalingExchangeAction::AnswerSend { + peer_id, + answer, + }); + } + } + Ok(()) + } + P2pChannelsSignalingExchangeAction::AnswerSend { answer, .. } => { + let Self::Ready { local, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingExchangeAction::AnswerSend`, state: {state:?}", + ); + return Ok(()); + }; + + let offerer_pub_key = match local { + SignalingExchangeState::Offered { + offerer_pub_key, .. + } => offerer_pub_key.clone(), + state => { + bug_condition!( + "Invalid state for `P2pChannelsSignalingExchangeAction::AnswerSend`, local state: {state:?}", + ); + return Ok(()); + } + }; + + *local = SignalingExchangeState::Answered { time: meta.time() }; + + let answer = answer.clone(); + let dispatcher = state_context.into_dispatcher(); + dispatcher.push( + P2pChannelsSignalingExchangeEffectfulAction::AnswerEncryptAndSend { + peer_id, + pub_key: offerer_pub_key.clone(), + answer: Some(answer), + }, + ); + dispatcher.push(P2pConnectionIncomingAction::AnswerSendSuccess { + peer_id: offerer_pub_key.peer_id(), + }); + Ok(()) + } + P2pChannelsSignalingExchangeAction::RequestReceived { .. } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingExchangeAction::RequestReceived`, state: {state:?}", + ); + return Ok(()); + }; + + *remote = SignalingExchangeState::Requested { time: meta.time() }; + + let (dispatcher, state) = state_context.into_dispatcher_and_state(); + let state: &P2pState = state.substate()?; + state.webrtc_discovery_respond_with_availble_peers(dispatcher, meta.time()); + Ok(()) + } + P2pChannelsSignalingExchangeAction::OfferSend { + offer, + offerer_pub_key, + .. + } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingExchangeAction::OfferSend`, state: {state:?}", + ); + return Ok(()); + }; + + *remote = SignalingExchangeState::Offered { + time: meta.time(), + offerer_pub_key: offerer_pub_key.clone(), + }; + let dispatcher = state_context.into_dispatcher(); + let message = SignalingExchangeChannelMsg::OfferToYou { + offerer_pub_key: offerer_pub_key.clone(), + offer: offer.clone(), + }; + dispatcher.push(P2pChannelsSignalingExchangeEffectfulAction::MessageSend { + peer_id, + message, + }); + Ok(()) + } + P2pChannelsSignalingExchangeAction::AnswerReceived { answer, .. } => { + let Self::Ready { remote, .. } = state else { + bug_condition!( + "Invalid state for `P2pChannelsSignalingExchangeAction::AnswerReceived`, state: {state:?}", + ); + return Ok(()); + }; + + let offerer_pub_key = match remote { + SignalingExchangeState::Offered { + offerer_pub_key, .. + } => offerer_pub_key.clone(), + state => { + bug_condition!( + "Invalid state for `P2pChannelsSignalingExchangeAction::AnswerReceived`, state: {state:?}", + ); + return Ok(()); + } + }; + *remote = SignalingExchangeState::Answered { time: meta.time() }; + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(P2pChannelsSignalingDiscoveryAction::AnswerSend { + peer_id: offerer_pub_key.peer_id(), + answer: answer.clone(), + }); + Ok(()) + } + } + } +} diff --git a/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_state.rs b/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_state.rs new file mode 100644 index 0000000000..346efa5a30 --- /dev/null +++ b/p2p/src/channels/signaling/exchange/p2p_channels_signaling_exchange_state.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; + +use crate::identity::PublicKey; + +#[allow(clippy::large_enum_variant)] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum P2pChannelsSignalingExchangeState { + Disabled, + Enabled, + Init { + time: redux::Timestamp, + }, + Pending { + time: redux::Timestamp, + }, + Ready { + time: redux::Timestamp, + /// We are the requestors here. + local: SignalingExchangeState, + /// We are the responders here. + remote: SignalingExchangeState, + }, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum SignalingExchangeState { + /// Next offer wasn't requested, so we shouldn't receive/send any offers. + WaitingForRequest { + time: redux::Timestamp, + }, + /// Next offer/incoming connection was requested. Offer will be received + /// once/if peer wants to connect. + Requested { + time: redux::Timestamp, + }, + Offered { + time: redux::Timestamp, + offerer_pub_key: PublicKey, + }, + Answered { + time: redux::Timestamp, + }, +} + +impl P2pChannelsSignalingExchangeState { + pub fn is_ready(&self) -> bool { + matches!(self, Self::Ready { .. }) + } +} diff --git a/p2p/src/channels/signaling/exchange_effectful/mod.rs b/p2p/src/channels/signaling/exchange_effectful/mod.rs new file mode 100644 index 0000000000..89a36e2c5b --- /dev/null +++ b/p2p/src/channels/signaling/exchange_effectful/mod.rs @@ -0,0 +1,4 @@ +mod p2p_channels_signaling_exchange_effectful_actions; +pub use p2p_channels_signaling_exchange_effectful_actions::P2pChannelsSignalingExchangeEffectfulAction; + +mod p2p_channels_signaling_exchange_effectful_effects; diff --git a/p2p/src/channels/signaling/exchange_effectful/p2p_channels_signaling_exchange_effectful_actions.rs b/p2p/src/channels/signaling/exchange_effectful/p2p_channels_signaling_exchange_effectful_actions.rs new file mode 100644 index 0000000000..6daa07af78 --- /dev/null +++ b/p2p/src/channels/signaling/exchange_effectful/p2p_channels_signaling_exchange_effectful_actions.rs @@ -0,0 +1,44 @@ +use openmina_core::ActionEvent; +use serde::{Deserialize, Serialize}; + +use crate::{ + channels::{signaling::exchange::SignalingExchangeChannelMsg, P2pChannelsEffectfulAction}, + connection::P2pConnectionResponse, + identity::PublicKey, + webrtc::EncryptedOffer, + P2pState, PeerId, +}; + +#[derive(Debug, Clone, Serialize, Deserialize, ActionEvent)] +#[action_event(fields(display(peer_id)))] +pub enum P2pChannelsSignalingExchangeEffectfulAction { + Init { + peer_id: PeerId, + }, + MessageSend { + peer_id: PeerId, + message: SignalingExchangeChannelMsg, + }, + OfferDecrypt { + peer_id: PeerId, + pub_key: PublicKey, + offer: EncryptedOffer, + }, + AnswerEncryptAndSend { + peer_id: PeerId, + pub_key: PublicKey, + answer: Option, + }, +} + +impl redux::EnablingCondition for P2pChannelsSignalingExchangeEffectfulAction { + fn is_enabled(&self, _state: &P2pState, _time: redux::Timestamp) -> bool { + true + } +} + +impl From for crate::P2pEffectfulAction { + fn from(action: P2pChannelsSignalingExchangeEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Channels(P2pChannelsEffectfulAction::SignalingExchange(action)) + } +} diff --git a/p2p/src/channels/signaling/exchange_effectful/p2p_channels_signaling_exchange_effectful_effects.rs b/p2p/src/channels/signaling/exchange_effectful/p2p_channels_signaling_exchange_effectful_effects.rs new file mode 100644 index 0000000000..d7fa962bc4 --- /dev/null +++ b/p2p/src/channels/signaling/exchange_effectful/p2p_channels_signaling_exchange_effectful_effects.rs @@ -0,0 +1,96 @@ +use openmina_core::bug_condition; +use redux::ActionMeta; + +use crate::{ + channels::{ + signaling::exchange::{P2pChannelsSignalingExchangeAction, SignalingExchangeChannelMsg}, + ChannelId, MsgId, + }, + webrtc::{EncryptedAnswer, Offer}, + P2pChannelsService, +}; + +use super::P2pChannelsSignalingExchangeEffectfulAction; + +impl P2pChannelsSignalingExchangeEffectfulAction { + pub fn effects(self, _meta: &ActionMeta, store: &mut Store) + where + Store: crate::P2pStore, + Store::Service: P2pChannelsService, + { + match self { + P2pChannelsSignalingExchangeEffectfulAction::Init { peer_id } => { + store + .service() + .channel_open(peer_id, ChannelId::SignalingExchange); + store.dispatch(P2pChannelsSignalingExchangeAction::Pending { peer_id }); + } + P2pChannelsSignalingExchangeEffectfulAction::MessageSend { peer_id, message } => { + message_send(store.service(), peer_id, message); + } + P2pChannelsSignalingExchangeEffectfulAction::OfferDecrypt { + peer_id, + pub_key, + offer, + } => { + match store.service().decrypt::(&pub_key, &offer) { + Err(()) => { + store.dispatch(P2pChannelsSignalingExchangeAction::OfferDecryptError { + peer_id, + }); + } + Ok(offer) if offer.identity_pub_key != pub_key => { + // TODO(binier): propagate specific error. + // This is invalid behavior either from relayer or offerer. + store.dispatch(P2pChannelsSignalingExchangeAction::OfferDecryptError { + peer_id, + }); + } + Ok(offer) => { + store.dispatch(P2pChannelsSignalingExchangeAction::OfferDecryptSuccess { + peer_id, + offer, + }); + } + } + } + P2pChannelsSignalingExchangeEffectfulAction::AnswerEncryptAndSend { + peer_id, + pub_key, + answer, + } => { + let answer = match answer { + None => { + answer_message_send(store.service(), peer_id, None); + return; + } + Some(v) => v, + }; + match store.service().encrypt(&pub_key, &answer) { + Err(()) => bug_condition!("Failed to encrypt webrtc answer. Shouldn't happen since we managed to decrypt sent offer."), + Ok(answer) => { + answer_message_send(store.service(), peer_id, Some(answer)); + } + } + } + } + } +} + +fn answer_message_send(service: &mut S, peer_id: crate::PeerId, answer: Option) +where + S: P2pChannelsService, +{ + message_send( + service, + peer_id, + SignalingExchangeChannelMsg::Answer(answer), + ) +} + +fn message_send(service: &mut S, peer_id: crate::PeerId, message: SignalingExchangeChannelMsg) +where + S: P2pChannelsService, +{ + service.channel_send(peer_id, MsgId::first(), message.into()) +} diff --git a/p2p/src/channels/signaling/mod.rs b/p2p/src/channels/signaling/mod.rs new file mode 100644 index 0000000000..7ee73a0da5 --- /dev/null +++ b/p2p/src/channels/signaling/mod.rs @@ -0,0 +1,79 @@ +//! There are 2 state machines in this module: +//! 1. `discovery` - used for discovering a new target peer and initiating +//! signaling process. +//! 2. `exchange` - used by intermediary peer to relay an offer to the +//! target peer and receive an answer from it. +//! +//! These are the overall steps that happens in these state machines in +//! order to connect two (dialer and listener) peers to each other using +//! intermediary peer (relayer): +//! 1. [discovery] Dialer asks relayer to discover an available peer. +//! 2. [discovery] Relayer responds with available peer's (listener's) public key. +//! 3. [discovery] Dialer accepts/rejects the target peer (listener). +//! 4. [discovery] If dialer accepts the peer, it sends webrtc offer to relayer. +//! 5. [exchange] Relayer relays received webrtc offer to the listener peer. +//! 6. [exchange] Relayer receives webrtc answer from the listener peer. +//! 7. [discovery] Relayer relays the answer to the dialer. + +pub mod discovery; +pub mod discovery_effectful; +pub mod exchange; +pub mod exchange_effectful; + +mod p2p_channels_signaling_state; +pub use p2p_channels_signaling_state::*; + +use std::collections::BTreeSet; + +use discovery::P2pChannelsSignalingDiscoveryAction; + +impl crate::P2pState { + pub(super) fn webrtc_discovery_respond_with_availble_peers( + &self, + dispatcher: &mut redux::Dispatcher, + time: redux::Timestamp, + ) where + State: crate::P2pStateTrait, + Action: crate::P2pActionTrait, + { + let (mut available_peers, requests) = self.ready_peers_iter().fold( + (BTreeSet::new(), BTreeSet::new()), + |(mut available, mut requests), (peer_id, peer)| { + if peer.channels.signaling.is_looking_for_incoming_peer() { + available.insert(peer_id); + } + if peer.channels.signaling.is_looking_for_peer() { + requests.insert(peer_id); + } else if let Some(peer_id) = peer.channels.signaling.sent_discovered_peer_id() { + available.remove(&peer_id); + } + (available, requests) + }, + ); + + /// random shuffle available peers + use rand::{seq::SliceRandom, SeedableRng}; + let mut rng = rand::rngs::StdRng::seed_from_u64(time.into()); + let mut available_peers_ordered = available_peers.iter().copied().collect::>(); + available_peers_ordered.shuffle(&mut rng); + + for &requester in requests { + if available_peers.is_empty() { + break; + } + for &&target_peer_id in &available_peers_ordered { + if target_peer_id == requester || !available_peers.contains(&target_peer_id) { + continue; + } + let action = P2pChannelsSignalingDiscoveryAction::DiscoveredSend { + peer_id: requester, + target_public_key: target_peer_id.to_public_key().unwrap(), + }; + if redux::EnablingCondition::is_enabled(&action, self, time) { + dispatcher.push(action); + available_peers.remove(&target_peer_id); + } + } + } + } +} diff --git a/p2p/src/channels/signaling/p2p_channels_signaling_state.rs b/p2p/src/channels/signaling/p2p_channels_signaling_state.rs new file mode 100644 index 0000000000..a3c2c64f82 --- /dev/null +++ b/p2p/src/channels/signaling/p2p_channels_signaling_state.rs @@ -0,0 +1,76 @@ +use serde::{Deserialize, Serialize}; + +use crate::PeerId; + +use super::{ + discovery::{P2pChannelsSignalingDiscoveryState, SignalingDiscoveryState}, + exchange::{P2pChannelsSignalingExchangeState, SignalingExchangeState}, +}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct P2pChannelsSignalingState { + pub discovery: P2pChannelsSignalingDiscoveryState, + pub exchange: P2pChannelsSignalingExchangeState, +} + +impl P2pChannelsSignalingState { + pub fn am_looking_for_peer(&self) -> bool { + match &self.discovery { + P2pChannelsSignalingDiscoveryState::Ready { remote, .. } => { + matches!(remote, SignalingDiscoveryState::DiscoveryRequested { .. }) + } + _ => false, + } + } + + pub fn received_discovered_peer_id(&self) -> Option { + match &self.discovery { + P2pChannelsSignalingDiscoveryState::Ready { + remote: + SignalingDiscoveryState::Discovered { + target_public_key, .. + } + | SignalingDiscoveryState::DiscoveredAccepted { + target_public_key, .. + }, + .. + } => Some(target_public_key.peer_id()), + _ => None, + } + } + + pub fn is_looking_for_peer(&self) -> bool { + match &self.discovery { + P2pChannelsSignalingDiscoveryState::Ready { local, .. } => { + matches!(local, SignalingDiscoveryState::DiscoveryRequested { .. }) + } + _ => false, + } + } + + pub fn sent_discovered_peer_id(&self) -> Option { + match &self.discovery { + P2pChannelsSignalingDiscoveryState::Ready { + local: + SignalingDiscoveryState::Discovered { + target_public_key, .. + } + | SignalingDiscoveryState::DiscoveredAccepted { + target_public_key, .. + }, + .. + } => Some(target_public_key.peer_id()), + _ => None, + } + } + + pub fn is_looking_for_incoming_peer(&self) -> bool { + matches!( + self.exchange, + P2pChannelsSignalingExchangeState::Ready { + remote: SignalingExchangeState::Requested { .. }, + .. + } + ) + } +} diff --git a/p2p/src/channels/snark/p2p_channels_snark_reducer.rs b/p2p/src/channels/snark/p2p_channels_snark_reducer.rs index c78f8933c2..8fa06efa63 100644 --- a/p2p/src/channels/snark/p2p_channels_snark_reducer.rs +++ b/p2p/src/channels/snark/p2p_channels_snark_reducer.rs @@ -11,7 +11,7 @@ use mina_p2p_messages::{gossip::GossipNetMessageV2, v2}; impl P2pChannelsSnarkState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pChannelsSnarkAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -32,7 +32,7 @@ impl P2pChannelsSnarkState { *state = Self::Init { time: meta.time() }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pChannelsSnarkEffectfulAction::Init { peer_id: *peer_id }); + dispatcher.push(P2pChannelsSnarkEffectfulAction::Init { peer_id }); Ok(()) } P2pChannelsSnarkAction::Pending { .. } => { @@ -61,14 +61,11 @@ impl P2pChannelsSnarkState { }; *local = SnarkPropagationState::Requested { time: meta.time(), - requested_limit: *limit, + requested_limit: limit, }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pChannelsSnarkEffectfulAction::RequestSend { - peer_id: *peer_id, - limit: *limit, - }); + dispatcher.push(P2pChannelsSnarkEffectfulAction::RequestSend { peer_id, limit }); Ok(()) } P2pChannelsSnarkAction::PromiseReceived { promised_count, .. } => { @@ -89,7 +86,7 @@ impl P2pChannelsSnarkState { *local = SnarkPropagationState::Responding { time: meta.time(), requested_limit: *requested_limit, - promised_count: *promised_count, + promised_count, current_count: 0, }; Ok(()) @@ -129,7 +126,7 @@ impl P2pChannelsSnarkState { let p2p_state: &P2pState = state.substate()?; if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_snark_received { - dispatcher.push_callback(callback.clone(), (*peer_id, snark.clone())); + dispatcher.push_callback(callback.clone(), (peer_id, snark)); } Ok(()) @@ -145,7 +142,7 @@ impl P2pChannelsSnarkState { }; *remote = SnarkPropagationState::Requested { time: meta.time(), - requested_limit: *limit, + requested_limit: limit, }; Ok(()) } @@ -181,16 +178,13 @@ impl P2pChannelsSnarkState { }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pChannelsSnarkEffectfulAction::ResponseSend { - peer_id: *peer_id, - snarks: snarks.clone(), - }); + dispatcher.push(P2pChannelsSnarkEffectfulAction::ResponseSend { peer_id, snarks }); Ok(()) } #[cfg(feature = "p2p-libp2p")] P2pChannelsSnarkAction::Libp2pBroadcast { snark, nonce } => { let dispatcher = state_context.into_dispatcher(); - let message = Box::new((snark.statement(), (snark).into())); + let message = Box::new((snark.statement(), (&snark).into())); let message = v2::NetworkPoolSnarkPoolDiffVersionedStableV2::AddSolvedWork(message); let nonce = nonce.into(); let message = Box::new(GossipNetMessageV2::SnarkPoolDiff { message, nonce }); @@ -204,7 +198,7 @@ impl P2pChannelsSnarkState { let p2p_state: &P2pState = state.substate()?; if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_snark_libp2p_received { - dispatcher.push_callback(callback.clone(), (*peer_id, snark.clone())); + dispatcher.push_callback(callback.clone(), (peer_id, snark)); } Ok(()) diff --git a/p2p/src/channels/snark_effectful/p2p_channels_snark_effectful_actions.rs b/p2p/src/channels/snark_effectful/p2p_channels_snark_effectful_actions.rs index 13747c2ecd..a3214d7e26 100644 --- a/p2p/src/channels/snark_effectful/p2p_channels_snark_effectful_actions.rs +++ b/p2p/src/channels/snark_effectful/p2p_channels_snark_effectful_actions.rs @@ -24,8 +24,8 @@ impl redux::EnablingCondition for P2pChannelsSnarkEffectfulAction { } } -impl From for crate::P2pAction { - fn from(action: P2pChannelsSnarkEffectfulAction) -> crate::P2pAction { - crate::P2pAction::ChannelsEffectful(P2pChannelsEffectfulAction::Snark(action)) +impl From for crate::P2pEffectfulAction { + fn from(action: P2pChannelsSnarkEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Channels(P2pChannelsEffectfulAction::Snark(action)) } } diff --git a/p2p/src/channels/snark_job_commitment/p2p_channels_snark_job_commitment_reducer.rs b/p2p/src/channels/snark_job_commitment/p2p_channels_snark_job_commitment_reducer.rs index ab1617a134..14586ab34e 100644 --- a/p2p/src/channels/snark_job_commitment/p2p_channels_snark_job_commitment_reducer.rs +++ b/p2p/src/channels/snark_job_commitment/p2p_channels_snark_job_commitment_reducer.rs @@ -17,7 +17,7 @@ impl P2pChannelsSnarkJobCommitmentState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pChannelsSnarkJobCommitmentAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -73,14 +73,12 @@ impl P2pChannelsSnarkJobCommitmentState { }; *local = SnarkJobCommitmentPropagationState::Requested { time: meta.time(), - requested_limit: *limit, + requested_limit: limit, }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pChannelsSnarkJobCommitmentAction::RequestSend { - peer_id, - limit: *limit, - }); + dispatcher + .push(P2pChannelsSnarkJobCommitmentAction::RequestSend { peer_id, limit }); Ok(()) } P2pChannelsSnarkJobCommitmentAction::PromiseReceived { promised_count, .. } => { @@ -105,7 +103,7 @@ impl P2pChannelsSnarkJobCommitmentState { *local = SnarkJobCommitmentPropagationState::Responding { time: meta.time(), requested_limit: *requested_limit, - promised_count: *promised_count, + promised_count, current_count: 0, }; Ok(()) @@ -151,7 +149,7 @@ impl P2pChannelsSnarkJobCommitmentState { .callbacks .on_p2p_channels_snark_job_commitment_received { - dispatcher.push_callback(callback.clone(), (peer_id, commitment.clone())); + dispatcher.push_callback(callback.clone(), (peer_id, commitment)); } Ok(()) } @@ -165,7 +163,7 @@ impl P2pChannelsSnarkJobCommitmentState { }; *remote = SnarkJobCommitmentPropagationState::Requested { time: meta.time(), - requested_limit: *limit, + requested_limit: limit, }; Ok(()) } @@ -201,7 +199,7 @@ impl P2pChannelsSnarkJobCommitmentState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pChannelsSnarkJobCommitmentEffectfulAction::ResponseSend { peer_id, - commitments: commitments.clone(), + commitments, }); Ok(()) } diff --git a/p2p/src/channels/snark_job_commitment_effectful/p2p_channels_snark_job_commitment_effectful_actions.rs b/p2p/src/channels/snark_job_commitment_effectful/p2p_channels_snark_job_commitment_effectful_actions.rs index 3c7ae59e94..d3836687f8 100644 --- a/p2p/src/channels/snark_job_commitment_effectful/p2p_channels_snark_job_commitment_effectful_actions.rs +++ b/p2p/src/channels/snark_job_commitment_effectful/p2p_channels_snark_job_commitment_effectful_actions.rs @@ -25,8 +25,8 @@ impl redux::EnablingCondition for P2pChannelsSnarkJobCommitmentEffectf } } -impl From for crate::P2pAction { - fn from(action: P2pChannelsSnarkJobCommitmentEffectfulAction) -> crate::P2pAction { - crate::P2pAction::ChannelsEffectful(P2pChannelsEffectfulAction::SnarkJobCommitment(action)) +impl From for crate::P2pEffectfulAction { + fn from(action: P2pChannelsSnarkJobCommitmentEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Channels(P2pChannelsEffectfulAction::SnarkJobCommitment(action)) } } diff --git a/p2p/src/channels/streaming_rpc/p2p_channels_streaming_rpc_reducer.rs b/p2p/src/channels/streaming_rpc/p2p_channels_streaming_rpc_reducer.rs index 7b4d425ceb..eb394d8e59 100644 --- a/p2p/src/channels/streaming_rpc/p2p_channels_streaming_rpc_reducer.rs +++ b/p2p/src/channels/streaming_rpc/p2p_channels_streaming_rpc_reducer.rs @@ -13,7 +13,7 @@ use super::{ impl P2pChannelsStreamingRpcState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pChannelsStreamingRpcAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -66,16 +66,19 @@ impl P2pChannelsStreamingRpcState { .. } => { let Self::Ready { local, .. } = streaming_rpc_state else { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::RequestSend` with state {:?}", + streaming_rpc_state + ); return Ok(()); }; *next_local_rpc_id += 1; *local = P2pStreamingRpcLocalState::Requested { time: meta.time(), - id: *id, + id, request: request.clone(), - progress: match &**request { + progress: match &*request { P2pStreamingRpcRequest::StagedLedgerParts(_) => { Into::into(StagedLedgerPartsReceiveProgress::BasePending { time: meta.time(), @@ -87,9 +90,9 @@ impl P2pChannelsStreamingRpcState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pChannelsStreamingRpcEffectfulAction::RequestSend { peer_id, - id: *id, - request: request.clone(), - on_init: on_init.clone(), + id, + request, + on_init, }); Ok(()) } @@ -98,7 +101,7 @@ impl P2pChannelsStreamingRpcState { let p2p_state: &P2pState = state.substate()?; if let Some(callback) = &p2p_state.callbacks.on_p2p_channels_streaming_rpc_timeout { - dispatcher.push_callback(callback.clone(), (peer_id, *id)); + dispatcher.push_callback(callback.clone(), (peer_id, id)); } Ok(()) @@ -123,10 +126,7 @@ impl P2pChannelsStreamingRpcState { let dispatcher = state_context.into_dispatcher(); dispatcher.push( - P2pChannelsStreamingRpcEffectfulAction::ResponseNextPartGet { - peer_id, - id: *id, - }, + P2pChannelsStreamingRpcEffectfulAction::ResponseNextPartGet { peer_id, id }, ); Ok(()) } @@ -136,11 +136,14 @@ impl P2pChannelsStreamingRpcState { .. } = streaming_rpc_state else { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::ResponsePartReceived` with state {:?}", + streaming_rpc_state + ); return Ok(()); }; if !progress.update(meta.time(), response) { - bug_condition!("progress response mismatch! {progress:?}\n{response:?}"); + bug_condition!("progress response mismatch! {progress:?}"); } let (dispatcher, state) = state_context.into_dispatcher_and_state(); @@ -152,13 +155,12 @@ impl P2pChannelsStreamingRpcState { if let Some(response) = peer.channels.streaming_rpc.local_done_response() { dispatcher.push(P2pChannelsStreamingRpcAction::ResponseReceived { peer_id, - id: *id, + id, response: Some(response), }); return Ok(()); } - dispatcher - .push(P2pChannelsStreamingRpcAction::ResponseNextPartGet { peer_id, id: *id }); + dispatcher.push(P2pChannelsStreamingRpcAction::ResponseNextPartGet { peer_id, id }); Ok(()) } P2pChannelsStreamingRpcAction::ResponseReceived { @@ -167,11 +169,17 @@ impl P2pChannelsStreamingRpcState { .. } => { let Self::Ready { local, .. } = streaming_rpc_state else { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::ResponseReceived` with state {:?}", + streaming_rpc_state + ); return Ok(()); }; let P2pStreamingRpcLocalState::Requested { id, request, .. } = local else { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::ResponseReceived` with state {:?}", + streaming_rpc_state + ); return Ok(()); }; *local = P2pStreamingRpcLocalState::Responded { @@ -187,20 +195,23 @@ impl P2pChannelsStreamingRpcState { .callbacks .on_p2p_channels_streaming_rpc_response_received { - dispatcher.push_callback(callback.clone(), (peer_id, *rpc_id, response.clone())) + dispatcher.push_callback(callback.clone(), (peer_id, rpc_id, response)) } Ok(()) } P2pChannelsStreamingRpcAction::RequestReceived { id, request, .. } => { let Self::Ready { remote, .. } = streaming_rpc_state else { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::RequestReceived` with state {:?}", + streaming_rpc_state + ); return Ok(()); }; *remote = P2pStreamingRpcRemoteState::Requested { time: meta.time(), - id: *id, - request: request.clone(), + id, + request, progress: StagedLedgerPartsSendProgress::LedgerGetIdle { time: meta.time() } .into(), }; @@ -236,10 +247,13 @@ impl P2pChannelsStreamingRpcState { .. } = streaming_rpc_state else { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::ResponseSendInit` with state {:?}", + streaming_rpc_state + ); return Ok(()); }; - match (&**request, response) { + match (&**request, &response) { (_, Some(P2pStreamingRpcResponseFull::StagedLedgerParts(data))) => { *progress = StagedLedgerPartsSendProgress::LedgerGetSuccess { time: meta.time(), @@ -256,8 +270,8 @@ impl P2pChannelsStreamingRpcState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pChannelsStreamingRpcEffectfulAction::ResponseSendInit { peer_id, - id: *id, - response: response.clone(), + id, + response, }); Ok(()) } @@ -274,7 +288,7 @@ impl P2pChannelsStreamingRpcState { dispatcher.push(P2pChannelsStreamingRpcAction::ResponsePartSend { peer_id, - id: *id, + id, response, }); @@ -286,7 +300,10 @@ impl P2pChannelsStreamingRpcState { .. } = streaming_rpc_state else { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::ResponsePartSend` with state {:?}", + streaming_rpc_state + ); return Ok(()); }; match progress { @@ -352,8 +369,8 @@ impl P2pChannelsStreamingRpcState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pChannelsStreamingRpcEffectfulAction::ResponsePartSend { peer_id, - id: *id, - response: response.clone(), + id, + response, }); Ok(()) } @@ -365,18 +382,24 @@ impl P2pChannelsStreamingRpcState { (remote, request) } _ => { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::ResponseSent` with state {:?}", + streaming_rpc_state + ); return Ok(()); } }, _ => { - bug_condition!("{:?} with state {:?}", action, streaming_rpc_state); + bug_condition!( + "`P2pChannelsStreamingRpcAction::ResponseSent` with state {:?}", + streaming_rpc_state + ); return Ok(()); } }; *remote = P2pStreamingRpcRemoteState::Responded { time: meta.time(), - id: *id, + id, request, }; diff --git a/p2p/src/channels/streaming_rpc/rpcs/mod.rs b/p2p/src/channels/streaming_rpc/rpcs/mod.rs index 76f4d84ebb..140127bc3d 100644 --- a/p2p/src/channels/streaming_rpc/rpcs/mod.rs +++ b/p2p/src/channels/streaming_rpc/rpcs/mod.rs @@ -144,7 +144,7 @@ impl P2pStreamingRpcReceiveProgress { } } - pub fn update(&mut self, time: redux::Timestamp, resp: &P2pStreamingRpcResponse) -> bool { + pub fn update(&mut self, time: redux::Timestamp, resp: P2pStreamingRpcResponse) -> bool { match (self, resp) { ( Self::StagedLedgerParts(progress), diff --git a/p2p/src/channels/streaming_rpc/rpcs/staged_ledger_parts.rs b/p2p/src/channels/streaming_rpc/rpcs/staged_ledger_parts.rs index 1d3a70b71a..ff9a17d26f 100644 --- a/p2p/src/channels/streaming_rpc/rpcs/staged_ledger_parts.rs +++ b/p2p/src/channels/streaming_rpc/rpcs/staged_ledger_parts.rs @@ -192,13 +192,10 @@ impl Default for StagedLedgerPartsSendProgress { } impl StagedLedgerPartsReceiveProgress { - pub fn update(&mut self, time: redux::Timestamp, resp: &StagedLedgerPartsResponse) -> bool { + pub fn update(&mut self, time: redux::Timestamp, resp: StagedLedgerPartsResponse) -> bool { match (std::mem::take(self), resp) { (Self::BasePending { .. }, StagedLedgerPartsResponse::Base(base)) => { - *self = Self::BaseSuccess { - time, - base: base.clone(), - }; + *self = Self::BaseSuccess { time, base }; true } ( @@ -208,7 +205,7 @@ impl StagedLedgerPartsReceiveProgress { *self = Self::ScanStateBaseSuccess { time, base, - scan_state_base: data.clone(), + scan_state_base: data, }; true } @@ -224,7 +221,7 @@ impl StagedLedgerPartsReceiveProgress { time, base, scan_state_base, - previous_incomplete_zkapp_updates: data.clone(), + previous_incomplete_zkapp_updates: data, }; true } @@ -238,7 +235,7 @@ impl StagedLedgerPartsReceiveProgress { }, StagedLedgerPartsResponse::ScanStateTree(tree), ) => { - trees.extend(std::iter::once(tree.clone())); + trees.extend(std::iter::once(tree)); *self = if trees.len() >= scan_state_base.trees.as_u32() as usize { // base, scan_state_base, previous_incomplete_zkapp_updates, trees diff --git a/p2p/src/channels/streaming_rpc_effectful/p2p_channels_streaming_rpc_effectful_actions.rs b/p2p/src/channels/streaming_rpc_effectful/p2p_channels_streaming_rpc_effectful_actions.rs index fa23a2e1a8..cc5fac9b05 100644 --- a/p2p/src/channels/streaming_rpc_effectful/p2p_channels_streaming_rpc_effectful_actions.rs +++ b/p2p/src/channels/streaming_rpc_effectful/p2p_channels_streaming_rpc_effectful_actions.rs @@ -46,8 +46,8 @@ impl redux::EnablingCondition for P2pChannelsStreamingRpcEffectfulActi } } -impl From for crate::P2pAction { - fn from(a: P2pChannelsStreamingRpcEffectfulAction) -> crate::P2pAction { - crate::P2pAction::ChannelsEffectful(P2pChannelsEffectfulAction::StreamingRpc(a)) +impl From for crate::P2pEffectfulAction { + fn from(a: P2pChannelsStreamingRpcEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Channels(P2pChannelsEffectfulAction::StreamingRpc(a)) } } diff --git a/p2p/src/channels/transaction/p2p_channels_transaction_reducer.rs b/p2p/src/channels/transaction/p2p_channels_transaction_reducer.rs index 5a85bcc568..c7f7a8c60a 100644 --- a/p2p/src/channels/transaction/p2p_channels_transaction_reducer.rs +++ b/p2p/src/channels/transaction/p2p_channels_transaction_reducer.rs @@ -12,7 +12,7 @@ use redux::ActionWithMeta; impl P2pChannelsTransactionState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pChannelsTransactionAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -33,7 +33,7 @@ impl P2pChannelsTransactionState { *state = Self::Init { time: meta.time() }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pChannelsTransactionEffectfulAction::Init { peer_id: *peer_id }); + dispatcher.push(P2pChannelsTransactionEffectfulAction::Init { peer_id }); Ok(()) } P2pChannelsTransactionAction::Pending { .. } => { @@ -62,14 +62,12 @@ impl P2pChannelsTransactionState { }; *local = TransactionPropagationState::Requested { time: meta.time(), - requested_limit: *limit, + requested_limit: limit, }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pChannelsTransactionEffectfulAction::RequestSend { - peer_id: *peer_id, - limit: *limit, - }); + dispatcher + .push(P2pChannelsTransactionEffectfulAction::RequestSend { peer_id, limit }); Ok(()) } P2pChannelsTransactionAction::PromiseReceived { promised_count, .. } => { @@ -94,7 +92,7 @@ impl P2pChannelsTransactionState { *local = TransactionPropagationState::Responding { time: meta.time(), requested_limit: *requested_limit, - promised_count: *promised_count, + promised_count, current_count: 0, }; Ok(()) @@ -138,7 +136,7 @@ impl P2pChannelsTransactionState { }; *remote = TransactionPropagationState::Requested { time: meta.time(), - requested_limit: *limit, + requested_limit: limit, }; Ok(()) } @@ -175,8 +173,8 @@ impl P2pChannelsTransactionState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pChannelsTransactionEffectfulAction::ResponseSend { - peer_id: *peer_id, - transactions: transactions.clone(), + peer_id, + transactions, }); Ok(()) } @@ -188,7 +186,7 @@ impl P2pChannelsTransactionState { .callbacks .on_p2p_channels_transaction_libp2p_received { - dispatcher.push_callback(callback.clone(), transaction.clone()); + dispatcher.push_callback(callback.clone(), transaction); } Ok(()) @@ -199,7 +197,7 @@ impl P2pChannelsTransactionState { P2pChannelsTransactionAction::Libp2pBroadcast { transaction, nonce } => { let dispatcher = state_context.into_dispatcher(); let message = v2::NetworkPoolTransactionPoolDiffVersionedStableV2( - std::iter::once(*transaction.clone()).collect(), + std::iter::once(*transaction).collect(), ); let nonce = nonce.into(); let message = Box::new(GossipNetMessageV2::TransactionPoolDiff { message, nonce }); diff --git a/p2p/src/channels/transaction_effectful/p2p_channels_transaction_effectful_actions.rs b/p2p/src/channels/transaction_effectful/p2p_channels_transaction_effectful_actions.rs index 82d7bf1c89..82728a0e0e 100644 --- a/p2p/src/channels/transaction_effectful/p2p_channels_transaction_effectful_actions.rs +++ b/p2p/src/channels/transaction_effectful/p2p_channels_transaction_effectful_actions.rs @@ -26,8 +26,8 @@ impl redux::EnablingCondition for P2pChannelsTransactionEffectfulActio } } -impl From for crate::P2pAction { - fn from(action: P2pChannelsTransactionEffectfulAction) -> crate::P2pAction { - crate::P2pAction::ChannelsEffectful(P2pChannelsEffectfulAction::Transaction(action)) +impl From for crate::P2pEffectfulAction { + fn from(action: P2pChannelsTransactionEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Channels(P2pChannelsEffectfulAction::Transaction(action)) } } diff --git a/p2p/src/connection/incoming/mod.rs b/p2p/src/connection/incoming/mod.rs index d47fde1365..e4f1b81fe6 100644 --- a/p2p/src/connection/incoming/mod.rs +++ b/p2p/src/connection/incoming/mod.rs @@ -19,9 +19,12 @@ pub struct P2pConnectionIncomingInitOpts { } // TODO(binier): maybe move to `crate::webrtc`? -#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] +#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone, Copy)] pub enum IncomingSignalingMethod { + /// Http rpc is used for sending offer and getting answer as a response. Http, + /// Intermediary/Relay peer is used for exchanging offer and answer messages. + P2p { relay_peer_id: PeerId }, } impl P2pState { diff --git a/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs b/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs index 5eab82f470..06a99bbf59 100644 --- a/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs +++ b/p2p/src/connection/incoming/p2p_connection_incoming_reducer.rs @@ -5,6 +5,7 @@ use openmina_core::{bug_condition, debug, warn, Substate}; use redux::{ActionWithMeta, Dispatcher, Timestamp}; use crate::{ + channels::signaling::exchange::P2pChannelsSignalingExchangeAction, connection::{ incoming::P2pConnectionIncomingError, incoming_effectful::P2pConnectionIncomingEffectfulAction, @@ -25,7 +26,7 @@ impl P2pConnectionIncomingState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pConnectionIncomingAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -44,7 +45,7 @@ impl P2pConnectionIncomingState { .entry(peer_id) .or_insert_with(|| P2pPeerState { is_libp2p: false, - dial_opts: opts.offer.listen_port.map(|listen_port| { + dial_opts: opts.offer.listen_port.and_then(|listen_port| { let signaling = match opts.signaling { IncomingSignalingMethod::Http => { SignalingMethod::Http(HttpSignalingInfo { @@ -52,23 +53,24 @@ impl P2pConnectionIncomingState { port: listen_port, }) } + IncomingSignalingMethod::P2p { .. } => return None, }; - P2pConnectionOutgoingInitOpts::WebRTC { peer_id, signaling } + Some(P2pConnectionOutgoingInitOpts::WebRTC { peer_id, signaling }) }), - status: P2pPeerStatus::Connecting(P2pConnectionState::incoming_init(opts)), + status: P2pPeerStatus::Connecting(P2pConnectionState::incoming_init(&opts)), identify: None, }); state.status = P2pPeerStatus::Connecting(P2pConnectionState::Incoming(Self::Init { time: meta.time(), - signaling: opts.signaling.clone(), + signaling: opts.signaling, offer: opts.offer.clone(), - rpc_id: *rpc_id, + rpc_id, })); let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pConnectionIncomingEffectfulAction::Init { opts: opts.clone() }); + dispatcher.push(P2pConnectionIncomingEffectfulAction::Init { opts }); Ok(()) } P2pConnectionIncomingAction::AnswerSdpCreatePending { .. } => { @@ -84,7 +86,7 @@ impl P2pConnectionIncomingState { { *state = Self::AnswerSdpCreatePending { time: meta.time(), - signaling: signaling.clone(), + signaling: *signaling, offer: offer.clone(), rpc_id: rpc_id.take(), }; @@ -99,15 +101,15 @@ impl P2pConnectionIncomingState { P2pConnectionIncomingAction::AnswerSdpCreateError { peer_id, error } => { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pConnectionIncomingAction::Error { - peer_id: *peer_id, + peer_id, error: P2pConnectionIncomingError::SdpCreateError(error.to_owned()), }); Ok(()) } P2pConnectionIncomingAction::AnswerSdpCreateSuccess { sdp, .. } => { - let state = p2p_state - .incoming_peer_connection_mut(&peer_id) - .ok_or_else(|| format!("Invalid state for: {:?}", action))?; + let state = p2p_state.incoming_peer_connection_mut(&peer_id).ok_or( + "Missing state for `P2pConnectionIncomingAction::AnswerSdpCreateSuccess`", + )?; if let Self::AnswerSdpCreatePending { signaling, offer, @@ -117,7 +119,7 @@ impl P2pConnectionIncomingState { { *state = Self::AnswerSdpCreateSuccess { time: meta.time(), - signaling: signaling.clone(), + signaling: *signaling, offer: offer.clone(), sdp: sdp.clone(), rpc_id: rpc_id.take(), @@ -133,7 +135,7 @@ impl P2pConnectionIncomingState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_state: &P2pState = state.substate()?; let answer = Box::new(crate::webrtc::Answer { - sdp: sdp.to_owned(), + sdp, identity_pub_key: p2p_state.config.identity_pub_key.clone(), target_peer_id: peer_id, }); @@ -142,47 +144,56 @@ impl P2pConnectionIncomingState { } P2pConnectionIncomingAction::AnswerReady { peer_id, answer } => { let state = p2p_state - .incoming_peer_connection_mut(peer_id) - .ok_or_else(|| format!("Invalid state for: {:?}", action))?; - if let Self::AnswerSdpCreateSuccess { + .incoming_peer_connection_mut(&peer_id) + .ok_or("Invalid state for: `P2pConnectionIncomingAction::AnswerReady`")?; + + let Self::AnswerSdpCreateSuccess { signaling, offer, rpc_id, .. } = state - { - *state = Self::AnswerReady { - time: meta.time(), - signaling: signaling.clone(), - offer: offer.clone(), - answer: answer.clone(), - rpc_id: rpc_id.take(), - }; - } else { + else { bug_condition!( "Invalid state for `P2pConnectionIncomingAction::AnswerReady`: {:?}", state ); - } + return Ok(()); + }; + let signaling = *signaling; + *state = Self::AnswerReady { + time: meta.time(), + signaling, + offer: offer.clone(), + answer: answer.clone(), + rpc_id: rpc_id.take(), + }; + let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_state: &P2pState = state.substate()?; - dispatcher.push(P2pConnectionIncomingEffectfulAction::AnswerSend { - peer_id: *peer_id, - answer: answer.clone(), - }); + match signaling { + IncomingSignalingMethod::Http => { + dispatcher.push(P2pConnectionIncomingEffectfulAction::AnswerSend { + peer_id, + answer: answer.clone(), + }); + } + IncomingSignalingMethod::P2p { relay_peer_id } => { + dispatcher.push(P2pChannelsSignalingExchangeAction::AnswerSend { + peer_id: relay_peer_id, + answer: P2pConnectionResponse::Accepted(answer.clone()), + }); + } + } - if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(peer_id) { + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(&peer_id) { if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_incoming_answer_ready { dispatcher.push_callback( callback.clone(), - ( - rpc_id, - *peer_id, - P2pConnectionResponse::Accepted(answer.clone()), - ), + (rpc_id, peer_id, P2pConnectionResponse::Accepted(answer)), ); } } @@ -203,7 +214,7 @@ impl P2pConnectionIncomingState { { *state = Self::AnswerSendSuccess { time: meta.time(), - signaling: signaling.clone(), + signaling: *signaling, offer: offer.clone(), answer: answer.clone(), rpc_id: rpc_id.take(), @@ -234,7 +245,7 @@ impl P2pConnectionIncomingState { { *state = Self::FinalizePending { time: meta.time(), - signaling: signaling.clone(), + signaling: *signaling, offer: offer.clone(), answer: answer.clone(), rpc_id: rpc_id.take(), @@ -269,7 +280,7 @@ impl P2pConnectionIncomingState { { *state = Self::FinalizeSuccess { time: meta.time(), - signaling: signaling.clone(), + signaling: *signaling, offer: offer.clone(), answer: answer.clone(), rpc_id: rpc_id.take(), @@ -316,12 +327,13 @@ impl P2pConnectionIncomingState { P2pConnectionIncomingAction::Error { error, .. } => { let state = p2p_state .incoming_peer_connection_mut(&peer_id) - .ok_or_else(|| format!("Invalid state for: {:?}", action))?; + .ok_or("Missing state for `P2pConnectionIncomingAction::Error`")?; let rpc_id = state.rpc_id(); + let str_error = format!("{:?}", error); *state = Self::Error { time: meta.time(), - error: error.clone(), + error, rpc_id, }; @@ -330,8 +342,7 @@ impl P2pConnectionIncomingState { if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(&peer_id) { if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_incoming_error { - dispatcher - .push_callback(callback.clone(), (rpc_id, format!("{:?}", error))); + dispatcher.push_callback(callback.clone(), (rpc_id, str_error)); } } @@ -352,7 +363,7 @@ impl P2pConnectionIncomingState { { *state = Self::Success { time: meta.time(), - signaling: signaling.clone(), + signaling: *signaling, offer: offer.clone(), answer: answer.clone(), rpc_id: rpc_id.take(), @@ -394,7 +405,7 @@ impl P2pConnectionIncomingState { identify: None, }); - Self::reduce_finalize_libp2p_pending(state, *addr, time, my_id, peer_id); + Self::reduce_finalize_libp2p_pending(state, addr, time, my_id, peer_id); let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_state: &P2pState = state.substate()?; @@ -440,7 +451,7 @@ impl P2pConnectionIncomingState { my_id: PeerId, peer_id: PeerId, time: Timestamp, - addr: &SocketAddr, + addr: SocketAddr, ) where State: crate::P2pStateTrait, Action: crate::P2pActionTrait, @@ -477,7 +488,7 @@ impl P2pConnectionIncomingState { incoming, }| { *incoming - && sock_addr != addr + && sock_addr != &addr && close_duplicates.contains(sock_addr) }, ) @@ -499,7 +510,7 @@ impl P2pConnectionIncomingState { warn!(time; node_id = display(my_id), summary = "rejecting incoming connection as duplicate", peer_id = display(peer_id)); dispatcher.push(P2pNetworkSchedulerAction::Disconnect { addr: ConnectionAddr { - sock_addr: *addr, + sock_addr: addr, incoming: true, }, reason: P2pDisconnectionReason::Libp2pIncomingRejected( diff --git a/p2p/src/connection/incoming_effectful/p2p_connection_incoming_effectful_actions.rs b/p2p/src/connection/incoming_effectful/p2p_connection_incoming_effectful_actions.rs index 536fedf83a..26a3298493 100644 --- a/p2p/src/connection/incoming_effectful/p2p_connection_incoming_effectful_actions.rs +++ b/p2p/src/connection/incoming_effectful/p2p_connection_incoming_effectful_actions.rs @@ -1,6 +1,6 @@ use crate::{ connection::{incoming::P2pConnectionIncomingInitOpts, P2pConnectionEffectfulAction}, - webrtc, P2pAction, P2pState, PeerId, + webrtc, P2pState, PeerId, }; use openmina_core::ActionEvent; use serde::{Deserialize, Serialize}; @@ -22,8 +22,8 @@ impl redux::EnablingCondition for P2pConnectionIncomingEffectfulAction } } -impl From for P2pAction { - fn from(a: P2pConnectionIncomingEffectfulAction) -> Self { - Self::ConnectionEffectful(P2pConnectionEffectfulAction::Incoming(a)) +impl From for crate::P2pEffectfulAction { + fn from(a: P2pConnectionIncomingEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Connection(P2pConnectionEffectfulAction::Incoming(a)) } } diff --git a/p2p/src/connection/mod.rs b/p2p/src/connection/mod.rs index eabd6e5e88..3ea18ab9e4 100644 --- a/p2p/src/connection/mod.rs +++ b/p2p/src/connection/mod.rs @@ -17,55 +17,14 @@ pub use p2p_connection_service::*; use serde::{Deserialize, Serialize}; -use crate::webrtc; - -#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone, Copy, thiserror::Error)] -pub enum RejectionReason { - #[error("peer_id does not match peer's public key")] - PeerIdAndPublicKeyMismatch, - #[error("target peer_id is not local node's peer_id")] - TargetPeerIdNotMe, - #[error("too many peers")] - PeerCapacityFull, - #[error("peer already connected")] - AlreadyConnected, - #[error("self connection detected")] - ConnectingToSelf, -} - -impl RejectionReason { - pub fn is_bad(&self) -> bool { - match self { - Self::PeerIdAndPublicKeyMismatch => true, - Self::TargetPeerIdNotMe => true, - Self::PeerCapacityFull => false, - Self::AlreadyConnected => true, - Self::ConnectingToSelf => false, - } - } -} +pub use crate::webrtc::{Answer, Offer, P2pConnectionResponse, RejectionReason}; #[derive(Serialize, Deserialize, Debug, Clone, thiserror::Error)] pub enum P2pConnectionErrorResponse { #[error("connection rejected: {0}")] Rejected(RejectionReason), + #[error("signal decryption failed")] + SignalDecryptionFailed, #[error("internal error")] InternalError, } - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub enum P2pConnectionResponse { - Accepted(Box), - Rejected(RejectionReason), - InternalError, -} - -impl P2pConnectionResponse { - pub fn internal_error_str() -> &'static str { - "InternalError" - } - - pub fn internal_error_json_str() -> &'static str { - "\"InternalError\"" - } -} diff --git a/p2p/src/connection/outgoing/mod.rs b/p2p/src/connection/outgoing/mod.rs index aa13628705..6bed5c356e 100644 --- a/p2p/src/connection/outgoing/mod.rs +++ b/p2p/src/connection/outgoing/mod.rs @@ -149,6 +149,20 @@ impl P2pConnectionOutgoingInitOpts { } } + pub fn can_connect_directly(&self) -> bool { + match self { + Self::LibP2P(..) => true, + Self::WebRTC { signaling, .. } => signaling.can_connect_directly(), + } + } + + pub fn webrtc_p2p_relay_peer_id(&self) -> Option { + match self { + Self::WebRTC { signaling, .. } => signaling.p2p_relay_peer_id(), + _ => None, + } + } + /// The OCaml implementation of Mina uses the `get_some_initial_peers` RPC to exchange peer information. /// Try to convert this RPC response into our peer address representation. /// Recognize a hack for marking the webrtc signaling server. @@ -226,6 +240,7 @@ impl P2pConnectionOutgoingInitOpts { (*peer_id).to_string().into_bytes().into(), ), }), + SignalingMethod::P2p { .. } => None, }, } } @@ -382,7 +397,11 @@ impl TryFrom<&multiaddr::Multiaddr> for P2pConnectionOutgoingInitLibp2pOpts { host: match iter.next() { Some(Protocol::Ip4(v)) => Host::Ipv4(v), Some(Protocol::Dns(v) | Protocol::Dns4(v) | Protocol::Dns6(v)) => { - Host::Domain(v.into_owned()) + Host::Domain(v.to_string()).resolve().ok_or( + P2pConnectionOutgoingInitOptsParseError::Other(format!( + "cannot resolve host {v}" + )), + )? } Some(_) => { return Err(P2pConnectionOutgoingInitOptsParseError::Other( diff --git a/p2p/src/connection/outgoing/p2p_connection_outgoing_actions.rs b/p2p/src/connection/outgoing/p2p_connection_outgoing_actions.rs index 68b643f31a..c758c7633e 100644 --- a/p2p/src/connection/outgoing/p2p_connection_outgoing_actions.rs +++ b/p2p/src/connection/outgoing/p2p_connection_outgoing_actions.rs @@ -8,9 +8,6 @@ use crate::{webrtc, P2pState, PeerId}; use super::{P2pConnectionOutgoingError, P2pConnectionOutgoingInitOpts}; -pub type P2pConnectionOutgoingActionWithMetaRef<'a> = - redux::ActionWithMeta<&'a P2pConnectionOutgoingAction>; - #[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)] #[action_event(fields(display(opts), display(peer_id), display(error)))] pub enum P2pConnectionOutgoingAction { @@ -189,6 +186,7 @@ impl redux::EnablingCondition for P2pConnectionOutgoingAction { matches!(s, P2pConnectionOutgoingState::OfferSdpCreatePending { .. }) } P2pConnectionOutgoingError::Rejected(_) + | P2pConnectionOutgoingError::RemoteSignalDecryptionFailed | P2pConnectionOutgoingError::RemoteInternalError => { matches!(s, P2pConnectionOutgoingState::AnswerRecvPending { .. }) } diff --git a/p2p/src/connection/outgoing/p2p_connection_outgoing_reducer.rs b/p2p/src/connection/outgoing/p2p_connection_outgoing_reducer.rs index 6da92bf33b..2dc1723297 100644 --- a/p2p/src/connection/outgoing/p2p_connection_outgoing_reducer.rs +++ b/p2p/src/connection/outgoing/p2p_connection_outgoing_reducer.rs @@ -1,8 +1,10 @@ use std::net::SocketAddr; use openmina_core::{bug_condition, warn, Substate}; +use redux::ActionWithMeta; use crate::{ + channels::signaling::discovery::P2pChannelsSignalingDiscoveryAction, connection::{ outgoing_effectful::P2pConnectionOutgoingEffectfulAction, P2pConnectionErrorResponse, P2pConnectionState, @@ -14,21 +16,22 @@ use crate::{ use super::{ libp2p_opts::P2pConnectionOutgoingInitLibp2pOptsTryToSocketAddrError, - P2pConnectionOutgoingAction, P2pConnectionOutgoingActionWithMetaRef, - P2pConnectionOutgoingError, P2pConnectionOutgoingInitOpts, P2pConnectionOutgoingState, + P2pConnectionOutgoingAction, P2pConnectionOutgoingError, P2pConnectionOutgoingInitOpts, + P2pConnectionOutgoingState, }; impl P2pConnectionOutgoingState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: P2pConnectionOutgoingActionWithMetaRef<'_>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, Action: crate::P2pActionTrait, { let (action, meta) = action.split(); + let time = meta.time(); let p2p_state = state_context.get_substate_mut()?; match action { @@ -44,24 +47,24 @@ impl P2pConnectionOutgoingState { .entry(*opts.peer_id()) .or_insert_with(|| P2pPeerState { is_libp2p: opts.is_libp2p(), - dial_opts: Some(opts.clone()), + dial_opts: Some(opts.clone()).filter(|v| v.can_connect_directly()), status: P2pPeerStatus::Connecting(P2pConnectionState::outgoing_init( - opts, + &opts, )), identify: None, }); peer_state.status = P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(Self::Init { - time: meta.time(), + time, opts: opts.clone(), - rpc_id: *rpc_id, + rpc_id, })); let dispatcher = state_context.into_dispatcher(); #[cfg(feature = "p2p-libp2p")] - if let P2pConnectionOutgoingInitOpts::LibP2P(libp2p_opts) = opts { + if let P2pConnectionOutgoingInitOpts::LibP2P(libp2p_opts) = &opts { match SocketAddr::try_from(libp2p_opts) { Ok(addr) => { dispatcher.push(P2pNetworkSchedulerAction::OutgoingConnect { addr }); @@ -81,29 +84,26 @@ impl P2pConnectionOutgoingState { return Ok(()); } - dispatcher.push(P2pConnectionOutgoingEffectfulAction::Init { - opts: opts.clone(), - rpc_id: *rpc_id, - }); + dispatcher.push(P2pConnectionOutgoingEffectfulAction::Init { opts, rpc_id }); Ok(()) } P2pConnectionOutgoingAction::Reconnect { opts, rpc_id } => { let peer_state = p2p_state .peers .get_mut(opts.peer_id()) - .ok_or_else(|| format!("Invalid state: {:?}", action))?; + .ok_or("Missing peer state for: `P2pConnectionOutgoingAction::Reconnect`")?; peer_state.status = P2pPeerStatus::Connecting(P2pConnectionState::Outgoing(Self::Init { - time: meta.time(), + time, opts: opts.clone(), - rpc_id: *rpc_id, + rpc_id, })); let dispatcher = state_context.into_dispatcher(); #[cfg(feature = "p2p-libp2p")] - if let P2pConnectionOutgoingInitOpts::LibP2P(libp2p_opts) = opts { + if let P2pConnectionOutgoingInitOpts::LibP2P(libp2p_opts) = &opts { match SocketAddr::try_from(libp2p_opts) { Ok(addr) => { dispatcher.push(P2pNetworkSchedulerAction::OutgoingConnect { addr }); @@ -123,20 +123,17 @@ impl P2pConnectionOutgoingState { return Ok(()); } - dispatcher.push(P2pConnectionOutgoingEffectfulAction::Init { - opts: opts.clone(), - rpc_id: *rpc_id, - }); + dispatcher.push(P2pConnectionOutgoingEffectfulAction::Init { opts, rpc_id }); Ok(()) } P2pConnectionOutgoingAction::OfferSdpCreatePending { peer_id, .. } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) - .ok_or_else(|| format!("Invalid state: {:?}", action))?; + .outgoing_peer_connection_mut(&peer_id) + .ok_or("Missing connection state for: `P2pConnectionOutgoingAction::OfferSdpCreatePending`")?; if let Self::Init { opts, rpc_id, .. } = state { *state = Self::OfferSdpCreatePending { - time: meta.time(), + time, opts: opts.clone(), rpc_id: rpc_id.take(), }; @@ -149,19 +146,19 @@ impl P2pConnectionOutgoingState { P2pConnectionOutgoingAction::OfferSdpCreateError { error, peer_id, .. } => { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pConnectionOutgoingAction::Error { - peer_id: *peer_id, + peer_id, error: P2pConnectionOutgoingError::SdpCreateError(error.to_owned()), }); Ok(()) } P2pConnectionOutgoingAction::OfferSdpCreateSuccess { sdp, peer_id } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) - .ok_or_else(|| format!("Invalid state: {:?}", action))?; + .outgoing_peer_connection_mut(&peer_id) + .ok_or("Missing peer connection for `P2pConnectionOutgoingAction::OfferSdpCreateSuccess`")?; if let Self::OfferSdpCreatePending { opts, rpc_id, .. } = state { *state = Self::OfferSdpCreateSuccess { - time: meta.time(), + time, opts: opts.clone(), sdp: sdp.clone(), rpc_id: rpc_id.take(), @@ -172,49 +169,55 @@ impl P2pConnectionOutgoingState { } let offer = Box::new(crate::webrtc::Offer { - sdp: sdp.to_owned(), + sdp, identity_pub_key: p2p_state.config.identity_pub_key.clone(), - target_peer_id: *peer_id, + target_peer_id: peer_id, // TODO(vlad9486): put real address host: Host::Ipv4([127, 0, 0, 1].into()), listen_port: p2p_state.config.listen_port, }); let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pConnectionOutgoingAction::OfferReady { - peer_id: *peer_id, - offer, - }); + dispatcher.push(P2pConnectionOutgoingAction::OfferReady { peer_id, offer }); Ok(()) } P2pConnectionOutgoingAction::OfferReady { offer, peer_id } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) - .ok_or_else(|| format!("Invalid state: {:?}", action))?; - if let Self::OfferSdpCreateSuccess { opts, rpc_id, .. } = state { - *state = Self::OfferReady { - time: meta.time(), - opts: opts.clone(), - offer: offer.clone(), - rpc_id: rpc_id.take(), - }; - } else { + .outgoing_peer_connection_mut(&peer_id) + .ok_or("Invalid state for `P2pConnectionOutgoingAction::OfferReady`")?; + + let Self::OfferSdpCreateSuccess { opts, rpc_id, .. } = state else { bug_condition!( "Invalid state for `P2pConnectionOutgoingAction::OfferReady`: {:?}", state ); return Ok(()); - } + }; + let opts = opts.clone(); + *state = Self::OfferReady { + time: meta.time(), + opts: opts.clone(), + offer: offer.clone(), + rpc_id: rpc_id.take(), + }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pConnectionOutgoingEffectfulAction::OfferSend { - peer_id: *peer_id, - offer: offer.clone(), - }); + + if let Some(relay_peer_id) = opts.webrtc_p2p_relay_peer_id() { + dispatcher.push(P2pChannelsSignalingDiscoveryAction::DiscoveredAccept { + peer_id: relay_peer_id, + offer: offer.clone(), + }); + } else { + dispatcher.push(P2pConnectionOutgoingEffectfulAction::OfferSend { + peer_id, + offer: offer.clone(), + }); + } Ok(()) } P2pConnectionOutgoingAction::OfferSendSuccess { peer_id } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) + .outgoing_peer_connection_mut(&peer_id) .ok_or_else(|| format!("Invalid state: {:?}", action))?; if let Self::OfferReady { opts, @@ -224,7 +227,7 @@ impl P2pConnectionOutgoingState { } = state { *state = Self::OfferSendSuccess { - time: meta.time(), + time, opts: opts.clone(), offer: offer.clone(), rpc_id: rpc_id.take(), @@ -238,13 +241,12 @@ impl P2pConnectionOutgoingState { } let dispatcher = state_context.into_dispatcher(); - dispatcher - .push(P2pConnectionOutgoingAction::AnswerRecvPending { peer_id: *peer_id }); + dispatcher.push(P2pConnectionOutgoingAction::AnswerRecvPending { peer_id }); Ok(()) } P2pConnectionOutgoingAction::AnswerRecvPending { peer_id } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) + .outgoing_peer_connection_mut(&peer_id) .ok_or_else(|| format!("Invalid state: {:?}", action))?; if let Self::OfferSendSuccess { opts, @@ -254,7 +256,7 @@ impl P2pConnectionOutgoingState { } = state { *state = Self::AnswerRecvPending { - time: meta.time(), + time, opts: opts.clone(), offer: offer.clone(), rpc_id: rpc_id.take(), @@ -271,10 +273,13 @@ impl P2pConnectionOutgoingState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pConnectionOutgoingAction::Error { - peer_id: *peer_id, + peer_id, error: match error { P2pConnectionErrorResponse::Rejected(reason) => { - P2pConnectionOutgoingError::Rejected(*reason) + P2pConnectionOutgoingError::Rejected(reason) + } + P2pConnectionErrorResponse::SignalDecryptionFailed => { + P2pConnectionOutgoingError::RemoteSignalDecryptionFailed } P2pConnectionErrorResponse::InternalError => { P2pConnectionOutgoingError::RemoteInternalError @@ -284,9 +289,9 @@ impl P2pConnectionOutgoingState { Ok(()) } P2pConnectionOutgoingAction::AnswerRecvSuccess { answer, peer_id } => { - let state = p2p_state - .outgoing_peer_connection_mut(peer_id) - .ok_or_else(|| format!("Invalid state: {:?}", action))?; + let state = p2p_state.outgoing_peer_connection_mut(&peer_id).ok_or( + "Missing peer connection for `P2pConnectionOutgoingAction::AnswerRecvSuccess`", + )?; if let Self::AnswerRecvPending { opts, @@ -296,7 +301,7 @@ impl P2pConnectionOutgoingState { } = state { *state = Self::AnswerRecvSuccess { - time: meta.time(), + time, opts: opts.clone(), offer: offer.clone(), answer: answer.clone(), @@ -308,23 +313,20 @@ impl P2pConnectionOutgoingState { state ); } - state_context.into_dispatcher().push( - P2pConnectionOutgoingEffectfulAction::AnswerSet { - peer_id: *peer_id, - answer: answer.clone(), - }, - ); + state_context + .into_dispatcher() + .push(P2pConnectionOutgoingEffectfulAction::AnswerSet { peer_id, answer }); Ok(()) } P2pConnectionOutgoingAction::FinalizePending { peer_id } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) + .outgoing_peer_connection_mut(&peer_id) .ok_or_else(|| format!("Invalid state: {:?}", action))?; match state { Self::Init { opts, rpc_id, .. } => { *state = Self::FinalizePending { - time: meta.time(), + time, opts: opts.clone(), offer: None, answer: None, @@ -340,7 +342,7 @@ impl P2pConnectionOutgoingState { .. } => { *state = Self::FinalizePending { - time: meta.time(), + time, opts: opts.clone(), offer: Some(offer.clone()), answer: Some(answer.clone()), @@ -360,14 +362,14 @@ impl P2pConnectionOutgoingState { P2pConnectionOutgoingAction::FinalizeError { error, peer_id } => { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pConnectionOutgoingAction::Error { - peer_id: *peer_id, + peer_id, error: P2pConnectionOutgoingError::FinalizeError(error.to_owned()), }); Ok(()) } P2pConnectionOutgoingAction::FinalizeSuccess { peer_id } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) + .outgoing_peer_connection_mut(&peer_id) .ok_or_else(|| format!("Invalid state: {:?}", action))?; if let Self::FinalizePending { @@ -379,7 +381,7 @@ impl P2pConnectionOutgoingState { } = state { *state = Self::FinalizeSuccess { - time: meta.time(), + time, opts: opts.clone(), offer: offer.clone(), answer: answer.clone(), @@ -394,25 +396,25 @@ impl P2pConnectionOutgoingState { } let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pConnectionOutgoingAction::Success { peer_id: *peer_id }); + dispatcher.push(P2pConnectionOutgoingAction::Success { peer_id }); Ok(()) } P2pConnectionOutgoingAction::Timeout { peer_id } => { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pConnectionOutgoingAction::Error { - peer_id: *peer_id, + peer_id, error: P2pConnectionOutgoingError::Timeout, }); Ok(()) } P2pConnectionOutgoingAction::Error { error, peer_id } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) - .ok_or_else(|| format!("Invalid state: {:?}", action))?; + .outgoing_peer_connection_mut(&peer_id) + .ok_or("Missing peer connection for `P2pConnectionOutgoingAction::Error`")?; let rpc_id = state.rpc_id(); *state = Self::Error { - time: meta.time(), + time, error: error.clone(), rpc_id, }; @@ -426,26 +428,26 @@ impl P2pConnectionOutgoingState { .network .scheduler .discovery_state() - .and_then(|discovery_state| discovery_state.request(peer_id)) + .and_then(|discovery_state| discovery_state.request(&peer_id)) .is_some() { dispatcher.push(P2pNetworkKadRequestAction::Error { - peer_id: *peer_id, + peer_id, error: error.to_string(), }); } } - if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(peer_id) { + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(&peer_id) { if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_outgoing_error { - dispatcher.push_callback(callback.clone(), (rpc_id, error.clone())); + dispatcher.push_callback(callback.clone(), (rpc_id, error)); } } Ok(()) } P2pConnectionOutgoingAction::Success { peer_id } => { let state = p2p_state - .outgoing_peer_connection_mut(peer_id) + .outgoing_peer_connection_mut(&peer_id) .ok_or_else(|| format!("Invalid state: {:?}", action))?; if let Self::FinalizeSuccess { @@ -456,7 +458,7 @@ impl P2pConnectionOutgoingState { } = state { *state = Self::Success { - time: meta.time(), + time, offer: offer.clone(), answer: answer.clone(), rpc_id: rpc_id.take(), @@ -472,11 +474,11 @@ impl P2pConnectionOutgoingState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_state: &P2pState = state.substate()?; dispatcher.push(P2pPeerAction::Ready { - peer_id: *peer_id, + peer_id, incoming: false, }); - if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(peer_id) { + if let Some(rpc_id) = p2p_state.peer_connection_rpc_id(&peer_id) { if let Some(callback) = &p2p_state.callbacks.on_p2p_connection_outgoing_success { dispatcher.push_callback(callback.clone(), rpc_id); diff --git a/p2p/src/connection/outgoing/p2p_connection_outgoing_state.rs b/p2p/src/connection/outgoing/p2p_connection_outgoing_state.rs index 8f31a42d6f..0c4ec47c14 100644 --- a/p2p/src/connection/outgoing/p2p_connection_outgoing_state.rs +++ b/p2p/src/connection/outgoing/p2p_connection_outgoing_state.rs @@ -125,6 +125,8 @@ pub enum P2pConnectionOutgoingError { SdpCreateError(String), #[error("rejected: {0}")] Rejected(RejectionReason), + #[error("remote signal decryption failed")] + RemoteSignalDecryptionFailed, #[error("remote internal error")] RemoteInternalError, #[error("finalization error: {0}")] diff --git a/p2p/src/connection/outgoing_effectful/p2p_connection_outgoing_effectful_actions.rs b/p2p/src/connection/outgoing_effectful/p2p_connection_outgoing_effectful_actions.rs index 7747c5b9ed..80a6056cd6 100644 --- a/p2p/src/connection/outgoing_effectful/p2p_connection_outgoing_effectful_actions.rs +++ b/p2p/src/connection/outgoing_effectful/p2p_connection_outgoing_effectful_actions.rs @@ -36,8 +36,8 @@ impl redux::EnablingCondition for P2pConnectionOutgoingEffectfulAction } } -impl From for crate::P2pAction { - fn from(a: P2pConnectionOutgoingEffectfulAction) -> Self { - Self::ConnectionEffectful(P2pConnectionEffectfulAction::Outgoing(a)) +impl From for crate::P2pEffectfulAction { + fn from(a: P2pConnectionOutgoingEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Connection(P2pConnectionEffectfulAction::Outgoing(a)) } } diff --git a/p2p/src/connection/outgoing_effectful/p2p_connection_outgoing_effectful_effects.rs b/p2p/src/connection/outgoing_effectful/p2p_connection_outgoing_effectful_effects.rs index 408128e106..104c576659 100644 --- a/p2p/src/connection/outgoing_effectful/p2p_connection_outgoing_effectful_effects.rs +++ b/p2p/src/connection/outgoing_effectful/p2p_connection_outgoing_effectful_effects.rs @@ -60,6 +60,10 @@ impl P2pConnectionOutgoingEffectfulAction { }; service.http_signaling_request(url, *offer); } + webrtc::SignalingMethod::P2p { .. } => { + bug_condition!("`P2pConnectionOutgoingEffectfulAction::OfferSend` shouldn't be called for `webrtc::SignalingMethod::P2p`"); + return; + } } store.dispatch(P2pConnectionOutgoingAction::OfferSendSuccess { peer_id }); } diff --git a/p2p/src/connection/p2p_connection_reducer.rs b/p2p/src/connection/p2p_connection_reducer.rs index cdee0e18b3..b4efdfafc3 100644 --- a/p2p/src/connection/p2p_connection_reducer.rs +++ b/p2p/src/connection/p2p_connection_reducer.rs @@ -10,7 +10,7 @@ use crate::P2pState; impl P2pConnectionState { pub fn reducer( state_context: Substate, - action: ActionWithMeta<&P2pConnectionAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, diff --git a/p2p/src/connection/p2p_connection_state.rs b/p2p/src/connection/p2p_connection_state.rs index 2585100b13..6c956ec073 100644 --- a/p2p/src/connection/p2p_connection_state.rs +++ b/p2p/src/connection/p2p_connection_state.rs @@ -26,7 +26,7 @@ impl P2pConnectionState { pub fn incoming_init(opts: &P2pConnectionIncomingInitOpts) -> Self { Self::Incoming(P2pConnectionIncomingState::Init { time: Timestamp::ZERO, - signaling: opts.signaling.clone(), + signaling: opts.signaling, offer: opts.offer.clone(), rpc_id: None, }) diff --git a/p2p/src/disconnection/p2p_disconnection_actions.rs b/p2p/src/disconnection/p2p_disconnection_actions.rs index 58171ad5f0..aec90cef41 100644 --- a/p2p/src/disconnection/p2p_disconnection_actions.rs +++ b/p2p/src/disconnection/p2p_disconnection_actions.rs @@ -10,7 +10,7 @@ pub type P2pDisconnectionActionWithMetaRef<'a> = redux::ActionWithMeta<&'a P2pDi #[action_event(fields(display(peer_id), display(reason)), level = info)] pub enum P2pDisconnectionAction { /// Initialize disconnection. - #[action_event(level = debug)] + #[action_event(fields(display(peer_id), display(reason)), level = info)] Init { peer_id: PeerId, reason: P2pDisconnectionReason, diff --git a/p2p/src/disconnection/p2p_disconnection_reducer.rs b/p2p/src/disconnection/p2p_disconnection_reducer.rs index 673327019b..9b4e55860f 100644 --- a/p2p/src/disconnection/p2p_disconnection_reducer.rs +++ b/p2p/src/disconnection/p2p_disconnection_reducer.rs @@ -11,7 +11,7 @@ use super::{P2pDisconnectedState, P2pDisconnectionAction}; impl P2pDisconnectedState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pDisconnectionAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -23,37 +23,34 @@ impl P2pDisconnectedState { match action { P2pDisconnectionAction::Init { peer_id, reason } => { #[cfg(feature = "p2p-libp2p")] - if p2p_state.is_libp2p_peer(peer_id) { + if p2p_state.is_libp2p_peer(&peer_id) { if let Some((&addr, _)) = p2p_state .network .scheduler .connections .iter() - .find(|(_, conn_state)| conn_state.peer_id() == Some(peer_id)) + .find(|(_, conn_state)| conn_state.peer_id() == Some(&peer_id)) { - let Some(peer) = p2p_state.peers.get_mut(peer_id) else { + let Some(peer) = p2p_state.peers.get_mut(&peer_id) else { bug_condition!("Invalid state for: `P2pDisconnectionAction::Finish`"); return Ok(()); }; peer.status = P2pPeerStatus::Disconnecting { time: meta.time() }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pNetworkSchedulerAction::Disconnect { - addr, - reason: reason.clone(), - }); - dispatcher.push(P2pDisconnectionAction::Finish { peer_id: *peer_id }); + dispatcher.push(P2pNetworkSchedulerAction::Disconnect { addr, reason }); + dispatcher.push(P2pDisconnectionAction::Finish { peer_id }); } return Ok(()); } let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pDisconnectionEffectfulAction::Init { peer_id: *peer_id }); + dispatcher.push(P2pDisconnectionEffectfulAction::Init { peer_id }); Ok(()) } #[cfg(not(feature = "p2p-libp2p"))] P2pDisconnectionAction::Finish { peer_id } => { - let Some(peer) = p2p_state.peers.get_mut(peer_id) else { + let Some(peer) = p2p_state.peers.get_mut(&peer_id) else { bug_condition!("Invalid state for: `P2pDisconnectionAction::Finish`"); return Ok(()); }; @@ -61,10 +58,10 @@ impl P2pDisconnectedState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_state: &P2pState = state.substate()?; - dispatcher.push(P2pPeerAction::Remove { peer_id: *peer_id }); + dispatcher.push(P2pPeerAction::Remove { peer_id }); if let Some(callback) = &p2p_state.callbacks.on_p2p_disconnection_finish { - dispatcher.push_callback(callback.clone(), *peer_id); + dispatcher.push_callback(callback.clone(), peer_id); } Ok(()) } @@ -76,13 +73,13 @@ impl P2pDisconnectedState { .connections .iter() .any(|(_addr, conn_state)| { - conn_state.peer_id() == Some(peer_id) && conn_state.closed.is_none() + conn_state.peer_id() == Some(&peer_id) && conn_state.closed.is_none() }) { return Ok(()); } - let Some(peer) = p2p_state.peers.get_mut(peer_id) else { + let Some(peer) = p2p_state.peers.get_mut(&peer_id) else { bug_condition!("Invalid state for: `P2pDisconnectionAction::Finish`"); return Ok(()); }; @@ -90,10 +87,10 @@ impl P2pDisconnectedState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_state: &P2pState = state.substate()?; - dispatcher.push(P2pPeerAction::Remove { peer_id: *peer_id }); + dispatcher.push(P2pPeerAction::Remove { peer_id }); if let Some(callback) = &p2p_state.callbacks.on_p2p_disconnection_finish { - dispatcher.push_callback(callback.clone(), *peer_id); + dispatcher.push_callback(callback.clone(), peer_id); } Ok(()) diff --git a/p2p/src/disconnection_effectful/p2p_disconnection_effectful_actions.rs b/p2p/src/disconnection_effectful/p2p_disconnection_effectful_actions.rs index 03efd82fd5..205aca2bea 100644 --- a/p2p/src/disconnection_effectful/p2p_disconnection_effectful_actions.rs +++ b/p2p/src/disconnection_effectful/p2p_disconnection_effectful_actions.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::{P2pPeerStatus, P2pState, PeerId}; #[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)] -#[action_event(fields(display(peer_id), display(reason)), level = info)] +#[action_event(fields(display(peer_id), display(reason)), level = debug)] pub enum P2pDisconnectionEffectfulAction { /// Initialize disconnection. Init { peer_id: PeerId }, diff --git a/p2p/src/identify/p2p_identify_reducer.rs b/p2p/src/identify/p2p_identify_reducer.rs index 2adc42e26a..d0a5c1f0d4 100644 --- a/p2p/src/identify/p2p_identify_reducer.rs +++ b/p2p/src/identify/p2p_identify_reducer.rs @@ -14,7 +14,7 @@ impl P2pState { #[cfg(feature = "p2p-libp2p")] pub fn identify_reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pIdentifyAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -30,7 +30,7 @@ impl P2pState { let scheduler = &p2p_state.network.scheduler; let stream_id = scheduler .connections - .get(addr) + .get(&addr) .ok_or_else(|| format!("connection with {addr} not found")) .and_then(|conn| { conn.mux @@ -45,7 +45,7 @@ impl P2pState { })?; dispatcher.push(P2pNetworkYamuxAction::OpenStream { - addr: *addr, + addr, stream_id, stream_kind: StreamKind::Identify(IdentifyAlgorithm::Identify1_0_0), }); @@ -57,8 +57,9 @@ impl P2pState { info, addr, } => { - if let Some(peer) = p2p_state.peers.get_mut(peer_id) { - peer.identify = Some(*info.clone()); + let info = *info; + if let Some(peer) = p2p_state.peers.get_mut(&peer_id) { + peer.identify = Some(info.clone()); } else { bug_condition!( "Peer state not found for `P2pIdentifyAction::UpdatePeerInformation`" @@ -67,11 +68,10 @@ impl P2pState { } let (dispatcher, state) = state_context.into_dispatcher_and_state(); - let peer_id = *peer_id; dispatcher.push(P2pNetworkKademliaAction::UpdateRoutingTable { peer_id, - addrs: info.listen_addrs.clone(), + addrs: info.listen_addrs, }); let stream_id = YamuxStreamKind::Rpc.stream_id(addr.incoming); @@ -91,7 +91,7 @@ impl P2pState { } dispatcher.push(P2pNetworkYamuxAction::OpenStream { - addr: *addr, + addr, stream_id, stream_kind, }); @@ -99,7 +99,7 @@ impl P2pState { let stream_kind = StreamKind::Broadcast(BroadcastAlgorithm::Meshsub1_1_0); if info.protocols.contains(&stream_kind) { dispatcher.push(P2pNetworkYamuxAction::OpenStream { - addr: *addr, + addr, stream_id: stream_id + 2, stream_kind, }); @@ -110,10 +110,7 @@ impl P2pState { if kad_state.map_or(false, |state| state.request(&peer_id).is_some()) && info.protocols.contains(&protocol) { - dispatcher.push(P2pNetworkKadRequestAction::MuxReady { - peer_id, - addr: *addr, - }); + dispatcher.push(P2pNetworkKadRequestAction::MuxReady { peer_id, addr }); } Ok(()) diff --git a/p2p/src/identity/mod.rs b/p2p/src/identity/mod.rs index 29addb3331..50947d1a9e 100644 --- a/p2p/src/identity/mod.rs +++ b/p2p/src/identity/mod.rs @@ -5,4 +5,4 @@ mod public_key; pub use public_key::PublicKey; mod secret_key; -pub use secret_key::SecretKey; +pub use secret_key::{EncryptableType, SecretKey}; diff --git a/p2p/src/identity/public_key.rs b/p2p/src/identity/public_key.rs index 14763b8661..44cd4ad680 100644 --- a/p2p/src/identity/public_key.rs +++ b/p2p/src/identity/public_key.rs @@ -1,5 +1,10 @@ -use std::{fmt, str::FromStr}; +use std::{ + fmt, + io::{Read, Write}, + str::FromStr, +}; +use binprot::{BinProtRead, BinProtWrite}; use ed25519_dalek::VerifyingKey as Ed25519PublicKey; use serde::{Deserialize, Serialize}; @@ -22,6 +27,10 @@ impl PublicKey { pub fn peer_id(&self) -> PeerId { PeerId::from_bytes(self.to_bytes()) } + + pub fn to_x25519(&self) -> x25519_dalek::PublicKey { + self.0.to_montgomery().to_bytes().into() + } } impl fmt::Display for PublicKey { @@ -98,3 +107,20 @@ impl<'de> serde::Deserialize<'de> for PublicKey { } } } + +impl BinProtWrite for PublicKey { + fn binprot_write(&self, w: &mut W) -> std::io::Result<()> { + w.write_all(&self.to_bytes()) + } +} + +impl BinProtRead for PublicKey { + fn binprot_read(r: &mut R) -> Result + where + Self: Sized, + { + let mut buf = [0; 32]; + r.read_exact(&mut buf)?; + Self::from_bytes(buf).map_err(|err| binprot::Error::CustomError(Box::new(err))) + } +} diff --git a/p2p/src/identity/secret_key.rs b/p2p/src/identity/secret_key.rs index 20d53f2ac4..5237391cc6 100644 --- a/p2p/src/identity/secret_key.rs +++ b/p2p/src/identity/secret_key.rs @@ -1,7 +1,7 @@ use std::{fmt, str::FromStr}; use ed25519_dalek::SigningKey as Ed25519SecretKey; -use rand::Rng; +use rand::{CryptoRng, Rng}; use serde::{Deserialize, Serialize}; use crate::identity::PublicKey; @@ -29,7 +29,7 @@ impl SecretKey { pub fn deterministic(i: usize) -> Self { let mut bytes = [0; 32]; let bytes_len = bytes.len(); - let i_bytes = i.to_be_bytes(); + let i_bytes = (i + 1).to_be_bytes(); let i = bytes_len - i_bytes.len(); bytes[i..bytes_len].copy_from_slice(&i_bytes); Self::from_bytes(bytes) @@ -46,6 +46,69 @@ impl SecretKey { pub fn public_key(&self) -> PublicKey { PublicKey(self.0.verifying_key()) } + + pub fn to_x25519(&self) -> x25519_dalek::StaticSecret { + self.0.to_scalar_bytes().into() + } +} + +use aes_gcm::{ + aead::{Aead, AeadCore}, + Aes256Gcm, KeyInit, +}; +impl SecretKey { + fn shared_key(&self, other_pk: &PublicKey) -> Result { + let key = self.to_x25519().diffie_hellman(&other_pk.to_x25519()); + if !key.was_contributory() { + return Err(()); + } + let key = key.to_bytes(); + // eprintln!("[shared_key] {} & {} = {}", self.public_key(), other_pk, hex::encode(&key)); + let key: &aes_gcm::Key = (&key).into(); + Ok(Aes256Gcm::new(key)) + } + + pub fn encrypt_raw( + &self, + other_pk: &PublicKey, + rng: impl Rng + CryptoRng, + data: &[u8], + ) -> Result, ()> { + let shared_key = self.shared_key(other_pk)?; + let nonce = Aes256Gcm::generate_nonce(rng); + let mut buffer = Vec::from(AsRef::<[u8]>::as_ref(&nonce)); + buffer.extend(shared_key.encrypt(&nonce, data).or(Err(()))?); + Ok(buffer) + } + + pub fn encrypt( + &self, + other_pk: &PublicKey, + rng: impl Rng + CryptoRng, + data: &M, + ) -> Result { + let data = serde_json::to_vec(data).or(Err(()))?; + self.encrypt_raw(other_pk, rng, &data).map(Into::into) + } + + pub fn decrypt_raw(&self, other_pk: &PublicKey, ciphertext: &[u8]) -> Result, ()> { + let shared_key = self.shared_key(other_pk)?; + let (nonce, ciphertext) = ciphertext.split_at_checked(12).ok_or(())?; + shared_key.decrypt(nonce.into(), ciphertext).or(Err(())) + } + + pub fn decrypt( + &self, + other_pk: &PublicKey, + ciphertext: &M::Encrypted, + ) -> Result { + let data = self.decrypt_raw(other_pk, ciphertext.as_ref())?; + serde_json::from_slice(&data).or(Err(())) + } +} + +pub trait EncryptableType: Serialize + for<'a> Deserialize<'a> { + type Encrypted: From> + AsRef<[u8]>; } impl fmt::Display for SecretKey { @@ -92,6 +155,16 @@ impl TryFrom for libp2p_identity::Keypair { } } +#[cfg(feature = "p2p-libp2p")] +impl TryFrom for SecretKey { + type Error = (); + + fn try_from(value: libp2p_identity::Keypair) -> Result { + let bytes = value.try_into_ed25519().or(Err(()))?.to_bytes(); + Ok(Self::from_bytes(bytes[0..32].try_into().or(Err(()))?)) + } +} + impl Serialize for SecretKey { fn serialize(&self, serializer: S) -> Result where diff --git a/p2p/src/lib.rs b/p2p/src/lib.rs index 4c78000813..92000312c4 100644 --- a/p2p/src/lib.rs +++ b/p2p/src/lib.rs @@ -6,9 +6,18 @@ pub mod disconnection_effectful; pub mod identity; use bootstrap::P2pNetworkKadBootstrapState; use channels::{ - best_tip::P2pChannelsBestTipAction, best_tip_effectful::P2pChannelsBestTipEffectfulAction, - rpc::P2pChannelsRpcAction, rpc_effectful::P2pChannelsRpcEffectfulAction, - snark::P2pChannelsSnarkAction, snark_effectful::P2pChannelsSnarkEffectfulAction, + best_tip::P2pChannelsBestTipAction, + best_tip_effectful::P2pChannelsBestTipEffectfulAction, + rpc::P2pChannelsRpcAction, + rpc_effectful::P2pChannelsRpcEffectfulAction, + signaling::{ + discovery::P2pChannelsSignalingDiscoveryAction, + discovery_effectful::P2pChannelsSignalingDiscoveryEffectfulAction, + exchange::P2pChannelsSignalingExchangeAction, + exchange_effectful::P2pChannelsSignalingExchangeEffectfulAction, + }, + snark::P2pChannelsSnarkAction, + snark_effectful::P2pChannelsSnarkEffectfulAction, snark_job_commitment::P2pChannelsSnarkJobCommitmentAction, snark_job_commitment_effectful::P2pChannelsSnarkJobCommitmentEffectfulAction, streaming_rpc::P2pChannelsStreamingRpcAction, @@ -56,7 +65,6 @@ pub use p2p_state::*; mod p2p_reducer; mod p2p_effects; -pub use self::p2p_effects::*; mod p2p_service; pub use p2p_service::*; @@ -108,6 +116,7 @@ pub trait P2pActionTrait: EnablingCondition + From + From + + From + From + From + From @@ -126,6 +135,10 @@ pub trait P2pActionTrait: + From + From + From + + From + + From + + From + + From + From + From + From @@ -144,5 +157,6 @@ pub trait P2pActionTrait: + From + From + From + + From { } diff --git a/p2p/src/network/identify/mod.rs b/p2p/src/network/identify/mod.rs index 7362bce195..15b22fae81 100644 --- a/p2p/src/network/identify/mod.rs +++ b/p2p/src/network/identify/mod.rs @@ -11,7 +11,7 @@ mod p2p_network_identify_protocol; pub use self::p2p_network_identify_protocol::*; mod p2p_network_identify_actions; -pub use self::p2p_network_identify_actions::P2pNetworkIdentifyAction; +pub use self::p2p_network_identify_actions::*; mod p2p_network_identify_state; pub use self::p2p_network_identify_state::*; diff --git a/p2p/src/network/identify/p2p_network_identify_actions.rs b/p2p/src/network/identify/p2p_network_identify_actions.rs index 2d2d610463..1a8dbd32a3 100644 --- a/p2p/src/network/identify/p2p_network_identify_actions.rs +++ b/p2p/src/network/identify/p2p_network_identify_actions.rs @@ -3,7 +3,7 @@ use crate::{ stream::P2pNetworkIdentifyStreamAction, stream_effectful::P2pNetworkIdentifyStreamEffectfulAction, }, - P2pAction, P2pNetworkAction, P2pState, + P2pAction, P2pEffectfulAction, P2pNetworkAction, P2pNetworkEffectfulAction, P2pState, }; use openmina_core::ActionEvent; use redux::EnablingCondition; @@ -13,14 +13,25 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, derive_more::From, ActionEvent)] pub enum P2pNetworkIdentifyAction { Stream(P2pNetworkIdentifyStreamAction), - StreamEffectful(P2pNetworkIdentifyStreamEffectfulAction), +} + +#[derive(Debug, Clone, Serialize, Deserialize, derive_more::From, ActionEvent)] +pub enum P2pNetworkIdentifyEffectfulAction { + Stream(P2pNetworkIdentifyStreamEffectfulAction), } impl EnablingCondition for P2pNetworkIdentifyAction { fn is_enabled(&self, state: &P2pState, time: redux::Timestamp) -> bool { match self { P2pNetworkIdentifyAction::Stream(action) => action.is_enabled(state, time), - P2pNetworkIdentifyAction::StreamEffectful(action) => action.is_enabled(state, time), + } + } +} + +impl EnablingCondition for P2pNetworkIdentifyEffectfulAction { + fn is_enabled(&self, state: &P2pState, time: redux::Timestamp) -> bool { + match self { + P2pNetworkIdentifyEffectfulAction::Stream(action) => action.is_enabled(state, time), } } } @@ -30,3 +41,9 @@ impl From for P2pAction { P2pNetworkAction::Identify(value).into() } } + +impl From for P2pEffectfulAction { + fn from(value: P2pNetworkIdentifyEffectfulAction) -> P2pEffectfulAction { + P2pEffectfulAction::Network(P2pNetworkEffectfulAction::Identify(value)) + } +} diff --git a/p2p/src/network/identify/p2p_network_identify_effects.rs b/p2p/src/network/identify/p2p_network_identify_effects.rs index 57db19d7f6..c1674ede30 100644 --- a/p2p/src/network/identify/p2p_network_identify_effects.rs +++ b/p2p/src/network/identify/p2p_network_identify_effects.rs @@ -1,20 +1,15 @@ +use super::p2p_network_identify_actions::P2pNetworkIdentifyEffectfulAction; use crate::P2pNetworkService; - -use super::P2pNetworkIdentifyAction; use redux::ActionMeta; -impl P2pNetworkIdentifyAction { - pub fn effects(self, meta: &ActionMeta, store: &mut Store) -> Result<(), String> +impl P2pNetworkIdentifyEffectfulAction { + pub fn effects(self, meta: &ActionMeta, store: &mut Store) where Store: crate::P2pStore, Store::Service: P2pNetworkService, { match self { - P2pNetworkIdentifyAction::Stream(_) => { - // handled by reducer - Ok(()) - } - P2pNetworkIdentifyAction::StreamEffectful(action) => action.effects(meta, store), + P2pNetworkIdentifyEffectfulAction::Stream(action) => action.effects(meta, store), } } } diff --git a/p2p/src/network/identify/p2p_network_identify_reducer.rs b/p2p/src/network/identify/p2p_network_identify_reducer.rs index 5e24a2fd64..76fc2d9157 100644 --- a/p2p/src/network/identify/p2p_network_identify_reducer.rs +++ b/p2p/src/network/identify/p2p_network_identify_reducer.rs @@ -6,7 +6,7 @@ use redux::ActionWithMeta; impl super::P2pNetworkIdentifyState { pub fn reducer( state_context: Substate, - action: ActionWithMeta<&P2pNetworkIdentifyAction>, + action: ActionWithMeta, limits: &P2pLimits, ) -> Result<(), String> where @@ -20,10 +20,6 @@ impl super::P2pNetworkIdentifyState { meta.with_action(action), limits, ), - P2pNetworkIdentifyAction::StreamEffectful(_) => { - // no reducer effectful action - Ok(()) - } } } } diff --git a/p2p/src/network/identify/stream/p2p_network_identify_stream_reducer.rs b/p2p/src/network/identify/stream/p2p_network_identify_stream_reducer.rs index 49674d6c88..dd6c04dc1a 100644 --- a/p2p/src/network/identify/stream/p2p_network_identify_stream_reducer.rs +++ b/p2p/src/network/identify/stream/p2p_network_identify_stream_reducer.rs @@ -19,7 +19,7 @@ use redux::{ActionWithMeta, Dispatcher}; impl P2pNetworkIdentifyStreamState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pNetworkIdentifyStreamAction>, + action: ActionWithMeta, limits: &P2pLimits, ) -> Result<(), String> where @@ -28,7 +28,7 @@ impl P2pNetworkIdentifyStreamState { { let (action, meta) = action.split(); let substate = state_context.get_substate_mut()?; - let stream_state = match action { + let stream_state = match &action { P2pNetworkIdentifyStreamAction::New { peer_id, stream_id, .. } => substate @@ -46,7 +46,7 @@ impl P2pNetworkIdentifyStreamState { } a => substate .find_identify_stream_state_mut(a.peer_id(), a.stream_id()) - .ok_or_else(|| format!("Identify stream not found for action {action:?}"))?, + .ok_or_else(|| format!("Identify stream not found for action {a:?}"))?, }; match &stream_state { @@ -63,7 +63,7 @@ impl P2pNetworkIdentifyStreamState { return Ok(()); }; - let kind = P2pNetworkIdentifyStreamKind::from(*incoming); + let kind = P2pNetworkIdentifyStreamKind::from(incoming); *stream_state = match kind { // For incoming streams we prepare to send the Identify message @@ -80,9 +80,9 @@ impl P2pNetworkIdentifyStreamState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkIdentifyStreamEffectfulAction::SendIdentify { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); } @@ -129,14 +129,14 @@ impl P2pNetworkIdentifyStreamState { stream_state { dispatcher.push(P2pIdentifyAction::UpdatePeerInformation { - peer_id: *peer_id, + peer_id, info: data, - addr: *addr, + addr, }); dispatcher.push(P2pNetworkIdentifyStreamAction::Close { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); } else { let P2pNetworkIdentifyStreamState::Error(error) = stream_state else { @@ -146,7 +146,7 @@ impl P2pNetworkIdentifyStreamState { warn!(meta.time(); summary = "error handling Identify action", error = display(&error)); dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::IdentifyStreamError( P2pNetworkIdentifyStreamError::from(error), ), @@ -166,7 +166,7 @@ impl P2pNetworkIdentifyStreamState { stream_id, } => { let dispatcher = state_context.into_dispatcher(); - Self::disconnect(dispatcher, *addr, *peer_id, *stream_id) + Self::disconnect(dispatcher, addr, peer_id, stream_id) } _ => { // State and connection cleanup should be handled by timeout @@ -182,7 +182,7 @@ impl P2pNetworkIdentifyStreamState { stream_id, } => { let mut data = data.clone(); - data.extend_from_slice(&new_data.0); + data.extend_from_slice(&new_data); if *len > data.len() { *stream_state = @@ -197,14 +197,14 @@ impl P2pNetworkIdentifyStreamState { let data = data.clone(); let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pIdentifyAction::UpdatePeerInformation { - peer_id: *peer_id, + peer_id, info: data, - addr: *addr, + addr, }); dispatcher.push(P2pNetworkIdentifyStreamAction::Close { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); } else { let P2pNetworkIdentifyStreamState::Error(error) = stream_state else { @@ -217,7 +217,7 @@ impl P2pNetworkIdentifyStreamState { warn!(meta.time(); summary = "error handling Identify action", error = display(&error)); dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::IdentifyStreamError( P2pNetworkIdentifyStreamError::from(error), ), @@ -238,7 +238,7 @@ impl P2pNetworkIdentifyStreamState { stream_id, } => { let dispatcher = state_context.into_dispatcher(); - Self::disconnect(dispatcher, *addr, *peer_id, *stream_id) + Self::disconnect(dispatcher, addr, peer_id, stream_id) } _ => { // State and connection cleanup should be handled by timeout @@ -258,9 +258,9 @@ impl P2pNetworkIdentifyStreamState { stream_id, } => { let dispatcher = state_context.into_dispatcher(); - Self::disconnect(dispatcher, *addr, *peer_id, *stream_id) + Self::disconnect(dispatcher, addr, peer_id, stream_id) } - _ => { + action => { // State and connection cleanup should be handled by timeout bug_condition!("Received action {:?} in SendIdentify state", action); Ok(()) @@ -278,7 +278,7 @@ impl P2pNetworkIdentifyStreamState { stream_id, } => { let dispatcher = state_context.into_dispatcher(); - Self::disconnect(dispatcher, *addr, *peer_id, *stream_id) + Self::disconnect(dispatcher, addr, peer_id, stream_id) } _ => Ok(()), }, @@ -300,7 +300,7 @@ impl P2pNetworkIdentifyStreamState { } }; - let data = match P2pNetworkIdentify::try_from(message.clone()) { + let data = match P2pNetworkIdentify::try_from(message) { Ok(v) => v, Err(e) => { *self = P2pNetworkIdentifyStreamState::Error(e.into()); diff --git a/p2p/src/network/identify/stream_effectful/p2p_network_identify_stream_effectful_actions.rs b/p2p/src/network/identify/stream_effectful/p2p_network_identify_stream_effectful_actions.rs index bca0d32e3a..6c3fa76df0 100644 --- a/p2p/src/network/identify/stream_effectful/p2p_network_identify_stream_effectful_actions.rs +++ b/p2p/src/network/identify/stream_effectful/p2p_network_identify_stream_effectful_actions.rs @@ -1,4 +1,4 @@ -use crate::{ConnectionAddr, P2pAction, P2pState, PeerId, StreamId}; +use crate::{ConnectionAddr, P2pState, PeerId, StreamId}; use openmina_core::ActionEvent; use redux::EnablingCondition; use serde::{Deserialize, Serialize}; @@ -37,8 +37,10 @@ impl EnablingCondition for P2pNetworkIdentifyStreamEffectfulAction { } } -impl From for P2pAction { - fn from(value: P2pNetworkIdentifyStreamEffectfulAction) -> Self { - P2pAction::Network(super::super::P2pNetworkIdentifyAction::StreamEffectful(value).into()) +impl From for crate::P2pEffectfulAction { + fn from(value: P2pNetworkIdentifyStreamEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Network(crate::P2pNetworkEffectfulAction::Identify( + crate::network::identify::P2pNetworkIdentifyEffectfulAction::Stream(value), + )) } } diff --git a/p2p/src/network/identify/stream_effectful/p2p_network_identify_stream_effectful_effects.rs b/p2p/src/network/identify/stream_effectful/p2p_network_identify_stream_effectful_effects.rs index 9f9fd9f3df..f7360c1ec1 100644 --- a/p2p/src/network/identify/stream_effectful/p2p_network_identify_stream_effectful_effects.rs +++ b/p2p/src/network/identify/stream_effectful/p2p_network_identify_stream_effectful_effects.rs @@ -38,7 +38,7 @@ where } impl P2pNetworkIdentifyStreamEffectfulAction { - pub fn effects(self, _meta: &ActionMeta, store: &mut Store) -> Result<(), String> + pub fn effects(self, _meta: &ActionMeta, store: &mut Store) where Store::Service: P2pNetworkService, Store: crate::P2pStore, @@ -90,7 +90,7 @@ impl P2pNetworkIdentifyStreamEffectfulAction { Ok(identify_msg_proto) => identify_msg_proto, Err(err) => { bug_condition!("error encoding message {:?}", err); - return Err(err.to_string()); + return; } }; @@ -98,7 +98,7 @@ impl P2pNetworkIdentifyStreamEffectfulAction { prost::Message::encode_length_delimited(&identify_msg_proto, &mut out) { bug_condition!("error serializing message {:?}", err); - return Err(err.to_string()); + return; } let data = fuzzed_maybe!( @@ -120,8 +120,6 @@ impl P2pNetworkIdentifyStreamEffectfulAction { peer_id, stream_id, }); - - Ok(()) } } } diff --git a/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_actions.rs b/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_actions.rs index 92285aa14a..a2f3a11f94 100644 --- a/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_actions.rs +++ b/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_actions.rs @@ -4,11 +4,18 @@ use serde::{Deserialize, Serialize}; use crate::{P2pAction, P2pNetworkKadAction, P2pNetworkKadLatestRequestPeers, P2pState, PeerId}; +use super::P2pNetworkKadBoostrapRequestState; + #[derive(Clone, Debug, Serialize, Deserialize, ActionEvent)] #[action_event(fields(display(peer_id), debug(closest_peers), error))] pub enum P2pNetworkKadBootstrapAction { /// Create `FIND_NODE` request. CreateRequests, + AppendRequest { + request: Option, + peer_id: PeerId, + }, + FinalizeRequests, /// `FIND_NODE` request successful. RequestDone { peer_id: PeerId, @@ -16,26 +23,34 @@ pub enum P2pNetworkKadBootstrapAction { }, /// `FIND_NODE` request failed. #[action_event(level = debug)] - RequestError { peer_id: PeerId, error: String }, + RequestError { + peer_id: PeerId, + error: String, + }, } impl EnablingCondition for P2pNetworkKadBootstrapAction { fn is_enabled(&self, state: &P2pState, _time: redux::Timestamp) -> bool { + let state = state + .network + .scheduler + .discovery_state + .as_ref() + .and_then(|discovery_state| discovery_state.bootstrap_state()); match self { - P2pNetworkKadBootstrapAction::CreateRequests => state - .network - .scheduler - .discovery_state - .as_ref() - .and_then(|discovery_state| discovery_state.bootstrap_state()) - .map_or(false, |bootstrap_state| bootstrap_state.requests.len() < 3), + P2pNetworkKadBootstrapAction::CreateRequests => { + state.map_or(false, |bootstrap_state| bootstrap_state.requests.len() < 3) + } + P2pNetworkKadBootstrapAction::AppendRequest { .. } => state + .map_or(false, |bootstrap_state| { + bootstrap_state.peer_id_req_vec.len() < 3 + }), + P2pNetworkKadBootstrapAction::FinalizeRequests => state + .map_or(false, |bootstrap_state| { + bootstrap_state.peer_id_req_vec.len() <= 3 + }), P2pNetworkKadBootstrapAction::RequestDone { peer_id, .. } | P2pNetworkKadBootstrapAction::RequestError { peer_id, .. } => state - .network - .scheduler - .discovery_state - .as_ref() - .and_then(|discovery_state| discovery_state.bootstrap_state()) .map_or(false, |bootstrap_state| { bootstrap_state.request(peer_id).is_some() }), diff --git a/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_reducer.rs b/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_reducer.rs index 24879007e0..3b0e592297 100644 --- a/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_reducer.rs +++ b/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_reducer.rs @@ -1,52 +1,24 @@ -use multiaddr::Multiaddr; +use std::mem; + use openmina_core::{bug_condition, Substate, SubstateAccess}; -use redux::{ActionWithMeta, Timestamp}; +use redux::ActionWithMeta; use crate::{ bootstrap::{ - P2pNetworkKadBoostrapRequestState, P2pNetworkKadBootstrapFailedRequest, - P2pNetworkKadBootstrapOngoingRequest, P2pNetworkKadBootstrapRequestStat, - P2pNetworkKadBootstrapSuccessfulRequest, + P2pNetworkKadBootstrapFailedRequest, P2pNetworkKadBootstrapOngoingRequest, + P2pNetworkKadBootstrapRequestStat, P2pNetworkKadBootstrapSuccessfulRequest, }, connection::outgoing::P2pConnectionOutgoingInitOpts, - socket_addr_try_from_multiaddr, P2pNetworkKadRequestAction, P2pNetworkKadState, + P2pNetworkKadEffectfulAction, P2pNetworkKadRequestAction, P2pNetworkKadState, P2pNetworkKademliaAction, P2pState, }; use super::{P2pNetworkKadBootstrapAction, P2pNetworkKadBootstrapState}; -fn prepare_next_request( - addrs: &[Multiaddr], - time: Timestamp, - filter_addrs: bool, -) -> Option { - let mut addrs = addrs - .iter() - .map(socket_addr_try_from_multiaddr) - .filter_map(Result::ok) - // TODO(akoptelov): remove this filtering when multiple address support is added - .filter(|addr| { - !filter_addrs - || match addr.ip() { - std::net::IpAddr::V4(ipv4) if ipv4.is_loopback() || ipv4.is_private() => false, - std::net::IpAddr::V6(ipv6) if ipv6.is_loopback() => false, - _ => true, - } - }); - - let addr = addrs.next()?; - let addrs_to_use = addrs.collect(); - Some(P2pNetworkKadBoostrapRequestState { - addr, - time, - addrs_to_use, - }) -} - impl P2pNetworkKadBootstrapState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pNetworkKadBootstrapAction>, + action: ActionWithMeta, filter_addrs: bool, ) -> Result<(), String> where @@ -62,20 +34,46 @@ impl P2pNetworkKadBootstrapState { let routing_table = &discovery_state.routing_table; let bootstrap_state: &Self = state_context.get_substate()?.substate()?; - let requests_to_create = 3_usize.saturating_sub(bootstrap_state.requests.len()); - let peer_id_req_vec = routing_table + let to_request = routing_table .closest_peers(&bootstrap_state.kademlia_key) // for the next request we take closest peer - .filter(|entry| !bootstrap_state.processed_peers.contains(&entry.peer_id)) // that is not yet processed during this bootstrap - .filter_map(|entry| { - // we create a request for it - prepare_next_request(entry.addresses(), meta.time(), filter_addrs) - .map(|req| (entry.peer_id, req)) - }) - .take(requests_to_create) // and stop when we create enough requests so up to 3 will be executed in parallel + .filter(|entry| !bootstrap_state.processed_peers.contains(&entry.peer_id)) + .cloned() .collect::>(); + let bootstrap_state: &mut Self = + state_context.get_substate_mut()?.substate_mut()?; + bootstrap_state.requests_number = to_request.len(); + let empty = to_request.is_empty(); + + let dispatcher = state_context.into_dispatcher(); + for entry in to_request { + dispatcher.push(P2pNetworkKadEffectfulAction::MakeRequest { + multiaddr: entry.addresses().clone(), + filter_local: filter_addrs, + peer_id: entry.peer_id, + }); + } + if empty { + dispatcher.push(P2pNetworkKadBootstrapAction::FinalizeRequests); + } + + Ok(()) + } + P2pNetworkKadBootstrapAction::AppendRequest { request, peer_id } => { + let state: &mut Self = state_context.get_substate_mut()?.substate_mut()?; + state.requests_number -= 1; + if let Some(request) = request { + state.peer_id_req_vec.push((peer_id, request)); + } + if state.peer_id_req_vec.len() == 3 || state.requests_number == 0 { + let dispatcher = state_context.into_dispatcher(); + dispatcher.push(P2pNetworkKadBootstrapAction::FinalizeRequests); + } + Ok(()) + } + P2pNetworkKadBootstrapAction::FinalizeRequests => { let state: &mut Self = state_context.get_substate_mut()?.substate_mut()?; - for (peer_id, request) in peer_id_req_vec { + for (peer_id, request) in mem::take(&mut state.peer_id_req_vec) { state.processed_peers.insert(peer_id); let address = P2pConnectionOutgoingInitOpts::LibP2P((peer_id, request.addr).into()); @@ -123,12 +121,12 @@ impl P2pNetworkKadBootstrapState { } => { let state: &mut P2pNetworkKadBootstrapState = state_context.get_substate_mut()?.substate_mut()?; - let Some(req) = state.requests.remove(peer_id) else { + let Some(req) = state.requests.remove(&peer_id) else { bug_condition!("cannot find request for peer {peer_id}"); return Ok(()); }; state.successful_requests += 1; - let address = P2pConnectionOutgoingInitOpts::LibP2P((*peer_id, req.addr).into()); + let address = P2pConnectionOutgoingInitOpts::LibP2P((peer_id, req.addr).into()); if let Some(request_stats) = state.stats.requests.iter_mut().rev().find(|req_stat| { @@ -145,11 +143,11 @@ impl P2pNetworkKadBootstrapState { { *request_stats = P2pNetworkKadBootstrapRequestStat::Successful( P2pNetworkKadBootstrapSuccessfulRequest { - peer_id: *peer_id, + peer_id, address, start: req.time, finish: meta.time(), - closest_peers: closest_peers.clone(), + closest_peers, }, ); } else { @@ -167,12 +165,12 @@ impl P2pNetworkKadBootstrapState { let bootstrap_state: &mut P2pNetworkKadBootstrapState = state_context.get_substate_mut()?.substate_mut()?; - let Some(req) = bootstrap_state.requests.remove(peer_id) else { + let Some(req) = bootstrap_state.requests.remove(&peer_id) else { bug_condition!("cannot find request for peer {peer_id}"); return Ok(()); }; - let address = P2pConnectionOutgoingInitOpts::LibP2P((*peer_id, req.addr).into()); + let address = P2pConnectionOutgoingInitOpts::LibP2P((peer_id, req.addr).into()); if let Some(request_stats) = bootstrap_state .stats @@ -193,11 +191,11 @@ impl P2pNetworkKadBootstrapState { { *request_stats = P2pNetworkKadBootstrapRequestStat::Failed( P2pNetworkKadBootstrapFailedRequest { - peer_id: *peer_id, + peer_id, address, start: req.time, finish: meta.time(), - error: error.clone(), + error, }, ); } else { diff --git a/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_state.rs b/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_state.rs index cf590101d7..4f5722349c 100644 --- a/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_state.rs +++ b/p2p/src/network/kad/bootstrap/p2p_network_kad_bootstrap_state.rs @@ -27,6 +27,10 @@ pub struct P2pNetworkKadBootstrapState { pub successful_requests: usize, /// Bootstrap requests statistics. pub stats: P2pNetworkKadBootstrapStats, + /// Constructing request + pub peer_id_req_vec: Vec<(PeerId, P2pNetworkKadBoostrapRequestState)>, + /// Number of requests to construct + pub requests_number: usize, } impl P2pNetworkKadBootstrapState { @@ -38,6 +42,8 @@ impl P2pNetworkKadBootstrapState { requests: BTreeMap::new(), successful_requests: 0, stats: Default::default(), + peer_id_req_vec: vec![], + requests_number: 0, }) } diff --git a/p2p/src/network/kad/kad_effectful/mod.rs b/p2p/src/network/kad/kad_effectful/mod.rs new file mode 100644 index 0000000000..5998764c8e --- /dev/null +++ b/p2p/src/network/kad/kad_effectful/mod.rs @@ -0,0 +1,5 @@ +mod p2p_network_kad_effectful_actions; +pub use self::p2p_network_kad_effectful_actions::P2pNetworkKadEffectfulAction; + +#[cfg(feature = "p2p-libp2p")] +mod p2p_network_kad_effectful_effects; diff --git a/p2p/src/network/kad/kad_effectful/p2p_network_kad_effectful_actions.rs b/p2p/src/network/kad/kad_effectful/p2p_network_kad_effectful_actions.rs new file mode 100644 index 0000000000..5ef7a1a93b --- /dev/null +++ b/p2p/src/network/kad/kad_effectful/p2p_network_kad_effectful_actions.rs @@ -0,0 +1,32 @@ +use openmina_core::ActionEvent; + +use multiaddr::Multiaddr; +use serde::{Deserialize, Serialize}; + +use crate::{P2pState, PeerId}; + +#[derive(Serialize, Deserialize, Debug, Clone, ActionEvent)] +pub enum P2pNetworkKadEffectfulAction { + Discovered { + multiaddr: Multiaddr, + filter_local: bool, + peer_id: PeerId, + }, + MakeRequest { + multiaddr: Vec, + filter_local: bool, + peer_id: PeerId, + }, +} + +impl From for crate::P2pEffectfulAction { + fn from(value: P2pNetworkKadEffectfulAction) -> Self { + crate::P2pEffectfulAction::Network(value.into()) + } +} + +impl redux::EnablingCondition for P2pNetworkKadEffectfulAction { + fn is_enabled(&self, _state: &P2pState, _time: redux::Timestamp) -> bool { + true + } +} diff --git a/p2p/src/network/kad/kad_effectful/p2p_network_kad_effectful_effects.rs b/p2p/src/network/kad/kad_effectful/p2p_network_kad_effectful_effects.rs new file mode 100644 index 0000000000..3b9ceb0185 --- /dev/null +++ b/p2p/src/network/kad/kad_effectful/p2p_network_kad_effectful_effects.rs @@ -0,0 +1,114 @@ +use std::net::{IpAddr, SocketAddr}; + +use multiaddr::Multiaddr; + +use crate::{ + bootstrap::P2pNetworkKadBoostrapRequestState, + connection::outgoing::P2pConnectionOutgoingInitOpts, P2pNetworkKadBootstrapAction, + P2pNetworkService, P2pPeerAction, SocketAddrTryFromMultiaddrError, +}; + +use super::P2pNetworkKadEffectfulAction; + +fn socket_addr_try_from_multiaddr( + service: &mut Service, + multiaddr: &Multiaddr, + filter_local: bool, +) -> Result, SocketAddrTryFromMultiaddrError> +where + Service: P2pNetworkService, +{ + let mut iter = multiaddr.iter(); + let ip_addr = match iter.next() { + Some(multiaddr::Protocol::Ip4(ip4)) => IpAddr::V4(ip4), + Some(multiaddr::Protocol::Ip6(ip6)) => IpAddr::V6(ip6), + Some(multiaddr::Protocol::Dns4(hostname) | multiaddr::Protocol::Dns6(hostname)) => { + service.resolve_name(&hostname)?.first().cloned().ok_or( + SocketAddrTryFromMultiaddrError::UnsupportedHost(hostname.to_string()), + )? + } + None => return Err(SocketAddrTryFromMultiaddrError::NoHost), + Some(p) => { + return Err(SocketAddrTryFromMultiaddrError::UnsupportedHost( + p.to_string(), + )) + } + }; + let port = match iter.next() { + Some(multiaddr::Protocol::Tcp(port)) => port, + None => return Err(SocketAddrTryFromMultiaddrError::NoPort), + Some(p) => { + return Err(SocketAddrTryFromMultiaddrError::UnsupportedPort( + p.to_string(), + )) + } + }; + if let Some(p) = iter.next() { + return Err(SocketAddrTryFromMultiaddrError::ExtraProtocol( + p.to_string(), + )); + } + + let filter = |addr: &SocketAddr| { + !filter_local + || match addr.ip() { + std::net::IpAddr::V4(ipv4) if ipv4.is_loopback() || ipv4.is_private() => false, + std::net::IpAddr::V6(ipv6) if ipv6.is_loopback() => false, + _ => true, + } + }; + + Ok(Some((ip_addr, port).into()).filter(filter)) +} + +impl P2pNetworkKadEffectfulAction { + pub fn effects(self, meta: &redux::ActionMeta, store: &mut Store) + where + Store: crate::P2pStore, + Store::Service: P2pNetworkService, + { + match self { + Self::Discovered { + multiaddr, + filter_local, + peer_id, + } => match socket_addr_try_from_multiaddr(store.service(), &multiaddr, filter_local) { + Ok(Some(addr)) => { + store.dispatch(P2pPeerAction::Discovered { + peer_id, + dial_opts: Some(P2pConnectionOutgoingInitOpts::LibP2P( + (peer_id, addr).into(), + )), + }); + } + Ok(None) => {} + Err(err) => { + let _ = err; + // TODO: report + } + }, + Self::MakeRequest { + multiaddr, + filter_local, + peer_id, + } => { + let addrs = multiaddr.iter().filter_map(|multiaddr| { + socket_addr_try_from_multiaddr(store.service(), multiaddr, filter_local) + .ok() + .flatten() + }); + let addrs_to_use = addrs.collect::>(); + let request = + addrs_to_use + .first() + .cloned() + .map(|addr| P2pNetworkKadBoostrapRequestState { + addr, + time: meta.time(), + addrs_to_use, + }); + store.dispatch(P2pNetworkKadBootstrapAction::AppendRequest { request, peer_id }); + } + } + } +} diff --git a/p2p/src/network/kad/mod.rs b/p2p/src/network/kad/mod.rs index d6b0ef1dc3..904d2a3db7 100644 --- a/p2p/src/network/kad/mod.rs +++ b/p2p/src/network/kad/mod.rs @@ -25,3 +25,6 @@ mod p2p_network_kad_internals; pub use self::p2p_network_kad_internals::*; const ALPHA: usize = 3; + +pub mod kad_effectful; +pub use kad_effectful::P2pNetworkKadEffectfulAction; diff --git a/p2p/src/network/kad/p2p_network_kad_internals.rs b/p2p/src/network/kad/p2p_network_kad_internals.rs index ac32597b35..3246db4126 100644 --- a/p2p/src/network/kad/p2p_network_kad_internals.rs +++ b/p2p/src/network/kad/p2p_network_kad_internals.rs @@ -12,8 +12,7 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use crate::{ - socket_addr_try_from_multiaddr, ConnectionType, P2pNetworkKademliaMultiaddrError, - P2pNetworkKademliaPeerIdError, PeerId, + ConnectionType, P2pNetworkKademliaMultiaddrError, P2pNetworkKademliaPeerIdError, PeerId, }; use super::CID; @@ -64,7 +63,7 @@ mod u256_serde { } /// Kademlia key, sha256 of the node's peer id. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct P2pNetworkKadKey(#[serde(with = "u256_serde")] U256); #[derive(Clone, Debug, Serialize, PartialEq, Deserialize, thiserror::Error)] @@ -245,7 +244,7 @@ pub struct P2pNetworkKadRoutingTableInsertError; impl P2pNetworkKadRoutingTable { pub fn new(this_entry: P2pNetworkKadEntry) -> Self { - let this_key = this_entry.key.clone(); + let this_key = this_entry.key; let global_bucket = P2pNetworkKadBucket(vec![this_entry]); let buckets = vec![global_bucket]; P2pNetworkKadRoutingTable { this_key, buckets } @@ -264,14 +263,13 @@ impl P2pNetworkKadRoutingTable { /// Filters `entry.addrs` for supported addresses if non of addresses are supported returns `Err(_)` pub fn insert( &mut self, - mut entry: P2pNetworkKadEntry, + entry: P2pNetworkKadEntry, ) -> Result { - entry.filter_addrs(); if entry.addrs.is_empty() { return Err(P2pNetworkKadRoutingTableInsertError); } // distance to this node - let dist = &self.this_key - &entry.key; + let dist = self.this_key - entry.key; // index of the closest k-bucket that can contain this node. let index = dist.to_index(); @@ -293,7 +291,7 @@ impl P2pNetworkKadRoutingTable { let Some((bucket1, bucket2)) = self .buckets .pop() - .map(|b| b.split(|e| (&self.this_key - &e.key) >= split_dist)) + .map(|b| b.split(|e| (self.this_key - e.key) >= split_dist)) else { bug_condition!("should be unreachable"); return Err(P2pNetworkKadRoutingTableInsertError); @@ -306,7 +304,7 @@ impl P2pNetworkKadRoutingTable { /// Looks up a Kademlia entry with the specified `key`. pub fn look_up(&self, key: &P2pNetworkKadKey) -> Option<&P2pNetworkKadEntry> { // distance to this node - let dist = &self.this_key - key; + let dist = self.this_key - key; // index of the closest k-bucket that can contain this node. let index = dist.to_index().min(self.buckets.len() - 1); @@ -340,18 +338,18 @@ impl P2pNetworkKadRoutingTable { let dist = P2pNetworkKadDist::from(i); for entry in &bucket.0 { assert!( - &self.this_key - &entry.key <= dist, + self.this_key - entry.key <= dist, "for {:#?} at {i} distance {:#?} is too big, expecting at most {dist:#?}\nrouting table:\n{self:+#?}", entry.key, - &self.this_key - &entry.key, + self.this_key - entry.key, ); if let Some(prev_dist) = &prev_dist { assert!( - &(&self.this_key - &entry.key) > prev_dist, + &(self.this_key - entry.key) > prev_dist, "distance too small: {:#?}\nrouting table:\n{:+#?}\ndist: {:#?}\nprev_dist: {:#?}", entry.key, self, - &self.this_key - &entry.key, + self.this_key - entry.key, prev_dist, ); } @@ -399,24 +397,12 @@ impl P2pNetworkKadEntry { } pub fn dist(&self, other: &P2pNetworkKadEntry) -> P2pNetworkKadDist { - &self.key - &other.key + self.key - other.key } pub fn addresses(&self) -> &Vec { &self.addrs } - - pub fn filter_addrs(&mut self) { - let addrs = std::mem::take(&mut self.addrs); - self.addrs = addrs - .into_iter() - .filter_map(|multiaddr| { - socket_addr_try_from_multiaddr(&multiaddr) - .is_ok() - .then_some(multiaddr) - }) - .collect() - } } #[derive(Clone, Debug, Serialize, PartialEq, Deserialize, thiserror::Error)] @@ -476,7 +462,7 @@ pub struct ClosestPeers<'a, const K: usize> { impl<'a, const K: usize> ClosestPeers<'a, K> { fn new(table: &'a P2pNetworkKadRoutingTable, key: &'a P2pNetworkKadKey) -> Self { - let dist = &table.this_key - key; + let dist = table.this_key - key; let mut index_iter = Self::bucket_index_iterator(dist, table.buckets.len()); let bucket_index = index_iter .next() @@ -514,7 +500,7 @@ impl<'a, const K: usize> ClosestPeers<'a, K> { .into_iter() .filter(|e| &e.key != key && &e.key != this_key), ); - vec.sort_by_cached_key(|entry| key - &entry.key); + vec.sort_by_cached_key(|entry| key - entry.key); vec.into_iter() } } @@ -651,7 +637,7 @@ mod tests { const THIS_KEY: P2pNetworkKadKey = P2pNetworkKadKey(U256::ZERO); fn this_key() -> P2pNetworkKadKey { - THIS_KEY.clone() + THIS_KEY } fn key_pow_2(pow: usize) -> P2pNetworkKadKey { diff --git a/p2p/src/network/kad/p2p_network_kad_protocol.rs b/p2p/src/network/kad/p2p_network_kad_protocol.rs index a463e4b93a..c2b5a18f9e 100644 --- a/p2p/src/network/kad/p2p_network_kad_protocol.rs +++ b/p2p/src/network/kad/p2p_network_kad_protocol.rs @@ -1,14 +1,11 @@ -use std::{ - borrow::Cow, - net::{IpAddr, SocketAddr}, -}; +use std::borrow::Cow; use libp2p_identity::DecodingError; use multiaddr::Multiaddr; use serde::{Deserialize, Serialize}; use super::{P2pNetworkKadEntry, P2pNetworkKadEntryTryFromError, P2pNetworkKadKeyError}; -use crate::{mod_Message::MessageType, PeerId}; +use crate::{mod_Message::MessageType, P2pNetworkServiceError, PeerId}; #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Serialize, Deserialize)] pub enum ConnectionType { @@ -254,37 +251,8 @@ pub enum SocketAddrTryFromMultiaddrError { UnsupportedPort(String), #[error("extra protocol {0}")] ExtraProtocol(String), -} - -pub fn socket_addr_try_from_multiaddr( - maddr: &Multiaddr, -) -> Result { - let mut iter = maddr.iter(); - let ip_addr = match iter.next() { - Some(multiaddr::Protocol::Ip4(ip4)) => IpAddr::V4(ip4), - Some(multiaddr::Protocol::Ip6(ip6)) => IpAddr::V6(ip6), - None => return Err(SocketAddrTryFromMultiaddrError::NoHost), - Some(p) => { - return Err(SocketAddrTryFromMultiaddrError::UnsupportedHost( - p.to_string(), - )) - } - }; - let port = match iter.next() { - Some(multiaddr::Protocol::Tcp(port)) => port, - None => return Err(SocketAddrTryFromMultiaddrError::NoPort), - Some(p) => { - return Err(SocketAddrTryFromMultiaddrError::UnsupportedPort( - p.to_string(), - )) - } - }; - if let Some(p) = iter.next() { - return Err(SocketAddrTryFromMultiaddrError::ExtraProtocol( - p.to_string(), - )); - } - Ok((ip_addr, port).into()) + #[error("{0}")] + Service(#[from] P2pNetworkServiceError), } #[cfg(test)] diff --git a/p2p/src/network/kad/p2p_network_kad_reducer.rs b/p2p/src/network/kad/p2p_network_kad_reducer.rs index 06e8a1a3c5..50f0327472 100644 --- a/p2p/src/network/kad/p2p_network_kad_reducer.rs +++ b/p2p/src/network/kad/p2p_network_kad_reducer.rs @@ -14,7 +14,7 @@ use super::{ impl super::P2pNetworkKadState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pNetworkKadAction>, + action: ActionWithMeta, limits: &P2pLimits, ) -> Result<(), String> where @@ -47,7 +47,7 @@ impl super::P2pNetworkKadState { pub fn system_reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pNetworkKademliaAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: SubstateAccess, @@ -66,7 +66,7 @@ impl super::P2pNetworkKadState { key, }, ) => { - let kad_key = P2pNetworkKadKey::from(key.clone()); + let kad_key = P2pNetworkKadKey::from(key); let closer_peers: Vec<_> = state.routing_table.find_node(&kad_key).cloned().collect(); debug!(meta.time(); "found {} peers", closer_peers.len()); @@ -74,9 +74,9 @@ impl super::P2pNetworkKadState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkKademliaStreamAction::SendResponse { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, data: message, }); Ok(()) @@ -91,7 +91,7 @@ impl super::P2pNetworkKadState { }, ) => { let mut latest_request_peers = Vec::new(); - for entry in closest_peers { + for entry in &closest_peers { let kind = match state.routing_table.insert(entry.clone()) { Ok(true) => P2pNetworkKadLatestRequestPeerKind::New, Ok(false) => P2pNetworkKadLatestRequestPeerKind::Existing, @@ -101,19 +101,18 @@ impl super::P2pNetworkKadState { } state.latest_request_peers = latest_request_peers.into(); - let data = closest_peers.clone(); let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkKadRequestAction::ReplyReceived { - peer_id: *peer_id, - stream_id: *stream_id, - data, + peer_id, + stream_id, + data: closest_peers, }); Ok(()) } (_, P2pNetworkKademliaAction::StartBootstrap { key }) => { state.status = P2pNetworkKadStatus::Bootstrapping( - P2pNetworkKadBootstrapState::new(*key).map_err(|k| k.to_string())?, + P2pNetworkKadBootstrapState::new(key).map_err(|k| k.to_string())?, ); if state.bootstrap_state().map_or(false, |bootstrap_state| { @@ -137,7 +136,7 @@ impl super::P2pNetworkKadState { } (_, P2pNetworkKademliaAction::UpdateRoutingTable { peer_id, addrs }) => { let _ = state.routing_table.insert( - P2pNetworkKadEntry::new(*peer_id, addrs.clone()).map_err(|e| e.to_string())?, + P2pNetworkKadEntry::new(peer_id, addrs.clone()).map_err(|e| e.to_string())?, ); Ok(()) } diff --git a/p2p/src/network/kad/request/p2p_network_kad_request_reducer.rs b/p2p/src/network/kad/request/p2p_network_kad_request_reducer.rs index 5480459196..941350c9c2 100644 --- a/p2p/src/network/kad/request/p2p_network_kad_request_reducer.rs +++ b/p2p/src/network/kad/request/p2p_network_kad_request_reducer.rs @@ -1,13 +1,11 @@ use openmina_core::{bug_condition, Substate, SubstateAccess}; use redux::{ActionWithMeta, Dispatcher}; -use std::net::SocketAddr; use crate::{ - connection::outgoing::{P2pConnectionOutgoingAction, P2pConnectionOutgoingInitOpts}, - peer::P2pPeerAction, - socket_addr_try_from_multiaddr, ConnectionAddr, P2pNetworkConnectionMuxState, - P2pNetworkKadBootstrapAction, P2pNetworkKadState, P2pNetworkKademliaRpcRequest, - P2pNetworkKademliaStreamAction, P2pNetworkYamuxAction, P2pPeerState, P2pState, + connection::outgoing::P2pConnectionOutgoingAction, ConnectionAddr, + P2pNetworkConnectionMuxState, P2pNetworkKadBootstrapAction, P2pNetworkKadEffectfulAction, + P2pNetworkKadState, P2pNetworkKademliaRpcRequest, P2pNetworkKademliaStreamAction, + P2pNetworkYamuxAction, P2pPeerState, P2pState, }; use super::{P2pNetworkKadRequestAction, P2pNetworkKadRequestState, P2pNetworkKadRequestStatus}; @@ -15,7 +13,7 @@ use super::{P2pNetworkKadRequestAction, P2pNetworkKadRequestState, P2pNetworkKad impl P2pNetworkKadRequestState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pNetworkKadRequestAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: SubstateAccess + SubstateAccess, @@ -27,12 +25,12 @@ impl P2pNetworkKadRequestState { let request_state = match action { P2pNetworkKadRequestAction::New { peer_id, addr, key } => state - .create_request(*addr, *peer_id, *key) + .create_request(addr, peer_id, key) .map_err(|_request| format!("kademlia request to {addr} is already in progress"))?, P2pNetworkKadRequestAction::Prune { peer_id } => { return state .requests - .remove(peer_id) + .remove(&peer_id) .map(|_| ()) .ok_or_else(|| "kademlia request for {peer_id} is not found".to_owned()); } @@ -46,10 +44,7 @@ impl P2pNetworkKadRequestState { P2pNetworkKadRequestAction::New { peer_id, addr, .. } => { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_state: &P2pState = state.substate()?; - let peer_state = p2p_state.peers.get(peer_id); - - let peer_id = *peer_id; - let addr = *addr; + let peer_state = p2p_state.peers.get(&peer_id); let on_initialize_connection = |dispatcher: &mut Dispatcher| { // initialize connection to the peer. @@ -142,7 +137,7 @@ impl P2pNetworkKadRequestState { .network .scheduler .connections - .get(addr) + .get(&addr) .ok_or_else(|| format!("connection with {addr} not found")) .and_then(|conn| { conn.mux @@ -158,20 +153,18 @@ impl P2pNetworkKadRequestState { // TODO: add callbacks dispatcher.push(P2pNetworkYamuxAction::OpenStream { - addr: *addr, + addr, stream_id, stream_kind: crate::token::StreamKind::Discovery( crate::token::DiscoveryAlgorithm::Kademlia1_0_0, ), }); - dispatcher.push(P2pNetworkKadRequestAction::StreamIsCreating { - peer_id: *peer_id, - stream_id, - }); + dispatcher + .push(P2pNetworkKadRequestAction::StreamIsCreating { peer_id, stream_id }); Ok(()) } P2pNetworkKadRequestAction::StreamIsCreating { stream_id, .. } => { - request_state.status = P2pNetworkKadRequestStatus::WaitingForKadStream(*stream_id); + request_state.status = P2pNetworkKadRequestStatus::WaitingForKadStream(stream_id); Ok(()) } @@ -199,9 +192,7 @@ impl P2pNetworkKadRequestState { }, super::P2pNetworkKadRequestStatus::Request, ); - let peer_id = *peer_id; - let stream_id = *stream_id; - let addr = *addr; + let key = request_state.key; let dispatcher = state_context.into_dispatcher(); @@ -232,7 +223,7 @@ impl P2pNetworkKadRequestState { let bootstrap_request = state .bootstrap_state() - .and_then(|bootstrap_state| bootstrap_state.request(peer_id)) + .and_then(|bootstrap_state| bootstrap_state.request(&peer_id)) .is_some(); let closest_peers = bootstrap_request @@ -243,45 +234,32 @@ impl P2pNetworkKadRequestState { if bootstrap_request { dispatcher.push(P2pNetworkKadBootstrapAction::RequestDone { - peer_id: *peer_id, + peer_id, closest_peers, }); } - let external_addr = |addr: &SocketAddr| { - !filter_local_addrs - || match addr.ip() { - std::net::IpAddr::V4(v) => !(v.is_loopback() || v.is_private()), - std::net::IpAddr::V6(v) => !(v.is_loopback()), - } - }; for entry in data { let peer_id = entry.peer_id; - let to_opts = |addr| (peer_id, addr).into(); - let mut addresses = entry - .addresses() - .iter() - .map(socket_addr_try_from_multiaddr) - .filter_map(Result::ok) - .filter(external_addr) - .map(to_opts) - .map(P2pConnectionOutgoingInitOpts::LibP2P); - // TODO: use all addresses - dispatcher.push(P2pPeerAction::Discovered { - peer_id, - dial_opts: addresses.next(), - }); + for multiaddr in entry.addresses().iter() { + let multiaddr = multiaddr.clone(); + dispatcher.push(P2pNetworkKadEffectfulAction::Discovered { + multiaddr, + filter_local: filter_local_addrs, + peer_id, + }); + } } dispatcher.push(P2pNetworkKademliaStreamAction::Close { addr: ConnectionAddr { sock_addr: addr, incoming: false, }, - peer_id: *peer_id, - stream_id: *stream_id, + peer_id, + stream_id, }); - dispatcher.push(P2pNetworkKadRequestAction::Prune { peer_id: *peer_id }); + dispatcher.push(P2pNetworkKadRequestAction::Prune { peer_id }); Ok(()) } P2pNetworkKadRequestAction::Prune { .. } => { @@ -292,19 +270,16 @@ impl P2pNetworkKadRequestState { request_state.status = P2pNetworkKadRequestStatus::Error(error.clone()); let bootstrap_request = state .bootstrap_state() - .and_then(|bootstrap_state| bootstrap_state.request(peer_id)) + .and_then(|bootstrap_state| bootstrap_state.request(&peer_id)) .is_some(); let dispatcher = state_context.into_dispatcher(); if bootstrap_request { - dispatcher.push(P2pNetworkKadBootstrapAction::RequestError { - peer_id: *peer_id, - error: error.clone(), - }); + dispatcher.push(P2pNetworkKadBootstrapAction::RequestError { peer_id, error }); } - dispatcher.push(P2pNetworkKadRequestAction::Prune { peer_id: *peer_id }); + dispatcher.push(P2pNetworkKadRequestAction::Prune { peer_id }); Ok(()) } } diff --git a/p2p/src/network/kad/stream/p2p_network_kad_stream_reducer.rs b/p2p/src/network/kad/stream/p2p_network_kad_stream_reducer.rs index fdfe7c1b68..c69a779477 100644 --- a/p2p/src/network/kad/stream/p2p_network_kad_stream_reducer.rs +++ b/p2p/src/network/kad/stream/p2p_network_kad_stream_reducer.rs @@ -17,7 +17,7 @@ use super::{ impl P2pNetworkKadIncomingStreamState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pNetworkKademliaStreamAction>, + action: ActionWithMeta, limits: &P2pLimits, ) -> Result<(), String> where @@ -36,7 +36,7 @@ impl P2pNetworkKadIncomingStreamState { ( P2pNetworkKadIncomingStreamState::Default, P2pNetworkKademliaStreamAction::New { incoming, .. }, - ) if *incoming => { + ) if incoming => { *state = P2pNetworkKadIncomingStreamState::WaitingForRequest { expect_close: false, }; @@ -97,20 +97,20 @@ impl P2pNetworkKadIncomingStreamState { } => { // TODO: add callback dispatcher.push(P2pNetworkKademliaStreamAction::WaitOutgoing { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); dispatcher.push(P2pNetworkKademliaAction::AnswerFindNodeRequest { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, key, }); } P2pNetworkKadIncomingStreamState::Error(error) => { dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::from( P2pNetworkKadIncomingStreamError::from(error), ), @@ -151,21 +151,21 @@ impl P2pNetworkKadIncomingStreamState { } => { // TODO: add callbacks dispatcher.push(P2pNetworkKademliaStreamAction::WaitOutgoing { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); dispatcher.push(P2pNetworkKademliaAction::AnswerFindNodeRequest { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, key, }); } P2pNetworkKadIncomingStreamState::Error(error) => { warn!(meta.time(); summary = "error handling kademlia action", error = display(&error)); dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::from( P2pNetworkKadIncomingStreamError::from(error), ), @@ -192,26 +192,26 @@ impl P2pNetworkKadIncomingStreamState { stream_id, }, ) => { - let message = Message::try_from(data).map_err(|e| e.to_string())?; + let message = Message::try_from(&data).map_err(|e| e.to_string())?; let bytes = serialize_into_vec(&message).map_err(|e| format!("{e}"))?; *state = P2pNetworkKadIncomingStreamState::ResponseBytesAreReady { bytes: bytes.clone(), }; let dispatcher = state_context.into_dispatcher(); - let data = fuzzed_maybe!(bytes.clone().into(), crate::fuzzer::mutate_kad_data); + let data = fuzzed_maybe!(Data::from(bytes), crate::fuzzer::mutate_kad_data); let flags = fuzzed_maybe!(Default::default(), crate::fuzzer::mutate_yamux_flags); dispatcher.push(P2pNetworkYamuxAction::OutgoingData { - addr: *addr, - stream_id: *stream_id, + addr, + stream_id, data, flags, }); dispatcher.push(P2pNetworkKademliaStreamAction::WaitIncoming { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); Ok(()) } @@ -234,19 +234,19 @@ impl P2pNetworkKadIncomingStreamState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkYamuxAction::OutgoingData { - addr: *addr, - stream_id: *stream_id, + addr, + stream_id, data: Data::empty(), flags: YamuxFlags::FIN, }); dispatcher.push(P2pNetworkKademliaStreamAction::Prune { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); Ok(()) } - _ => Err(format!( + action => Err(format!( "kademlia incoming stream state {state:?} is incorrect for action {action:?}", )), } @@ -265,7 +265,7 @@ impl P2pNetworkKadIncomingStreamState { } }; - let data = match P2pNetworkKademliaRpcRequest::try_from(message.clone()) { + let data = match P2pNetworkKademliaRpcRequest::try_from(message) { Ok(v) => v, Err(e) => { *self = P2pNetworkKadIncomingStreamState::Error(e.into()); @@ -281,7 +281,7 @@ impl P2pNetworkKadIncomingStreamState { impl P2pNetworkKadOutgoingStreamState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pNetworkKademliaStreamAction>, + action: ActionWithMeta, limits: &P2pLimits, ) -> Result<(), String> where @@ -301,7 +301,7 @@ impl P2pNetworkKadOutgoingStreamState { ( P2pNetworkKadOutgoingStreamState::Default, P2pNetworkKademliaStreamAction::New { incoming, .. }, - ) if !*incoming => { + ) if !incoming => { *state = P2pNetworkKadOutgoingStreamState::WaitingForRequest { expect_close: false, }; @@ -317,26 +317,26 @@ impl P2pNetworkKadOutgoingStreamState { peer_id, }, ) => { - let message = Message::from(data); + let message = Message::from(&data); let bytes = serialize_into_vec(&message).map_err(|e| format!("{e}"))?; *state = P2pNetworkKadOutgoingStreamState::RequestBytesAreReady { bytes: bytes.clone(), }; let dispatcher = state_context.into_dispatcher(); - let data = fuzzed_maybe!(bytes.clone().into(), crate::fuzzer::mutate_kad_data); + let data = fuzzed_maybe!(Data::from(bytes), crate::fuzzer::mutate_kad_data); let flags = fuzzed_maybe!(Default::default(), crate::fuzzer::mutate_yamux_flags); dispatcher.push(P2pNetworkYamuxAction::OutgoingData { - addr: *addr, - stream_id: *stream_id, + addr, + stream_id, data, flags, }); dispatcher.push(P2pNetworkKademliaStreamAction::WaitIncoming { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); Ok(()) } @@ -406,21 +406,21 @@ impl P2pNetworkKadOutgoingStreamState { }, } => { dispatcher.push(P2pNetworkKademliaStreamAction::WaitOutgoing { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); dispatcher.push(P2pNetworkKademliaAction::UpdateFindNodeRequest { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, closest_peers, }); } P2pNetworkKadOutgoingStreamState::Error(error) => { warn!(meta.time(); summary = "error handling kademlia action", error = display(&error)); dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::from( P2pNetworkKadOutgoingStreamError::from(error), ), @@ -443,7 +443,7 @@ impl P2pNetworkKadOutgoingStreamState { }, ) => { let mut data = data.clone(); - data.extend_from_slice(&new_data.0); + data.extend_from_slice(&new_data); if *len > data.len() { *state = @@ -463,21 +463,21 @@ impl P2pNetworkKadOutgoingStreamState { }, } => { dispatcher.push(P2pNetworkKademliaStreamAction::WaitOutgoing { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); dispatcher.push(P2pNetworkKademliaAction::UpdateFindNodeRequest { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, closest_peers, }); } P2pNetworkKadOutgoingStreamState::Error(error) => { warn!(meta.time(); summary = "error handling kademlia action", error = display(&error)); dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::from( P2pNetworkKadOutgoingStreamError::from(error), ), @@ -508,15 +508,15 @@ impl P2pNetworkKadOutgoingStreamState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkYamuxAction::OutgoingData { - addr: *addr, - stream_id: *stream_id, + addr, + stream_id, data: Data::empty(), flags: YamuxFlags::FIN, }); dispatcher.push(P2pNetworkKademliaStreamAction::Prune { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); Ok(()) } @@ -531,13 +531,13 @@ impl P2pNetworkKadOutgoingStreamState { *state = P2pNetworkKadOutgoingStreamState::Closed; let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkKademliaStreamAction::Prune { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, }); Ok(()) } - _ => Err(format!( + (state, action) => Err(format!( "kademlia outgoing stream state {state:?} is incorrect for action {action:?}", )), } @@ -556,7 +556,7 @@ impl P2pNetworkKadOutgoingStreamState { } }; - let data = match P2pNetworkKademliaRpcReply::try_from(message.clone()) { + let data = match P2pNetworkKademliaRpcReply::try_from(message) { Ok(v) => v, Err(e) => { *self = P2pNetworkKadOutgoingStreamState::Error(e.into()); @@ -572,7 +572,7 @@ impl P2pNetworkKadOutgoingStreamState { impl P2pNetworkKadStreamState { pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pNetworkKademliaStreamAction>, + action: ActionWithMeta, limits: &P2pLimits, ) -> Result<(), String> where @@ -580,8 +580,8 @@ impl P2pNetworkKadStreamState { Action: crate::P2pActionTrait, { let state = state_context.get_substate_mut()?; - let action_ = action.action(); - let stream_state = match action_ { + let (action, meta) = action.split(); + let stream_state = match &action { P2pNetworkKademliaStreamAction::New { peer_id, stream_id, @@ -601,17 +601,21 @@ impl P2pNetworkKadStreamState { .ok_or_else(|| format!("kademlia stream not found for action {action:?}")) } _ => state - .find_kad_stream_state(action_.peer_id(), action_.stream_id()) + .find_kad_stream_state(action.peer_id(), action.stream_id()) .ok_or_else(|| format!("kademlia stream not found for action {action:?}"))?, }; match stream_state { - P2pNetworkKadStreamState::Incoming(_) => { - P2pNetworkKadIncomingStreamState::reducer(state_context, action, limits) - } - P2pNetworkKadStreamState::Outgoing(_) => { - P2pNetworkKadOutgoingStreamState::reducer(state_context, action, limits) - } + P2pNetworkKadStreamState::Incoming(_) => P2pNetworkKadIncomingStreamState::reducer( + state_context, + meta.with_action(action), + limits, + ), + P2pNetworkKadStreamState::Outgoing(_) => P2pNetworkKadOutgoingStreamState::reducer( + state_context, + meta.with_action(action), + limits, + ), } } } diff --git a/p2p/src/network/noise/p2p_network_noise_reducer.rs b/p2p/src/network/noise/p2p_network_noise_reducer.rs index 0b199b0f81..27b06329b9 100644 --- a/p2p/src/network/noise/p2p_network_noise_reducer.rs +++ b/p2p/src/network/noise/p2p_network_noise_reducer.rs @@ -5,7 +5,7 @@ use openmina_core::{bug_condition, fuzzed_maybe, Substate}; use crate::connection::incoming::{P2pConnectionIncomingAction, P2pConnectionIncomingState}; use crate::{ Data, P2pNetworkConnectionError, P2pNetworkPnetAction, P2pNetworkSchedulerAction, - P2pNetworkSchedulerState, P2pNetworkSelectAction, P2pState, SelectKind, + P2pNetworkSchedulerState, P2pNetworkSelectAction, P2pState, PeerId, SelectKind, }; use self::p2p_network_noise_state::ResponderConsumeOutput; @@ -22,13 +22,13 @@ const MAX_CHUNK_SIZE: usize = u16::MAX as usize - 19; impl P2pNetworkNoiseState { pub fn reducer( mut state_context: Substate, - action: redux::ActionWithMeta<&P2pNetworkNoiseAction>, + action: redux::ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, Action: crate::P2pActionTrait, { - let action = action.action(); + let (action, _meta) = action.split(); let noise_state = state_context .get_substate_mut()? .connection_state_mut(action.addr()) @@ -38,18 +38,15 @@ impl P2pNetworkNoiseState { match action { P2pNetworkNoiseAction::Init { incoming, - ephemeral_sk, - static_sk, - signature, + ephemeral_sk: esk, + static_sk: ssk, + signature: payload, addr, } => { - let esk = ephemeral_sk.clone(); let epk = esk.pk(); - let ssk = static_sk.clone(); let spk = ssk.pk(); - let payload = signature.clone(); - noise_state.inner = if *incoming { + noise_state.inner = if incoming { // Luckily the name is 32 bytes long, if it were longer you would have to take a sha2_256 hash of it. let mut noise = NoiseState::new(*b"Noise_XX_25519_ChaChaPoly_SHA256"); noise.mix_hash(b""); @@ -90,13 +87,13 @@ impl P2pNetworkNoiseState { let mut outgoing = noise_state.outgoing_chunks.clone(); let dispatcher = state_context.into_dispatcher(); while let Some(data) = outgoing.pop_front() { - dispatcher.push(P2pNetworkNoiseAction::OutgoingChunk { addr: *addr, data }); + dispatcher.push(P2pNetworkNoiseAction::OutgoingChunk { addr, data }); } Ok(()) } P2pNetworkNoiseAction::IncomingData { data, addr } => { - noise_state.buffer.extend_from_slice(data); + noise_state.buffer.extend_from_slice(&data); let mut offset = 0; loop { let buf = &noise_state.buffer[offset..]; @@ -123,11 +120,11 @@ impl P2pNetworkNoiseState { let incoming_chunks = noise_state.incoming_chunks.len(); let dispatcher = state_context.into_dispatcher(); for _ in 0..incoming_chunks { - dispatcher.push(P2pNetworkNoiseAction::IncomingChunk { addr: *addr }); + dispatcher.push(P2pNetworkNoiseAction::IncomingChunk { addr }); } Ok(()) } - action @ P2pNetworkNoiseAction::IncomingChunk { addr } => { + ref action @ P2pNetworkNoiseAction::IncomingChunk { addr } => { let Some(state) = &mut noise_state.inner else { bug_condition!("action {:?}: no inner state", action); return Ok(()); @@ -155,9 +152,13 @@ impl P2pNetworkNoiseState { if noise_state.expected_peer_id.is_some_and(|expected_per_id| { expected_per_id != remote_peer_id }) { - *state = P2pNetworkNoiseStateInner::Error(dbg!( - NoiseError::RemotePeerIdMismatch - )); + let lhs = noise_state + .expected_peer_id + .map_or("none".to_string(), PeerId::to_libp2p_string); + let rhs = remote_peer_id.to_libp2p_string(); + *state = P2pNetworkNoiseStateInner::Error( + NoiseError::RemotePeerIdMismatch(format!("{lhs} != {rhs}")), + ); } else { *state = P2pNetworkNoiseStateInner::Done { incoming: true, @@ -226,7 +227,7 @@ impl P2pNetworkNoiseState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); if let Some(error) = error { dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::Noise(error), }); } else { @@ -249,12 +250,12 @@ impl P2pNetworkNoiseState { } while let Some(data) = outgoing.pop_front() { - dispatcher.push(P2pNetworkNoiseAction::OutgoingChunk { addr: *addr, data }); + dispatcher.push(P2pNetworkNoiseAction::OutgoingChunk { addr, data }); } if let Some(data) = decrypted { dispatcher.push(P2pNetworkNoiseAction::DecryptedData { - addr: *addr, + addr, peer_id: remote_peer_id, data, }); @@ -262,7 +263,7 @@ impl P2pNetworkNoiseState { if middle_initiator || middle_responder { dispatcher.push(P2pNetworkNoiseAction::OutgoingData { - addr: *addr, + addr, data: Data::empty(), }); } @@ -270,8 +271,8 @@ impl P2pNetworkNoiseState { Ok(()) } - action @ P2pNetworkNoiseAction::OutgoingChunk { addr, data } - | action @ P2pNetworkNoiseAction::OutgoingChunkSelectMux { addr, data } => { + ref action @ P2pNetworkNoiseAction::OutgoingChunk { addr, ref data } + | ref action @ P2pNetworkNoiseAction::OutgoingChunkSelectMux { addr, ref data } => { noise_state.outgoing_chunks.pop_front(); let handshake_done = noise_state.handshake_done(action); @@ -285,10 +286,10 @@ impl P2pNetworkNoiseState { .into(), crate::fuzzer::mutate_noise ); - dispatcher.push(P2pNetworkPnetAction::OutgoingData { addr: *addr, data }); + dispatcher.push(P2pNetworkPnetAction::OutgoingData { addr, data }); if let Some((peer_id, incoming)) = handshake_done { dispatcher.push(P2pNetworkNoiseAction::HandshakeDone { - addr: *addr, + addr, peer_id, incoming, }); @@ -298,7 +299,7 @@ impl P2pNetworkNoiseState { } P2pNetworkNoiseAction::OutgoingData { data, addr } => { let Some(state) = &mut noise_state.inner else { - bug_condition!("action {:?}: no inner state", action); + bug_condition!("action `P2pNetworkNoiseAction::OutgoingData`: no inner state"); return Ok(()); }; @@ -342,7 +343,7 @@ impl P2pNetworkNoiseState { noise_state.outgoing_chunks.push_back(chunks); } P2pNetworkNoiseStateInner::Initiator(i) => { - match (i.generate(data), i.remote_pk.clone()) { + match (i.generate(&data), i.remote_pk.clone()) { ( Ok(Some(InitiatorOutput { send_key, @@ -357,8 +358,12 @@ impl P2pNetworkNoiseState { if noise_state.expected_peer_id.is_some_and(|expected_per_id| { expected_per_id != remote_peer_id }) { + let lhs = noise_state + .expected_peer_id + .map_or("none".to_string(), PeerId::to_libp2p_string); + let rhs = remote_peer_id.to_libp2p_string(); *state = P2pNetworkNoiseStateInner::Error( - NoiseError::RemotePeerIdMismatch, + NoiseError::RemotePeerIdMismatch(format!("{lhs} != {rhs}")), ); } else { *state = P2pNetworkNoiseStateInner::Done { @@ -380,7 +385,7 @@ impl P2pNetworkNoiseState { } } P2pNetworkNoiseStateInner::Responder(r) => { - if let Some(chunk) = r.generate(data) { + if let Some(chunk) = r.generate(&data) { noise_state.outgoing_chunks.push_back(vec![chunk.into()]); } } @@ -394,12 +399,12 @@ impl P2pNetworkNoiseState { if let Some(error) = error { dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::Noise(error), }); } else { while let Some(data) = outgoing.pop_front() { - dispatcher.push(P2pNetworkNoiseAction::OutgoingChunk { addr: *addr, data }); + dispatcher.push(P2pNetworkNoiseAction::OutgoingChunk { addr, data }); } } Ok(()) @@ -411,7 +416,9 @@ impl P2pNetworkNoiseState { .. }) = &mut noise_state.inner else { - bug_condition!("action {:?}: no inner state", action); + bug_condition!( + "action `P2pNetworkNoiseAction::OutgoingDataSelectMux`: no inner state" + ); return Ok(()); }; @@ -419,7 +426,7 @@ impl P2pNetworkNoiseState { let mut chunk = Vec::with_capacity(18 + data.len()); chunk.extend_from_slice(&((data.len() + 16) as u16).to_be_bytes()); - chunk.extend_from_slice(data); + chunk.extend_from_slice(&data); let mut nonce = GenericArray::default(); nonce[4..].clone_from_slice(&send_nonce.to_le_bytes()); @@ -442,12 +449,11 @@ impl P2pNetworkNoiseState { if let Some(error) = error { dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::Noise(error), }); } else if let Some(data) = outgoing { - dispatcher - .push(P2pNetworkNoiseAction::OutgoingChunkSelectMux { addr: *addr, data }) + dispatcher.push(P2pNetworkNoiseAction::OutgoingChunkSelectMux { addr, data }) } Ok(()) @@ -462,9 +468,9 @@ impl P2pNetworkNoiseState { let remote_peer_id = noise_state.remote_peer_id(); let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkSelectAction::IncomingDataMux { - addr: *addr, - peer_id: (*peer_id).or(remote_peer_id), - data: data.clone(), + addr, + peer_id: peer_id.or(remote_peer_id), + data, fin: false, }); @@ -477,9 +483,9 @@ impl P2pNetworkNoiseState { } => { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkSelectAction::Init { - addr: *addr, - kind: SelectKind::Multiplexing(*peer_id), - incoming: *incoming, + addr, + kind: SelectKind::Multiplexing(peer_id), + incoming, }); Ok(()) } diff --git a/p2p/src/network/noise/p2p_network_noise_state.rs b/p2p/src/network/noise/p2p_network_noise_state.rs index 9872696dda..ce714edfd8 100644 --- a/p2p/src/network/noise/p2p_network_noise_state.rs +++ b/p2p/src/network/noise/p2p_network_noise_state.rs @@ -74,7 +74,7 @@ impl P2pNetworkNoiseState { pub fn as_error(&self) -> Option { match &self.inner { - Some(P2pNetworkNoiseStateInner::Error(error)) => Some(*error), + Some(P2pNetworkNoiseStateInner::Error(error)) => Some(error.clone()), _ => None, } } @@ -224,7 +224,7 @@ impl NoiseState { } } -#[derive(Debug, Error, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Error, Serialize, Deserialize, Clone, PartialEq, Eq)] pub enum NoiseError { #[error("chunk too short")] ChunkTooShort, @@ -238,8 +238,8 @@ pub enum NoiseError { InvalidSignature, #[error("remote and local public keys are same")] SelfConnection, - #[error("remote peer id doesn't match expected peer id")] - RemotePeerIdMismatch, + #[error("remote peer id doesn't match expected peer id: {0}")] + RemotePeerIdMismatch(String), #[error("failed to encrypt data")] Encryption, } diff --git a/p2p/src/network/p2p_network_actions.rs b/p2p/src/network/p2p_network_actions.rs index e724e813b7..06bc476aa7 100644 --- a/p2p/src/network/p2p_network_actions.rs +++ b/p2p/src/network/p2p_network_actions.rs @@ -11,16 +11,13 @@ use crate::P2pState; #[derive(derive_more::From, Serialize, Deserialize, Debug, Clone, ActionEvent)] pub enum P2pNetworkAction { Scheduler(P2pNetworkSchedulerAction), - SchedulerEffectful(P2pNetworkSchedulerEffectfulAction), Pnet(P2pNetworkPnetAction), - PnetEffectful(P2pNetworkPnetEffectfulAction), Select(P2pNetworkSelectAction), Noise(P2pNetworkNoiseAction), Yamux(P2pNetworkYamuxAction), Identify(P2pNetworkIdentifyAction), Kad(P2pNetworkKadAction), Pubsub(P2pNetworkPubsubAction), - PubsubEffectful(P2pNetworkPubsubEffectfulAction), Rpc(P2pNetworkRpcAction), } @@ -28,17 +25,35 @@ impl redux::EnablingCondition for P2pNetworkAction { fn is_enabled(&self, state: &P2pState, time: redux::Timestamp) -> bool { match self { Self::Scheduler(v) => v.is_enabled(state, time), - Self::SchedulerEffectful(v) => v.is_enabled(state, time), Self::Pnet(v) => v.is_enabled(state, time), - Self::PnetEffectful(v) => v.is_enabled(state, time), Self::Select(v) => v.is_enabled(state, time), Self::Noise(v) => v.is_enabled(state, time), Self::Yamux(v) => v.is_enabled(state, time), Self::Identify(v) => v.is_enabled(state, time), Self::Kad(v) => v.is_enabled(state, time), Self::Pubsub(v) => v.is_enabled(state, time), - Self::PubsubEffectful(v) => v.is_enabled(state, time), Self::Rpc(v) => v.is_enabled(state, time), } } } + +#[derive(derive_more::From, Serialize, Deserialize, Debug, Clone, ActionEvent)] +pub enum P2pNetworkEffectfulAction { + Scheduler(P2pNetworkSchedulerEffectfulAction), + Pnet(P2pNetworkPnetEffectfulAction), + Pubsub(P2pNetworkPubsubEffectfulAction), + Identify(P2pNetworkIdentifyEffectfulAction), + Kad(P2pNetworkKadEffectfulAction), +} + +impl redux::EnablingCondition for P2pNetworkEffectfulAction { + fn is_enabled(&self, state: &P2pState, time: redux::Timestamp) -> bool { + match self { + Self::Scheduler(v) => v.is_enabled(state, time), + Self::Pnet(v) => v.is_enabled(state, time), + Self::Pubsub(v) => v.is_enabled(state, time), + Self::Identify(v) => v.is_enabled(state, time), + Self::Kad(v) => v.is_enabled(state, time), + } + } +} diff --git a/p2p/src/network/p2p_network_effects.rs b/p2p/src/network/p2p_network_effects.rs index ef7ef05043..64432f08da 100644 --- a/p2p/src/network/p2p_network_effects.rs +++ b/p2p/src/network/p2p_network_effects.rs @@ -1,31 +1,17 @@ -use openmina_core::error; - use super::*; -impl P2pNetworkAction { +impl P2pNetworkEffectfulAction { pub fn effects(self, meta: &redux::ActionMeta, store: &mut Store) where Store: crate::P2pStore, Store::Service: P2pMioService + P2pCryptoService + P2pNetworkService, { match self { - P2pNetworkAction::Identify(v) => match v.effects(meta, store) { - Ok(_) => {} - Err(e) => error!(meta.time(); "error dispatching Identify stream action: {e}"), - }, - P2pNetworkAction::Pnet(_) - | P2pNetworkAction::Select(_) - | P2pNetworkAction::Noise(_) - | P2pNetworkAction::Yamux(_) - | P2pNetworkAction::Kad(_) - | P2pNetworkAction::Pubsub(_) - | P2pNetworkAction::Rpc(_) - | P2pNetworkAction::Scheduler(_) => { - // handled by reducer - } - P2pNetworkAction::SchedulerEffectful(v) => v.effects(meta, store), - P2pNetworkAction::PubsubEffectful(v) => v.effects(meta, store), - P2pNetworkAction::PnetEffectful(v) => v.effects(meta, store), + P2pNetworkEffectfulAction::Scheduler(a) => a.effects(meta, store), + P2pNetworkEffectfulAction::Pnet(v) => v.effects(meta, store), + P2pNetworkEffectfulAction::Pubsub(v) => v.effects(meta, store), + P2pNetworkEffectfulAction::Identify(v) => v.effects(meta, store), + P2pNetworkEffectfulAction::Kad(v) => v.effects(meta, store), } } } diff --git a/p2p/src/network/p2p_network_reducer.rs b/p2p/src/network/p2p_network_reducer.rs index 92944a4c90..c0560551c0 100644 --- a/p2p/src/network/p2p_network_reducer.rs +++ b/p2p/src/network/p2p_network_reducer.rs @@ -7,7 +7,7 @@ use super::*; impl P2pNetworkState { pub fn reducer( state_context: Substate, - action: redux::ActionWithMeta<&P2pNetworkAction>, + action: redux::ActionWithMeta, limits: &P2pLimits, ) -> Result<(), String> where @@ -55,12 +55,6 @@ impl P2pNetworkState { meta.with_action(a), limits, ), - P2pNetworkAction::PubsubEffectful(_) - | P2pNetworkAction::SchedulerEffectful(_) - | P2pNetworkAction::PnetEffectful(_) => { - // Effectful action; no reducer - Ok(()) - } } } diff --git a/p2p/src/network/pnet/p2p_network_pnet_reducer.rs b/p2p/src/network/pnet/p2p_network_pnet_reducer.rs index c7cd98ca70..3e7fb1b3b4 100644 --- a/p2p/src/network/pnet/p2p_network_pnet_reducer.rs +++ b/p2p/src/network/pnet/p2p_network_pnet_reducer.rs @@ -43,7 +43,7 @@ impl P2pNetworkPnetState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: redux::ActionWithMeta<&P2pNetworkPnetAction>, + action: redux::ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -58,13 +58,13 @@ impl P2pNetworkPnetState { match action { P2pNetworkPnetAction::IncomingData { data, addr } => { - pnet_state.incoming.reduce(&pnet_state.shared_secret, data); + pnet_state.incoming.reduce(&pnet_state.shared_secret, &data); let (dispatcher, state) = state_context.into_dispatcher_and_state(); let scheduler_state: &P2pNetworkSchedulerState = state.substate()?; let pnet_state = &scheduler_state - .connection_state(addr) - .ok_or_else(|| format!("Missing connection for action: {:?}", action))? + .connection_state(&addr) + .ok_or("Missing connection for action: `P2pNetworkPnetAction::IncomingData`")? .pnet; let Half::Done { to_send, .. } = &pnet_state.incoming else { @@ -75,7 +75,7 @@ impl P2pNetworkPnetState { if !to_send.is_empty() { let data = Data::from(to_send.clone()); dispatcher.push(P2pNetworkSelectAction::IncomingDataAuth { - addr: *addr, + addr, data, fin: false, }); @@ -84,13 +84,13 @@ impl P2pNetworkPnetState { Ok(()) } P2pNetworkPnetAction::OutgoingData { data, addr } => { - pnet_state.outgoing.reduce(&pnet_state.shared_secret, data); + pnet_state.outgoing.reduce(&pnet_state.shared_secret, &data); let (dispatcher, state) = state_context.into_dispatcher_and_state(); let scheduler_state: &P2pNetworkSchedulerState = state.substate()?; let pnet_state = &scheduler_state - .connection_state(addr) - .ok_or_else(|| format!("Missing connection for action: {:?}", action))? + .connection_state(&addr) + .ok_or("Missing connection for action: `P2pNetworkPnetAction::OutgoingData`")? .pnet; let Half::Done { to_send, .. } = &pnet_state.outgoing else { @@ -100,7 +100,7 @@ impl P2pNetworkPnetState { if !to_send.is_empty() { dispatcher.push(P2pNetworkPnetEffectfulAction::OutgoingData { - addr: *addr, + addr, data: to_send.clone(), }); } @@ -111,13 +111,15 @@ impl P2pNetworkPnetState { addr, incoming, } => { - pnet_state.outgoing.reduce(&pnet_state.shared_secret, nonce); + pnet_state + .outgoing + .reduce(&pnet_state.shared_secret, &nonce); let (dispatcher, state) = state_context.into_dispatcher_and_state(); let scheduler_state: &P2pNetworkSchedulerState = state.substate()?; let pnet_state = &scheduler_state - .connection_state(addr) - .ok_or_else(|| format!("Missing connection for action: {:?}", action))? + .connection_state(&addr) + .ok_or("Missing connection for action: `P2pNetworkPnetAction::SetupNonce`")? .pnet; if !matches!(&pnet_state.outgoing, Half::Done { .. }) { @@ -129,16 +131,16 @@ impl P2pNetworkPnetState { }; dispatcher.push(P2pNetworkPnetEffectfulAction::SetupNonce { - addr: *addr, - nonce: nonce.clone(), - incoming: *incoming, + addr, + nonce, + incoming, }); Ok(()) } P2pNetworkPnetAction::Timeout { addr } => { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkSchedulerAction::Disconnect { - addr: *addr, + addr, reason: P2pDisconnectionReason::Timeout, }); Ok(()) diff --git a/p2p/src/network/pnet_effectful/p2p_network_pnet_effectful_actions.rs b/p2p/src/network/pnet_effectful/p2p_network_pnet_effectful_actions.rs index ba9215798e..64a78e5826 100644 --- a/p2p/src/network/pnet_effectful/p2p_network_pnet_effectful_actions.rs +++ b/p2p/src/network/pnet_effectful/p2p_network_pnet_effectful_actions.rs @@ -26,9 +26,9 @@ impl P2pNetworkPnetEffectfulAction { } } -impl From for crate::P2pAction { - fn from(a: P2pNetworkPnetEffectfulAction) -> Self { - Self::Network(a.into()) +impl From for crate::P2pEffectfulAction { + fn from(a: P2pNetworkPnetEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Network(crate::P2pNetworkEffectfulAction::Pnet(a)) } } diff --git a/p2p/src/network/pubsub/p2p_network_pubsub_reducer.rs b/p2p/src/network/pubsub/p2p_network_pubsub_reducer.rs index 3eedd38f32..ed8eae6802 100644 --- a/p2p/src/network/pubsub/p2p_network_pubsub_reducer.rs +++ b/p2p/src/network/pubsub/p2p_network_pubsub_reducer.rs @@ -21,15 +21,16 @@ use super::{ impl P2pNetworkPubsubState { pub fn reducer( mut state_context: Substate, - action: redux::ActionWithMeta<&P2pNetworkPubsubAction>, + action: redux::ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, Action: crate::P2pActionTrait, { let pubsub_state = state_context.get_substate_mut()?; + let (action, _meta) = action.split(); - match action.action() { + match action { P2pNetworkPubsubAction::NewStream { incoming: true, peer_id, @@ -37,15 +38,15 @@ impl P2pNetworkPubsubState { protocol, .. } => { - let entry = pubsub_state.clients.entry(*peer_id); + let entry = pubsub_state.clients.entry(peer_id); // preserve it let outgoing_stream_id = match &entry { Entry::Occupied(v) => v.get().outgoing_stream_id, Entry::Vacant(_) => None, }; let state = entry.or_insert_with(|| P2pNetworkPubsubClientState { - protocol: *protocol, - addr: *addr, + protocol, + addr, outgoing_stream_id, message: pb::Rpc { subscriptions: vec![], @@ -55,8 +56,8 @@ impl P2pNetworkPubsubState { buffer: vec![], incoming_messages: vec![], }); - state.protocol = *protocol; - state.addr = *addr; + state.protocol = protocol; + state.addr = addr; pubsub_state .topics @@ -71,11 +72,11 @@ impl P2pNetworkPubsubState { addr, protocol, } => { - let state = pubsub_state.clients.entry(*peer_id).or_insert_with(|| { + let state = pubsub_state.clients.entry(peer_id).or_insert_with(|| { P2pNetworkPubsubClientState { - protocol: *protocol, - addr: *addr, - outgoing_stream_id: Some(*stream_id), + protocol, + addr, + outgoing_stream_id: Some(stream_id), message: pb::Rpc { subscriptions: vec![], publish: vec![], @@ -85,9 +86,9 @@ impl P2pNetworkPubsubState { incoming_messages: vec![], } }); - state.outgoing_stream_id = Some(*stream_id); - state.protocol = *protocol; - state.addr = *addr; + state.outgoing_stream_id = Some(stream_id); + state.protocol = protocol; + state.addr = addr; let (dispatcher, state) = state_context.into_dispatcher_and_state(); let config: &P2pConfig = state.substate()?; @@ -106,12 +107,12 @@ impl P2pNetworkPubsubState { publish: vec![], control: None, }, - peer_id: *peer_id, + peer_id, }); let mesh_size = map.values().filter(|s| s.on_mesh()).count(); if mesh_size < config.meshsub.outbound_degree_desired { dispatcher.push(P2pNetworkPubsubAction::Graft { - peer_id: *peer_id, + peer_id, topic_id: TOPIC.to_owned(), }); } @@ -124,12 +125,12 @@ impl P2pNetworkPubsubState { seen_limit, .. } => { - pubsub_state.reduce_incoming_data(peer_id, data)?; + pubsub_state.reduce_incoming_data(&peer_id, data)?; let dispatcher: &mut Dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkPubsubEffectfulAction::IncomingData { - peer_id: *peer_id, - seen_limit: *seen_limit, + peer_id, + seen_limit, }); Ok(()) @@ -139,12 +140,11 @@ impl P2pNetworkPubsubState { message, seen_limit, } => { - pubsub_state.reduce_incoming_message(peer_id, message, *seen_limit)?; + pubsub_state.reduce_incoming_message(peer_id, message, seen_limit)?; let (dispatcher, global_state) = state_context.into_dispatcher_and_state(); let state: &Self = global_state.substate()?; let config: &P2pConfig = global_state.substate()?; - let peer_id = *peer_id; let incoming_block = state.incoming_block.as_ref().cloned(); let incoming_transactions = state.incoming_transactions.clone(); @@ -189,8 +189,8 @@ impl P2pNetworkPubsubState { P2pNetworkPubsubAction::Graft { peer_id, topic_id } => { let Some(state) = pubsub_state .topics - .get_mut(topic_id) - .and_then(|m| m.get_mut(peer_id)) + .get_mut(&topic_id) + .and_then(|m| m.get_mut(&peer_id)) else { return Ok(()); }; @@ -205,25 +205,22 @@ impl P2pNetworkPubsubState { ihave: vec![], iwant: vec![], graft: vec![pb::ControlGraft { - topic_id: Some(topic_id.clone()), + topic_id: Some(topic_id), }], prune: vec![], }), }; - dispatcher.push(P2pNetworkPubsubAction::OutgoingMessage { - msg, - peer_id: *peer_id, - }); + dispatcher.push(P2pNetworkPubsubAction::OutgoingMessage { msg, peer_id }); Ok(()) } P2pNetworkPubsubAction::Prune { peer_id, topic_id } => { let Some(state) = pubsub_state .topics - .get_mut(topic_id) - .and_then(|m| m.get_mut(peer_id)) + .get_mut(&topic_id) + .and_then(|m| m.get_mut(&peer_id)) else { - bug_condition!("State not found for action: {action:?}"); + bug_condition!("State not found for action: `P2pNetworkPubsubAction::Prune`"); return Ok(()); }; @@ -238,7 +235,7 @@ impl P2pNetworkPubsubState { iwant: vec![], graft: vec![], prune: vec![pb::ControlPrune { - topic_id: Some(topic_id.clone()), + topic_id: Some(topic_id), peers: vec![pb::PeerInfo { peer_id: None, signed_peer_record: None, @@ -248,33 +245,30 @@ impl P2pNetworkPubsubState { }), }; - dispatcher.push(P2pNetworkPubsubAction::OutgoingMessage { - msg, - peer_id: *peer_id, - }); + dispatcher.push(P2pNetworkPubsubAction::OutgoingMessage { msg, peer_id }); Ok(()) } P2pNetworkPubsubAction::OutgoingMessage { peer_id, msg } => { - if let Some(v) = pubsub_state.clients.get_mut(peer_id) { + if let Some(v) = pubsub_state.clients.get_mut(&peer_id) { v.message.subscriptions.clear(); v.message.publish.clear(); v.message.control = None; } else { - bug_condition!("Invalid state for action: {action:?}"); + bug_condition!( + "Invalid state for action: `P2pNetworkPubsubAction::OutgoingMessage`" + ); } let dispatcher = state_context.into_dispatcher(); - if !message_is_empty(msg) { + if !message_is_empty(&msg) { let mut data = vec![]; - if prost::Message::encode_length_delimited(msg, &mut data).is_err() { - dispatcher.push(P2pNetworkPubsubAction::OutgoingMessageError { - msg: msg.clone(), - peer_id: *peer_id, - }); + if prost::Message::encode_length_delimited(&msg, &mut data).is_err() { + dispatcher + .push(P2pNetworkPubsubAction::OutgoingMessageError { msg, peer_id }); } else { dispatcher.push(P2pNetworkPubsubAction::OutgoingData { data: Data::from(data), - peer_id: *peer_id, + peer_id, }); } } @@ -289,7 +283,7 @@ impl P2pNetworkPubsubState { let mut buffer = vec![0; 8]; - if binprot::BinProtWrite::binprot_write(message, &mut buffer).is_err() { + if binprot::BinProtWrite::binprot_write(&message, &mut buffer).is_err() { bug_condition!("binprot serialization error"); return Ok(()); } @@ -315,21 +309,18 @@ impl P2pNetworkPubsubState { pubsub_state.seq += 1; let libp2p_peer_id = - libp2p_identity::PeerId::try_from(*author).expect("valid peer_id"); // This can't happen unless something is broken in the configuration + libp2p_identity::PeerId::try_from(author).expect("valid peer_id"); // This can't happen unless something is broken in the configuration pubsub_state.to_sign.push_back(pb::Message { from: Some(libp2p_peer_id.to_bytes()), - data: Some(data.0.clone().into_vec()), - seqno: Some((*seqno).to_be_bytes().to_vec()), + data: Some(data.0.into_vec()), + seqno: Some(seqno.to_be_bytes().to_vec()), topic: topic.clone(), signature: None, key: None, }); let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pNetworkPubsubEffectfulAction::Sign { - author: *author, - topic: topic.clone(), - }); + dispatcher.push(P2pNetworkPubsubEffectfulAction::Sign { author, topic }); Ok(()) } P2pNetworkPubsubAction::SignError { .. } => { @@ -338,7 +329,7 @@ impl P2pNetworkPubsubState { } P2pNetworkPubsubAction::BroadcastSigned { signature } => { if let Some(mut message) = pubsub_state.to_sign.pop_front() { - message.signature = Some(signature.clone().0.to_vec()); + message.signature = Some(signature.0.to_vec()); pubsub_state .clients .iter_mut() @@ -348,12 +339,14 @@ impl P2pNetworkPubsubState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); broadcast(dispatcher, state) } - P2pNetworkPubsubAction::OutgoingData { data, peer_id } => { + P2pNetworkPubsubAction::OutgoingData { mut data, peer_id } => { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let state: &Self = state.substate()?; - let mut data = data.clone(); - let Some(state) = state.clients.get(peer_id) else { - bug_condition!("Missing state for action: {action:?}"); + + let Some(state) = state.clients.get(&peer_id) else { + bug_condition!( + "Missing state for action: `P2pNetworkPubsubAction::OutgoingData`" + ); return Ok(()); }; fuzz_maybe!(&mut data, crate::fuzzer::mutate_pubsub); @@ -374,14 +367,14 @@ impl P2pNetworkPubsubState { fn reduce_incoming_message( &mut self, - peer_id: &PeerId, - message: &Message, + peer_id: PeerId, + message: Message, seen_limit: usize, ) -> Result<(), String> { self.incoming_transactions.clear(); self.incoming_snarks.clear(); - let Some(state) = self.clients.get_mut(peer_id) else { + let Some(state) = self.clients.get_mut(&peer_id) else { bug_condition!("State not found for action P2pNetworkPubsubAction::IncomingMessage"); return Ok(()); }; @@ -408,7 +401,7 @@ impl P2pNetworkPubsubState { .iter_mut() .filter(|(c, _)| { // don't send back to who sent this - **c != *peer_id + *c != &peer_id }) .for_each(|(c, state)| { let Some(topic_state) = topic.get(c) else { @@ -430,7 +423,7 @@ impl P2pNetworkPubsubState { let mut slice = &data[8..]; match gossip::GossipNetMessageV2::binprot_read(&mut slice) { Ok(gossip::GossipNetMessageV2::NewState(block)) => { - self.incoming_block = Some((*peer_id, block)); + self.incoming_block = Some((peer_id, block)); } Ok(gossip::GossipNetMessageV2::TransactionPoolDiff { message, nonce }) => { let nonce = nonce.as_u32(); @@ -454,16 +447,16 @@ impl P2pNetworkPubsubState { Ok(()) } - fn reduce_incoming_data(&mut self, peer_id: &PeerId, data: &Data) -> Result<(), String> { + fn reduce_incoming_data(&mut self, peer_id: &PeerId, data: Data) -> Result<(), String> { let Some(state) = self.clients.get_mut(peer_id) else { // TODO: investigate, cannot reproduce this // bug_condition!("State not found for action: P2pNetworkPubsubAction::IncomingData"); return Ok(()); }; let slice = if state.buffer.is_empty() { - &**data + &*data } else { - state.buffer.extend_from_slice(data); + state.buffer.extend_from_slice(&data); &state.buffer }; let mut subscriptions = vec![]; diff --git a/p2p/src/network/pubsub/pubsub_effectful/p2p_network_pubsub_effectful_actions.rs b/p2p/src/network/pubsub/pubsub_effectful/p2p_network_pubsub_effectful_actions.rs index 5bad8aa7a8..4b9acbddc1 100644 --- a/p2p/src/network/pubsub/pubsub_effectful/p2p_network_pubsub_effectful_actions.rs +++ b/p2p/src/network/pubsub/pubsub_effectful/p2p_network_pubsub_effectful_actions.rs @@ -8,9 +8,9 @@ pub enum P2pNetworkPubsubEffectfulAction { IncomingData { peer_id: PeerId, seen_limit: usize }, } -impl From for crate::P2pAction { - fn from(value: P2pNetworkPubsubEffectfulAction) -> Self { - crate::P2pAction::Network(value.into()) +impl From for crate::P2pEffectfulAction { + fn from(value: P2pNetworkPubsubEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Network(crate::P2pNetworkEffectfulAction::Pubsub(value)) } } diff --git a/p2p/src/network/rpc/p2p_network_rpc_reducer.rs b/p2p/src/network/rpc/p2p_network_rpc_reducer.rs index f5ef15fa57..1899fbdf5d 100644 --- a/p2p/src/network/rpc/p2p_network_rpc_reducer.rs +++ b/p2p/src/network/rpc/p2p_network_rpc_reducer.rs @@ -31,20 +31,20 @@ impl P2pNetworkRpcState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: redux::ActionWithMeta<&P2pNetworkRpcAction>, + action: redux::ActionWithMeta, limits: &P2pLimits, ) -> Result<(), String> where State: crate::P2pStateTrait, Action: crate::P2pActionTrait, { + let (action, meta) = action.split(); let rpc_state = state_context .get_substate_mut()? - .find_rpc_state_mut(action.action()) - .ok_or_else(|| format!("RPC state not found for action: {:?}", action.action())) + .find_rpc_state_mut(&action) + .ok_or_else(|| format!("RPC state not found for action: {:?}", action)) .inspect_err(|e| bug_condition!("{}", e))?; - let (action, meta) = action.split(); match action { P2pNetworkRpcAction::Init { incoming, @@ -52,13 +52,13 @@ impl P2pNetworkRpcState { peer_id, stream_id, } => { - rpc_state.is_incoming = *incoming; + rpc_state.is_incoming = incoming; let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkRpcAction::OutgoingData { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, data: Data::from(RpcMessage::Handshake.into_bytes()), fin: false, }); @@ -70,7 +70,7 @@ impl P2pNetworkRpcState { peer_id, stream_id, } => { - rpc_state.buffer.extend_from_slice(data); + rpc_state.buffer.extend_from_slice(&data); let mut offset = 0; // TODO(akoptelov): there shouldn't be the case where we have multiple incoming messages at once (or at least other than heartbeat) loop { @@ -127,22 +127,22 @@ impl P2pNetworkRpcState { if let Some(message) = incoming { dispatcher.push(P2pNetworkRpcAction::IncomingMessage { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, message, }) } Ok(()) } - action @ P2pNetworkRpcAction::IncomingMessage { - message, + ref action @ P2pNetworkRpcAction::IncomingMessage { + ref message, addr, peer_id, stream_id, } => { - if let RpcMessage::Response { header, .. } = message { + if let RpcMessage::Response { header, .. } = &message { if let Some(QueryHeader { id, tag, version }) = &rpc_state.pending { *rpc_state .total_stats @@ -154,7 +154,7 @@ impl P2pNetworkRpcState { } else { error!(meta.time(); "receiving response without query"); } - } else if let RpcMessage::Query { header, .. } = message { + } else if let RpcMessage::Query { header, .. } = &message { if rpc_state.pending.is_none() { rpc_state.pending = Some(header.clone()); } else { @@ -173,14 +173,14 @@ impl P2pNetworkRpcState { match &message { RpcMessage::Handshake => { if !state.is_incoming { - dispatcher.push(P2pChannelsRpcAction::Ready { peer_id: *peer_id }); + dispatcher.push(P2pChannelsRpcAction::Ready { peer_id }); } } RpcMessage::Heartbeat => {} RpcMessage::Query { header, bytes } => { - if let Err(e) = dispatch_rpc_query(*peer_id, header, bytes, dispatcher) { + if let Err(e) = dispatch_rpc_query(peer_id, header, bytes, dispatcher) { dispatcher.push(P2pDisconnectionAction::Init { - peer_id: *peer_id, + peer_id, reason: P2pDisconnectionReason::P2pChannelReceiveFailed( e.to_string(), ), @@ -203,16 +203,13 @@ impl P2pNetworkRpcState { } }; // unset pending - dispatcher.push(P2pNetworkRpcAction::PrunePending { - peer_id: *peer_id, - stream_id: *stream_id, - }); + dispatcher.push(P2pNetworkRpcAction::PrunePending { peer_id, stream_id }); if let Err(e) = - dispatch_rpc_response(*peer_id, &query_header, bytes, dispatcher) + dispatch_rpc_response(peer_id, &query_header, bytes, dispatcher) { dispatcher.push(P2pDisconnectionAction::Init { - peer_id: *peer_id, + peer_id, reason: P2pDisconnectionReason::P2pChannelReceiveFailed( e.to_string(), ), @@ -223,9 +220,9 @@ impl P2pNetworkRpcState { if let Some(message) = state.incoming.front().cloned() { dispatcher.push(P2pNetworkRpcAction::IncomingMessage { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, message, }); } @@ -245,9 +242,9 @@ impl P2pNetworkRpcState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkRpcAction::OutgoingData { - addr: *addr, - peer_id: *peer_id, - stream_id: *stream_id, + addr, + peer_id, + stream_id, data: Data::from(RpcMessage::Heartbeat.into_bytes()), fin: false, }); @@ -267,12 +264,12 @@ impl P2pNetworkRpcState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkRpcAction::OutgoingData { addr, - peer_id: *peer_id, + peer_id, stream_id, data: Data::from( RpcMessage::Query { - header: query.clone(), - bytes: data.clone(), + header: query, + bytes: data, } .into_bytes(), ), @@ -284,17 +281,16 @@ impl P2pNetworkRpcState { P2pNetworkRpcAction::OutgoingData { addr, stream_id, - data, + mut data, .. } => { let dispatcher = state_context.into_dispatcher(); - let mut data = data.clone(); fuzz_maybe!(&mut data, crate::fuzzer::mutate_rpc_data); let flags = fuzzed_maybe!(Default::default(), crate::fuzzer::mutate_yamux_flags); dispatcher.push(P2pNetworkYamuxAction::OutgoingData { - addr: *addr, - stream_id: *stream_id, + addr, + stream_id, data, flags, }); @@ -314,18 +310,15 @@ impl P2pNetworkRpcState { let addr = rpc_state.addr; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pNetworkRpcAction::PrunePending { - peer_id: *peer_id, - stream_id, - }); + dispatcher.push(P2pNetworkRpcAction::PrunePending { peer_id, stream_id }); dispatcher.push(P2pNetworkRpcAction::OutgoingData { addr, - peer_id: *peer_id, + peer_id, stream_id, data: Data::from( RpcMessage::Response { - header: response.clone(), - bytes: data.clone(), + header: response, + bytes: data, } .into_bytes(), ), diff --git a/p2p/src/network/rpc/p2p_network_rpc_state.rs b/p2p/src/network/rpc/p2p_network_rpc_state.rs index eb3eaca572..37c127969c 100644 --- a/p2p/src/network/rpc/p2p_network_rpc_state.rs +++ b/p2p/src/network/rpc/p2p_network_rpc_state.rs @@ -85,7 +85,7 @@ impl RpcMessage { .unwrap_or_default(); } Self::Query { header, bytes } => { - MessageHeader::Query(header.clone()) + MessageHeader::Query(header) .binprot_write(&mut v) .unwrap_or_default(); v.extend_from_slice(&bytes); diff --git a/p2p/src/network/scheduler/p2p_network_scheduler_actions.rs b/p2p/src/network/scheduler/p2p_network_scheduler_actions.rs index 45681cf7ff..43263a09b9 100644 --- a/p2p/src/network/scheduler/p2p_network_scheduler_actions.rs +++ b/p2p/src/network/scheduler/p2p_network_scheduler_actions.rs @@ -32,6 +32,9 @@ pub enum P2pNetworkSchedulerAction { listener: SocketAddr, error: String, }, + IncomingConnectionIsReady { + listener: SocketAddr, + }, #[action_event(fields(debug(addr), debug(result)))] IncomingDidAccept { addr: Option, @@ -126,6 +129,7 @@ impl redux::EnablingCondition for P2pNetworkSchedulerAction { | P2pNetworkSchedulerAction::InterfaceExpired { .. } | P2pNetworkSchedulerAction::ListenerReady { .. } | P2pNetworkSchedulerAction::ListenerError { .. } + | P2pNetworkSchedulerAction::IncomingConnectionIsReady { .. } | P2pNetworkSchedulerAction::SelectDone { .. } | P2pNetworkSchedulerAction::SelectError { .. } => true, P2pNetworkSchedulerAction::IncomingDidAccept { addr, .. } => { diff --git a/p2p/src/network/scheduler/p2p_network_scheduler_reducer.rs b/p2p/src/network/scheduler/p2p_network_scheduler_reducer.rs index d5821f2444..566041f343 100644 --- a/p2p/src/network/scheduler/p2p_network_scheduler_reducer.rs +++ b/p2p/src/network/scheduler/p2p_network_scheduler_reducer.rs @@ -24,7 +24,7 @@ use super::{super::*, p2p_network_scheduler_state::P2pNetworkConnectionState, *} impl P2pNetworkSchedulerState { pub fn reducer( mut state_context: Substate, - action: redux::ActionWithMeta<&P2pNetworkSchedulerAction>, + action: redux::ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -35,36 +35,34 @@ impl P2pNetworkSchedulerState { match action { P2pNetworkSchedulerAction::InterfaceDetected { ip, .. } => { - scheduler_state.interfaces.insert(*ip); + scheduler_state.interfaces.insert(ip); let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_config: &P2pConfig = state.substate()?; if let Some(port) = p2p_config.libp2p_port { - dispatcher.push(P2pNetworkSchedulerEffectfulAction::InterfaceDetected { - ip: *ip, - port, - }); + dispatcher + .push(P2pNetworkSchedulerEffectfulAction::InterfaceDetected { ip, port }); } Ok(()) } P2pNetworkSchedulerAction::InterfaceExpired { ip, .. } => { - scheduler_state.interfaces.remove(ip); + scheduler_state.interfaces.remove(&ip); Ok(()) } P2pNetworkSchedulerAction::ListenerReady { listener } => { - scheduler_state.listeners.insert(*listener); + scheduler_state.listeners.insert(listener); Ok(()) } P2pNetworkSchedulerAction::ListenerError { listener, .. } => { - scheduler_state.listeners.remove(listener); + scheduler_state.listeners.remove(&listener); Ok(()) } P2pNetworkSchedulerAction::IncomingDataIsReady { addr } => { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let scheduler: &Self = state.substate()?; - let Some(connection_state) = scheduler.connection_state(addr) else { + let Some(connection_state) = scheduler.connection_state(&addr) else { bug_condition!( "Invalid state for `P2pNetworkSchedulerAction::IncomingDataIsReady`" ); @@ -74,7 +72,7 @@ impl P2pNetworkSchedulerState { let limit = connection_state.limit(); if limit > 0 { dispatcher.push(P2pNetworkSchedulerEffectfulAction::IncomingDataIsReady { - addr: *addr, + addr, limit, }); } @@ -84,7 +82,7 @@ impl P2pNetworkSchedulerState { P2pNetworkSchedulerAction::IncomingDidAccept { addr, result } => { if let Some(addr) = addr { scheduler_state.connections.insert( - *addr, + addr, P2pNetworkConnectionState { incoming: true, pnet: P2pNetworkPnetState::new(scheduler_state.pnet_key, meta.time()), @@ -102,8 +100,8 @@ impl P2pNetworkSchedulerState { let dispatcher = state_context.into_dispatcher(); if let Some(addr) = addr { dispatcher.push(P2pNetworkSchedulerEffectfulAction::IncomingDidAccept { - addr: *addr, - result: result.clone(), + addr, + result, }); } @@ -112,7 +110,7 @@ impl P2pNetworkSchedulerState { P2pNetworkSchedulerAction::OutgoingConnect { addr } => { scheduler_state.connections.insert( ConnectionAddr { - sock_addr: *addr, + sock_addr: addr, incoming: false, }, P2pNetworkConnectionState { @@ -135,8 +133,7 @@ impl P2pNetworkSchedulerState { ); let dispatcher = state_context.into_dispatcher(); - dispatcher - .push(P2pNetworkSchedulerEffectfulAction::OutgoingConnect { addr: *addr }); + dispatcher.push(P2pNetworkSchedulerEffectfulAction::OutgoingConnect { addr }); Ok(()) } P2pNetworkSchedulerAction::OutgoingDidConnect { addr, result } => { @@ -147,12 +144,11 @@ impl P2pNetworkSchedulerState { match result { Ok(()) => { - dispatcher.push(P2pNetworkSchedulerEffectfulAction::OutgoingDidConnect { - addr: *addr, - }); + dispatcher + .push(P2pNetworkSchedulerEffectfulAction::OutgoingDidConnect { addr }); } Err(error) => { - let Some((peer_id, peer_state)) = p2p_state.peer_with_connection(*addr) + let Some((peer_id, peer_state)) = p2p_state.peer_with_connection(addr) else { bug_condition!( "outgoing connection to {addr} failed, but there is no peer for it" @@ -176,26 +172,23 @@ impl P2pNetworkSchedulerState { } P2pNetworkSchedulerAction::IncomingDataDidReceive { result, addr } => { // since both actions dispatcher later require connection state, if we can't find it we shouldn't dispatcher them - let Some(state) = scheduler_state.connection_state_mut(addr) else { + let Some(state) = scheduler_state.connection_state_mut(&addr) else { bug_condition!("Unable to find connection for `P2pNetworkSchedulerAction::IncomingDataDidReceive`"); return Ok(()); }; - if let Ok(data) = result { + if let Ok(data) = &result { state.consume(data.len()); }; let dispatcher = state_context.into_dispatcher(); match result { Ok(data) => { - dispatcher.push(P2pNetworkPnetAction::IncomingData { - addr: *addr, - data: data.clone(), - }); + dispatcher.push(P2pNetworkPnetAction::IncomingData { addr, data }); } Err(error) => dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, - error: P2pNetworkConnectionError::MioError(error.clone()), + addr, + error: P2pNetworkConnectionError::MioError(error), }), } Ok(()) @@ -208,18 +201,16 @@ impl P2pNetworkSchedulerState { expected_peer_id, } => { scheduler_state.reducer_select_done( - *addr, - *kind, - *protocol, - *incoming, - *expected_peer_id, + addr, + kind, + protocol, + incoming, + expected_peer_id, ); let (dispatcher, state) = state_context.into_dispatcher_and_state(); let p2p_state: &P2pState = state.substate()?; - Self::forward_select_done( - dispatcher, p2p_state, *protocol, *addr, *incoming, *kind, - ); + Self::forward_select_done(dispatcher, p2p_state, protocol, addr, incoming, kind); Ok(()) } P2pNetworkSchedulerAction::SelectError { addr, kind, error } => { @@ -232,28 +223,26 @@ impl P2pNetworkSchedulerState { warn!(meta.time(); summary="select error for stream", addr = display(addr), peer_id = display(peer_id)); // just close the stream dispatcher.push(P2pNetworkYamuxAction::OutgoingData { - addr: *addr, - stream_id: *stream_id, + addr, + stream_id, data: Data::default(), flags: YamuxFlags::RST, }); - dispatcher.push(P2pNetworkSchedulerAction::PruneStream { - peer_id: *peer_id, - stream_id: *stream_id, - }); + dispatcher + .push(P2pNetworkSchedulerAction::PruneStream { peer_id, stream_id }); } _ => { dispatcher.push(P2pNetworkSchedulerAction::Error { - addr: *addr, + addr, error: P2pNetworkConnectionError::SelectError, }); } } dispatcher.push(P2pNetworkSchedulerEffectfulAction::SelectError { - addr: *addr, - kind: *kind, - error: error.to_owned(), + addr, + kind, + error, }); Ok(()) @@ -263,7 +252,7 @@ impl P2pNetworkSchedulerState { message_size_limit, peer_id, } => { - let Some(cn) = scheduler_state.connections.get_mut(addr) else { + let Some(cn) = scheduler_state.connections.get_mut(&addr) else { bug_condition!( "Missing connection state for `P2pNetworkSchedulerAction::YamuxDidInit`" ); @@ -271,12 +260,11 @@ impl P2pNetworkSchedulerState { }; if let Some(P2pNetworkConnectionMuxState::Yamux(yamux)) = &mut cn.mux { yamux.init = true; - yamux.message_size_limit = *message_size_limit; + yamux.message_size_limit = message_size_limit; } let incoming = cn.incoming; let dispatcher = state_context.into_dispatcher(); - let peer_id = *peer_id; if incoming { dispatcher.push(P2pConnectionIncomingAction::Libp2pReceived { peer_id }); @@ -284,14 +272,11 @@ impl P2pNetworkSchedulerState { dispatcher.push(P2pConnectionOutgoingAction::FinalizeSuccess { peer_id }); } - dispatcher.push(P2pIdentifyAction::NewRequest { - peer_id, - addr: *addr, - }); + dispatcher.push(P2pIdentifyAction::NewRequest { peer_id, addr }); Ok(()) } P2pNetworkSchedulerAction::Disconnect { addr, reason } => { - let Some(conn_state) = scheduler_state.connections.get_mut(addr) else { + let Some(conn_state) = scheduler_state.connections.get_mut(&addr) else { bug_condition!( "`P2pNetworkSchedulerAction::Disconnect`: connection {addr} does not exist" ); @@ -306,15 +291,12 @@ impl P2pNetworkSchedulerState { conn_state.closed = Some(reason.clone().into()); let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pNetworkSchedulerEffectfulAction::Disconnect { - addr: *addr, - reason: reason.clone(), - }); + dispatcher.push(P2pNetworkSchedulerEffectfulAction::Disconnect { addr, reason }); Ok(()) } P2pNetworkSchedulerAction::Error { addr, error } => { - let Some(conn_state) = scheduler_state.connections.get_mut(addr) else { + let Some(conn_state) = scheduler_state.connections.get_mut(&addr) else { bug_condition!( "`P2pNetworkSchedulerAction::Error`: connection {addr} does not exist" ); @@ -329,14 +311,11 @@ impl P2pNetworkSchedulerState { conn_state.closed = Some(error.clone().into()); let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pNetworkSchedulerEffectfulAction::Error { - addr: *addr, - error: error.clone(), - }); + dispatcher.push(P2pNetworkSchedulerEffectfulAction::Error { addr, error }); Ok(()) } P2pNetworkSchedulerAction::Disconnected { addr, reason } => { - let Some(cn) = scheduler_state.connections.get_mut(addr) else { + let Some(cn) = scheduler_state.connections.get_mut(&addr) else { bug_condition!( "P2pNetworkSchedulerAction::Disconnected: connection {addr} does not exist" ); @@ -352,7 +331,7 @@ impl P2pNetworkSchedulerState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let state: &P2pState = state.substate()?; - let peer_with_state = state.peer_with_connection(*addr); + let peer_with_state = state.peer_with_connection(addr); if reason.is_disconnected() { // statemachine behaviour should continue with this, i.e. dispatch P2pDisconnectionAction::Finish @@ -406,7 +385,7 @@ impl P2pNetworkSchedulerState { Ok(()) } P2pNetworkSchedulerAction::Prune { addr } => { - if let Some(old) = scheduler_state.connections.remove(addr) { + if let Some(old) = scheduler_state.connections.remove(&addr) { if let Some(peer_id) = old.peer_id() { scheduler_state.prune_peer_state(peer_id); } @@ -419,18 +398,25 @@ impl P2pNetworkSchedulerState { let Some((_, conn_state)) = scheduler_state .connections .iter_mut() - .find(|(_, conn_state)| conn_state.peer_id() == Some(peer_id)) + .find(|(_, conn_state)| conn_state.peer_id() == Some(&peer_id)) else { bug_condition!("PruneStream: peer {peer_id} not found"); return Ok(()); }; - if conn_state.streams.remove(stream_id).is_none() { + if conn_state.streams.remove(&stream_id).is_none() { bug_condition!("PruneStream: peer {peer_id} does not have stream {stream_id}"); } Ok(()) } + P2pNetworkSchedulerAction::IncomingConnectionIsReady { listener } => { + let dispatcher = state_context.into_dispatcher(); + dispatcher.push( + P2pNetworkSchedulerEffectfulAction::IncomingConnectionIsReady { listener }, + ); + Ok(()) + } } } diff --git a/p2p/src/network/scheduler_effectful/p2p_network_scheduler_effectful_actions.rs b/p2p/src/network/scheduler_effectful/p2p_network_scheduler_effectful_actions.rs index 1b8b029b23..e0b23194e8 100644 --- a/p2p/src/network/scheduler_effectful/p2p_network_scheduler_effectful_actions.rs +++ b/p2p/src/network/scheduler_effectful/p2p_network_scheduler_effectful_actions.rs @@ -62,9 +62,9 @@ pub enum P2pNetworkSchedulerEffectfulAction { }, } -impl From for crate::P2pAction { - fn from(value: P2pNetworkSchedulerEffectfulAction) -> Self { - crate::P2pAction::Network(value.into()) +impl From for crate::P2pEffectfulAction { + fn from(value: P2pNetworkSchedulerEffectfulAction) -> crate::P2pEffectfulAction { + crate::P2pEffectfulAction::Network(crate::P2pNetworkEffectfulAction::Scheduler(value)) } } diff --git a/p2p/src/network/scheduler_effectful/p2p_network_scheduler_effectful_effects.rs b/p2p/src/network/scheduler_effectful/p2p_network_scheduler_effectful_effects.rs index 3189bc7cea..b4ff0e6d08 100644 --- a/p2p/src/network/scheduler_effectful/p2p_network_scheduler_effectful_effects.rs +++ b/p2p/src/network/scheduler_effectful/p2p_network_scheduler_effectful_effects.rs @@ -17,7 +17,7 @@ impl P2pNetworkSchedulerEffectfulAction { .service() .send_mio_cmd(MioCmd::ListenOn(SocketAddr::new(ip, port))); } - P2pNetworkSchedulerEffectfulAction::IncomingConnectionIsReady { listener, .. } => { + P2pNetworkSchedulerEffectfulAction::IncomingConnectionIsReady { listener } => { let state = store.state(); if state.network.scheduler.connections.len() >= state.config.limits.max_connections() diff --git a/p2p/src/network/select/p2p_network_select_reducer.rs b/p2p/src/network/select/p2p_network_select_reducer.rs index 4ba52933f6..eac32b761a 100644 --- a/p2p/src/network/select/p2p_network_select_reducer.rs +++ b/p2p/src/network/select/p2p_network_select_reducer.rs @@ -1,4 +1,4 @@ -use openmina_core::{bug_condition, error, fuzz_maybe, fuzzed_maybe, warn, Substate}; +use openmina_core::{bug_condition, error, fuzz_maybe, fuzzed_maybe, Substate}; use redux::Timestamp; use token::{ AuthKind, DiscoveryAlgorithm, IdentifyAlgorithm, MuxKind, Protocol, RpcAlgorithm, StreamKind, @@ -20,7 +20,7 @@ use super::*; impl P2pNetworkSelectState { pub fn reducer( mut state_context: Substate, - action: redux::ActionWithMeta<&P2pNetworkSelectAction>, + action: redux::ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -61,8 +61,8 @@ impl P2pNetworkSelectState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let state: &P2pNetworkSchedulerState = state.substate()?; let state = state - .connection_state(addr) - .and_then(|state| state.select_state(kind)) + .connection_state(&addr) + .and_then(|state| state.select_state(&kind)) .ok_or_else(|| format!("Select state not found for {action:?}"))?; if state.negotiated.is_none() && !incoming { @@ -78,11 +78,7 @@ impl P2pNetworkSelectState { } _ => {} }; - dispatcher.push(P2pNetworkSelectAction::OutgoingTokens { - addr: *addr, - kind: *kind, - tokens, - }); + dispatcher.push(P2pNetworkSelectAction::OutgoingTokens { addr, kind, tokens }); } Ok(()) @@ -94,26 +90,24 @@ impl P2pNetworkSelectState { | P2pNetworkSelectAction::IncomingDataMux { data, addr, fin, .. } => { - select_state.handle_incoming_data(data); + select_state.handle_incoming_data(&data); let (dispatcher, state) = state_context.into_dispatcher_and_state(); let state: &P2pNetworkSchedulerState = state.substate()?; let select_state = state - .connection_state(action.addr()) + .connection_state(&addr) .and_then(|conn| conn.select_state(&select_kind)) - .ok_or_else(|| format!("Select state not found for {action:?}"))?; + .ok_or("Select state not found for incoming data")?; if let P2pNetworkSelectStateInner::Error(error) = &select_state.inner { dispatcher.push(P2pNetworkSchedulerAction::SelectError { - addr: *addr, + addr, kind: select_kind, error: error.to_owned(), }) } else { - for action in - select_state.forward_incoming_data(select_kind, *addr, data.clone(), *fin) - { + for action in select_state.forward_incoming_data(select_kind, addr, data, fin) { dispatcher.push(action) } } @@ -134,7 +128,7 @@ impl P2pNetworkSelectState { select_kind, addr, data, - *fin, + fin, meta.time(), ) } @@ -148,14 +142,14 @@ impl P2pNetworkSelectState { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let scheduler: &P2pNetworkSchedulerState = state.substate()?; let select_state = scheduler - .connection_state(addr) - .and_then(|stream| stream.select_state(kind)) + .connection_state(&addr) + .and_then(|stream| stream.select_state(&kind)) .ok_or_else(|| format!("Select state not found for {action:?}"))?; if let P2pNetworkSelectStateInner::Error(error) = &select_state.inner { dispatcher.push(P2pNetworkSchedulerAction::SelectError { - addr: *addr, - kind: *kind, + addr, + kind, error: error.to_owned(), }); return Ok(()); @@ -163,8 +157,8 @@ impl P2pNetworkSelectState { if let Some(token) = &select_state.to_send { dispatcher.push(P2pNetworkSelectAction::OutgoingTokens { - addr: *addr, - kind: *kind, + addr, + kind, tokens: vec![token.clone()], }) } @@ -173,7 +167,7 @@ impl P2pNetworkSelectState { let p2p_state: &P2pState = state.substate()?; let expected_peer_id = p2p_state - .peer_with_connection(*addr) + .peer_with_connection(addr) .map(|(peer_id, _)| peer_id); let incoming = matches!( @@ -181,8 +175,8 @@ impl P2pNetworkSelectState { P2pNetworkSelectStateInner::Responder { .. } ); dispatcher.push(P2pNetworkSchedulerAction::SelectDone { - addr: *addr, - kind: *kind, + addr, + kind, protocol, incoming, expected_peer_id, @@ -196,13 +190,12 @@ impl P2pNetworkSelectState { let mut data = { let mut data = vec![]; - for token in tokens { + for token in &tokens { data.extend_from_slice(token.name()) } data.into() }; - let addr = *addr; match kind { SelectKind::Authentication => { fuzz_maybe!(&mut data, mutate_select_authentication); @@ -217,7 +210,7 @@ impl P2pNetworkSelectState { if let Some(na) = tokens.iter().find(|t| t.name() == Token::Na.name()) { dispatcher.push(P2pNetworkYamuxAction::OutgoingData { addr, - stream_id: *stream_id, + stream_id, data: na.name().to_vec().into(), flags: YamuxFlags::FIN, }); @@ -229,7 +222,7 @@ impl P2pNetworkSelectState { ); dispatcher.push(P2pNetworkYamuxAction::OutgoingData { addr, - stream_id: *stream_id, + stream_id, data, flags: Default::default(), }); @@ -243,14 +236,8 @@ impl P2pNetworkSelectState { P2pNetworkSelectAction::Timeout { addr, kind } => { let error = "timeout".to_owned(); select_state.inner = P2pNetworkSelectStateInner::Error(error.clone()); - warn!(meta.time(); "timeout"); - let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pNetworkSchedulerAction::SelectError { - addr: *addr, - kind: *kind, - error, - }); + dispatcher.push(P2pNetworkSchedulerAction::SelectError { addr, kind, error }); Ok(()) } } @@ -418,8 +405,8 @@ impl P2pNetworkSelectState { fn handle_negotiated_token( state_context: Substate, select_kind: SelectKind, - addr: &ConnectionAddr, - data: &Data, + addr: ConnectionAddr, + data: Data, fin: bool, time: Timestamp, ) -> Result<(), String> @@ -431,7 +418,7 @@ impl P2pNetworkSelectState { let p2p_state: &P2pState = state.substate()?; let state: &P2pNetworkSchedulerState = state.substate()?; let state = state - .connection_state(addr) + .connection_state(&addr) .and_then(|state| state.select_state(&select_kind)) .ok_or_else(|| "Select state not found incoming payload".to_owned())?; @@ -442,8 +429,6 @@ impl P2pNetworkSelectState { ); return Ok(()); }; - let addr = *addr; - let data = data.clone(); match negotiated { Protocol::Auth(AuthKind::Noise) => { diff --git a/p2p/src/network/yamux/p2p_network_yamux_reducer.rs b/p2p/src/network/yamux/p2p_network_yamux_reducer.rs index fad0dedd58..48454331b0 100644 --- a/p2p/src/network/yamux/p2p_network_yamux_reducer.rs +++ b/p2p/src/network/yamux/p2p_network_yamux_reducer.rs @@ -21,7 +21,7 @@ impl P2pNetworkYamuxState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: redux::ActionWithMeta<&P2pNetworkYamuxAction>, + action: redux::ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -45,7 +45,7 @@ impl P2pNetworkYamuxState { match action { P2pNetworkYamuxAction::IncomingData { data, addr } => { - yamux_state.buffer.extend_from_slice(data); + yamux_state.buffer.extend_from_slice(&data); let mut offset = 0; loop { let buf = &yamux_state.buffer[offset..]; @@ -144,7 +144,7 @@ impl P2pNetworkYamuxState { let incoming_data = yamux_state.incoming.clone(); let dispatcher = state_context.into_dispatcher(); incoming_data.into_iter().for_each(|frame| { - dispatcher.push(P2pNetworkYamuxAction::IncomingFrame { addr: *addr, frame }) + dispatcher.push(P2pNetworkYamuxAction::IncomingFrame { addr, frame }) }); Ok(()) @@ -153,15 +153,13 @@ impl P2pNetworkYamuxState { addr, stream_id, data, - flags, + mut flags, } => { let yamux_state = yamux_state .streams - .get(stream_id) + .get(&stream_id) .ok_or_else(|| format!("Stream with id {stream_id} not found for `P2pNetworkYamuxAction::OutgoingData`"))?; - let mut flags = *flags; - if !yamux_state.incoming && !yamux_state.established && !yamux_state.syn_sent { flags.insert(YamuxFlags::SYN); } else if yamux_state.incoming && !yamux_state.established { @@ -172,12 +170,12 @@ impl P2pNetworkYamuxState { let frame = YamuxFrame { flags, - stream_id: *stream_id, - inner: YamuxFrameInner::Data(data.clone()), + stream_id, + inner: YamuxFrameInner::Data(data), }; let dispatcher = state_context.into_dispatcher(); - dispatcher.push(P2pNetworkYamuxAction::OutgoingFrame { addr: *addr, frame }); + dispatcher.push(P2pNetworkYamuxAction::OutgoingFrame { addr, frame }); Ok(()) } @@ -225,13 +223,12 @@ impl P2pNetworkYamuxState { } let (dispatcher, state) = state_context.into_dispatcher_and_state(); - let addr = *addr; let limits: &P2pLimits = state.substate()?; let max_streams = limits.max_streams(); let connection_state = >::substate(state)? .connection_state(&addr) - .ok_or_else(|| format!("Connection not found {}", action.addr()))?; + .ok_or_else(|| format!("Connection not found {}", addr))?; let stream = connection_state .yamux_state() @@ -357,16 +354,16 @@ impl P2pNetworkYamuxState { let dispatcher = state_context.into_dispatcher(); let data = fuzzed_maybe!( - Data::from(frame.clone().into_bytes()), + Data::from(frame.into_bytes()), crate::fuzzer::mutate_yamux_frame ); - dispatcher.push(P2pNetworkNoiseAction::OutgoingData { addr: *addr, data }); + dispatcher.push(P2pNetworkNoiseAction::OutgoingData { addr, data }); Ok(()) } P2pNetworkYamuxAction::PingStream { addr, ping } => { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkYamuxAction::OutgoingFrame { - addr: *addr, + addr, frame: ping.into_frame(), }); @@ -379,10 +376,10 @@ impl P2pNetworkYamuxState { } => { yamux_state .streams - .insert(*stream_id, YamuxStreamState::default()); + .insert(stream_id, YamuxStreamState::default()); connection_state.streams.insert( - *stream_id, - P2pNetworkStreamState::new(*stream_kind, meta.time()), + stream_id, + P2pNetworkStreamState::new(stream_kind, meta.time()), ); let peer_id = match connection_state @@ -396,8 +393,8 @@ impl P2pNetworkYamuxState { let dispatcher = state_context.into_dispatcher(); dispatcher.push(P2pNetworkSelectAction::Init { - addr: *addr, - kind: SelectKind::Stream(peer_id, *stream_id), + addr, + kind: SelectKind::Stream(peer_id, stream_id), incoming: false, }); Ok(()) diff --git a/p2p/src/p2p_actions.rs b/p2p/src/p2p_actions.rs index 5a9357e6e4..01198c02ed 100644 --- a/p2p/src/p2p_actions.rs +++ b/p2p/src/p2p_actions.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::channels::P2pChannelsEffectfulAction; use crate::connection::P2pConnectionEffectfulAction; use crate::disconnection_effectful::P2pDisconnectionEffectfulAction; +use crate::P2pNetworkEffectfulAction; use super::channels::P2pChannelsAction; use super::connection::P2pConnectionAction; @@ -14,23 +15,26 @@ use super::network::P2pNetworkAction; use super::peer::P2pPeerAction; use super::P2pState; -pub type P2pActionWithMeta = redux::ActionWithMeta; -pub type P2pActionWithMetaRef<'a> = redux::ActionWithMeta<&'a P2pAction>; - #[derive(Serialize, Deserialize, Debug, Clone, derive_more::From, ActionEvent)] pub enum P2pAction { Initialization(P2pInitializeAction), Connection(P2pConnectionAction), - ConnectionEffectful(P2pConnectionEffectfulAction), Disconnection(P2pDisconnectionAction), - DisconnectionEffectful(P2pDisconnectionEffectfulAction), Identify(P2pIdentifyAction), Channels(P2pChannelsAction), - ChannelsEffectful(P2pChannelsEffectfulAction), Peer(P2pPeerAction), Network(P2pNetworkAction), } +#[derive(Serialize, Deserialize, Debug, Clone, derive_more::From, ActionEvent)] +pub enum P2pEffectfulAction { + Initialize, + Channels(P2pChannelsEffectfulAction), + Connection(P2pConnectionEffectfulAction), + Disconnection(P2pDisconnectionEffectfulAction), + Network(P2pNetworkEffectfulAction), +} + #[derive(Serialize, Deserialize, Debug, Clone, derive_more::From, ActionEvent)] #[action_event(level = info, fields(display(chain_id)))] pub enum P2pInitializeAction { @@ -51,14 +55,23 @@ impl redux::EnablingCondition for P2pAction { match self { P2pAction::Initialization(a) => a.is_enabled(state, time), P2pAction::Connection(a) => a.is_enabled(state, time), - P2pAction::ConnectionEffectful(a) => a.is_enabled(state, time), P2pAction::Disconnection(a) => a.is_enabled(state, time), - P2pAction::DisconnectionEffectful(a) => a.is_enabled(state, time), P2pAction::Channels(a) => a.is_enabled(state, time), P2pAction::Peer(a) => a.is_enabled(state, time), P2pAction::Identify(a) => a.is_enabled(state, time), P2pAction::Network(a) => a.is_enabled(state, time), - P2pAction::ChannelsEffectful(a) => a.is_enabled(state, time), + } + } +} + +impl redux::EnablingCondition for P2pEffectfulAction { + fn is_enabled(&self, state: &crate::P2pState, time: redux::Timestamp) -> bool { + match self { + P2pEffectfulAction::Channels(a) => a.is_enabled(state, time), + P2pEffectfulAction::Connection(a) => a.is_enabled(state, time), + P2pEffectfulAction::Disconnection(a) => a.is_enabled(state, time), + P2pEffectfulAction::Network(a) => a.is_enabled(state, time), + P2pEffectfulAction::Initialize => true, } } } @@ -68,3 +81,9 @@ impl From for P2pAction { *action.0.downcast::().expect("Downcast failed") } } + +impl From for P2pEffectfulAction { + fn from(action: redux::AnyAction) -> Self { + *action.0.downcast::().expect("Downcast failed") + } +} diff --git a/p2p/src/p2p_config.rs b/p2p/src/p2p_config.rs index f09fa603be..349a69b21f 100644 --- a/p2p/src/p2p_config.rs +++ b/p2p/src/p2p_config.rs @@ -7,9 +7,9 @@ use crate::{ }; pub const DEVNET_SEEDS: &[&str] = &[ - "/ip4/34.45.167.81/tcp/10003/p2p/12D3KooWAdgYL6hv18M3iDBdaK1dRygPivSfAfBNDzie6YqydVbs", - "/ip4/34.28.194.121/tcp/10003/p2p/12D3KooWLjs54xHzVmMmGYb7W5RVibqbwD1co7M2ZMfPgPm7iAag", - "/ip4/34.44.189.148/tcp/10003/p2p/12D3KooWEiGVAFC7curXWXiGZyMWnZK9h8BKr88U8D5PKV3dXciv", + "/dns4/seed-1.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWAdgYL6hv18M3iDBdaK1dRygPivSfAfBNDzie6YqydVbs", + "/dns4/seed-2.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWLjs54xHzVmMmGYb7W5RVibqbwD1co7M2ZMfPgPm7iAag", + "/dns4/seed-3.devnet.gcp.o1test.net/tcp/10003/p2p/12D3KooWEiGVAFC7curXWXiGZyMWnZK9h8BKr88U8D5PKV3dXciv", ]; #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/p2p/src/p2p_effects.rs b/p2p/src/p2p_effects.rs index dad83e8c6d..5a360e5722 100644 --- a/p2p/src/p2p_effects.rs +++ b/p2p/src/p2p_effects.rs @@ -1,43 +1,42 @@ use crate::{ - channels::P2pChannelsEffectfulAction, connection::P2pConnectionEffectfulAction, P2pAction, - P2pStore, + channels::P2pChannelsEffectfulAction, connection::P2pConnectionEffectfulAction, + P2pEffectfulAction, P2pStore, }; -use redux::ActionWithMeta; +use redux::ActionMeta; -pub fn p2p_effects(store: &mut Store, action: ActionWithMeta) -where - Store: P2pStore, - Store::Service: crate::P2pService, -{ - let (action, meta) = action.split(); - match action { - P2pAction::Initialization(_) => { - // Noop - } - P2pAction::ConnectionEffectful(action) => match action { - P2pConnectionEffectfulAction::Outgoing(action) => action.effects(&meta, store), - P2pConnectionEffectfulAction::Incoming(action) => action.effects(&meta, store), - }, - P2pAction::DisconnectionEffectful(action) => action.effects(&meta, store), - - P2pAction::ChannelsEffectful(action) => match action { - P2pChannelsEffectfulAction::BestTip(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::Transaction(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::StreamingRpc(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::SnarkJobCommitment(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::Rpc(action) => action.effects(&meta, store), - P2pChannelsEffectfulAction::Snark(action) => action.effects(&meta, store), - }, - P2pAction::Network(_action) => { +impl P2pEffectfulAction { + pub fn effects(self, meta: ActionMeta, store: &mut Store) + where + Store: P2pStore, + Store::Service: crate::P2pService, + { + match self { + P2pEffectfulAction::Initialize => {} + P2pEffectfulAction::Channels(action) => match action { + P2pChannelsEffectfulAction::SignalingDiscovery(action) => { + action.effects(&meta, store) + } + P2pChannelsEffectfulAction::SignalingExchange(action) => { + action.effects(&meta, store) + } + P2pChannelsEffectfulAction::BestTip(action) => action.effects(&meta, store), + P2pChannelsEffectfulAction::Transaction(action) => action.effects(&meta, store), + P2pChannelsEffectfulAction::StreamingRpc(action) => action.effects(&meta, store), + P2pChannelsEffectfulAction::SnarkJobCommitment(action) => { + action.effects(&meta, store) + } + P2pChannelsEffectfulAction::Rpc(action) => action.effects(&meta, store), + P2pChannelsEffectfulAction::Snark(action) => action.effects(&meta, store), + }, + P2pEffectfulAction::Connection(action) => match action { + P2pConnectionEffectfulAction::Outgoing(action) => action.effects(&meta, store), + P2pConnectionEffectfulAction::Incoming(action) => action.effects(&meta, store), + }, + P2pEffectfulAction::Disconnection(action) => action.effects(&meta, store), #[cfg(feature = "p2p-libp2p")] - _action.effects(&meta, store); - } - P2pAction::Connection(_) - | P2pAction::Channels(_) - | P2pAction::Disconnection(_) - | P2pAction::Peer(_) - | P2pAction::Identify(_) => { - // handled by reducer + P2pEffectfulAction::Network(action) => action.effects(&meta, store), + #[cfg(not(feature = "p2p-libp2p"))] + P2pEffectfulAction::Network(action) => {} } } } diff --git a/p2p/src/p2p_event.rs b/p2p/src/p2p_event.rs index f4d84a6249..b349831a1f 100644 --- a/p2p/src/p2p_event.rs +++ b/p2p/src/p2p_event.rs @@ -4,6 +4,8 @@ use std::net::{IpAddr, SocketAddr}; use derive_more::From; use serde::{Deserialize, Serialize}; +use crate::channels::signaling::discovery::SignalingDiscoveryChannelMsg; +use crate::channels::signaling::exchange::SignalingExchangeChannelMsg; use crate::channels::streaming_rpc::StreamingRpcChannelMsg; use crate::ConnectionAddr; use crate::{ @@ -105,6 +107,9 @@ impl fmt::Display for P2pConnectionEvent { P2pConnectionResponse::Rejected(reason) => { write!(f, "AnswerReceived, {peer_id}, Rejected, {reason:?}") } + P2pConnectionResponse::SignalDecryptionFailed => { + write!(f, "SignalDecryptionFailed, {peer_id}") + } P2pConnectionResponse::InternalError => { write!(f, "AnswerReceived, {peer_id}, InternalError") } @@ -148,6 +153,27 @@ impl fmt::Display for P2pChannelEvent { }; match msg { + ChannelMsg::SignalingDiscovery(v) => match v { + SignalingDiscoveryChannelMsg::GetNext => write!(f, "GetNext"), + SignalingDiscoveryChannelMsg::Discover => write!(f, "Discover"), + SignalingDiscoveryChannelMsg::Discovered { target_public_key } => { + write!(f, "Discovered, {}", target_public_key.peer_id()) + } + SignalingDiscoveryChannelMsg::DiscoveredReject => write!(f, "Discovered"), + SignalingDiscoveryChannelMsg::DiscoveredAccept(_) => { + write!(f, "DiscoveredAccept") + } + SignalingDiscoveryChannelMsg::Answer(_) => write!(f, "Answer"), + }, + ChannelMsg::SignalingExchange(v) => match v { + SignalingExchangeChannelMsg::GetNext => write!(f, "GetNext"), + SignalingExchangeChannelMsg::OfferToYou { + offerer_pub_key, .. + } => { + write!(f, "OfferToYou, {}", offerer_pub_key.peer_id()) + } + SignalingExchangeChannelMsg::Answer(_) => write!(f, "Answer"), + }, ChannelMsg::BestTipPropagation(v) => { match v { BestTipPropagationChannelMsg::GetNext => write!(f, "GetNext"), diff --git a/p2p/src/p2p_reducer.rs b/p2p/src/p2p_reducer.rs index 7261a220fa..e200dbd275 100644 --- a/p2p/src/p2p_reducer.rs +++ b/p2p/src/p2p_reducer.rs @@ -1,23 +1,23 @@ use crate::{ channels::{ - rpc::P2pChannelsRpcAction, streaming_rpc::P2pChannelsStreamingRpcAction, P2pChannelsState, + rpc::P2pChannelsRpcAction, signaling::discovery::P2pChannelsSignalingDiscoveryAction, + streaming_rpc::P2pChannelsStreamingRpcAction, P2pChannelsState, }, connection::{ incoming::P2pConnectionIncomingAction, outgoing::P2pConnectionOutgoingAction, P2pConnectionState, }, disconnection::P2pDisconnectedState, - P2pAction, P2pActionWithMetaRef, P2pNetworkKadKey, P2pNetworkKademliaAction, - P2pNetworkPnetAction, P2pNetworkRpcAction, P2pNetworkSelectAction, P2pNetworkState, - P2pPeerState, P2pState, PeerId, + P2pAction, P2pNetworkKadKey, P2pNetworkKademliaAction, P2pNetworkPnetAction, + P2pNetworkRpcAction, P2pNetworkSelectAction, P2pNetworkState, P2pPeerState, P2pState, PeerId, }; use openmina_core::{bug_condition, Substate}; -use redux::{ActionMeta, Dispatcher, Timestamp}; +use redux::{ActionMeta, ActionWithMeta, Dispatcher, Timestamp}; impl P2pState { pub fn reducer( mut state_context: Substate, - action: P2pActionWithMetaRef<'_>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -64,12 +64,6 @@ impl P2pState { } Ok(()) } - P2pAction::ConnectionEffectful(_) - | P2pAction::DisconnectionEffectful(_) - | P2pAction::ChannelsEffectful(_) => { - // effectful - Ok(()) - } } } @@ -196,6 +190,14 @@ impl P2pState { return Ok(()); } + for (&peer_id, _) in self + .ready_peers_iter() + .filter(|(_, peer)| peer.channels.signaling.discovery.is_ready()) + { + dispatcher.push(P2pChannelsSignalingDiscoveryAction::RequestSend { peer_id }); + dispatcher.push(P2pChannelsSignalingDiscoveryAction::DiscoveryRequestSend { peer_id }); + } + if let Some(_d) = config.timeouts.initial_peers { // ask initial peers // TODO: use RPC to ask initial peers diff --git a/p2p/src/p2p_state.rs b/p2p/src/p2p_state.rs index e03d4b1906..16c7a26811 100644 --- a/p2p/src/p2p_state.rs +++ b/p2p/src/p2p_state.rs @@ -296,7 +296,7 @@ impl P2pState { pub fn peer_with_connection( &self, conn_id: crate::ConnectionAddr, - ) -> Option<(PeerId, P2pPeerState)> { + ) -> Option<(PeerId, &P2pPeerState)> { let result = if let crate::ConnectionAddr { sock_addr, incoming: false, @@ -326,7 +326,7 @@ impl P2pState { .and_then(|peer_id| self.peers.iter().find(|(id, _)| *id == peer_id)) }) }) - .map(|(peer_id, peer_state)| (*peer_id, peer_state.clone())) + .map(|(peer_id, peer_state)| (*peer_id, peer_state)) } pub fn incoming_peer_connection_mut( diff --git a/p2p/src/peer/p2p_peer_reducer.rs b/p2p/src/peer/p2p_peer_reducer.rs index 23f5a532dc..ec10196a22 100644 --- a/p2p/src/peer/p2p_peer_reducer.rs +++ b/p2p/src/peer/p2p_peer_reducer.rs @@ -9,7 +9,7 @@ impl P2pPeerState { /// Substate is accessed pub fn reducer( mut state_context: Substate, - action: ActionWithMeta<&P2pPeerAction>, + action: ActionWithMeta, ) -> Result<(), String> where State: crate::P2pStateTrait, @@ -23,10 +23,10 @@ impl P2pPeerState { // TODO: add bound to peers let peer_state = p2p_state .peers - .entry(*peer_id) + .entry(peer_id) .or_insert_with(|| P2pPeerState { is_libp2p: true, - dial_opts: dial_opts.clone(), + dial_opts: None, identify: None, status: P2pPeerStatus::Disconnected { time: Timestamp::ZERO, @@ -34,16 +34,16 @@ impl P2pPeerState { }); if let Some(dial_opts) = dial_opts { - peer_state.dial_opts.get_or_insert(dial_opts.clone()); + peer_state.dial_opts.get_or_insert(dial_opts); } Ok(()) } P2pPeerAction::Ready { peer_id, incoming } => { - let Some(peer) = p2p_state.peers.get_mut(peer_id) else { + let Some(peer) = p2p_state.peers.get_mut(&peer_id) else { return Ok(()); }; peer.status = P2pPeerStatus::Ready(P2pPeerStatusReady::new( - *incoming, + incoming, meta.time(), &p2p_state.config.enabled_channels, )); @@ -51,13 +51,13 @@ impl P2pPeerState { if !peer.is_libp2p { let (dispatcher, state) = state_context.into_dispatcher_and_state(); let state: &P2pState = state.substate()?; - state.channels_init(dispatcher, *peer_id); + state.channels_init(dispatcher, peer_id); } Ok(()) } P2pPeerAction::BestTipUpdate { peer_id, best_tip } => { - let Some(peer) = p2p_state.get_ready_peer_mut(peer_id) else { + let Some(peer) = p2p_state.get_ready_peer_mut(&peer_id) else { bug_condition!("Peer state not found for `P2pPeerAction::BestTipUpdate`"); return Ok(()); }; @@ -67,12 +67,12 @@ impl P2pPeerState { let p2p_state: &P2pState = state.substate()?; if let Some(callback) = &p2p_state.callbacks.on_p2p_peer_best_tip_update { - dispatcher.push_callback(callback.clone(), best_tip.clone()); + dispatcher.push_callback(callback.clone(), best_tip); } Ok(()) } P2pPeerAction::Remove { peer_id } => { - if p2p_state.peers.remove(peer_id).is_none() { + if p2p_state.peers.remove(&peer_id).is_none() { bug_condition!( "Missing state for peer {peer_id} action: `P2pPeerAction::Remove`" ); diff --git a/p2p/src/service_impl/mod.rs b/p2p/src/service_impl/mod.rs index 5cd808cbe1..f5d871677f 100644 --- a/p2p/src/service_impl/mod.rs +++ b/p2p/src/service_impl/mod.rs @@ -21,7 +21,7 @@ pub mod webrtc { use crate::{ channels::{ChannelId, ChannelMsg, MsgId}, connection::outgoing::P2pConnectionOutgoingInitOpts, - identity::SecretKey, + identity::{EncryptableType, PublicKey, SecretKey}, webrtc, P2pEvent, PeerId, }; @@ -72,5 +72,17 @@ pub mod webrtc { fn channel_open(&mut self, peer_id: PeerId, id: ChannelId) {} fn channel_send(&mut self, peer_id: PeerId, msg_id: MsgId, msg: ChannelMsg) {} + + fn encrypt( + &mut self, + other_pk: &PublicKey, + message: &T, + ) -> Result; + + fn decrypt( + &mut self, + other_pub_key: &PublicKey, + encrypted: &T::Encrypted, + ) -> Result; } } diff --git a/p2p/src/service_impl/webrtc/mod.rs b/p2p/src/service_impl/webrtc/mod.rs index 9a396a4745..9fea2a0300 100644 --- a/p2p/src/service_impl/webrtc/mod.rs +++ b/p2p/src/service_impl/webrtc/mod.rs @@ -17,6 +17,7 @@ use wasm_bindgen_futures::spawn_local; use openmina_core::channels::{mpsc, oneshot}; +use crate::identity::{EncryptableType, PublicKey}; use crate::{ channels::{ChannelId, ChannelMsg, MsgId}, connection::outgoing::P2pConnectionOutgoingInitOpts, @@ -183,13 +184,17 @@ impl Drop for RTCChannel { } } -async fn wait_for_ice_gathering_complete(pc: &RTCConnection) { - let timeout = Duration::from_secs(3); - +async fn sleep(dur: Duration) { #[cfg(not(target_arch = "wasm32"))] - let timeout = tokio::time::sleep(timeout); + let fut = tokio::time::sleep(dur); #[cfg(target_arch = "wasm32")] - let timeout = gloo_timers::future::TimeoutFuture::new(timeout.as_millis() as u32); + let fut = gloo_timers::future::TimeoutFuture::new(dur.as_millis() as u32); + fut.await +} + +async fn wait_for_ice_gathering_complete(pc: &RTCConnection) { + let timeout = sleep(Duration::from_secs(3)); + tokio::select! { _ = timeout => {} _ = pc.wait_for_ice_gathering_complete() => {} @@ -512,6 +517,11 @@ async fn peer_loop( let chan_clone = chan.clone(); let event_sender_clone = event_sender.clone(); spawn_local(async move { + // Add a delay for sending messages after channel + // was opened. Some initial messages get lost otherwise. + // TODO(binier): find deeper cause and fix it. + sleep(Duration::from_secs(3)).await; + while let Some((msg_id, encoded)) = sender_rx.recv().await { let encoded = bytes::Bytes::from(encoded); let mut chunks = @@ -714,4 +724,16 @@ pub trait P2pServiceWebrtc: redux::Service { let _ = peer.cmd_sender.send(PeerCmd::ChannelSend(msg_id, msg)); } } + + fn encrypt( + &mut self, + other_pk: &PublicKey, + message: &T, + ) -> Result; + + fn decrypt( + &mut self, + other_pub_key: &PublicKey, + encrypted: &T::Encrypted, + ) -> Result; } diff --git a/p2p/src/service_impl/webrtc/web.rs b/p2p/src/service_impl/webrtc/web.rs index f1aa77459f..d512bdec1f 100644 --- a/p2p/src/service_impl/webrtc/web.rs +++ b/p2p/src/service_impl/webrtc/web.rs @@ -234,12 +234,11 @@ pub async fn webrtc_signal_send( url: &str, offer: Offer, ) -> std::result::Result { - use web_sys::{Request, RequestInit, Response}; + use web_sys::{Request, Response}; - let mut opts = RequestInit::new(); - opts.method("POST"); - opts.body(Some(&JsValue::from(serde_json::to_string(&offer)?))); - let request = Request::new_with_str_and_init(url, &opts)?; + let offer = bs58::encode(serde_json::to_string(&offer)?).into_string(); + let url = format!("{url}/{offer}"); + let request = Request::new_with_str(&url)?; request.headers().set("content-type", "application/json")?; let window = web_sys::window().unwrap(); diff --git a/p2p/src/service_impl/webrtc_with_libp2p.rs b/p2p/src/service_impl/webrtc_with_libp2p.rs index e45a6c8da5..a665eca348 100644 --- a/p2p/src/service_impl/webrtc_with_libp2p.rs +++ b/p2p/src/service_impl/webrtc_with_libp2p.rs @@ -36,10 +36,14 @@ pub trait P2pServiceWebrtcWithLibp2p: P2pServiceWebrtc { #[cfg(feature = "p2p-libp2p")] fn resolve_name( &mut self, - _host: &str, + hostname: &str, ) -> Result, P2pNetworkServiceError> { - // TODO: resolve host - Ok(Vec::new()) + use std::net::ToSocketAddrs; + + let it = format!("{hostname}:0") + .to_socket_addrs() + .map_err(|err| P2pNetworkServiceError::Resolve(format!("{hostname}, {err}")))?; + Ok(it.map(|addr| addr.ip()).collect()) } #[cfg(feature = "p2p-libp2p")] @@ -145,6 +149,22 @@ impl P2pChannelsService for T { P2pServiceWebrtc::channel_send(self, peer_id, msg_id, msg) } } + + fn encrypt( + &mut self, + other_pk: &crate::identity::PublicKey, + message: &M, + ) -> Result { + P2pServiceWebrtc::encrypt(self, other_pk, message) + } + + fn decrypt( + &mut self, + other_pk: &crate::identity::PublicKey, + encrypted: &M::Encrypted, + ) -> Result { + P2pServiceWebrtc::decrypt(self, other_pk, encrypted) + } } #[cfg(feature = "p2p-libp2p")] diff --git a/p2p/src/webrtc/host.rs b/p2p/src/webrtc/host.rs index 6194b9bc56..ee76d6e054 100644 --- a/p2p/src/webrtc/host.rs +++ b/p2p/src/webrtc/host.rs @@ -1,5 +1,5 @@ use std::{ - net::{IpAddr, Ipv4Addr, Ipv6Addr}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, ToSocketAddrs}, str::FromStr, }; @@ -22,6 +22,21 @@ pub enum Host { Ipv6(Ipv6Addr), } +impl Host { + pub fn resolve(self) -> Option { + if let Self::Domain(domain) = self { + let ip = format!("{domain}:0") + .to_socket_addrs() + .ok() + .and_then(|mut it| it.next()) + .map(|a| a.ip())?; + Some(ip.into()) + } else { + Some(self) + } + } +} + impl<'a> From<&'a Host> for multiaddr::Protocol<'a> { fn from(value: &'a Host) -> Self { match value { diff --git a/p2p/src/webrtc/mod.rs b/p2p/src/webrtc/mod.rs index 3500ddfc63..8a38c6eeb1 100644 --- a/p2p/src/webrtc/mod.rs +++ b/p2p/src/webrtc/mod.rs @@ -2,7 +2,9 @@ mod host; pub use host::Host; mod signal; -pub use signal::{Answer, Offer, Signal}; +pub use signal::{ + Answer, EncryptedAnswer, EncryptedOffer, Offer, P2pConnectionResponse, RejectionReason, Signal, +}; mod signaling_method; pub use signaling_method::{HttpSignalingInfo, SignalingMethod, SignalingMethodParseError}; diff --git a/p2p/src/webrtc/signal.rs b/p2p/src/webrtc/signal.rs index 5cb2e96bdc..47fde57da6 100644 --- a/p2p/src/webrtc/signal.rs +++ b/p2p/src/webrtc/signal.rs @@ -1,7 +1,8 @@ +use binprot_derive::{BinProtRead, BinProtWrite}; use derive_more::From; use serde::{Deserialize, Serialize}; -use crate::identity::{PeerId, PublicKey}; +use crate::identity::{EncryptableType, PeerId, PublicKey}; use super::Host; @@ -33,3 +34,75 @@ pub enum Signal { Offer(Offer), Answer(Answer), } + +#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone, Copy, thiserror::Error)] +pub enum RejectionReason { + #[error("peer_id does not match peer's public key")] + PeerIdAndPublicKeyMismatch, + #[error("target peer_id is not local node's peer_id")] + TargetPeerIdNotMe, + #[error("too many peers")] + PeerCapacityFull, + #[error("peer already connected")] + AlreadyConnected, + #[error("self connection detected")] + ConnectingToSelf, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum P2pConnectionResponse { + Accepted(Box), + Rejected(RejectionReason), + SignalDecryptionFailed, + InternalError, +} + +impl RejectionReason { + pub fn is_bad(&self) -> bool { + match self { + Self::PeerIdAndPublicKeyMismatch => true, + Self::TargetPeerIdNotMe => true, + Self::PeerCapacityFull => false, + Self::AlreadyConnected => true, + Self::ConnectingToSelf => false, + } + } +} + +impl P2pConnectionResponse { + pub fn internal_error_str() -> &'static str { + "InternalError" + } + + pub fn internal_error_json_str() -> &'static str { + "\"InternalError\"" + } +} + +/// Encrypted `webrtc::Offer`. +#[derive(BinProtWrite, BinProtRead, Serialize, Deserialize, From, Debug, Clone)] +pub struct EncryptedOffer(Vec); + +/// Encrypted `P2pConnectionResponse`. +#[derive(BinProtWrite, BinProtRead, Serialize, Deserialize, From, Debug, Clone)] +pub struct EncryptedAnswer(Vec); + +impl AsRef<[u8]> for EncryptedOffer { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8]> for EncryptedAnswer { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl EncryptableType for Offer { + type Encrypted = EncryptedOffer; +} + +impl EncryptableType for P2pConnectionResponse { + type Encrypted = EncryptedAnswer; +} diff --git a/p2p/src/webrtc/signaling_method/mod.rs b/p2p/src/webrtc/signaling_method/mod.rs index 45f9672657..ff09d5a7d8 100644 --- a/p2p/src/webrtc/signaling_method/mod.rs +++ b/p2p/src/webrtc/signaling_method/mod.rs @@ -7,26 +7,43 @@ use binprot_derive::{BinProtRead, BinProtWrite}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use crate::PeerId; + #[derive(BinProtWrite, BinProtRead, Eq, PartialEq, Ord, PartialOrd, Debug, Clone)] pub enum SignalingMethod { Http(HttpSignalingInfo), Https(HttpSignalingInfo), + P2p { relay_peer_id: PeerId }, } impl SignalingMethod { + pub fn can_connect_directly(&self) -> bool { + match self { + Self::Http(_) | Self::Https(_) => true, + Self::P2p { .. } => false, + } + } + /// If method is http or https, it will return url to which an /// offer can be sent. pub fn http_url(&self) -> Option { let (http, info) = match self { Self::Http(info) => ("http", info), Self::Https(info) => ("https", info), - // _ => return None, + _ => return None, }; Some(format!( "{http}://{}:{}/mina/webrtc/signal", info.host, info.port, )) } + + pub fn p2p_relay_peer_id(&self) -> Option { + match self { + Self::P2p { relay_peer_id } => Some(*relay_peer_id), + _ => None, + } + } } impl fmt::Display for SignalingMethod { @@ -40,6 +57,9 @@ impl fmt::Display for SignalingMethod { write!(f, "/https")?; signaling.fmt(f) } + Self::P2p { relay_peer_id } => { + write!(f, "/p2p/{relay_peer_id}") + } } } } diff --git a/p2p/testing/Cargo.toml b/p2p/testing/Cargo.toml index 399ca02e69..2889b171dd 100644 --- a/p2p/testing/Cargo.toml +++ b/p2p/testing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "p2p-testing" -version = "0.10.0" +version = "0.10.3" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/p2p/testing/src/cluster.rs b/p2p/testing/src/cluster.rs index 76544d6a56..8440a26f2e 100644 --- a/p2p/testing/src/cluster.rs +++ b/p2p/testing/src/cluster.rs @@ -15,7 +15,7 @@ use p2p::{ P2pConnectionOutgoingInitOpts, P2pConnectionOutgoingInitOptsParseError, }, identity::SecretKey, - p2p_effects, P2pCallbacks, P2pConfig, P2pMeshsubConfig, P2pState, PeerId, + P2pCallbacks, P2pConfig, P2pMeshsubConfig, P2pState, PeerId, }; use redux::SystemTime; use tokio::sync::mpsc; @@ -367,6 +367,7 @@ impl Cluster { pub fn add_rust_node(&mut self, config: RustNodeConfig) -> Result { let override_fn = config.override_fn; + let reducer_override_fn = config.override_reducer; let node_idx = self.rust_nodes.len(); let (event_sender, event_receiver) = mpsc::unbounded_channel(); let (config, secret_key) = self.rust_node_config(config)?; @@ -381,7 +382,7 @@ impl Cluster { ); let store = crate::redux::Store::new( - |state, action, dispatcher| { + reducer_override_fn.unwrap_or(|state, action, dispatcher| { let meta = action.meta().clone(); let action = action.action(); @@ -391,22 +392,23 @@ impl Cluster { let state_context = Substate::new(state, dispatcher); let result = match action { Action::P2p(action) => { - P2pState::reducer(state_context, meta.with_action(action)) + P2pState::reducer(state_context, meta.with_action(action.clone())) } Action::Idle(_) => P2pState::p2p_timeout_dispatch(state_context, &meta), + Action::P2pEffectful(_) => Ok(()), }; if let Err(error) = result { openmina_core::warn!(time; "error = {error}"); } - }, + }), override_fn.unwrap_or(|store, action| { let (action, meta) = action.split(); match action { Action::P2p(a) => { - p2p_effects(store, meta.with_action(a.clone())); event_mapper_effect(store, a); } + Action::P2pEffectful(a) => a.effects(meta, store), Action::Idle(_) => { // handled by reducer } diff --git a/p2p/testing/src/redux.rs b/p2p/testing/src/redux.rs index f4e9e7d315..674680705c 100644 --- a/p2p/testing/src/redux.rs +++ b/p2p/testing/src/redux.rs @@ -12,9 +12,18 @@ use openmina_core::{ use p2p::{ bootstrap::P2pNetworkKadBootstrapState, channels::{ - best_tip::P2pChannelsBestTipAction, best_tip_effectful::P2pChannelsBestTipEffectfulAction, - rpc::P2pChannelsRpcAction, rpc_effectful::P2pChannelsRpcEffectfulAction, - snark::P2pChannelsSnarkAction, snark_effectful::P2pChannelsSnarkEffectfulAction, + best_tip::P2pChannelsBestTipAction, + best_tip_effectful::P2pChannelsBestTipEffectfulAction, + rpc::P2pChannelsRpcAction, + rpc_effectful::P2pChannelsRpcEffectfulAction, + signaling::{ + discovery::P2pChannelsSignalingDiscoveryAction, + discovery_effectful::P2pChannelsSignalingDiscoveryEffectfulAction, + exchange::P2pChannelsSignalingExchangeAction, + exchange_effectful::P2pChannelsSignalingExchangeEffectfulAction, + }, + snark::P2pChannelsSnarkAction, + snark_effectful::P2pChannelsSnarkEffectfulAction, snark_job_commitment::P2pChannelsSnarkJobCommitmentAction, snark_job_commitment_effectful::P2pChannelsSnarkJobCommitmentEffectfulAction, streaming_rpc::P2pChannelsStreamingRpcAction, @@ -35,9 +44,10 @@ use p2p::{ P2pNetworkIdentifyStreamAction, }, peer::P2pPeerAction, - MioEvent, P2pAction, P2pEvent, P2pNetworkKadBootstrapAction, P2pNetworkKadRequestAction, - P2pNetworkKademliaAction, P2pNetworkKademliaStreamAction, P2pNetworkSchedulerAction, - P2pNetworkSchedulerEffectfulAction, P2pNetworkYamuxAction, P2pState, P2pStateTrait, PeerId, + MioEvent, P2pAction, P2pEffectfulAction, P2pEvent, P2pNetworkKadBootstrapAction, + P2pNetworkKadEffectfulAction, P2pNetworkKadRequestAction, P2pNetworkKademliaAction, + P2pNetworkKademliaStreamAction, P2pNetworkSchedulerAction, P2pNetworkYamuxAction, P2pState, + P2pStateTrait, PeerId, }; use redux::{ActionMeta, EnablingCondition, SubStore}; @@ -50,6 +60,9 @@ impl State { pub fn state(&self) -> &P2pState { &self.0 } + pub fn state_mut(&mut self) -> &mut P2pState { + &mut self.0 + } } impl EnablingCondition for Action { @@ -57,6 +70,7 @@ impl EnablingCondition for Action { match self { Action::P2p(a) => a.is_enabled(&state.0, time), Action::Idle(a) => a.is_enabled(state, time), + Action::P2pEffectful(a) => a.is_enabled(state.state(), time), } } } @@ -126,6 +140,7 @@ impl P2pStateTrait for State {} #[derive(Debug, derive_more::From)] pub enum Action { P2p(P2pAction), + P2pEffectful(P2pEffectfulAction), Idle(IdleAction), } @@ -202,7 +217,7 @@ pub(super) fn event_effect(store: &mut crate::redux::Store, event: P2pEvent) -> ), MioEvent::IncomingConnectionIsReady { listener } => SubStore::dispatch( store, - P2pNetworkSchedulerEffectfulAction::IncomingConnectionIsReady { listener }, + P2pNetworkSchedulerAction::IncomingConnectionIsReady { listener }, ), MioEvent::IncomingConnectionDidAccept(addr, result) => SubStore::dispatch( store, @@ -259,6 +274,13 @@ macro_rules! impl_from_p2p { } } }; + (effectful $sub_action:ty) => { + impl From<$sub_action> for Action { + fn from(value: $sub_action) -> Self { + Self::P2pEffectful(P2pEffectfulAction::from(value)) + } + } + }; } impl_from_p2p!(P2pNetworkKademliaAction); @@ -271,31 +293,37 @@ impl_from_p2p!(P2pConnectionOutgoingAction); impl_from_p2p!(P2pNetworkSchedulerAction); impl_from_p2p!(P2pNetworkIdentifyStreamAction); impl_from_p2p!(P2pIdentifyAction); -impl_from_p2p!(P2pNetworkIdentifyStreamEffectfulAction); impl_from_p2p!(p2p::P2pNetworkSelectAction); impl_from_p2p!(p2p::P2pNetworkPnetAction); impl_from_p2p!(p2p::P2pNetworkNoiseAction); impl_from_p2p!(p2p::connection::incoming::P2pConnectionIncomingAction); impl_from_p2p!(p2p::P2pNetworkPubsubAction); -impl_from_p2p!(p2p::P2pNetworkPubsubEffectfulAction); +impl_from_p2p!(P2pChannelsSignalingDiscoveryAction); +impl_from_p2p!(P2pChannelsSignalingExchangeAction); impl_from_p2p!(P2pChannelsTransactionAction); impl_from_p2p!(P2pChannelsSnarkAction); impl_from_p2p!(p2p::P2pNetworkRpcAction); impl_from_p2p!(P2pChannelsRpcAction); impl_from_p2p!(P2pDisconnectionAction); -impl_from_p2p!(p2p::P2pNetworkSchedulerEffectfulAction); -impl_from_p2p!(p2p::P2pNetworkPnetEffectfulAction); impl_from_p2p!(P2pChannelsBestTipAction); impl_from_p2p!(P2pChannelsSnarkJobCommitmentAction); impl_from_p2p!(P2pChannelsStreamingRpcAction); -impl_from_p2p!(P2pConnectionIncomingEffectfulAction); -impl_from_p2p!(P2pConnectionOutgoingEffectfulAction); -impl_from_p2p!(P2pDisconnectionEffectfulAction); -impl_from_p2p!(P2pChannelsBestTipEffectfulAction); -impl_from_p2p!(P2pChannelsStreamingRpcEffectfulAction); -impl_from_p2p!(P2pChannelsTransactionEffectfulAction); -impl_from_p2p!(P2pChannelsSnarkJobCommitmentEffectfulAction); -impl_from_p2p!(P2pChannelsRpcEffectfulAction); -impl_from_p2p!(P2pChannelsSnarkEffectfulAction); + +impl_from_p2p!(effectful P2pNetworkKadEffectfulAction); +impl_from_p2p!(effectful P2pConnectionIncomingEffectfulAction); +impl_from_p2p!(effectful p2p::P2pNetworkSchedulerEffectfulAction); +impl_from_p2p!(effectful p2p::P2pNetworkPnetEffectfulAction); +impl_from_p2p!(effectful p2p::P2pNetworkPubsubEffectfulAction); +impl_from_p2p!(effectful P2pNetworkIdentifyStreamEffectfulAction); +impl_from_p2p!(effectful P2pConnectionOutgoingEffectfulAction); +impl_from_p2p!(effectful P2pDisconnectionEffectfulAction); +impl_from_p2p!(effectful P2pChannelsSignalingDiscoveryEffectfulAction); +impl_from_p2p!(effectful P2pChannelsSignalingExchangeEffectfulAction); +impl_from_p2p!(effectful P2pChannelsBestTipEffectfulAction); +impl_from_p2p!(effectful P2pChannelsStreamingRpcEffectfulAction); +impl_from_p2p!(effectful P2pChannelsTransactionEffectfulAction); +impl_from_p2p!(effectful P2pChannelsSnarkJobCommitmentEffectfulAction); +impl_from_p2p!(effectful P2pChannelsRpcEffectfulAction); +impl_from_p2p!(effectful P2pChannelsSnarkEffectfulAction); impl p2p::P2pActionTrait for Action {} diff --git a/p2p/testing/src/rust_node.rs b/p2p/testing/src/rust_node.rs index 28992572d8..8067ee6b0c 100644 --- a/p2p/testing/src/rust_node.rs +++ b/p2p/testing/src/rust_node.rs @@ -6,7 +6,7 @@ use std::{ use futures::Stream; use p2p::{P2pAction, P2pEvent, P2pLimits, P2pState, P2pTimeouts, PeerId}; -use redux::{Effects, EnablingCondition, SubStore}; +use redux::{Effects, EnablingCondition, Reducer, SubStore}; use tokio::sync::mpsc; use crate::{ @@ -28,6 +28,7 @@ pub struct RustNodeConfig { pub limits: P2pLimits, pub discovery: bool, pub override_fn: Option>, + pub override_reducer: Option>, } impl RustNodeConfig { @@ -63,6 +64,11 @@ impl RustNodeConfig { self.override_fn = Some(override_fn); self } + + pub fn with_override_reducer(mut self, override_fn: Reducer) -> Self { + self.override_reducer = Some(override_fn); + self + } } pub struct RustNode { diff --git a/p2p/testing/src/service.rs b/p2p/testing/src/service.rs index 1237415723..67044f5f0f 100644 --- a/p2p/testing/src/service.rs +++ b/p2p/testing/src/service.rs @@ -99,6 +99,22 @@ impl P2pServiceWebrtc for ClusterService { ) -> &mut std::collections::BTreeMap { &mut self.peers } + + fn encrypt( + &mut self, + _other_pk: &p2p::identity::PublicKey, + _message: &T, + ) -> Result { + unreachable!("this is webrtc only and this crate tests libp2p only") + } + + fn decrypt( + &mut self, + _other_pub_key: &p2p::identity::PublicKey, + _encrypted: &T::Encrypted, + ) -> Result { + unreachable!("this is webrtc only and this crate tests libp2p only") + } } impl P2pCryptoService for ClusterService { diff --git a/p2p/tests/identify.rs b/p2p/tests/identify.rs index 381cbdb134..2753caece4 100644 --- a/p2p/tests/identify.rs +++ b/p2p/tests/identify.rs @@ -7,16 +7,15 @@ use multiaddr::{multiaddr, Multiaddr}; use p2p::{ identity::SecretKey, network::identify::{ - stream::P2pNetworkIdentifyStreamState, P2pNetworkIdentify, P2pNetworkIdentifyAction, - P2pNetworkIdentifyStreamAction, + stream_effectful::P2pNetworkIdentifyStreamEffectfulAction, P2pNetworkIdentify, + P2pNetworkIdentifyEffectfulAction, P2pNetworkIdentifyStreamAction, }, - p2p_effects, token::{self, DiscoveryAlgorithm}, - Data, P2pAction, P2pNetworkAction, P2pNetworkYamuxAction, PeerId, + Data, P2pEffectfulAction, P2pNetworkEffectfulAction, P2pNetworkYamuxAction, PeerId, }; use p2p_testing::{ cluster::{Cluster, ClusterBuilder, ClusterEvent, Listener}, - event::{event_mapper_effect, RustNodeEvent}, + event::{allow_disconnections, event_mapper_effect, RustNodeEvent}, futures::TryStreamExt, predicates::{async_fn, listener_is_ready, peer_is_connected}, redux::{Action, State}, @@ -104,12 +103,12 @@ async fn rust_node_to_rust_node() -> anyhow::Result<()> { } #[tokio::test] -#[ignore = "TODO: Add override for reducer"] /// Test that even if bad node spams many different listen_addrs we don't end up with duplicates async fn test_bad_node() -> anyhow::Result<()> { let mut cluster = ClusterBuilder::new() .ports_with_len(100) .idle_duration(Duration::from_millis(100)) + .is_error(allow_disconnections) .start() .await?; @@ -155,6 +154,9 @@ async fn test_bad_node() -> anyhow::Result<()> { let expected_addrs = [ multiaddr!(Ip4([127, 0, 0, 1]), Tcp(bad_node_port)), + multiaddr!(Unix("domain.com")), + multiaddr!(Dns("domain.com"), Tcp(10530u16)), + multiaddr!(Https), multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)), multiaddr!(Ip6([0; 16]), Tcp(10500u16)), multiaddr!(Ip6([1; 16]), Tcp(10500u16)), @@ -175,101 +177,78 @@ fn bad_node_effects( let (action, meta) = action.split(); match action { Action::P2p(a) => { - match a.clone() { - P2pAction::Network(P2pNetworkAction::Identify( - P2pNetworkIdentifyAction::Stream(P2pNetworkIdentifyStreamAction::New { - addr, - peer_id, - stream_id, - .. - }), - )) => { - let state = store - .state() - .state() - .network - .scheduler - .identify_state - .find_identify_stream_state(&peer_id, &stream_id) - .expect("Unable to find identify stream"); - - if let P2pNetworkIdentifyStreamState::SendIdentify = state { - let listen_addrs = vec![ - multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)), - multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)), - multiaddr!(Ip6([0; 16]), Tcp(10500u16)), - multiaddr!(Ip6([0; 16]), Tcp(10500u16)), - multiaddr!(Ip6([1; 16]), Tcp(10500u16)), - multiaddr!(Ip6([1; 16]), Tcp(10500u16)), - multiaddr!(Dns("domain.com"), Tcp(10530u16)), - multiaddr!(Dns("domain.com"), Tcp(10530u16)), - multiaddr!(Dns("domain.com"), Tcp(10530u16)), - multiaddr!(Dns("domain.com"), Tcp(10530u16)), - multiaddr!(Unix("domain.com")), - multiaddr!(Https), - ]; - - let public_key = Some(SecretKey::rand().public_key()); - - let protocols = vec![ - token::StreamKind::Identify( - token::IdentifyAlgorithm::Identify1_0_0, - ), - token::StreamKind::Broadcast( - p2p::token::BroadcastAlgorithm::Meshsub1_1_0, - ), - p2p::token::StreamKind::Rpc(token::RpcAlgorithm::Rpc0_0_1), - p2p::token::StreamKind::Discovery( - DiscoveryAlgorithm::Kademlia1_0_0, - ), - ]; - - let identify_msg = P2pNetworkIdentify { - protocol_version: Some("ipfs/0.1.0".to_string()), - agent_version: Some("openmina".to_owned()), - public_key, - listen_addrs, - observed_addr: None, - protocols, - }; - - let mut out = Vec::new(); - let identify_msg_proto = - identify_msg.to_proto_message().expect("serialized message"); - - prost::Message::encode_length_delimited(&identify_msg_proto, &mut out) - .expect("Error converting message"); - - store.dispatch(Action::P2p( - P2pNetworkYamuxAction::OutgoingData { - addr, - stream_id, - data: Data(out.into_boxed_slice()), - flags: Default::default(), - } - .into(), - )); - - store.dispatch(Action::P2p( - P2pNetworkIdentifyStreamAction::Close { - addr, - peer_id, - stream_id, - } - .into(), - )); - } - } - - a => { - p2p_effects(store, meta.with_action(a.clone())); - } - } event_mapper_effect(store, a); } - Action::Idle(_) => { - // p2p_timeout_effects(store, &meta); + Action::P2pEffectful(P2pEffectfulAction::Network( + P2pNetworkEffectfulAction::Identify(P2pNetworkIdentifyEffectfulAction::Stream( + P2pNetworkIdentifyStreamEffectfulAction::SendIdentify { + addr, + peer_id, + stream_id, + }, + )), + )) => { + let listen_addrs = vec![ + multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)), + multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16)), + multiaddr!(Ip6([0; 16]), Tcp(10500u16)), + multiaddr!(Ip6([0; 16]), Tcp(10500u16)), + multiaddr!(Ip6([1; 16]), Tcp(10500u16)), + multiaddr!(Ip6([1; 16]), Tcp(10500u16)), + multiaddr!(Dns("domain.com"), Tcp(10530u16)), + multiaddr!(Dns("domain.com"), Tcp(10530u16)), + multiaddr!(Dns("domain.com"), Tcp(10530u16)), + multiaddr!(Dns("domain.com"), Tcp(10530u16)), + multiaddr!(Unix("domain.com")), + multiaddr!(Https), + ]; + + let public_key = Some(SecretKey::rand().public_key()); + + let protocols = vec![ + token::StreamKind::Identify(token::IdentifyAlgorithm::Identify1_0_0), + token::StreamKind::Broadcast(p2p::token::BroadcastAlgorithm::Meshsub1_1_0), + p2p::token::StreamKind::Rpc(token::RpcAlgorithm::Rpc0_0_1), + p2p::token::StreamKind::Discovery(DiscoveryAlgorithm::Kademlia1_0_0), + ]; + + let identify_msg = P2pNetworkIdentify { + protocol_version: Some("ipfs/0.1.0".to_string()), + agent_version: Some("openmina".to_owned()), + public_key, + listen_addrs, + observed_addr: None, + protocols, + }; + + let mut out = Vec::new(); + let identify_msg_proto = + identify_msg.to_proto_message().expect("serialized message"); + + prost::Message::encode_length_delimited(&identify_msg_proto, &mut out) + .expect("Error converting message"); + + store.dispatch(Action::P2p( + P2pNetworkYamuxAction::OutgoingData { + addr, + stream_id, + data: Data(out.into_boxed_slice()), + flags: Default::default(), + } + .into(), + )); + + store.dispatch(Action::P2p( + P2pNetworkIdentifyStreamAction::Close { + addr, + peer_id, + stream_id, + } + .into(), + )); } + Action::P2pEffectful(action) => action.effects(meta, store), + _ => {} }; } } diff --git a/p2p/tests/kademlia.rs b/p2p/tests/kademlia.rs index c126ff705e..3b93112ea2 100644 --- a/p2p/tests/kademlia.rs +++ b/p2p/tests/kademlia.rs @@ -1,16 +1,16 @@ +use openmina_core::Substate; use p2p::{ - identity::SecretKey, p2p_effects, P2pAction, P2pNetworkAction, P2pNetworkKadAction, - P2pNetworkKadBucket, P2pNetworkKademliaAction, P2pNetworkKademliaRpcReply, - P2pNetworkKademliaStreamAction, PeerId, + identity::SecretKey, P2pAction, P2pNetworkAction, P2pNetworkKadAction, P2pNetworkKadBucket, + P2pNetworkKademliaAction, P2pNetworkKademliaRpcReply, P2pNetworkKademliaStreamAction, P2pState, + PeerId, }; use p2p_testing::{ cluster::{Cluster, ClusterBuilder, ClusterEvent, Listener}, - event::{allow_disconnections, event_mapper_effect, RustNodeEvent}, + event::{allow_disconnections, RustNodeEvent}, futures::{StreamExt, TryStreamExt}, predicates::kad_finished_bootstrap, redux::{Action, State}, rust_node::{RustNodeConfig, RustNodeId}, - service::ClusterService, stream::ClusterStreamExt, test_node::TestNode, utils::{ @@ -18,7 +18,7 @@ use p2p_testing::{ try_wait_for_nodes_to_listen, }, }; -use redux::{ActionWithMeta, Store}; +use redux::{ActionWithMeta, Dispatcher}; use std::{future::ready, net::Ipv4Addr, time::Duration}; #[tokio::test] @@ -312,7 +312,6 @@ async fn discovery_seed_multiple_peers() -> anyhow::Result<()> { } #[tokio::test] -#[ignore = "TODO: Add override for reducer"] async fn test_bad_node() -> anyhow::Result<()> { std::env::set_var("OPENMINA_DISCOVERY_FILTER_ADDR", "false"); @@ -325,7 +324,7 @@ async fn test_bad_node() -> anyhow::Result<()> { let bad_node = cluster.add_rust_node( RustNodeConfig::default() .with_discovery(true) - .with_override(bad_node_effects), + .with_override_reducer(bad_node_reducer), )?; let node = cluster.add_rust_node( @@ -353,57 +352,56 @@ async fn test_bad_node() -> anyhow::Result<()> { Ok(()) } -fn bad_node_effects( - store: &mut Store, - action: ActionWithMeta, +fn bad_node_reducer( + state: &mut State, + action: &ActionWithMeta, + dispatcher: &mut Dispatcher, ) { { - let (action, meta) = action.split(); + let meta = action.meta().clone(); + let action = action.action(); + let time = meta.time(); match action { - Action::P2p(a) => { - match a.clone() { - P2pAction::Network(P2pNetworkAction::Kad(P2pNetworkKadAction::System( - P2pNetworkKademliaAction::AnswerFindNodeRequest { - addr, - peer_id, - stream_id, - .. - }, - ))) => { - let closer_peers = store - .state - .get() - .state() - .network - .scheduler - .discovery_state() - .expect("Error getting discovery state") - .routing_table - .buckets - .iter() - .flat_map(|bucket| bucket.clone().into_iter()) - .collect(); - - let message = P2pNetworkKademliaRpcReply::FindNode { closer_peers }; - store.dispatch(Action::P2p( - P2pNetworkKademliaStreamAction::SendResponse { - addr, - peer_id, - stream_id, - data: message, - } - .into(), - )); + Action::P2p(P2pAction::Network(P2pNetworkAction::Kad( + P2pNetworkKadAction::System(P2pNetworkKademliaAction::AnswerFindNodeRequest { + addr, + peer_id, + stream_id, + .. + }), + ))) => { + let closer_peers = state + .state() + .network + .scheduler + .discovery_state() + .expect("Error getting discovery state") + .routing_table + .buckets + .iter() + .flat_map(|bucket| bucket.clone().into_iter()) + .collect(); + + let message = P2pNetworkKademliaRpcReply::FindNode { closer_peers }; + dispatcher.push(Action::P2p( + P2pNetworkKademliaStreamAction::SendResponse { + addr: *addr, + peer_id: *peer_id, + stream_id: *stream_id, + data: message, } - a => { - p2p_effects(store, meta.with_action(a.clone())); - } - } - event_mapper_effect(store, a); + .into(), + )); } - Action::Idle(_) => { - // p2p_timeout_effects(store, &meta); + Action::P2p(action) => { + if let Err(error) = P2pState::reducer( + Substate::new(state, dispatcher), + meta.with_action(action.clone()), + ) { + openmina_core::warn!(time; "error = {error}"); + } } + Action::Idle(_) | Action::P2pEffectful(_) => {} }; } } diff --git a/producer-dashboard/Cargo.toml b/producer-dashboard/Cargo.toml index 7ceaf952e2..5ebd51a772 100644 --- a/producer-dashboard/Cargo.toml +++ b/producer-dashboard/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-producer-dashboard" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/snark/Cargo.toml b/snark/Cargo.toml index 800ef7fda9..eb930bede0 100644 --- a/snark/Cargo.toml +++ b/snark/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "snark" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0" diff --git a/tools/bootstrap-sandbox/Cargo.toml b/tools/bootstrap-sandbox/Cargo.toml index 16b08f56d0..922779d113 100644 --- a/tools/bootstrap-sandbox/Cargo.toml +++ b/tools/bootstrap-sandbox/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-bootstrap-sandbox" -version = "0.10.0" +version = "0.10.3" edition = "2021" [dependencies] diff --git a/tools/gossipsub-sandbox/Cargo.toml b/tools/gossipsub-sandbox/Cargo.toml index 9503fbcc66..99c29f9037 100644 --- a/tools/gossipsub-sandbox/Cargo.toml +++ b/tools/gossipsub-sandbox/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmina-gossipsub-sandbox" -version = "0.10.0" +version = "0.10.3" edition = "2021" [dependencies] diff --git a/tools/hash-tool/Cargo.toml b/tools/hash-tool/Cargo.toml index ab59c84d84..5fdf78b37e 100644 --- a/tools/hash-tool/Cargo.toml +++ b/tools/hash-tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hash-tool" -version = "0.10.0" +version = "0.10.3" edition = "2021" [dependencies] diff --git a/tools/ledger-tool/Cargo.toml b/tools/ledger-tool/Cargo.toml index 86f2b897e7..6b8fb37fb8 100644 --- a/tools/ledger-tool/Cargo.toml +++ b/tools/ledger-tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ledger-tool" -version = "0.10.0" +version = "0.10.3" edition = "2021" [dependencies] diff --git a/tools/salsa-simple/Cargo.toml b/tools/salsa-simple/Cargo.toml index e5bbb93d05..88168e5e4d 100644 --- a/tools/salsa-simple/Cargo.toml +++ b/tools/salsa-simple/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "salsa-simple" -version = "0.10.0" +version = "0.10.3" edition = "2021" [dev-dependencies] diff --git a/tools/transport/Cargo.toml b/tools/transport/Cargo.toml index 764def46a2..56e61929cf 100644 --- a/tools/transport/Cargo.toml +++ b/tools/transport/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mina-transport" -version = "0.10.0" +version = "0.10.3" edition = "2021" [dependencies] diff --git a/vrf/Cargo.toml b/vrf/Cargo.toml index 6912c72bd9..38421f3359 100644 --- a/vrf/Cargo.toml +++ b/vrf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vrf" -version = "0.10.0" +version = "0.10.3" edition = "2021" license = "Apache-2.0"