diff --git a/.github/mergify.yml b/.github/mergify.yml index 59424a29dd49..c630d23cea0c 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -4,17 +4,12 @@ defaults: method: squash commit_message_template: | {{ title }} - + {{ body | get_section("## Description", "") }} - + Pull-Request: #{{ number }}. - {# Here comes some fancy Jinja2 stuff for correctly attributing co-authorship: #} - {% for commit in (commits | unique(False, 'email_author')) | rejectattr("author", "==", author) %} - {% if commit.parents|length == 1 %} - Co-Authored-By: {{ commit.author }} <{{ commit.email_author }}> - {% endif %} - {% endfor %} - {# GitHub requires that the `Co-authored-by` lines are AT THE VERY END of a commit, hence nothing must come after this. #} + + {{ body | get_section("## Attributions", "") }} pull_request_rules: - name: Ask to resolve conflict @@ -38,6 +33,14 @@ pull_request_rules: actions: queue: + - name: Add approved dependabot PRs to merge queue + conditions: + # All branch protection rules are implicit: https://docs.mergify.com/conditions/#about-branch-protection + - author=dependabot[bot] + - base=master + actions: + queue: + - name: Remove reviews on updates after PR is queued for merging conditions: - base=master @@ -57,6 +60,15 @@ pull_request_rules: actions: review: + - name: Approve dependabot PRs of semver-compatible updates + conditions: + - author=dependabot[bot] + - or: + - title~=bump [^\s]+ from ([1-9]+)\..+ to \1\. # For major >= 1 versions, only approve updates with the same major version. + - title~=bump [^\s]+ from 0\.([\d]+)\..+ to 0\.\1\. # For major == 0 versions, only approve updates with the same minor version. + actions: + review: + queue_rules: - name: default conditions: [] diff --git a/.github/workflows/cache-factory.yml b/.github/workflows/cache-factory.yml index 64241d15af3a..56c42416005d 100644 --- a/.github/workflows/cache-factory.yml +++ b/.github/workflows/cache-factory.yml @@ -22,7 +22,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: shared-key: stable-cache diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd3737a49f41..d148c9e1474e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: shared-key: stable-cache save-if: false @@ -76,12 +76,41 @@ jobs: run: | PACKAGE_VERSION=$(cargo metadata --format-version=1 --no-deps | jq -e -r '.packages[] | select(.name == "'"$CRATE"'") | .version') SPECIFIED_VERSION=$(tomlq "workspace.dependencies.$CRATE.version" --file ./Cargo.toml) - + echo "Package version: $PACKAGE_VERSION"; echo "Specified version: $SPECIFIED_VERSION"; test "$PACKAGE_VERSION" = "$SPECIFIED_VERSION" + wasm_tests: + name: Run all WASM tests + runs-on: ubuntu-latest + env: + CHROMEDRIVER_VERSION: '114.0.5735.90' + steps: + - uses: actions/checkout@v3 + + - uses: dtolnay/rust-toolchain@stable + with: + target: wasm32-unknown-unknown + + - uses: taiki-e/cache-cargo-install-action@v1 + with: + tool: wasm-pack@0.12.0 + + - name: Install Google Chrome + run: | + curl -o /tmp/google-chrome-stable_amd64.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROMEDRIVER_VERSION}-1_amd64.deb + sudo dpkg -i /tmp/google-chrome-stable_amd64.deb + + - name: Install chromedriver + uses: nanasess/setup-chromedriver@v2 + with: + chromedriver-version: ${{ env.CHROMEDRIVER_VERSION }} + + - name: Run all tests + run: ./wasm-tests/run-all.sh + cross: name: Compile on ${{ matrix.target }} strategy: @@ -107,7 +136,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@d58b70c4a13c4866d96436315da451d8106f8f08 #v1.3.0 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: key: ${{ matrix.target }} save-if: ${{ github.ref == 'refs/heads/master' }} @@ -132,7 +161,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@d58b70c4a13c4866d96436315da451d8106f8f08 #v1.3.0 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: save-if: ${{ github.ref == 'refs/heads/master' }} @@ -153,7 +182,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@d58b70c4a13c4866d96436315da451d8106f8f08 #v1.3.0 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: key: ${{ matrix.features }} save-if: ${{ github.ref == 'refs/heads/master' }} @@ -170,7 +199,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@d58b70c4a13c4866d96436315da451d8106f8f08 #v1.3.0 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: save-if: ${{ github.ref == 'refs/heads/master' }} @@ -196,7 +225,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@d58b70c4a13c4866d96436315da451d8106f8f08 #v1.3.0 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: save-if: ${{ github.ref == 'refs/heads/master' }} @@ -213,7 +242,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@d58b70c4a13c4866d96436315da451d8106f8f08 #v1.3.0 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: save-if: ${{ github.ref == 'refs/heads/master' }} @@ -229,7 +258,7 @@ jobs: - uses: r7kamura/rust-problem-matchers@d58b70c4a13c4866d96436315da451d8106f8f08 #v1.3.0 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 with: shared-key: stable-cache save-if: false @@ -284,6 +313,8 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + - id: cargo-metadata run: | WORKSPACE_MEMBERS=$(cargo metadata --format-version=1 --no-deps | jq -c '.packages | map(select(.publish == null) | .name)') @@ -295,7 +326,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 - run: cargo install --version 0.10.0 pb-rs --locked @@ -321,5 +352,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 # v2.4.0 + - uses: Swatinem/rust-cache@dd05243424bd5c0e585e4b55eb2d7615cdd32f1f # v2.5.1 - run: cargo metadata --locked --format-version=1 > /dev/null diff --git a/.github/workflows/interop-test.yml b/.github/workflows/interop-test.yml index e6527709aa30..4d63a76c8115 100644 --- a/.github/workflows/interop-test.yml +++ b/.github/workflows/interop-test.yml @@ -12,16 +12,21 @@ concurrency: jobs: run-multidim-interop: name: Run multidimensional interoperability tests - runs-on: ${{ fromJSON(github.repository == 'libp2p/rust-libp2p' && '["self-hosted", "linux", "x64", "xlarge"]' || '"ubuntu-latest"') }} + runs-on: ${{ fromJSON(github.repository == 'libp2p/rust-libp2p' && '["self-hosted", "linux", "x64", "4xlarge"]' || '"ubuntu-latest"') }} + strategy: + matrix: + flavour: [chromium, native] steps: - uses: actions/checkout@v3 - uses: docker/setup-buildx-action@v2 - - name: Build image - run: docker buildx build --load -t rust-libp2p-head . -f interop-tests/Dockerfile - - uses: libp2p/test-plans/.github/actions/run-interop-ping-test@master + - name: Build ${{ matrix.flavour }} image + run: docker buildx build --load -t ${{ matrix.flavour }}-rust-libp2p-head . -f interop-tests/Dockerfile.${{ matrix.flavour }} + - name: Run ${{ matrix.flavour }} tests + uses: libp2p/test-plans/.github/actions/run-interop-ping-test@master with: - test-filter: rust-libp2p-head - extra-versions: ${{ github.workspace }}/interop-tests/ping-version.json + test-filter: ${{ matrix.flavour }}-rust-libp2p-head + extra-versions: ${{ github.workspace }}/interop-tests/${{ matrix.flavour }}-ping-version.json s3-cache-bucket: libp2p-by-tf-aws-bootstrap s3-access-key-id: ${{ vars.TEST_PLANS_BUILD_CACHE_KEY_ID }} s3-secret-access-key: ${{ secrets.TEST_PLANS_BUILD_CACHE_KEY }} + worker-count: 16 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000000..f69e44476af2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,40 @@ +# Contributing Guidelines + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-libp2p-blue.svg?style=flat-square)](https://libp2p.io/) + +Welcome to the rust-libp2p contribution guide! We appreciate your interest in improving our library. + +## Looking for ways to contribute? + +There are several ways you can contribute to rust-libp2p: +- Start contributing immediately via the opened [help wanted](https://github.com/libp2p/rust-libp2p/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [difficulty:easy](https://github.com/libp2p/rust-libp2p/issues?q=is%3Aissue+is%3Aopen+label%3Adifficulty%3Aeasy) issues on GitHub. + These issues are suitable for newcomers and provide an excellent starting point. +- Reporting issues, bugs, mistakes, or inconsistencies. + As many open source projects, we are short-staffed, we thus kindly ask you to be open to contribute a fix for discovered issues. + +### We squash-merge pull Requests + +We always squash merge submitted pull requests. +This means that we discourage force pushes, in order to make the diff between pushes easier for us to review. +Squash merging allows us to maintain a clean and organized commit history. + +The PR title, which will become the commit message after the squashing process, should follow [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/). + +### Write changelog entries for user-facing changes + +When making user-facing changes, it is important to include corresponding entries in the changelog, providing a comprehensive summary for the users. +For detailed instructions on how to write changelog entries, please refer to the documentation in [`docs/release.md`](https://github.com/libp2p/rust-libp2p/blob/master/docs/release.md). + + +### Merging of PRs is automated + +To streamline our workflow, we utilize Mergify and the "send-it" label. +Mergify automates merging actions and helps us manage pull requests more efficiently. +The "send-it" label indicates that a pull request is ready to be merged. +Please refrain from making further commits after the "send-it" label has been applied otherwise your PR will be dequeued from merging automatically. + +### Treat CI as a self-service platform + +We have a lot of automated CI checks for common errors. +Please treat our CI as a self-service platform and try to fix any issues before requesting a review. diff --git a/Cargo.lock b/Cargo.lock index 994c4da29cf9..53d5491b4ebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -181,7 +190,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -191,14 +200,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "arbitrary" @@ -415,7 +424,7 @@ dependencies = [ "futures-lite", "rustix", "signal-hook", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -469,13 +478,13 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] [[package]] @@ -514,6 +523,70 @@ dependencies = [ "libp2p", ] +[[package]] +name = "axum" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a1de45611fdb535bfde7b7de4fd54f4fd2b17b1737c0a59b69bf9b92074b8c" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite 0.2.9", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base-x" version = "0.2.11" @@ -580,6 +653,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "blake2" version = "0.10.6" @@ -797,9 +876,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.3" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" dependencies = [ "clap_builder", "clap_derive", @@ -808,13 +887,12 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.3" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] @@ -828,7 +906,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] [[package]] @@ -866,12 +944,33 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen", +] + [[package]] name = "const-oid" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -1300,7 +1399,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] [[package]] @@ -1417,6 +1516,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "enum-as-inner" version = "0.5.1" @@ -1452,6 +1560,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "errno" version = "0.3.1" @@ -1460,7 +1574,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1479,6 +1593,28 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "fantoccini" +version = "0.20.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5eb32b0001134a1d3b9e16010eb4b119451edf68446963a30a8130a0d056e98" +dependencies = [ + "base64 0.13.1", + "cookie", + "futures-core", + "futures-util", + "http", + "hyper", + "hyper-rustls", + "mime", + "serde", + "serde_json", + "time", + "tokio", + "url", + "webdriver", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -1544,6 +1680,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -1625,7 +1776,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] [[package]] @@ -1767,6 +1918,12 @@ dependencies = [ "polyval 0.6.0", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "glob" version = "0.3.1" @@ -1819,7 +1976,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -1847,6 +2004,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -1958,6 +2121,12 @@ dependencies = [ "pin-project-lite 0.2.9", ] +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + [[package]] name = "httparse" version = "1.8.0" @@ -1978,14 +2147,15 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", @@ -1999,6 +2169,34 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls 0.20.8", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2076,6 +2274,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "inout" version = "0.1.3" @@ -2121,17 +2329,33 @@ name = "interop-tests" version = "0.1.0" dependencies = [ "anyhow", + "axum", + "console_error_panic_hook", "either", "env_logger 0.10.0", "futures", + "futures-timer", + "instant", "libp2p", "libp2p-mplex", "libp2p-quic", "libp2p-webrtc", "log", + "mime_guess", "rand 0.8.5", "redis", + "reqwest", + "rust-embed", + "serde", + "serde_json", + "thirtyfour", "tokio", + "tower-http", + "tracing", + "tracing-subscriber", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-logger", ] [[package]] @@ -2142,7 +2366,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2153,8 +2377,8 @@ checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ "socket2 0.5.3", "widestring", - "windows-sys", - "winreg", + "windows-sys 0.48.0", + "winreg 0.50.0", ] [[package]] @@ -2195,7 +2419,7 @@ dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2261,9 +2485,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libm" @@ -2273,7 +2497,7 @@ checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" [[package]] name = "libp2p" -version = "0.52.0" +version = "0.52.1" dependencies = [ "async-std", "async-trait", @@ -2312,6 +2536,7 @@ dependencies = [ "libp2p-uds", "libp2p-wasm-ext", "libp2p-websocket", + "libp2p-webtransport-websys", "libp2p-yamux", "multiaddr", "pin-project", @@ -2544,7 +2769,7 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.0" +version = "0.2.1" dependencies = [ "asn1_der", "base64 0.21.2", @@ -2572,7 +2797,7 @@ dependencies = [ [[package]] name = "libp2p-kad" -version = "0.44.0" +version = "0.44.2" dependencies = [ "arrayvec", "async-std", @@ -2909,7 +3134,7 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.43.0" +version = "0.43.1" dependencies = [ "async-std", "either", @@ -2948,7 +3173,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] [[package]] @@ -3034,7 +3259,7 @@ dependencies = [ [[package]] name = "libp2p-webrtc" -version = "0.5.0-alpha" +version = "0.6.0-alpha" dependencies = [ "anyhow", "async-trait", @@ -3089,7 +3314,27 @@ dependencies = [ "rw-stream-sink", "soketto", "url", - "webpki-roots 0.23.1", + "webpki-roots 0.24.0", +] + +[[package]] +name = "libp2p-webtransport-websys" +version = "0.1.0" +dependencies = [ + "futures", + "js-sys", + "libp2p-core", + "libp2p-identity", + "libp2p-noise", + "log", + "multiaddr", + "multibase", + "multihash", + "send_wrapper 0.6.0", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] @@ -3186,9 +3431,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" dependencies = [ "hashbrown 0.13.2", ] @@ -3208,12 +3453,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matches" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + [[package]] name = "md-5" version = "0.10.5" @@ -3260,6 +3520,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3283,7 +3559,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -3348,6 +3624,24 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "netlink-packet-core" version = "0.4.2" @@ -3367,7 +3661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" dependencies = [ "anyhow", - "bitflags", + "bitflags 1.3.2", "byteorder", "libc", "netlink-packet-core", @@ -3421,7 +3715,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "libc", "memoffset 0.6.5", @@ -3443,6 +3737,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -3483,6 +3787,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "oid-registry" version = "0.4.0" @@ -3519,6 +3832,56 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "p256" version = "0.11.1" @@ -3633,22 +3996,22 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] [[package]] @@ -3699,6 +4062,12 @@ dependencies = [ "spki 0.7.2", ] +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "platforms" version = "3.0.2" @@ -3740,13 +4109,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "concurrent-queue", "libc", "log", "pin-project-lite 0.2.9", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -3800,30 +4169,54 @@ dependencies = [ ] [[package]] -name = "proc-macro-warning" -version = "0.4.1" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70550716265d1ec349c41f70dd4f964b4fd88394efe4405f0c1da679c4799a07" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ + "proc-macro-error-attr", "proc-macro2", "quote", - "syn 2.0.18", + "syn 1.0.109", + "version_check", ] [[package]] -name = "proc-macro2" -version = "1.0.60" +name = "proc-macro-error-attr" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-warning" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70550716265d1ec349c41f70dd4f964b4fd88394efe4405f0c1da679c4799a07" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "prometheus-client" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c2f43e8969d51935d2a7284878ae053ba30034cd563f673cde37ba5205685e" +checksum = "3c99afa9a01501019ac3a14d71d9f94050346f55ca471ce90c799a15c58f61e2" dependencies = [ "dtoa", "itoa", @@ -3917,9 +4310,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.28" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" dependencies = [ "proc-macro2", ] @@ -4067,25 +4460,52 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.4" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.2", + "regex-syntax 0.7.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.3", ] [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" [[package]] name = "relay-server-example" @@ -4113,6 +4533,43 @@ dependencies = [ "tokio", ] +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite 0.2.9", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.10.1", +] + [[package]] name = "resolv-conf" version = "0.7.0" @@ -4230,6 +4687,46 @@ dependencies = [ "webrtc-util", ] +[[package]] +name = "rust-embed" +version = "6.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.26", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" +dependencies = [ + "sha2 0.10.7", + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -4260,12 +4757,12 @@ version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4301,10 +4798,22 @@ checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.100.1", "sct 0.7.0", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.2" @@ -4324,6 +4833,22 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.101.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + [[package]] name = "rw-stream-sink" version = "0.4.0" @@ -4358,6 +4883,21 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.1.0" @@ -4424,6 +4964,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.17" @@ -4441,12 +5004,15 @@ name = "send_wrapper" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] [[package]] name = "serde" -version = "1.0.164" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" dependencies = [ "serde_derive", ] @@ -4463,21 +5029,54 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] [[package]] name = "serde_json" -version = "1.0.97" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" dependencies = [ + "indexmap 2.0.0", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f05c1d5476066defcdfacce1f52fc3cae3af1d3089727100c02ae92e5abbe0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.26", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", "itoa", "ryu", "serde", @@ -4541,6 +5140,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook" version = "0.3.15" @@ -4591,9 +5199,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "smol" @@ -4655,7 +5263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4705,6 +5313,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringmatch" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aadc0801d92f0cdc26127c67c4b8766284f52a5ba22894f285e3101fa57d05d" +dependencies = [ + "regex", +] + [[package]] name = "strsim" version = "0.10.0" @@ -4758,15 +5375,21 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.12.6" @@ -4785,7 +5408,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] @@ -4811,7 +5434,7 @@ dependencies = [ "fastrand", "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4823,24 +5446,72 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thirtyfour" +version = "0.32.0-rc.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0fe180d5f1f7dd32bb5f1a8d19231bb63dc9bbb1985e1dbb6f07163b6a8578" +dependencies = [ + "async-trait", + "base64 0.21.2", + "cookie", + "fantoccini", + "futures", + "http", + "indexmap 1.9.3", + "log", + "parking_lot", + "paste", + "serde", + "serde_json", + "serde_repr", + "stringmatch", + "thirtyfour-macros", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "thirtyfour-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cae91d1c7c61ec65817f1064954640ee350a50ae6548ff9a1bdd2489d6ffbb0" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", ] [[package]] @@ -4897,11 +5568,12 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.2" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -4911,7 +5583,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.4.9", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -4922,7 +5594,17 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", ] [[package]] @@ -4951,6 +5633,53 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite 0.2.9", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8bd22a874a2d0b70452d5597b12c537331d49060824a95f49f108994f94aa4c" +dependencies = [ + "bitflags 2.3.3", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite 0.2.9", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -4964,6 +5693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", + "log", "pin-project-lite 0.2.9", "tracing-attributes", "tracing-core", @@ -4977,7 +5707,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] [[package]] @@ -4987,6 +5717,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -5104,6 +5864,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -5125,6 +5894,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -5193,12 +5968,24 @@ dependencies = [ "getrandom 0.2.10", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "value-bag" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4d330786735ea358f3bc09eea4caa098569c1c93f342d9aca0514915022fe7e" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -5279,7 +6066,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", "wasm-bindgen-shared", ] @@ -5313,7 +6100,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5324,16 +6111,70 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "wasm-logger" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074649a66bb306c8f2068c9016395fa65d8e08d2affcbf95acf3c24c3ab19718" +dependencies = [ + "log", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "web-sys" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webdriver" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9973cb72c8587d5ad5efdb91e663d36177dc37725e6c90ca86c626b0cc45c93f" +dependencies = [ + "base64 0.13.1", + "bytes", + "cookie", + "http", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "unicode-segmentation", + "url", +] + [[package]] name = "webpki" version = "0.21.4" @@ -5365,11 +6206,11 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.23.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" dependencies = [ - "rustls-webpki", + "rustls-webpki 0.101.1", ] [[package]] @@ -5570,7 +6411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" dependencies = [ "async-trait", - "bitflags", + "bitflags 1.3.2", "bytes", "cc", "ipnet", @@ -5584,6 +6425,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "webtransport-tests" +version = "0.1.0" +dependencies = [ + "futures", + "getrandom 0.2.10", + "libp2p-core", + "libp2p-identity", + "libp2p-noise", + "libp2p-webtransport-websys", + "multiaddr", + "multihash", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", +] + [[package]] name = "widestring" version = "1.0.2" @@ -5634,6 +6493,21 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -5649,15 +6523,21 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", "windows_i686_gnu 0.48.0", "windows_i686_msvc 0.48.0", "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.48.0", "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" @@ -5670,6 +6550,12 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" @@ -5682,6 +6568,12 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.0" @@ -5694,6 +6586,12 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.0" @@ -5706,12 +6604,24 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" @@ -5724,12 +6634,27 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "winreg" version = "0.50.0" @@ -5737,7 +6662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if 1.0.0", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -5856,5 +6781,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.26", ] diff --git a/Cargo.toml b/Cargo.toml index 3d247cfa0464..eff66e42986b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [workspace] members = [ "core", - "examples/chat-example", "examples/autonat", + "examples/chat-example", "examples/dcutr", "examples/distributed-key-value-store", "examples/file-sharing", @@ -53,6 +53,8 @@ members = [ "transports/wasm-ext", "transports/webrtc", "transports/websocket", + "transports/webtransport-websys", + "wasm-tests/webtransport-tests", ] resolver = "2" @@ -70,8 +72,8 @@ libp2p-dns = { version = "0.40.0", path = "transports/dns" } libp2p-floodsub = { version = "0.43.0", path = "protocols/floodsub" } libp2p-gossipsub = { version = "0.45.0", path = "protocols/gossipsub" } libp2p-identify = { version = "0.43.0", path = "protocols/identify" } -libp2p-identity = { version = "0.2.0" } -libp2p-kad = { version = "0.44.0", path = "protocols/kad" } +libp2p-identity = { version = "0.2.1" } +libp2p-kad = { version = "0.44.2", path = "protocols/kad" } libp2p-mdns = { version = "0.44.0", path = "protocols/mdns" } libp2p-metrics = { version = "0.13.0", path = "misc/metrics" } libp2p-mplex = { version = "0.40.0", path = "muxers/mplex" } @@ -85,15 +87,16 @@ libp2p-quic = { version = "0.8.0-alpha", path = "transports/quic" } libp2p-relay = { version = "0.16.0", path = "protocols/relay" } libp2p-rendezvous = { version = "0.13.0", path = "protocols/rendezvous" } libp2p-request-response = { version = "0.25.0", path = "protocols/request-response" } -libp2p-swarm = { version = "0.43.0", path = "swarm" } +libp2p-swarm = { version = "0.43.1", path = "swarm" } libp2p-swarm-derive = { version = "0.33.0", path = "swarm-derive" } libp2p-swarm-test = { version = "0.2.0", path = "swarm-test" } libp2p-tcp = { version = "0.40.0", path = "transports/tcp" } libp2p-tls = { version = "0.2.0", path = "transports/tls" } libp2p-uds = { version = "0.39.0", path = "transports/uds" } libp2p-wasm-ext = { version = "0.40.0", path = "transports/wasm-ext" } -libp2p-webrtc = { version = "0.5.0-alpha", path = "transports/webrtc" } +libp2p-webrtc = { version = "0.6.0-alpha", path = "transports/webrtc" } libp2p-websocket = { version = "0.42.0", path = "transports/websocket" } +libp2p-webtransport-websys = { version = "0.1.0", path = "transports/webtransport-websys" } libp2p-yamux = { version = "0.44.0", path = "muxers/yamux" } multistream-select = { version = "0.13.0", path = "misc/multistream-select" } quick-protobuf-codec = { version = "0.2.0", path = "misc/quick-protobuf-codec" } diff --git a/README.md b/README.md index c5db804204e6..a268f319c05f 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,9 @@ This repository is the central place for Rust development of the [libp2p](https: - For **discussions and questions related to multiple libp2p implementations** please use the libp2p _Discourse_ forum https://discuss.libp2p.io. -- For general project updates and discussions join the [biweekly libp2p Community - Calls](https://discuss.libp2p.io/t/libp2p-community-calls/1157). +- For synchronous discussions join the [open rust-libp2p maintainer + calls](https://github.com/libp2p/rust-libp2p/discussions?discussions_q=open+maintainers+call+) + or the [biweekly libp2p community calls](https://discuss.libp2p.io/t/libp2p-community-calls/1157). ## Repository Structure diff --git a/core/Cargo.toml b/core/Cargo.toml index ef7de0d148f7..bc18a7719339 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -23,12 +23,12 @@ multihash = { workspace = true } multistream-select = { workspace = true } once_cell = "1.18.0" parking_lot = "0.12.0" -pin-project = "1.1.0" +pin-project = "1.1.2" quick-protobuf = "0.8" rand = "0.8" rw-stream-sink = { workspace = true } serde = { version = "1", optional = true, features = ["derive"] } -smallvec = "1.6.1" +smallvec = "1.11.0" thiserror = "1.0" unsigned-varint = "0.7" void = "1" diff --git a/core/src/transport/boxed.rs b/core/src/transport/boxed.rs index 8274d557e61f..1cede676c8ec 100644 --- a/core/src/transport/boxed.rs +++ b/core/src/transport/boxed.rs @@ -43,7 +43,7 @@ where /// A `Boxed` transport is a `Transport` whose `Dial`, `Listener` /// and `ListenerUpgrade` futures are `Box`ed and only the `Output` -/// and `Error` types are captured in type variables. +/// type is captured in a type variable. pub struct Boxed { inner: Box + Send + Unpin>, } diff --git a/core/src/transport/choice.rs b/core/src/transport/choice.rs index b7eaacabfeba..8d3bfdecb794 100644 --- a/core/src/transport/choice.rs +++ b/core/src/transport/choice.rs @@ -22,6 +22,7 @@ use crate::either::EitherFuture; use crate::transport::{ListenerId, Transport, TransportError, TransportEvent}; use either::Either; use futures::future; +use log::{debug, trace}; use multiaddr::Multiaddr; use std::{pin::Pin, task::Context, task::Poll}; @@ -51,13 +52,37 @@ where id: ListenerId, addr: Multiaddr, ) -> Result<(), TransportError> { + trace!( + "Attempting to listen on {} using {}", + addr, + std::any::type_name::() + ); let addr = match self.0.listen_on(id, addr) { - Err(TransportError::MultiaddrNotSupported(addr)) => addr, + Err(TransportError::MultiaddrNotSupported(addr)) => { + debug!( + "Failed to listen on {} using {}", + addr, + std::any::type_name::() + ); + addr + } res => return res.map_err(|err| err.map(Either::Left)), }; + trace!( + "Attempting to listen on {} using {}", + addr, + std::any::type_name::() + ); let addr = match self.1.listen_on(id, addr) { - Err(TransportError::MultiaddrNotSupported(addr)) => addr, + Err(TransportError::MultiaddrNotSupported(addr)) => { + debug!( + "Failed to listen on {} using {}", + addr, + std::any::type_name::() + ); + addr + } res => return res.map_err(|err| err.map(Either::Right)), }; @@ -69,17 +94,41 @@ where } fn dial(&mut self, addr: Multiaddr) -> Result> { + trace!( + "Attempting to dial {} using {}", + addr, + std::any::type_name::() + ); let addr = match self.0.dial(addr) { Ok(connec) => return Ok(EitherFuture::First(connec)), - Err(TransportError::MultiaddrNotSupported(addr)) => addr, + Err(TransportError::MultiaddrNotSupported(addr)) => { + debug!( + "Failed to dial {} using {}", + addr, + std::any::type_name::() + ); + addr + } Err(TransportError::Other(err)) => { return Err(TransportError::Other(Either::Left(err))) } }; + trace!( + "Attempting to dial {} using {}", + addr, + std::any::type_name::() + ); let addr = match self.1.dial(addr) { Ok(connec) => return Ok(EitherFuture::Second(connec)), - Err(TransportError::MultiaddrNotSupported(addr)) => addr, + Err(TransportError::MultiaddrNotSupported(addr)) => { + debug!( + "Failed to dial {} using {}", + addr, + std::any::type_name::() + ); + addr + } Err(TransportError::Other(err)) => { return Err(TransportError::Other(Either::Right(err))) } diff --git a/core/src/upgrade/apply.rs b/core/src/upgrade/apply.rs index 50aa8e2c0dde..aa997435673a 100644 --- a/core/src/upgrade/apply.rs +++ b/core/src/upgrade/apply.rs @@ -55,7 +55,7 @@ where { InboundUpgradeApply { inner: InboundUpgradeApplyState::Init { - future: multistream_select::listener_select_proto(conn, up.protocol_info().into_iter()), + future: multistream_select::listener_select_proto(conn, up.protocol_info()), upgrade: up, }, } diff --git a/examples/autonat/Cargo.toml b/examples/autonat/Cargo.toml index 0f285a26d916..6c7b642c4a35 100644 --- a/examples/autonat/Cargo.toml +++ b/examples/autonat/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT" [dependencies] async-std = { version = "1.12", features = ["attributes"] } -clap = { version = "4.3.3", features = ["derive"] } +clap = { version = "4.3.11", features = ["derive"] } env_logger = "0.10.0" futures = "0.3.28" libp2p = { path = "../../libp2p", features = ["async-std", "tcp", "noise", "yamux", "autonat", "identify", "macros"] } diff --git a/examples/dcutr/Cargo.toml b/examples/dcutr/Cargo.toml index d30c4ca74e85..d37dfcaca62e 100644 --- a/examples/dcutr/Cargo.toml +++ b/examples/dcutr/Cargo.toml @@ -6,7 +6,7 @@ publish = false license = "MIT" [dependencies] -clap = { version = "4.3.3", features = ["derive"] } +clap = { version = "4.3.11", features = ["derive"] } env_logger = "0.10.0" futures = "0.3.28" futures-timer = "3.0" diff --git a/examples/distributed-key-value-store/src/main.rs b/examples/distributed-key-value-store/src/main.rs index 226a438f2ccb..ce0998a9ac56 100644 --- a/examples/distributed-key-value-store/src/main.rs +++ b/examples/distributed-key-value-store/src/main.rs @@ -24,6 +24,7 @@ use async_std::io; use futures::{prelude::*, select}; use libp2p::core::upgrade::Version; use libp2p::kad::record::store::MemoryStore; +use libp2p::kad::Mode; use libp2p::kad::{ record::Key, AddProviderOk, GetProvidersOk, GetRecordOk, Kademlia, KademliaEvent, PeerRecord, PutRecordOk, QueryResult, Quorum, Record, @@ -85,6 +86,8 @@ async fn main() -> Result<(), Box> { SwarmBuilder::with_async_std_executor(transport, behaviour, local_peer_id).build() }; + swarm.behaviour_mut().kademlia.set_mode(Some(Mode::Server)); + // Read full lines from stdin let mut stdin = io::BufReader::new(io::stdin()).lines().fuse(); diff --git a/examples/file-sharing/Cargo.toml b/examples/file-sharing/Cargo.toml index 41d607586ba1..b631a7cf1942 100644 --- a/examples/file-sharing/Cargo.toml +++ b/examples/file-sharing/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT" [dependencies] serde = { version = "1.0", features = ["derive"] } async-std = { version = "1.12", features = ["attributes"] } -clap = { version = "4.3.3", features = ["derive"] } +clap = { version = "4.3.11", features = ["derive"] } either = "1.8" env_logger = "0.10" futures = "0.3.28" diff --git a/examples/metrics/Cargo.toml b/examples/metrics/Cargo.toml index 463b98a23839..f883127ee25c 100644 --- a/examples/metrics/Cargo.toml +++ b/examples/metrics/Cargo.toml @@ -12,4 +12,4 @@ hyper = { version = "0.14", features = ["server", "tcp", "http1"] } libp2p = { path = "../../libp2p", features = ["async-std", "metrics", "ping", "noise", "identify", "tcp", "yamux", "macros"] } log = "0.4.19" tokio = { version = "1", features = ["rt-multi-thread"] } -prometheus-client = "0.21.0" +prometheus-client = "0.21.2" diff --git a/examples/relay-server/Cargo.toml b/examples/relay-server/Cargo.toml index 55b52cac7592..ce5825e02ffd 100644 --- a/examples/relay-server/Cargo.toml +++ b/examples/relay-server/Cargo.toml @@ -6,7 +6,7 @@ publish = false license = "MIT" [dependencies] -clap = { version = "4.3.3", features = ["derive"] } +clap = { version = "4.3.11", features = ["derive"] } async-std = { version = "1.12", features = ["attributes"] } async-trait = "0.1" env_logger = "0.10.0" diff --git a/examples/rendezvous/Cargo.toml b/examples/rendezvous/Cargo.toml index 591b738c98d6..ffbde3fd7dca 100644 --- a/examples/rendezvous/Cargo.toml +++ b/examples/rendezvous/Cargo.toml @@ -12,4 +12,4 @@ env_logger = "0.10.0" futures = "0.3.28" libp2p = { path = "../../libp2p", features = ["async-std", "identify", "macros", "noise", "ping", "rendezvous", "tcp", "tokio", "yamux"] } log = "0.4" -tokio = { version = "1.28", features = [ "rt-multi-thread", "macros", "time" ] } +tokio = { version = "1.29", features = [ "rt-multi-thread", "macros", "time" ] } diff --git a/identity/CHANGELOG.md b/identity/CHANGELOG.md index 5f436bab9f63..c81fbd47e38b 100644 --- a/identity/CHANGELOG.md +++ b/identity/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.2.1 + +- Expose `KeyType` for `PublicKey` and `Keypair`. + See [PR 4107]. + +[PR 4107]: https://github.com/libp2p/rust-libp2p/pull/4107 + ## 0.2.0 - Raise MSRV to 1.65. diff --git a/identity/Cargo.toml b/identity/Cargo.toml index 3ebefb7e17da..e3f966ebafd0 100644 --- a/identity/Cargo.toml +++ b/identity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-identity" -version = "0.2.0" +version = "0.2.1" edition = "2021" description = "Data structures and algorithms for identifying peers in libp2p." rust-version = { workspace = true } diff --git a/identity/src/keypair.rs b/identity/src/keypair.rs index ef1cd7f71790..053be4a5ca26 100644 --- a/identity/src/keypair.rs +++ b/identity/src/keypair.rs @@ -59,6 +59,7 @@ use crate::secp256k1; #[cfg(feature = "ecdsa")] use crate::ecdsa; +use crate::KeyType; /// Identity keypair of a node. /// @@ -319,6 +320,20 @@ impl Keypair { )))] unreachable!() } + + /// Return a [`KeyType`] of the [`Keypair`]. + pub fn key_type(&self) -> KeyType { + match self.keypair { + #[cfg(feature = "ed25519")] + KeyPairInner::Ed25519(_) => KeyType::Ed25519, + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + KeyPairInner::Rsa(_) => KeyType::RSA, + #[cfg(feature = "secp256k1")] + KeyPairInner::Secp256k1(_) => KeyType::Secp256k1, + #[cfg(feature = "ecdsa")] + KeyPairInner::Ecdsa(_) => KeyType::Ecdsa, + } + } } #[cfg(feature = "ecdsa")] @@ -552,6 +567,20 @@ impl PublicKey { pub fn to_peer_id(&self) -> crate::PeerId { self.into() } + + /// Return a [`KeyType`] of the [`PublicKey`]. + pub fn key_type(&self) -> KeyType { + match self.publickey { + #[cfg(feature = "ed25519")] + PublicKeyInner::Ed25519(_) => KeyType::Ed25519, + #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))] + PublicKeyInner::Rsa(_) => KeyType::RSA, + #[cfg(feature = "secp256k1")] + PublicKeyInner::Secp256k1(_) => KeyType::Secp256k1, + #[cfg(feature = "ecdsa")] + PublicKeyInner::Ecdsa(_) => KeyType::Ecdsa, + } + } } #[cfg(any( @@ -750,7 +779,7 @@ mod tests { .unwrap(); let pub_key = PublicKey::try_decode_protobuf(&hex_literal::hex!("0803125b3059301306072a8648ce3d020106082a8648ce3d03010703420004de3d300fa36ae0e8f5d530899d83abab44abf3161f162a4bc901d8e6ecda020e8b6d5f8da30525e71d6851510c098e5c47c646a597fb4dcec034e9f77c409e62")).unwrap(); - roundtrip_protobuf_encoding(&priv_key, &pub_key); + roundtrip_protobuf_encoding(&priv_key, &pub_key, KeyType::Ecdsa); } #[test] @@ -765,11 +794,11 @@ mod tests { )) .unwrap(); - roundtrip_protobuf_encoding(&priv_key, &pub_key); + roundtrip_protobuf_encoding(&priv_key, &pub_key, KeyType::Secp256k1); } #[cfg(feature = "peerid")] - fn roundtrip_protobuf_encoding(private_key: &Keypair, public_key: &PublicKey) { + fn roundtrip_protobuf_encoding(private_key: &Keypair, public_key: &PublicKey, tpe: KeyType) { assert_eq!(&private_key.public(), public_key); let encoded_priv = private_key.to_protobuf_encoding().unwrap(); @@ -789,6 +818,7 @@ mod tests { decoded_public.to_peer_id(), "PeerId from roundtripped public key should be the same" ); + assert_eq!(private_key.key_type(), tpe) } #[test] @@ -845,6 +875,7 @@ mod tests { let converted_pubkey = PublicKey::from(ed25519_pubkey); assert_eq!(converted_pubkey, pubkey); + assert_eq!(converted_pubkey.key_type(), KeyType::Ed25519) } #[test] @@ -858,6 +889,7 @@ mod tests { let converted_pubkey = PublicKey::from(secp256k1_pubkey); assert_eq!(converted_pubkey, pubkey); + assert_eq!(converted_pubkey.key_type(), KeyType::Secp256k1) } #[test] @@ -868,5 +900,6 @@ mod tests { let converted_pubkey = PublicKey::from(ecdsa_pubkey); assert_eq!(converted_pubkey, pubkey); + assert_eq!(converted_pubkey.key_type(), KeyType::Ecdsa) } } diff --git a/interop-tests/Cargo.toml b/interop-tests/Cargo.toml index 6d65861c3c29..b88cd0e7af18 100644 --- a/interop-tests/Cargo.toml +++ b/interop-tests/Cargo.toml @@ -5,16 +5,40 @@ version = "0.1.0" publish = false license = "MIT" +[lib] +crate-type = ["cdylib", "rlib"] + [dependencies] anyhow = "1" either = "1.8.0" env_logger = "0.10.0" futures = "0.3.28" -libp2p = { path = "../libp2p", features = ["websocket", "yamux", "tcp", "tokio", "ping", "noise", "tls", "dns", "rsa", "macros"] } +log = "0.4" +serde = { version = "1", features = ["derive"] } +rand = "0.8.5" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +axum = "0.6" +libp2p = { path = "../libp2p", features = ["ping", "noise", "tls", "rsa", "macros", "websocket", "tokio", "yamux", "tcp", "dns"] } libp2p-quic = { workspace = true, features = ["tokio"] } libp2p-webrtc = { workspace = true, features = ["tokio"] } libp2p-mplex = { path = "../muxers/mplex" } -log = "0.4" -rand = "0.8.5" +mime_guess = "2.0" redis = { version = "0.23.0", default-features = false, features = ["tokio-comp"] } -tokio = { version = "1.28.2", features = ["full"] } +rust-embed = "6.8" +serde_json = "1" +thirtyfour = "=0.32.0-rc.8" # https://github.com/stevepryde/thirtyfour/issues/169 +tokio = { version = "1.29.1", features = ["full"] } +tower-http = { version = "0.4", features = ["cors", "fs", "trace"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +libp2p = { path = "../libp2p", features = ["ping", "macros", "webtransport-websys", "wasm-bindgen"] } +wasm-bindgen = { version = "0.2" } +wasm-bindgen-futures = { version = "0.4" } +wasm-logger = { version = "0.2.0" } +instant = "0.1.12" +reqwest = { version = "0.11", features = ["json"] } +console_error_panic_hook = { version = "0.1.7" } +futures-timer = "3.0.2" diff --git a/interop-tests/Dockerfile.chromium b/interop-tests/Dockerfile.chromium new file mode 100644 index 000000000000..ebe66850197c --- /dev/null +++ b/interop-tests/Dockerfile.chromium @@ -0,0 +1,31 @@ +FROM rust:1.67.0 as builder + +# Run with access to the target cache to speed up builds +WORKDIR /workspace +ADD . . + +RUN rustup target add wasm32-unknown-unknown + +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + cargo install wasm-pack@0.11.1 --locked + +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + cargo install wasm-opt@0.113.0 --locked + +RUN --mount=type=cache,target=./target \ + --mount=type=cache,target=/usr/local/cargo/registry \ + wasm-pack build --target web interop-tests + +RUN --mount=type=cache,target=./target \ + --mount=type=cache,target=/usr/local/cargo/registry \ + cargo build --release --package interop-tests --bin wasm_ping + +RUN --mount=type=cache,target=./target \ + mv ./target/release/wasm_ping /usr/local/bin/testplan + +FROM selenium/standalone-chrome:112.0 +COPY --from=builder /usr/local/bin/testplan /usr/local/bin/testplan + +ENV RUST_BACKTRACE=1 + +ENTRYPOINT ["testplan"] diff --git a/interop-tests/Dockerfile b/interop-tests/Dockerfile.native similarity index 77% rename from interop-tests/Dockerfile rename to interop-tests/Dockerfile.native index 0d80a872f5ca..f78b85e424c7 100644 --- a/interop-tests/Dockerfile +++ b/interop-tests/Dockerfile.native @@ -6,10 +6,10 @@ WORKDIR /workspace ADD . . RUN --mount=type=cache,target=./target \ --mount=type=cache,target=/usr/local/cargo/registry \ - cargo build --release --package interop-tests + cargo build --release --package interop-tests --bin native_ping RUN --mount=type=cache,target=./target \ - mv ./target/release/ping /usr/local/bin/testplan + mv ./target/release/native_ping /usr/local/bin/testplan FROM gcr.io/distroless/cc COPY --from=builder /usr/local/bin/testplan /usr/local/bin/testplan diff --git a/interop-tests/README.md b/interop-tests/README.md index 6a933edf6c09..88cd75188332 100644 --- a/interop-tests/README.md +++ b/interop-tests/README.md @@ -10,15 +10,25 @@ can dial/listen for ourselves we can do the following: 1. Start redis (needed by the tests): `docker run --rm -it -p 6379:6379 redis/redis-stack`. -2. In one terminal run the dialer: `REDIS_ADDR=localhost:6379 ip="0.0.0.0" +2. In one terminal run the dialer: `redis_addr=localhost:6379 ip="0.0.0.0" transport=quic-v1 security=quic muxer=quic is_dialer="true" cargo run --bin ping` -3. In another terminal, run the listener: `REDIS_ADDR=localhost:6379 - ip="0.0.0.0" transport=quic-v1 security=quic muxer=quic is_dialer="false" cargo run --bin ping` +3. In another terminal, run the listener: `redis_addr=localhost:6379 + ip="0.0.0.0" transport=quic-v1 security=quic muxer=quic is_dialer="false" cargo run --bin native_ping` To test the interop with other versions do something similar, except replace one of these nodes with the other version's interop test. +# Running this test with webtransport dialer in browser + +To run the webtransport test from within the browser, you'll need the +`chromedriver` in your `$PATH`, compatible with your Chrome browser. +Firefox is not yet supported as it doesn't support all required features yet +(in v114 there is no support for certhashes). + +1. Build the wasm package: `wasm-pack build --target web` +2. Run the dialer: `redis_addr=127.0.0.1:6379 ip=0.0.0.0 transport=webtransport is_dialer=true cargo run --bin wasm_ping` + # Running all interop tests locally with Compose To run this test against all released libp2p versions you'll need to have the diff --git a/interop-tests/chromium-ping-version.json b/interop-tests/chromium-ping-version.json new file mode 100644 index 000000000000..9fb2cd2252c7 --- /dev/null +++ b/interop-tests/chromium-ping-version.json @@ -0,0 +1,7 @@ +{ + "id": "chromium-rust-libp2p-head", + "containerImageID": "chromium-rust-libp2p-head", + "transports": [{ "name": "webtransport", "onlyDial": true }], + "secureChannels": [], + "muxers": [] +} diff --git a/interop-tests/ping-version.json b/interop-tests/native-ping-version.json similarity index 67% rename from interop-tests/ping-version.json rename to interop-tests/native-ping-version.json index fbf858e85e84..c509f72bfd0b 100644 --- a/interop-tests/ping-version.json +++ b/interop-tests/native-ping-version.json @@ -1,6 +1,6 @@ { - "id": "rust-libp2p-head", - "containerImageID": "rust-libp2p-head", + "id": "native-rust-libp2p-head", + "containerImageID": "native-rust-libp2p-head", "transports": [ "ws", "tcp", diff --git a/interop-tests/pkg/readme.md b/interop-tests/pkg/readme.md new file mode 100644 index 000000000000..28a771b5ac58 --- /dev/null +++ b/interop-tests/pkg/readme.md @@ -0,0 +1,6 @@ +# Wasm package directory + +Content of this directory should be generated with +``` +wasm pack build --target web +``` diff --git a/interop-tests/src/arch.rs b/interop-tests/src/arch.rs new file mode 100644 index 000000000000..2f4a7495a04b --- /dev/null +++ b/interop-tests/src/arch.rs @@ -0,0 +1,231 @@ +use libp2p::core::muxing::StreamMuxerBox; +use libp2p::core::transport::Boxed; +use libp2p::PeerId; + +// Native re-exports +#[cfg(not(target_arch = "wasm32"))] +pub(crate) use native::{build_transport, init_logger, sleep, swarm_builder, Instant, RedisClient}; + +// Wasm re-exports +#[cfg(target_arch = "wasm32")] +pub(crate) use wasm::{build_transport, init_logger, sleep, swarm_builder, Instant, RedisClient}; + +type BoxedTransport = Boxed<(PeerId, StreamMuxerBox)>; + +#[cfg(not(target_arch = "wasm32"))] +pub(crate) mod native { + use std::time::Duration; + + use anyhow::{bail, Context, Result}; + use either::Either; + use env_logger::{Env, Target}; + use futures::future::BoxFuture; + use futures::FutureExt; + use libp2p::core::muxing::StreamMuxerBox; + use libp2p::core::upgrade::Version; + use libp2p::identity::Keypair; + use libp2p::swarm::{NetworkBehaviour, SwarmBuilder}; + use libp2p::websocket::WsConfig; + use libp2p::{noise, tcp, tls, yamux, PeerId, Transport as _}; + use libp2p_mplex as mplex; + use libp2p_quic as quic; + use libp2p_webrtc as webrtc; + use redis::AsyncCommands; + + use crate::{from_env, Muxer, SecProtocol, Transport}; + + use super::BoxedTransport; + + pub(crate) type Instant = std::time::Instant; + + pub(crate) fn init_logger() { + env_logger::Builder::from_env(Env::default().default_filter_or("info")) + .target(Target::Stdout) + .init(); + } + + pub(crate) fn sleep(duration: Duration) -> BoxFuture<'static, ()> { + tokio::time::sleep(duration).boxed() + } + + fn muxer_protocol_from_env() -> Result> { + Ok(match from_env("muxer")? { + Muxer::Yamux => Either::Left(yamux::Config::default()), + Muxer::Mplex => Either::Right(mplex::MplexConfig::new()), + }) + } + + pub(crate) fn build_transport( + local_key: Keypair, + ip: &str, + transport: Transport, + ) -> Result<(BoxedTransport, String)> { + let (transport, addr) = match (transport, from_env::("security")) { + (Transport::QuicV1, _) => ( + quic::tokio::Transport::new(quic::Config::new(&local_key)) + .map(|(p, c), _| (p, StreamMuxerBox::new(c))) + .boxed(), + format!("/ip4/{ip}/udp/0/quic-v1"), + ), + (Transport::Tcp, Ok(SecProtocol::Tls)) => ( + tcp::tokio::Transport::new(tcp::Config::new()) + .upgrade(Version::V1Lazy) + .authenticate(tls::Config::new(&local_key).context("failed to initialise tls")?) + .multiplex(muxer_protocol_from_env()?) + .timeout(Duration::from_secs(5)) + .boxed(), + format!("/ip4/{ip}/tcp/0"), + ), + (Transport::Tcp, Ok(SecProtocol::Noise)) => ( + tcp::tokio::Transport::new(tcp::Config::new()) + .upgrade(Version::V1Lazy) + .authenticate( + noise::Config::new(&local_key).context("failed to intialise noise")?, + ) + .multiplex(muxer_protocol_from_env()?) + .timeout(Duration::from_secs(5)) + .boxed(), + format!("/ip4/{ip}/tcp/0"), + ), + (Transport::Ws, Ok(SecProtocol::Tls)) => ( + WsConfig::new(tcp::tokio::Transport::new(tcp::Config::new())) + .upgrade(Version::V1Lazy) + .authenticate(tls::Config::new(&local_key).context("failed to initialise tls")?) + .multiplex(muxer_protocol_from_env()?) + .timeout(Duration::from_secs(5)) + .boxed(), + format!("/ip4/{ip}/tcp/0/ws"), + ), + (Transport::Ws, Ok(SecProtocol::Noise)) => ( + WsConfig::new(tcp::tokio::Transport::new(tcp::Config::new())) + .upgrade(Version::V1Lazy) + .authenticate( + noise::Config::new(&local_key).context("failed to intialise noise")?, + ) + .multiplex(muxer_protocol_from_env()?) + .timeout(Duration::from_secs(5)) + .boxed(), + format!("/ip4/{ip}/tcp/0/ws"), + ), + (Transport::WebRtcDirect, _) => ( + webrtc::tokio::Transport::new( + local_key, + webrtc::tokio::Certificate::generate(&mut rand::thread_rng())?, + ) + .map(|(peer_id, conn), _| (peer_id, StreamMuxerBox::new(conn))) + .boxed(), + format!("/ip4/{ip}/udp/0/webrtc-direct"), + ), + (Transport::Tcp, Err(_)) => bail!("Missing security protocol for TCP transport"), + (Transport::Ws, Err(_)) => bail!("Missing security protocol for Websocket transport"), + (Transport::Webtransport, _) => bail!("Webtransport can only be used with wasm"), + }; + Ok((transport, addr)) + } + + pub(crate) fn swarm_builder( + transport: BoxedTransport, + behaviour: TBehaviour, + peer_id: PeerId, + ) -> SwarmBuilder { + SwarmBuilder::with_tokio_executor(transport, behaviour, peer_id) + } + + pub(crate) struct RedisClient(redis::Client); + + impl RedisClient { + pub(crate) fn new(redis_addr: &str) -> Result { + Ok(Self( + redis::Client::open(redis_addr).context("Could not connect to redis")?, + )) + } + + pub(crate) async fn blpop(&self, key: &str, timeout: u64) -> Result> { + let mut conn = self.0.get_async_connection().await?; + Ok(conn.blpop(key, timeout as usize).await?) + } + + pub(crate) async fn rpush(&self, key: &str, value: String) -> Result<()> { + let mut conn = self.0.get_async_connection().await?; + conn.rpush(key, value).await?; + Ok(()) + } + } +} + +#[cfg(target_arch = "wasm32")] +pub(crate) mod wasm { + use anyhow::{bail, Result}; + use futures::future::{BoxFuture, FutureExt}; + use libp2p::identity::Keypair; + use libp2p::swarm::{NetworkBehaviour, SwarmBuilder}; + use libp2p::PeerId; + use std::time::Duration; + + use crate::{BlpopRequest, Transport}; + + use super::BoxedTransport; + + pub(crate) type Instant = instant::Instant; + + pub(crate) fn init_logger() { + console_error_panic_hook::set_once(); + wasm_logger::init(wasm_logger::Config::default()); + } + + pub(crate) fn sleep(duration: Duration) -> BoxFuture<'static, ()> { + futures_timer::Delay::new(duration).boxed() + } + + pub(crate) fn build_transport( + local_key: Keypair, + ip: &str, + transport: Transport, + ) -> Result<(BoxedTransport, String)> { + if let Transport::Webtransport = transport { + Ok(( + libp2p::webtransport_websys::Transport::new( + libp2p::webtransport_websys::Config::new(&local_key), + ) + .boxed(), + format!("/ip4/{ip}/udp/0/quic/webtransport"), + )) + } else { + bail!("Only webtransport supported with wasm") + } + } + + pub(crate) fn swarm_builder( + transport: BoxedTransport, + behaviour: TBehaviour, + peer_id: PeerId, + ) -> SwarmBuilder { + SwarmBuilder::with_wasm_executor(transport, behaviour, peer_id) + } + + pub(crate) struct RedisClient(String); + + impl RedisClient { + pub(crate) fn new(base_url: &str) -> Result { + Ok(Self(base_url.to_owned())) + } + + pub(crate) async fn blpop(&self, key: &str, timeout: u64) -> Result> { + let res = reqwest::Client::new() + .post(&format!("http://{}/blpop", self.0)) + .json(&BlpopRequest { + key: key.to_owned(), + timeout, + }) + .send() + .await? + .json() + .await?; + Ok(res) + } + + pub(crate) async fn rpush(&self, _: &str, _: String) -> Result<()> { + bail!("unimplemented") + } + } +} diff --git a/interop-tests/src/bin/config/mod.rs b/interop-tests/src/bin/config/mod.rs new file mode 100644 index 000000000000..82747e82802b --- /dev/null +++ b/interop-tests/src/bin/config/mod.rs @@ -0,0 +1,37 @@ +use std::env; + +use anyhow::{Context, Result}; + +#[derive(Debug, Clone)] +pub(crate) struct Config { + pub(crate) transport: String, + pub(crate) ip: String, + pub(crate) is_dialer: bool, + pub(crate) test_timeout: u64, + pub(crate) redis_addr: String, +} + +impl Config { + pub(crate) fn from_env() -> Result { + let transport = + env::var("transport").context("transport environment variable is not set")?; + let ip = env::var("ip").context("ip environment variable is not set")?; + let is_dialer = env::var("is_dialer") + .unwrap_or_else(|_| "true".into()) + .parse::()?; + let test_timeout = env::var("test_timeout_seconds") + .unwrap_or_else(|_| "180".into()) + .parse::()?; + let redis_addr = env::var("redis_addr") + .map(|addr| format!("redis://{addr}")) + .unwrap_or_else(|_| "redis://redis:6379".into()); + + Ok(Self { + transport, + ip, + is_dialer, + test_timeout, + redis_addr, + }) + } +} diff --git a/interop-tests/src/bin/native_ping.rs b/interop-tests/src/bin/native_ping.rs new file mode 100644 index 000000000000..88905803d26d --- /dev/null +++ b/interop-tests/src/bin/native_ping.rs @@ -0,0 +1,21 @@ +use anyhow::Result; + +mod config; + +#[tokio::main] +async fn main() -> Result<()> { + let config = config::Config::from_env()?; + + let report = interop_tests::run_test( + &config.transport, + &config.ip, + config.is_dialer, + config.test_timeout, + &config.redis_addr, + ) + .await?; + + println!("{}", serde_json::to_string(&report)?); + + Ok(()) +} diff --git a/interop-tests/src/bin/ping.rs b/interop-tests/src/bin/ping.rs deleted file mode 100644 index 23cc86f4571d..000000000000 --- a/interop-tests/src/bin/ping.rs +++ /dev/null @@ -1,267 +0,0 @@ -use std::env; -use std::str::FromStr; -use std::time::{Duration, Instant}; - -use anyhow::{bail, Context, Result}; -use either::Either; -use env_logger::{Env, Target}; -use futures::StreamExt; -use libp2p::core::muxing::StreamMuxerBox; -use libp2p::core::upgrade::Version; -use libp2p::swarm::{keep_alive, NetworkBehaviour, SwarmEvent}; -use libp2p::websocket::WsConfig; -use libp2p::{ - identity, noise, ping, swarm::SwarmBuilder, tcp, tls, yamux, Multiaddr, PeerId, Transport as _, -}; -use libp2p_mplex as mplex; -use libp2p_quic as quic; -use libp2p_webrtc as webrtc; -use redis::AsyncCommands; - -#[tokio::main] -async fn main() -> Result<()> { - let local_key = identity::Keypair::generate_ed25519(); - let local_peer_id = PeerId::from(local_key.public()); - - let transport_param: Transport = from_env("transport")?; - - let ip = env::var("ip").context("ip environment variable is not set")?; - - let is_dialer = env::var("is_dialer") - .unwrap_or_else(|_| "true".into()) - .parse::()?; - - let test_timeout = env::var("test_timeout_seconds") - .unwrap_or_else(|_| "180".into()) - .parse::()?; - - let redis_addr = env::var("redis_addr") - .map(|addr| format!("redis://{addr}")) - .unwrap_or_else(|_| "redis://redis:6379".into()); - - let client = redis::Client::open(redis_addr).context("Could not connect to redis")?; - - // Build the transport from the passed ENV var. - let (boxed_transport, local_addr) = match (transport_param, from_env("security")) { - (Transport::QuicV1, _) => ( - quic::tokio::Transport::new(quic::Config::new(&local_key)) - .map(|(p, c), _| (p, StreamMuxerBox::new(c))) - .boxed(), - format!("/ip4/{ip}/udp/0/quic-v1"), - ), - (Transport::Tcp, Ok(SecProtocol::Tls)) => ( - tcp::tokio::Transport::new(tcp::Config::new()) - .upgrade(Version::V1Lazy) - .authenticate(tls::Config::new(&local_key).context("failed to initialise tls")?) - .multiplex(muxer_protocol_from_env()?) - .timeout(Duration::from_secs(5)) - .boxed(), - format!("/ip4/{ip}/tcp/0"), - ), - (Transport::Tcp, Ok(SecProtocol::Noise)) => ( - tcp::tokio::Transport::new(tcp::Config::new()) - .upgrade(Version::V1Lazy) - .authenticate(noise::Config::new(&local_key).context("failed to intialise noise")?) - .multiplex(muxer_protocol_from_env()?) - .timeout(Duration::from_secs(5)) - .boxed(), - format!("/ip4/{ip}/tcp/0"), - ), - (Transport::Ws, Ok(SecProtocol::Tls)) => ( - WsConfig::new(tcp::tokio::Transport::new(tcp::Config::new())) - .upgrade(Version::V1Lazy) - .authenticate(tls::Config::new(&local_key).context("failed to initialise tls")?) - .multiplex(muxer_protocol_from_env()?) - .timeout(Duration::from_secs(5)) - .boxed(), - format!("/ip4/{ip}/tcp/0/ws"), - ), - (Transport::Ws, Ok(SecProtocol::Noise)) => ( - WsConfig::new(tcp::tokio::Transport::new(tcp::Config::new())) - .upgrade(Version::V1Lazy) - .authenticate(noise::Config::new(&local_key).context("failed to intialise noise")?) - .multiplex(muxer_protocol_from_env()?) - .timeout(Duration::from_secs(5)) - .boxed(), - format!("/ip4/{ip}/tcp/0/ws"), - ), - (Transport::WebRtcDirect, _) => ( - webrtc::tokio::Transport::new( - local_key, - webrtc::tokio::Certificate::generate(&mut rand::thread_rng())?, - ) - .map(|(peer_id, conn), _| (peer_id, StreamMuxerBox::new(conn))) - .boxed(), - format!("/ip4/{ip}/udp/0/webrtc-direct"), - ), - (Transport::Tcp, Err(_)) => bail!("Missing security protocol for TCP transport"), - (Transport::Ws, Err(_)) => bail!("Missing security protocol for Websocket transport"), - }; - - let mut swarm = SwarmBuilder::with_tokio_executor( - boxed_transport, - Behaviour { - ping: ping::Behaviour::new(ping::Config::new().with_interval(Duration::from_secs(1))), - keep_alive: keep_alive::Behaviour, - }, - local_peer_id, - ) - .build(); - - let mut conn = client.get_async_connection().await?; - - log::info!("Running ping test: {}", swarm.local_peer_id()); - env_logger::Builder::from_env(Env::default().default_filter_or("info")) - .target(Target::Stdout) - .init(); - - log::info!( - "Test instance, listening for incoming connections on: {:?}.", - local_addr - ); - let id = swarm.listen_on(local_addr.parse()?)?; - - // Run a ping interop test. Based on `is_dialer`, either dial the address - // retrieved via `listenAddr` key over the redis connection. Or wait to be pinged and have - // `dialerDone` key ready on the redis connection. - if is_dialer { - let result: Vec = conn.blpop("listenerAddr", test_timeout as usize).await?; - let other = result - .get(1) - .context("Failed to wait for listener to be ready")?; - - let handshake_start = Instant::now(); - - swarm.dial(other.parse::()?)?; - log::info!("Test instance, dialing multiaddress on: {}.", other); - - let rtt = loop { - if let Some(SwarmEvent::Behaviour(BehaviourEvent::Ping(ping::Event { - result: Ok(rtt), - .. - }))) = swarm.next().await - { - log::info!("Ping successful: {rtt:?}"); - break rtt.as_millis() as f32; - } - }; - - let handshake_plus_ping = handshake_start.elapsed().as_millis() as f32; - println!( - r#"{{"handshakePlusOneRTTMillis": {handshake_plus_ping:.1}, "pingRTTMilllis": {rtt:.1}}}"# - ); - } else { - loop { - if let Some(SwarmEvent::NewListenAddr { - listener_id, - address, - }) = swarm.next().await - { - if address.to_string().contains("127.0.0.1") { - continue; - } - if listener_id == id { - let ma = format!("{address}/p2p/{local_peer_id}"); - conn.rpush("listenerAddr", ma).await?; - break; - } - } - } - - // Drive Swarm in the background while we await for `dialerDone` to be ready. - tokio::spawn(async move { - loop { - swarm.next().await; - } - }); - tokio::time::sleep(Duration::from_secs(test_timeout)).await; - bail!("Test should have been killed by the test runner!"); - } - - Ok(()) -} - -fn muxer_protocol_from_env() -> Result> { - Ok(match from_env("muxer")? { - Muxer::Yamux => Either::Left(yamux::Config::default()), - Muxer::Mplex => Either::Right(mplex::MplexConfig::new()), - }) -} - -/// Supported transports by rust-libp2p. -#[derive(Clone, Debug)] -pub enum Transport { - Tcp, - QuicV1, - WebRtcDirect, - Ws, -} - -impl FromStr for Transport { - type Err = anyhow::Error; - - fn from_str(s: &str) -> std::result::Result { - Ok(match s { - "tcp" => Self::Tcp, - "quic-v1" => Self::QuicV1, - "webrtc-direct" => Self::WebRtcDirect, - "ws" => Self::Ws, - other => bail!("unknown transport {other}"), - }) - } -} - -/// Supported stream multiplexers by rust-libp2p. -#[derive(Clone, Debug)] -pub enum Muxer { - Mplex, - Yamux, -} - -impl FromStr for Muxer { - type Err = anyhow::Error; - - fn from_str(s: &str) -> std::result::Result { - Ok(match s { - "mplex" => Self::Mplex, - "yamux" => Self::Yamux, - other => bail!("unknown muxer {other}"), - }) - } -} - -/// Supported security protocols by rust-libp2p. -#[derive(Clone, Debug)] -pub enum SecProtocol { - Noise, - Tls, -} - -impl FromStr for SecProtocol { - type Err = anyhow::Error; - - fn from_str(s: &str) -> std::result::Result { - Ok(match s { - "noise" => Self::Noise, - "tls" => Self::Tls, - other => bail!("unknown security protocol {other}"), - }) - } -} - -#[derive(NetworkBehaviour)] -struct Behaviour { - ping: ping::Behaviour, - keep_alive: keep_alive::Behaviour, -} - -/// Helper function to get a ENV variable into an test parameter like `Transport`. -pub fn from_env(env_var: &str) -> Result -where - T: FromStr, -{ - env::var(env_var) - .with_context(|| format!("{env_var} environment variable is not set"))? - .parse() - .map_err(Into::into) -} diff --git a/interop-tests/src/bin/wasm_ping.rs b/interop-tests/src/bin/wasm_ping.rs new file mode 100644 index 000000000000..20350170d598 --- /dev/null +++ b/interop-tests/src/bin/wasm_ping.rs @@ -0,0 +1,219 @@ +use std::process::Stdio; +use std::time::Duration; + +use anyhow::{bail, Context, Result}; +use axum::body; +use axum::http::{header, Uri}; +use axum::response::{Html, IntoResponse, Response}; +use axum::routing::get; +use axum::{extract::State, http::StatusCode, routing::post, Json, Router}; +use redis::{AsyncCommands, Client}; +use thirtyfour::prelude::*; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::process::Child; +use tokio::sync::mpsc; +use tower_http::cors::CorsLayer; +use tower_http::trace::TraceLayer; +use tracing::{error, warn}; +use tracing_subscriber::{fmt, prelude::*, EnvFilter}; + +use interop_tests::{BlpopRequest, Report}; + +mod config; + +const BIND_ADDR: &str = "127.0.0.1:8080"; + +/// Embedded Wasm package +/// +/// Make sure to build the wasm with `wasm-pack build --target web` +#[derive(rust_embed::RustEmbed)] +#[folder = "pkg"] +struct WasmPackage; + +#[derive(Clone)] +struct TestState { + redis_client: Client, + config: config::Config, + results_tx: mpsc::Sender>, +} + +#[tokio::main] +async fn main() -> Result<()> { + // start logging + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_default_env()) + .init(); + + // read env variables + let config = config::Config::from_env()?; + let test_timeout = Duration::from_secs(config.test_timeout); + + // create a redis client + let redis_client = + Client::open(config.redis_addr.as_str()).context("Could not connect to redis")?; + let (results_tx, mut results_rx) = mpsc::channel(1); + + let state = TestState { + redis_client, + config, + results_tx, + }; + + // create a wasm-app service + let app = Router::new() + // Redis proxy + .route("/blpop", post(redis_blpop)) + // Report tests status + .route("/results", post(post_results)) + // Wasm ping test trigger + .route("/", get(serve_index_html)) + // Wasm app static files + .fallback(serve_wasm_pkg) + // Middleware + .layer(CorsLayer::very_permissive()) + .layer(TraceLayer::new_for_http()) + .with_state(state); + + // Run the service in background + tokio::spawn(axum::Server::bind(&BIND_ADDR.parse()?).serve(app.into_make_service())); + + // Start executing the test in a browser + let (mut chrome, driver) = open_in_browser().await?; + + // Wait for the outcome to be reported + let test_result = match tokio::time::timeout(test_timeout, results_rx.recv()).await { + Ok(received) => received.unwrap_or(Err("Results channel closed".to_owned())), + Err(_) => Err("Test timed out".to_owned()), + }; + + // Close the browser after we got the results + driver.quit().await?; + chrome.kill().await?; + + match test_result { + Ok(report) => println!("{}", serde_json::to_string(&report)?), + Err(error) => bail!("Tests failed: {error}"), + } + + Ok(()) +} + +async fn open_in_browser() -> Result<(Child, WebDriver)> { + // start a webdriver process + // currently only the chromedriver is supported as firefox doesn't + // have support yet for the certhashes + let mut chrome = tokio::process::Command::new("chromedriver") + .arg("--port=45782") + .stdout(Stdio::piped()) + .spawn()?; + // read driver's stdout + let driver_out = chrome + .stdout + .take() + .context("No stdout found for webdriver")?; + // wait for the 'ready' message + let mut reader = BufReader::new(driver_out).lines(); + while let Some(line) = reader.next_line().await? { + if line.contains("ChromeDriver was started successfully.") { + break; + } + } + + // run a webdriver client + let mut caps = DesiredCapabilities::chrome(); + caps.set_headless()?; + let driver = WebDriver::new("http://localhost:45782", caps).await?; + // go to the wasm test service + driver.goto(format!("http://{BIND_ADDR}")).await?; + + Ok((chrome, driver)) +} + +/// Redis proxy handler. +/// `blpop` is currently the only redis client method used in a ping dialer. +async fn redis_blpop( + state: State, + request: Json, +) -> Result>, StatusCode> { + let client = state.0.redis_client; + let mut conn = client.get_async_connection().await.map_err(|e| { + warn!("Failed to connect to redis: {e}"); + StatusCode::INTERNAL_SERVER_ERROR + })?; + let res = conn + .blpop(&request.key, request.timeout as usize) + .await + .map_err(|e| { + warn!( + "Failed to get list elem {} within timeout {}: {e}", + request.key, request.timeout + ); + StatusCode::INTERNAL_SERVER_ERROR + })?; + + Ok(Json(res)) +} + +/// Receive test results +async fn post_results( + state: State, + request: Json>, +) -> Result<(), StatusCode> { + state.0.results_tx.send(request.0).await.map_err(|_| { + error!("Failed to send results"); + StatusCode::INTERNAL_SERVER_ERROR + }) +} + +/// Serve the main page which loads our javascript +async fn serve_index_html(state: State) -> Result { + let config::Config { + transport, + ip, + is_dialer, + test_timeout, + .. + } = state.0.config; + Ok(Html(format!( + r#" + + + + + libp2p ping test + + + + + + "# + ))) +} + +async fn serve_wasm_pkg(uri: Uri) -> Result { + let path = uri.path().trim_start_matches('/').to_string(); + if let Some(content) = WasmPackage::get(&path) { + let mime = mime_guess::from_path(&path).first_or_octet_stream(); + Ok(Response::builder() + .header(header::CONTENT_TYPE, mime.as_ref()) + .body(body::boxed(body::Full::from(content.data))) + .unwrap()) + } else { + Err(StatusCode::NOT_FOUND) + } +} diff --git a/interop-tests/src/lib.rs b/interop-tests/src/lib.rs new file mode 100644 index 000000000000..beb7c91c63d9 --- /dev/null +++ b/interop-tests/src/lib.rs @@ -0,0 +1,251 @@ +use std::str::FromStr; +use std::time::Duration; + +use anyhow::{bail, Context, Result}; +use futures::{FutureExt, StreamExt}; +use libp2p::swarm::{keep_alive, NetworkBehaviour, SwarmEvent}; +use libp2p::{identity, ping, Multiaddr, PeerId}; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +mod arch; + +use arch::{build_transport, init_logger, swarm_builder, Instant, RedisClient}; + +pub async fn run_test( + transport: &str, + ip: &str, + is_dialer: bool, + test_timeout_seconds: u64, + redis_addr: &str, +) -> Result { + init_logger(); + + let test_timeout = Duration::from_secs(test_timeout_seconds); + let transport = transport.parse().context("Couldn't parse transport")?; + + let local_key = identity::Keypair::generate_ed25519(); + let local_peer_id = PeerId::from(local_key.public()); + let redis_client = RedisClient::new(redis_addr).context("Could not connect to redis")?; + + // Build the transport from the passed ENV var. + let (boxed_transport, local_addr) = build_transport(local_key, ip, transport)?; + let mut swarm = swarm_builder( + boxed_transport, + Behaviour { + ping: ping::Behaviour::new(ping::Config::new().with_interval(Duration::from_secs(1))), + keep_alive: keep_alive::Behaviour, + }, + local_peer_id, + ) + .build(); + + log::info!("Running ping test: {}", swarm.local_peer_id()); + + let mut maybe_id = None; + + // See https://github.com/libp2p/rust-libp2p/issues/4071. + if transport == Transport::WebRtcDirect { + maybe_id = Some(swarm.listen_on(local_addr.parse()?)?); + } + + // Run a ping interop test. Based on `is_dialer`, either dial the address + // retrieved via `listenAddr` key over the redis connection. Or wait to be pinged and have + // `dialerDone` key ready on the redis connection. + match is_dialer { + true => { + let result: Vec = redis_client + .blpop("listenerAddr", test_timeout.as_secs()) + .await?; + let other = result + .get(1) + .context("Failed to wait for listener to be ready")?; + + let handshake_start = Instant::now(); + + swarm.dial(other.parse::()?)?; + log::info!("Test instance, dialing multiaddress on: {}.", other); + + let rtt = loop { + if let Some(SwarmEvent::Behaviour(BehaviourEvent::Ping(ping::Event { + result: Ok(rtt), + .. + }))) = swarm.next().await + { + log::info!("Ping successful: {rtt:?}"); + break rtt.as_micros() as f32 / 1000.; + } + }; + + let handshake_plus_ping = handshake_start.elapsed().as_micros() as f32 / 1000.; + Ok(Report { + handshake_plus_one_rtt_millis: handshake_plus_ping, + ping_rtt_millis: rtt, + }) + } + false => { + // Listen if we haven't done so already. + // This is a hack until https://github.com/libp2p/rust-libp2p/issues/4071 is fixed at which point we can do this unconditionally here. + let id = match maybe_id { + None => swarm.listen_on(local_addr.parse()?)?, + Some(id) => id, + }; + + log::info!( + "Test instance, listening for incoming connections on: {:?}.", + local_addr + ); + + loop { + if let Some(SwarmEvent::NewListenAddr { + listener_id, + address, + }) = swarm.next().await + { + if address.to_string().contains("127.0.0.1") { + continue; + } + if listener_id == id { + let ma = format!("{address}/p2p/{local_peer_id}"); + redis_client.rpush("listenerAddr", ma).await?; + break; + } + } + } + + // Drive Swarm while we await for `dialerDone` to be ready. + futures::future::select( + async move { + loop { + swarm.next().await; + } + } + .boxed(), + arch::sleep(test_timeout), + ) + .await; + + // The loop never ends so if we get here, we hit the timeout. + bail!("Test should have been killed by the test runner!"); + } + } +} + +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub async fn run_test_wasm( + transport: &str, + ip: &str, + is_dialer: bool, + test_timeout_secs: u64, + base_url: &str, +) -> Result<(), JsValue> { + let result = run_test(transport, ip, is_dialer, test_timeout_secs, base_url).await; + log::info!("Sending test result: {result:?}"); + reqwest::Client::new() + .post(&format!("http://{}/results", base_url)) + .json(&result.map_err(|e| e.to_string())) + .send() + .await? + .error_for_status() + .map_err(|e| format!("Sending test result failed: {e}"))?; + + Ok(()) +} + +/// A request to redis proxy that will pop the value from the list +/// and will wait for it being inserted until a timeout is reached. +#[derive(serde::Deserialize, serde::Serialize)] +pub struct BlpopRequest { + pub key: String, + pub timeout: u64, +} + +/// A report generated by the test +#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct Report { + #[serde(rename = "handshakePlusOneRTTMillis")] + handshake_plus_one_rtt_millis: f32, + #[serde(rename = "pingRTTMilllis")] + ping_rtt_millis: f32, +} + +/// Supported transports by rust-libp2p. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Transport { + Tcp, + QuicV1, + WebRtcDirect, + Ws, + Webtransport, +} + +impl FromStr for Transport { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + Ok(match s { + "tcp" => Self::Tcp, + "quic-v1" => Self::QuicV1, + "webrtc-direct" => Self::WebRtcDirect, + "ws" => Self::Ws, + "webtransport" => Self::Webtransport, + other => bail!("unknown transport {other}"), + }) + } +} + +/// Supported stream multiplexers by rust-libp2p. +#[derive(Clone, Debug)] +pub enum Muxer { + Mplex, + Yamux, +} + +impl FromStr for Muxer { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + Ok(match s { + "mplex" => Self::Mplex, + "yamux" => Self::Yamux, + other => bail!("unknown muxer {other}"), + }) + } +} + +/// Supported security protocols by rust-libp2p. +#[derive(Clone, Debug)] +pub enum SecProtocol { + Noise, + Tls, +} + +impl FromStr for SecProtocol { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + Ok(match s { + "noise" => Self::Noise, + "tls" => Self::Tls, + other => bail!("unknown security protocol {other}"), + }) + } +} + +#[derive(NetworkBehaviour)] +struct Behaviour { + ping: ping::Behaviour, + keep_alive: keep_alive::Behaviour, +} + +/// Helper function to get a ENV variable into an test parameter like `Transport`. +pub fn from_env(env_var: &str) -> Result +where + T: FromStr, +{ + std::env::var(env_var) + .with_context(|| format!("{env_var} environment variable is not set"))? + .parse() + .map_err(Into::into) +} diff --git a/libp2p/CHANGELOG.md b/libp2p/CHANGELOG.md index 5046cbd1f76f..fa2992846675 100644 --- a/libp2p/CHANGELOG.md +++ b/libp2p/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.52.1 + +- Add `libp2p-webtransport-websys` providing WebTransport for WASM environments. + See [PR 4015]. + +[PR 4015]: https://github.com/libp2p/rust-libp2p/pull/4015 + ## 0.52.0 - Raise MSRV to 1.65. diff --git a/libp2p/Cargo.toml b/libp2p/Cargo.toml index ddea2d3ea2d7..06f269466ea1 100644 --- a/libp2p/Cargo.toml +++ b/libp2p/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p" edition = "2021" rust-version = "1.65.0" description = "Peer-to-peer networking library" -version = "0.52.0" +version = "0.52.1" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -45,6 +45,7 @@ full = [ "wasm-ext", "wasm-ext-websocket", "websocket", + "webtransport-websys", "yamux", ] @@ -81,6 +82,7 @@ wasm-bindgen = ["futures-timer/wasm-bindgen", "instant/wasm-bindgen", "getrandom wasm-ext = ["dep:libp2p-wasm-ext"] wasm-ext-websocket = ["wasm-ext", "libp2p-wasm-ext?/websocket"] websocket = ["dep:libp2p-websocket"] +webtransport-websys = ["dep:libp2p-webtransport-websys"] yamux = ["dep:libp2p-yamux"] [dependencies] @@ -110,6 +112,7 @@ libp2p-rendezvous = { workspace = true, optional = true } libp2p-request-response = { workspace = true, optional = true } libp2p-swarm = { workspace = true } libp2p-wasm-ext = { workspace = true, optional = true } +libp2p-webtransport-websys = { workspace = true, optional = true } libp2p-yamux = { workspace = true, optional = true } multiaddr = { workspace = true } diff --git a/libp2p/src/lib.rs b/libp2p/src/lib.rs index 91ae1ddef824..d22e4c25d308 100644 --- a/libp2p/src/lib.rs +++ b/libp2p/src/lib.rs @@ -126,6 +126,10 @@ pub use libp2p_wasm_ext as wasm_ext; #[cfg(not(target_arch = "wasm32"))] #[doc(inline)] pub use libp2p_websocket as websocket; +#[cfg(feature = "webtransport-websys")] +#[cfg_attr(docsrs, doc(cfg(feature = "webtransport-websys")))] +#[doc(inline)] +pub use libp2p_webtransport_websys as webtransport_websys; #[cfg(feature = "yamux")] #[doc(inline)] pub use libp2p_yamux as yamux; diff --git a/misc/allow-block-list/src/lib.rs b/misc/allow-block-list/src/lib.rs index 7aa0dd878221..eed79d740a12 100644 --- a/misc/allow-block-list/src/lib.rs +++ b/misc/allow-block-list/src/lib.rs @@ -354,9 +354,16 @@ mod tests { .block_peer(*listener.local_peer_id()); let ( - [SwarmEvent::ConnectionClosed { peer_id: closed_dialer_peer, .. }], - [SwarmEvent::ConnectionClosed { peer_id: closed_listener_peer, .. }] - ) = libp2p_swarm_test::drive(&mut dialer, &mut listener).await else { + [SwarmEvent::ConnectionClosed { + peer_id: closed_dialer_peer, + .. + }], + [SwarmEvent::ConnectionClosed { + peer_id: closed_listener_peer, + .. + }], + ) = libp2p_swarm_test::drive(&mut dialer, &mut listener).await + else { panic!("unexpected events") }; assert_eq!(closed_dialer_peer, *listener.local_peer_id()); @@ -417,9 +424,22 @@ mod tests { .unwrap(); let ( - [SwarmEvent::OutgoingConnectionError { error: DialError::Denied { cause: outgoing_cause }, .. }], - [_, _, _, SwarmEvent::IncomingConnectionError { error: ListenError::Denied { cause: incoming_cause }, .. }], - ) = libp2p_swarm_test::drive(&mut dialer, &mut listener).await else { + [SwarmEvent::OutgoingConnectionError { + error: + DialError::Denied { + cause: outgoing_cause, + }, + .. + }], + [_, SwarmEvent::IncomingConnectionError { + error: + ListenError::Denied { + cause: incoming_cause, + }, + .. + }], + ) = libp2p_swarm_test::drive(&mut dialer, &mut listener).await + else { panic!("unexpected events") }; assert!(outgoing_cause.downcast::().is_ok()); @@ -447,9 +467,16 @@ mod tests { .list .disallow_peer(*listener.local_peer_id()); let ( - [SwarmEvent::ConnectionClosed { peer_id: closed_dialer_peer, .. }], - [SwarmEvent::ConnectionClosed { peer_id: closed_listener_peer, .. }] - ) = libp2p_swarm_test::drive(&mut dialer, &mut listener).await else { + [SwarmEvent::ConnectionClosed { + peer_id: closed_dialer_peer, + .. + }], + [SwarmEvent::ConnectionClosed { + peer_id: closed_listener_peer, + .. + }], + ) = libp2p_swarm_test::drive(&mut dialer, &mut listener).await + else { panic!("unexpected events") }; assert_eq!(closed_dialer_peer, *listener.local_peer_id()); diff --git a/misc/keygen/Cargo.toml b/misc/keygen/Cargo.toml index a76c4d0a4bbe..3ed0f482b221 100644 --- a/misc/keygen/Cargo.toml +++ b/misc/keygen/Cargo.toml @@ -10,10 +10,10 @@ categories = ["network-programming", "asynchronous"] publish = false [dependencies] -clap = { version = "4.3.3", features = ["derive"] } +clap = { version = "4.3.11", features = ["derive"] } zeroize = "1" -serde = { version = "1.0.164", features = ["derive"] } -serde_json = "1.0.97" +serde = { version = "1.0.171", features = ["derive"] } +serde_json = "1.0.100" libp2p-core = { workspace = true } base64 = "0.21.2" libp2p-identity = { workspace = true } diff --git a/misc/metrics/Cargo.toml b/misc/metrics/Cargo.toml index bbc677f9a471..ea7421558806 100644 --- a/misc/metrics/Cargo.toml +++ b/misc/metrics/Cargo.toml @@ -29,8 +29,9 @@ libp2p-kad = { workspace = true, optional = true } libp2p-ping = { workspace = true, optional = true } libp2p-relay = { workspace = true, optional = true } libp2p-swarm = { workspace = true } +libp2p-identity = { workspace = true } once_cell = "1.18.0" -prometheus-client = { version = "0.21.1"} +prometheus-client = { version = "0.21.2"} # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/misc/multistream-select/Cargo.toml b/misc/multistream-select/Cargo.toml index f75852ba1d7f..c47c4c0788dd 100644 --- a/misc/multistream-select/Cargo.toml +++ b/misc/multistream-select/Cargo.toml @@ -14,8 +14,8 @@ categories = ["network-programming", "asynchronous"] bytes = "1" futures = "0.3" log = "0.4" -pin-project = "1.1.0" -smallvec = "1.6.1" +pin-project = "1.1.2" +smallvec = "1.11.0" unsigned-varint = "0.7" [dev-dependencies] diff --git a/misc/multistream-select/src/length_delimited.rs b/misc/multistream-select/src/length_delimited.rs index 85f6ffc141d1..cff2f4abc391 100644 --- a/misc/multistream-select/src/length_delimited.rs +++ b/misc/multistream-select/src/length_delimited.rs @@ -411,7 +411,7 @@ mod tests { assert!(len < (1 << 15)); let frame = (0..len).map(|n| (n & 0xff) as u8).collect::>(); let mut data = vec![(len & 0x7f) as u8 | 0x80, (len >> 7) as u8]; - data.extend(frame.clone().into_iter()); + data.extend(frame.clone()); let mut framed = LengthDelimited::new(Cursor::new(data)); let recved = futures::executor::block_on(async move { framed.next().await }).unwrap(); assert_eq!(recved.unwrap(), frame); diff --git a/misc/multistream-select/tests/dialer_select.rs b/misc/multistream-select/tests/dialer_select.rs index 18f8238cd25f..d1b276458c4b 100644 --- a/misc/multistream-select/tests/dialer_select.rs +++ b/misc/multistream-select/tests/dialer_select.rs @@ -47,10 +47,9 @@ fn select_proto_basic() { let client = async_std::task::spawn(async move { let protos = vec!["/proto3", "/proto2"]; - let (proto, mut io) = - dialer_select_proto(client_connection, protos.into_iter(), version) - .await - .unwrap(); + let (proto, mut io) = dialer_select_proto(client_connection, protos, version) + .await + .unwrap(); assert_eq!(proto, "/proto2"); io.write_all(b"ping").await.unwrap(); @@ -99,25 +98,21 @@ fn negotiation_failed() { } }); - let client = - async_std::task::spawn(async move { - let mut io = - match dialer_select_proto(client_connection, dial_protos.into_iter(), version) - .await - { - Err(NegotiationError::Failed) => return, - Ok((_, io)) => io, - Err(_) => panic!(), - }; - // The dialer may write a payload that is even sent before it - // got confirmation of the last proposed protocol, when `V1Lazy` - // is used. - io.write_all(&dial_payload).await.unwrap(); - match io.complete().await { - Err(NegotiationError::Failed) => {} - _ => panic!(), - } - }); + let client = async_std::task::spawn(async move { + let mut io = match dialer_select_proto(client_connection, dial_protos, version).await { + Err(NegotiationError::Failed) => return, + Ok((_, io)) => io, + Err(_) => panic!(), + }; + // The dialer may write a payload that is even sent before it + // got confirmation of the last proposed protocol, when `V1Lazy` + // is used. + io.write_all(&dial_payload).await.unwrap(); + match io.complete().await { + Err(NegotiationError::Failed) => {} + _ => panic!(), + } + }); server.await; client.await; @@ -185,10 +180,9 @@ async fn v1_lazy_do_not_wait_for_negotiation_on_poll_close() { let client = async_std::task::spawn(async move { // Single protocol to allow for lazy (or optimistic) protocol negotiation. let protos = vec!["/proto1"]; - let (proto, mut io) = - dialer_select_proto(client_connection, protos.into_iter(), Version::V1Lazy) - .await - .unwrap(); + let (proto, mut io) = dialer_select_proto(client_connection, protos, Version::V1Lazy) + .await + .unwrap(); assert_eq!(proto, "/proto1"); // client can close the connection even though protocol negotiation is not yet done, i.e. diff --git a/misc/rw-stream-sink/Cargo.toml b/misc/rw-stream-sink/Cargo.toml index 61640ccd890f..e1886c5f1c8b 100644 --- a/misc/rw-stream-sink/Cargo.toml +++ b/misc/rw-stream-sink/Cargo.toml @@ -12,7 +12,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.28" -pin-project = "1.1.0" +pin-project = "1.1.2" static_assertions = "1" [dev-dependencies] diff --git a/muxers/mplex/Cargo.toml b/muxers/mplex/Cargo.toml index 6e617482422c..7a98fd1350be 100644 --- a/muxers/mplex/Cargo.toml +++ b/muxers/mplex/Cargo.toml @@ -20,7 +20,7 @@ log = "0.4" nohash-hasher = "0.2" parking_lot = "0.12" rand = "0.8" -smallvec = "1.6.1" +smallvec = "1.11.0" unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } [dev-dependencies] diff --git a/muxers/mplex/src/lib.rs b/muxers/mplex/src/lib.rs index fa36fecfefbe..7aa665d7467c 100644 --- a/muxers/mplex/src/lib.rs +++ b/muxers/mplex/src/lib.rs @@ -55,6 +55,7 @@ where fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future { future::ready(Ok(Multiplex { + #[allow(clippy::arc_with_non_send_sync)] // `T` is not enforced to be `Send` but we don't want to constrain it either. io: Arc::new(Mutex::new(io::Multiplexed::new(socket, self))), })) } @@ -70,6 +71,7 @@ where fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future { future::ready(Ok(Multiplex { + #[allow(clippy::arc_with_non_send_sync)] // `T` is not enforced to be `Send` but we don't want to constrain it either. io: Arc::new(Mutex::new(io::Multiplexed::new(socket, self))), })) } diff --git a/protocols/dcutr/Cargo.toml b/protocols/dcutr/Cargo.toml index c74b40b00027..7cbfa11a80f2 100644 --- a/protocols/dcutr/Cargo.toml +++ b/protocols/dcutr/Cargo.toml @@ -27,7 +27,7 @@ void = "1" [dev-dependencies] async-std = { version = "1.12.0", features = ["attributes"] } -clap = { version = "4.3.3", features = ["derive"] } +clap = { version = "4.3.11", features = ["derive"] } env_logger = "0.10.0" libp2p-dns = { workspace = true, features = ["async-std"] } libp2p-identify = { workspace = true } diff --git a/protocols/floodsub/Cargo.toml b/protocols/floodsub/Cargo.toml index 00fa57c02721..1905c8d36d72 100644 --- a/protocols/floodsub/Cargo.toml +++ b/protocols/floodsub/Cargo.toml @@ -22,8 +22,8 @@ log = "0.4" quick-protobuf = "0.8" quick-protobuf-codec = { workspace = true } rand = "0.8" -smallvec = "1.6.1" -thiserror = "1.0.40" +smallvec = "1.11.0" +thiserror = "1.0.43" # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/protocols/gossipsub/Cargo.toml b/protocols/gossipsub/Cargo.toml index 5b4a5e545750..25232ff5bfd6 100644 --- a/protocols/gossipsub/Cargo.toml +++ b/protocols/gossipsub/Cargo.toml @@ -32,15 +32,15 @@ log = "0.4.19" quick-protobuf = "0.8" quick-protobuf-codec = { workspace = true } rand = "0.8" -regex = "1.8.3" +regex = "1.9.1" serde = { version = "1", optional = true, features = ["derive"] } sha2 = "0.10.7" -smallvec = "1.6.1" +smallvec = "1.11.0" unsigned-varint = { version = "0.7.0", features = ["asynchronous_codec"] } void = "1.0.2" # Metrics dependencies -prometheus-client = "0.21.0" +prometheus-client = "0.21.2" [dev-dependencies] async-std = { version = "1.6.3", features = ["unstable"] } diff --git a/protocols/gossipsub/src/behaviour/tests.rs b/protocols/gossipsub/src/behaviour/tests.rs index 37f25631ae44..b2414fd7afc8 100644 --- a/protocols/gossipsub/src/behaviour/tests.rs +++ b/protocols/gossipsub/src/behaviour/tests.rs @@ -858,7 +858,7 @@ fn test_handle_received_subscriptions() { // UNSUBSCRIBE - Remove topic from peer_topics for peer. // - Remove peer from topic_peers. - let topics = vec!["topic1", "topic2", "topic3", "topic4"] + let topics = ["topic1", "topic2", "topic3", "topic4"] .iter() .map(|&t| String::from(t)) .collect(); @@ -1280,7 +1280,7 @@ fn test_handle_graft_is_not_subscribed() { #[test] // tests multiple topics in a single graft message fn test_handle_graft_multiple_topics() { - let topics: Vec = vec!["topic1", "topic2", "topic3", "topic4"] + let topics: Vec = ["topic1", "topic2", "topic3", "topic4"] .iter() .map(|&t| String::from(t)) .collect(); diff --git a/protocols/gossipsub/src/subscription_filter.rs b/protocols/gossipsub/src/subscription_filter.rs index 8ec633d07178..9f883f12a1b7 100644 --- a/protocols/gossipsub/src/subscription_filter.rs +++ b/protocols/gossipsub/src/subscription_filter.rs @@ -222,7 +222,7 @@ mod test { let t1 = TopicHash::from_raw("t1"); let t2 = TopicHash::from_raw("t2"); - let old = BTreeSet::from_iter(vec![t1.clone()].into_iter()); + let old = BTreeSet::from_iter(vec![t1.clone()]); let subscriptions = vec![ Subscription { action: Unsubscribe, diff --git a/protocols/identify/Cargo.toml b/protocols/identify/Cargo.toml index f323d1db5d6e..aa9587a0d368 100644 --- a/protocols/identify/Cargo.toml +++ b/protocols/identify/Cargo.toml @@ -18,10 +18,10 @@ libp2p-core = { workspace = true } libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } log = "0.4.19" -lru = "0.10.0" +lru = "0.10.1" quick-protobuf-codec = { workspace = true } quick-protobuf = "0.8" -smallvec = "1.6.1" +smallvec = "1.11.0" thiserror = "1.0" void = "1.0" either = "1.8.0" diff --git a/protocols/kad/CHANGELOG.md b/protocols/kad/CHANGELOG.md index f545ba698852..f0d0d689144a 100644 --- a/protocols/kad/CHANGELOG.md +++ b/protocols/kad/CHANGELOG.md @@ -1,4 +1,18 @@ -## 0.44.0 +## 0.44.2 + +- Allow to explicitly set `Mode::{Client,Server}`. + See [PR 4132] + +[PR 4132]: https://github.com/libp2p/rust-libp2p/pull/4132 + +## 0.44.1 + +- Expose `KBucketDistance`. + See [PR 4109]. + +[PR 4109]: https://github.com/libp2p/rust-libp2p/pull/4109 + +## 0.44.0 - Raise MSRV to 1.65. See [PR 3715]. diff --git a/protocols/kad/Cargo.toml b/protocols/kad/Cargo.toml index 6fac39006df7..49732c85d8be 100644 --- a/protocols/kad/Cargo.toml +++ b/protocols/kad/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-kad" edition = "2021" rust-version = "1.65.0" description = "Kademlia protocol for libp2p" -version = "0.44.0" +version = "0.44.2" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -24,7 +24,7 @@ quick-protobuf = "0.8" libp2p-identity = { workspace = true } rand = "0.8" sha2 = "0.10.7" -smallvec = "1.6.1" +smallvec = "1.11.0" uint = "0.9" unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } void = "1.0" diff --git a/protocols/kad/src/behaviour.rs b/protocols/kad/src/behaviour.rs index 7df17c91e1af..a594946ecb6d 100644 --- a/protocols/kad/src/behaviour.rs +++ b/protocols/kad/src/behaviour.rs @@ -52,7 +52,7 @@ use smallvec::SmallVec; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::fmt; use std::num::NonZeroUsize; -use std::task::{Context, Poll}; +use std::task::{Context, Poll, Waker}; use std::time::Duration; use std::vec; use thiserror::Error; @@ -114,6 +114,8 @@ pub struct Kademlia { local_peer_id: PeerId, mode: Mode, + auto_mode: bool, + no_events_waker: Option, /// The record storage. store: TStore, @@ -456,6 +458,8 @@ where local_peer_id: id, connections: Default::default(), mode: Mode::Client, + auto_mode: true, + no_events_waker: None, } } @@ -990,6 +994,94 @@ where id } + /// Set the [`Mode`] in which we should operate. + /// + /// By default, we are in [`Mode::Client`] and will swap into [`Mode::Server`] as soon as we have a confirmed, external address via [`FromSwarm::ExternalAddrConfirmed`]. + /// + /// Setting a mode via this function disables this automatic behaviour and unconditionally operates in the specified mode. + /// To reactivate the automatic configuration, pass [`None`] instead. + pub fn set_mode(&mut self, mode: Option) { + match mode { + Some(mode) => { + self.mode = mode; + self.auto_mode = false; + self.reconfigure_mode(); + } + None => { + self.auto_mode = true; + self.determine_mode_from_external_addresses(); + } + } + + if let Some(waker) = self.no_events_waker.take() { + waker.wake(); + } + } + + fn reconfigure_mode(&mut self) { + if self.connections.is_empty() { + return; + } + + let num_connections = self.connections.len(); + + log::debug!( + "Re-configuring {} established connection{}", + num_connections, + if num_connections > 1 { "s" } else { "" } + ); + + self.queued_events + .extend( + self.connections + .iter() + .map(|(conn_id, peer_id)| ToSwarm::NotifyHandler { + peer_id: *peer_id, + handler: NotifyHandler::One(*conn_id), + event: KademliaHandlerIn::ReconfigureMode { + new_mode: self.mode, + }, + }), + ); + } + + fn determine_mode_from_external_addresses(&mut self) { + self.mode = match (self.external_addresses.as_slice(), self.mode) { + ([], Mode::Server) => { + log::debug!("Switching to client-mode because we no longer have any confirmed external addresses"); + + Mode::Client + } + ([], Mode::Client) => { + // Previously client-mode, now also client-mode because no external addresses. + + Mode::Client + } + (confirmed_external_addresses, Mode::Client) => { + if log::log_enabled!(log::Level::Debug) { + let confirmed_external_addresses = + to_comma_separated_list(confirmed_external_addresses); + + log::debug!("Switching to server-mode assuming that one of [{confirmed_external_addresses}] is externally reachable"); + } + + Mode::Server + } + (confirmed_external_addresses, Mode::Server) => { + debug_assert!( + !confirmed_external_addresses.is_empty(), + "Previous match arm handled empty list" + ); + + // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. + + Mode::Server + } + }; + + self.reconfigure_mode(); + } + /// Processes discovered peers from a successful request in an iterative `Query`. fn discovered<'a, I>(&'a mut self, query_id: &QueryId, source: &PeerId, peers: I) where @@ -2428,6 +2520,8 @@ where // If no new events have been queued either, signal `NotReady` to // be polled again later. if self.queued_events.is_empty() { + self.no_events_waker = Some(cx.waker().clone()); + return Poll::Pending; } } @@ -2437,60 +2531,8 @@ where self.listen_addresses.on_swarm_event(&event); let external_addresses_changed = self.external_addresses.on_swarm_event(&event); - self.mode = match (self.external_addresses.as_slice(), self.mode) { - ([], Mode::Server) => { - log::debug!("Switching to client-mode because we no longer have any confirmed external addresses"); - - Mode::Client - } - ([], Mode::Client) => { - // Previously client-mode, now also client-mode because no external addresses. - - Mode::Client - } - (confirmed_external_addresses, Mode::Client) => { - if log::log_enabled!(log::Level::Debug) { - let confirmed_external_addresses = - to_comma_separated_list(confirmed_external_addresses); - - log::debug!("Switching to server-mode assuming that one of [{confirmed_external_addresses}] is externally reachable"); - } - - Mode::Server - } - (confirmed_external_addresses, Mode::Server) => { - debug_assert!( - !confirmed_external_addresses.is_empty(), - "Previous match arm handled empty list" - ); - - // Previously, server-mode, now also server-mode because > 1 external address. Don't log anything to avoid spam. - - Mode::Server - } - }; - - if external_addresses_changed && !self.connections.is_empty() { - let num_connections = self.connections.len(); - - log::debug!( - "External addresses changed, re-configuring {} established connection{}", - num_connections, - if num_connections > 1 { "s" } else { "" } - ); - - self.queued_events - .extend( - self.connections - .iter() - .map(|(conn_id, peer_id)| ToSwarm::NotifyHandler { - peer_id: *peer_id, - handler: NotifyHandler::One(*conn_id), - event: KademliaHandlerIn::ReconfigureMode { - new_mode: self.mode, - }, - }), - ); + if self.auto_mode && external_addresses_changed { + self.determine_mode_from_external_addresses(); } match event { diff --git a/protocols/kad/src/lib.rs b/protocols/kad/src/lib.rs index 7e51f8b9908d..ccdb06b885d2 100644 --- a/protocols/kad/src/lib.rs +++ b/protocols/kad/src/lib.rs @@ -68,7 +68,7 @@ pub use behaviour::{ AddProviderContext, AddProviderError, AddProviderOk, AddProviderPhase, AddProviderResult, BootstrapError, BootstrapOk, BootstrapResult, GetClosestPeersError, GetClosestPeersOk, GetClosestPeersResult, GetProvidersError, GetProvidersOk, GetProvidersResult, GetRecordError, - GetRecordOk, GetRecordResult, InboundRequest, NoKnownPeers, PeerRecord, PutRecordContext, + GetRecordOk, GetRecordResult, InboundRequest, Mode, NoKnownPeers, PeerRecord, PutRecordContext, PutRecordError, PutRecordOk, PutRecordPhase, PutRecordResult, QueryInfo, QueryMut, QueryRef, QueryResult, QueryStats, RoutingUpdate, }; @@ -76,7 +76,7 @@ pub use behaviour::{ Kademlia, KademliaBucketInserts, KademliaCaching, KademliaConfig, KademliaEvent, KademliaStoreInserts, ProgressStep, Quorum, }; -pub use kbucket::{EntryView, KBucketRef, Key as KBucketKey}; +pub use kbucket::{Distance as KBucketDistance, EntryView, KBucketRef, Key as KBucketKey}; pub use protocol::KadConnectionType; pub use query::QueryId; pub use record_priv::{store, Key as RecordKey, ProviderRecord, Record}; diff --git a/protocols/kad/tests/client_mode.rs b/protocols/kad/tests/client_mode.rs index b187b6b96856..b25305695180 100644 --- a/protocols/kad/tests/client_mode.rs +++ b/protocols/kad/tests/client_mode.rs @@ -1,7 +1,7 @@ use libp2p_identify as identify; use libp2p_identity as identity; use libp2p_kad::store::MemoryStore; -use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent}; +use libp2p_kad::{Kademlia, KademliaConfig, KademliaEvent, Mode}; use libp2p_swarm::Swarm; use libp2p_swarm_test::SwarmExt; @@ -59,7 +59,11 @@ async fn two_servers_add_each_other_to_routing_table() { match libp2p_swarm_test::drive(&mut server2, &mut server1).await { ( - [Identify(_), Kad(RoutingUpdated { peer: peer2, .. }), Identify(_)], + [Identify(_), Kad(UnroutablePeer { .. }), Identify(_), Kad(RoutingUpdated { peer: peer2, .. }), Identify(_)], + [Identify(_), Identify(_)], + ) + | ( + [Identify(_), Kad(UnroutablePeer { .. }), Identify(_), Identify(_), Kad(RoutingUpdated { peer: peer2, .. })], [Identify(_), Identify(_)], ) => { assert_eq!(peer2, server1_peer_id); @@ -107,6 +111,50 @@ async fn adding_an_external_addresses_activates_server_mode_on_existing_connecti } } +#[async_std::test] +async fn set_client_to_server_mode() { + let _ = env_logger::try_init(); + + let mut client = Swarm::new_ephemeral(MyBehaviour::new); + client.behaviour_mut().kad.set_mode(Some(Mode::Client)); + + let mut server = Swarm::new_ephemeral(MyBehaviour::new); + + server.listen().await; + client.connect(&mut server).await; + + let server_peer_id = *server.local_peer_id(); + + match libp2p_swarm_test::drive(&mut client, &mut server).await { + ( + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(_), MyBehaviourEvent::Kad(KademliaEvent::RoutingUpdated { peer, .. })], + [MyBehaviourEvent::Identify(_), MyBehaviourEvent::Identify(identify::Event::Received { info, .. })], + ) => { + assert_eq!(peer, server_peer_id); + assert!(info + .protocols + .iter() + .all(|proto| libp2p_kad::PROTOCOL_NAME.ne(proto))) + } + other => panic!("Unexpected events: {other:?}"), + } + + client.behaviour_mut().kad.set_mode(Some(Mode::Server)); + + match libp2p_swarm_test::drive(&mut client, &mut server).await { + ( + [MyBehaviourEvent::Identify(_)], + [MyBehaviourEvent::Identify(identify::Event::Received { info, .. }), MyBehaviourEvent::Kad(_)], + ) => { + assert!(info + .protocols + .iter() + .any(|proto| libp2p_kad::PROTOCOL_NAME.eq(proto))) + } + other => panic!("Unexpected events: {other:?}"), + } +} + #[derive(libp2p_swarm::NetworkBehaviour)] #[behaviour(prelude = "libp2p_swarm::derive_prelude")] struct MyBehaviour { diff --git a/protocols/mdns/Cargo.toml b/protocols/mdns/Cargo.toml index cd7e658e4183..830547e7bbde 100644 --- a/protocols/mdns/Cargo.toml +++ b/protocols/mdns/Cargo.toml @@ -20,9 +20,9 @@ libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } log = "0.4.19" rand = "0.8.3" -smallvec = "1.6.1" +smallvec = "1.11.0" socket2 = { version = "0.5.3", features = ["all"] } -tokio = { version = "1.28", default-features = false, features = ["net", "time"], optional = true} +tokio = { version = "1.29", default-features = false, features = ["net", "time"], optional = true} trust-dns-proto = { version = "0.22.0", default-features = false, features = ["mdns", "tokio-runtime"] } void = "1.0.2" @@ -37,7 +37,7 @@ libp2p-noise = { workspace = true } libp2p-swarm = { workspace = true, features = ["tokio", "async-std"] } libp2p-tcp = { workspace = true, features = ["tokio", "async-io"] } libp2p-yamux = { workspace = true } -tokio = { version = "1.28", default-features = false, features = ["macros", "rt", "rt-multi-thread", "time"] } +tokio = { version = "1.29", default-features = false, features = ["macros", "rt", "rt-multi-thread", "time"] } libp2p-swarm-test = { path = "../../swarm-test" } [[test]] diff --git a/protocols/mdns/tests/use-async-std.rs b/protocols/mdns/tests/use-async-std.rs index bfc3cd1201d6..6d45d92cdd96 100644 --- a/protocols/mdns/tests/use-async-std.rs +++ b/protocols/mdns/tests/use-async-std.rs @@ -148,7 +148,20 @@ async fn run_discovery_test(config: Config) { async fn create_swarm(config: Config) -> Swarm { let mut swarm = Swarm::new_ephemeral(|key| Behaviour::new(config, key.public().to_peer_id()).unwrap()); - swarm.listen().await; + + // Manually listen on all interfaces because mDNS only works for non-loopback addresses. + let expected_listener_id = swarm + .listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()) + .unwrap(); + + swarm + .wait(|e| match e { + SwarmEvent::NewListenAddr { listener_id, .. } => { + (listener_id == expected_listener_id).then_some(()) + } + _ => None, + }) + .await; swarm } diff --git a/protocols/mdns/tests/use-tokio.rs b/protocols/mdns/tests/use-tokio.rs index 229418437f45..50d6be0c00f3 100644 --- a/protocols/mdns/tests/use-tokio.rs +++ b/protocols/mdns/tests/use-tokio.rs @@ -19,7 +19,7 @@ // DEALINGS IN THE SOFTWARE.use futures::StreamExt; use futures::future::Either; use libp2p_mdns::{tokio::Behaviour, Config, Event}; -use libp2p_swarm::Swarm; +use libp2p_swarm::{Swarm, SwarmEvent}; use libp2p_swarm_test::SwarmExt as _; use std::time::Duration; @@ -104,7 +104,20 @@ async fn run_discovery_test(config: Config) { async fn create_swarm(config: Config) -> Swarm { let mut swarm = Swarm::new_ephemeral(|key| Behaviour::new(config, key.public().to_peer_id()).unwrap()); - swarm.listen().await; + + // Manually listen on all interfaces because mDNS only works for non-loopback addresses. + let expected_listener_id = swarm + .listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()) + .unwrap(); + + swarm + .wait(|e| match e { + SwarmEvent::NewListenAddr { listener_id, .. } => { + (listener_id == expected_listener_id).then_some(()) + } + _ => None, + }) + .await; swarm } diff --git a/protocols/perf/Cargo.toml b/protocols/perf/Cargo.toml index ed78a57bdc24..6f5903e76357 100644 --- a/protocols/perf/Cargo.toml +++ b/protocols/perf/Cargo.toml @@ -13,7 +13,7 @@ categories = ["network-programming", "asynchronous"] [dependencies] anyhow = "1" async-trait = "0.1" -clap = { version = "4.3.3", features = ["derive"] } +clap = { version = "4.3.11", features = ["derive"] } env_logger = "0.10.0" futures = "0.3.28" instant = "0.1.12" @@ -30,7 +30,7 @@ log = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -tokio = { version = "1.28.2", features = ["full"] } +tokio = { version = "1.29.1", features = ["full"] } void = "1" [dev-dependencies] diff --git a/protocols/perf/src/bin/perf.rs b/protocols/perf/src/bin/perf.rs index 83ab7fda61ca..b6b090608f5d 100644 --- a/protocols/perf/src/bin/perf.rs +++ b/protocols/perf/src/bin/perf.rs @@ -198,25 +198,22 @@ async fn custom(server_address: Multiaddr, params: RunParams) -> Result<()> { info!("start benchmark: custom"); let mut swarm = swarm().await?; - let (server_peer_id, connection_established) = - connect(&mut swarm, server_address.clone()).await?; + let start = Instant::now(); + + let server_peer_id = connect(&mut swarm, server_address.clone()).await?; - let RunDuration { upload, download } = perf(&mut swarm, server_peer_id, params).await?; + perf(&mut swarm, server_peer_id, params).await?; #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct CustomResult { - connection_established_seconds: f64, - upload_seconds: f64, - download_seconds: f64, + latency: f64, } println!( "{}", serde_json::to_string(&CustomResult { - connection_established_seconds: connection_established.as_secs_f64(), - upload_seconds: upload.as_secs_f64(), - download_seconds: download.as_secs_f64(), + latency: start.elapsed().as_secs_f64(), }) .unwrap() ); @@ -228,7 +225,7 @@ async fn latency(server_address: Multiaddr) -> Result<()> { info!("start benchmark: round-trip-time latency"); let mut swarm = swarm().await?; - let (server_peer_id, _) = connect(&mut swarm, server_address.clone()).await?; + let server_peer_id = connect(&mut swarm, server_address.clone()).await?; let mut rounds = 0; let start = Instant::now(); @@ -275,7 +272,7 @@ async fn throughput(server_address: Multiaddr) -> Result<()> { info!("start benchmark: single connection single channel throughput"); let mut swarm = swarm().await?; - let (server_peer_id, _) = connect(&mut swarm, server_address.clone()).await?; + let server_peer_id = connect(&mut swarm, server_address.clone()).await?; let params = RunParams { to_send: 10 * 1024 * 1024, @@ -291,7 +288,7 @@ async fn requests_per_second(server_address: Multiaddr) -> Result<()> { info!("start benchmark: single connection parallel requests per second"); let mut swarm = swarm().await?; - let (server_peer_id, _) = connect(&mut swarm, server_address.clone()).await?; + let server_peer_id = connect(&mut swarm, server_address.clone()).await?; let num = 1_000; let to_send = 1; @@ -356,7 +353,7 @@ async fn sequential_connections_per_second(server_address: Multiaddr) -> Result< let start = Instant::now(); - let (server_peer_id, _) = connect(&mut swarm, server_address.clone()).await?; + let server_peer_id = connect(&mut swarm, server_address.clone()).await?; latency_connection_establishment.push(start.elapsed().as_secs_f64()); @@ -427,7 +424,7 @@ async fn swarm() -> Result> { async fn connect( swarm: &mut Swarm, server_address: Multiaddr, -) -> Result<(PeerId, Duration)> { +) -> Result { let start = Instant::now(); swarm.dial(server_address.clone()).unwrap(); @@ -446,7 +443,7 @@ async fn connect( info!("established connection in {duration_seconds:.4} s"); - Ok((server_peer_id, duration)) + Ok(server_peer_id) } async fn perf( diff --git a/protocols/rendezvous/Cargo.toml b/protocols/rendezvous/Cargo.toml index 8b973ad020e1..b24781dcd748 100644 --- a/protocols/rendezvous/Cargo.toml +++ b/protocols/rendezvous/Cargo.toml @@ -37,7 +37,7 @@ libp2p-identify = { workspace = true } libp2p-yamux = { workspace = true } libp2p-tcp = { workspace = true, features = ["tokio"] } rand = "0.8" -tokio = { version = "1.28", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } +tokio = { version = "1.29", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs", "net" ] } libp2p-swarm-test = { path = "../../swarm-test" } # Passing arguments to the docsrs builder in order to properly document cfg's. diff --git a/protocols/request-response/Cargo.toml b/protocols/request-response/Cargo.toml index 6aae99a9193d..47e6c9e5fc61 100644 --- a/protocols/request-response/Cargo.toml +++ b/protocols/request-response/Cargo.toml @@ -19,9 +19,9 @@ libp2p-swarm = { workspace = true } libp2p-identity = { workspace = true } rand = "0.8" serde = { version = "1.0", optional = true} -serde_json = { version = "1.0.97", optional = true } +serde_json = { version = "1.0.100", optional = true } serde_cbor = { version = "0.11.2", optional = true } -smallvec = "1.6.1" +smallvec = "1.11.0" void = "1.0.2" log = "0.4.19" diff --git a/swarm-derive/Cargo.toml b/swarm-derive/Cargo.toml index 30fcf6a67c77..68c2ad2d94f0 100644 --- a/swarm-derive/Cargo.toml +++ b/swarm-derive/Cargo.toml @@ -16,7 +16,7 @@ proc-macro = true [dependencies] heck = "0.4" quote = "1.0" -syn = { version = "2.0.18", default-features = false, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] } +syn = { version = "2.0.26", default-features = false, features = ["clone-impls", "derive", "parsing", "printing", "proc-macro"] } proc-macro-warning = "0.4.0" proc-macro2 = "1.0" diff --git a/swarm-test/Cargo.toml b/swarm-test/Cargo.toml index 617e65529993..45a45f8daa37 100644 --- a/swarm-test/Cargo.toml +++ b/swarm-test/Cargo.toml @@ -12,7 +12,7 @@ categories = ["network-programming", "asynchronous"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.68" +async-trait = "0.1.71" libp2p-core = { workspace = true } libp2p-identity = { workspace = true } libp2p-plaintext = { workspace = true } diff --git a/swarm-test/src/lib.rs b/swarm-test/src/lib.rs index d7d543835799..0ed8dbce2208 100644 --- a/swarm-test/src/lib.rs +++ b/swarm-test/src/lib.rs @@ -312,7 +312,7 @@ where self.add_external_address(memory_multiaddr.clone()); let tcp_addr_listener_id = self - .listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()) + .listen_on("/ip4/127.0.0.1/tcp/0".parse().unwrap()) .unwrap(); let tcp_multiaddr = self diff --git a/swarm/CHANGELOG.md b/swarm/CHANGELOG.md index ab5be59c5786..ecf49a382363 100644 --- a/swarm/CHANGELOG.md +++ b/swarm/CHANGELOG.md @@ -1,4 +1,12 @@ -## 0.43.0 +## 0.43.1 + +- Do not announce external address candidate before address translation, unless translation does not apply. + This will prevent ephemeral TCP addresses being announced as external address candidates. + See [PR 4158]. + +[PR 4158]: https://github.com/libp2p/rust-libp2p/pull/4158 + +## 0.43.0 - Allow `NetworkBehaviours` to create and remove listeners. See [PR 3292]. diff --git a/swarm/Cargo.toml b/swarm/Cargo.toml index e09158fcc0f1..35279c4d1933 100644 --- a/swarm/Cargo.toml +++ b/swarm/Cargo.toml @@ -3,7 +3,7 @@ name = "libp2p-swarm" edition = "2021" rust-version = { workspace = true } description = "The libp2p swarm" -version = "0.43.0" +version = "0.43.1" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" @@ -21,7 +21,7 @@ libp2p-identity = { workspace = true } libp2p-swarm-derive = { workspace = true, optional = true } log = "0.4" rand = "0.8" -smallvec = "1.6.1" +smallvec = "1.11.0" void = "1" wasm-bindgen-futures = { version = "0.4.37", optional = true } getrandom = { version = "0.2.9", features = ["js"], optional = true } # Explicit dependency to be used in `wasm-bindgen` feature @@ -30,7 +30,7 @@ multistream-select = { workspace = true } [target.'cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))'.dependencies] async-std = { version = "1.6.2", optional = true } -tokio = { version = "1.28", features = ["rt"], optional = true } +tokio = { version = "1.29", features = ["rt"], optional = true } [features] macros = ["dep:libp2p-swarm-derive"] diff --git a/swarm/src/behaviour.rs b/swarm/src/behaviour.rs index 0615457291a6..0ecdf7b31f04 100644 --- a/swarm/src/behaviour.rs +++ b/swarm/src/behaviour.rs @@ -443,7 +443,7 @@ pub enum FromSwarm<'a, Handler> { ListenerClosed(ListenerClosed<'a>), /// Informs the behaviour that we have discovered a new candidate for an external address for us. NewExternalAddrCandidate(NewExternalAddrCandidate<'a>), - /// Informs the behaviour that an external address of the local node was removed. + /// Informs the behaviour that an external address of the local node was confirmed. ExternalAddrConfirmed(ExternalAddrConfirmed<'a>), /// Informs the behaviour that an external address of the local node expired, i.e. is no-longer confirmed. ExternalAddrExpired(ExternalAddrExpired<'a>), @@ -541,14 +541,13 @@ pub struct ListenerClosed<'a> { pub reason: Result<(), &'a std::io::Error>, } -/// [`FromSwarm`] variant that informs the behaviour -/// that we have discovered a new candidate for an external address for us. +/// [`FromSwarm`] variant that informs the behaviour about a new candidate for an external address for us. #[derive(Clone, Copy)] pub struct NewExternalAddrCandidate<'a> { pub addr: &'a Multiaddr, } -/// [`FromSwarm`] variant that informs the behaviour that an external address was removed. +/// [`FromSwarm`] variant that informs the behaviour that an external address was confirmed. #[derive(Clone, Copy)] pub struct ExternalAddrConfirmed<'a> { pub addr: &'a Multiaddr, diff --git a/swarm/src/lib.rs b/swarm/src/lib.rs index 9b739f337806..f8c8e855bd58 100644 --- a/swarm/src/lib.rs +++ b/swarm/src/lib.rs @@ -536,7 +536,7 @@ where &self.local_peer_id } - /// TODO + /// List all **confirmed** external address for the local node. pub fn external_addresses(&self) -> impl Iterator { self.confirmed_external_addr.iter() } @@ -1062,14 +1062,8 @@ where self.pending_event = Some((peer_id, handler, event)); } ToSwarm::NewExternalAddrCandidate(addr) => { - self.behaviour - .on_swarm_event(FromSwarm::NewExternalAddrCandidate( - NewExternalAddrCandidate { addr: &addr }, - )); - - // Generate more candidates based on address translation. + // Apply address translation to the candidate address. // For TCP without port-reuse, the observed address contains an ephemeral port which needs to be replaced by the port of a listen address. - let translated_addresses = { let mut addrs: Vec<_> = self .listened_addrs @@ -1083,11 +1077,20 @@ where addrs.dedup(); addrs }; - for addr in translated_addresses { + + // If address translation yielded nothing, broacast the original candidate address. + if translated_addresses.is_empty() { self.behaviour .on_swarm_event(FromSwarm::NewExternalAddrCandidate( NewExternalAddrCandidate { addr: &addr }, )); + } else { + for addr in translated_addresses { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); + } } } ToSwarm::ExternalAddrConfirmed(addr) => { diff --git a/transports/dns/Cargo.toml b/transports/dns/Cargo.toml index b8956d20e96a..d1afd0411f94 100644 --- a/transports/dns/Cargo.toml +++ b/transports/dns/Cargo.toml @@ -18,7 +18,7 @@ futures = "0.3.28" async-std-resolver = { version = "0.22", optional = true } parking_lot = "0.12.0" trust-dns-resolver = { version = "0.22", default-features = false, features = ["system-config"] } -smallvec = "1.6.1" +smallvec = "1.11.0" [dev-dependencies] env_logger = "0.10" diff --git a/transports/dns/src/lib.rs b/transports/dns/src/lib.rs index c39b9ad05ecb..771c7dbc94aa 100644 --- a/transports/dns/src/lib.rs +++ b/transports/dns/src/lib.rs @@ -129,7 +129,10 @@ where } #[cfg(feature = "async-std")] -impl DnsConfig { +impl DnsConfig +where + T: Send, +{ /// Creates a new [`DnsConfig`] from the OS's DNS configuration and defaults. pub async fn system(inner: T) -> Result, io::Error> { let (cfg, opts) = system_conf::read_system_conf()?; @@ -150,7 +153,10 @@ impl DnsConfig { } #[cfg(feature = "tokio")] -impl TokioDnsConfig { +impl TokioDnsConfig +where + T: Send, +{ /// Creates a new [`TokioDnsConfig`] from the OS's DNS configuration and defaults. pub fn system(inner: T) -> Result, io::Error> { let (cfg, opts) = system_conf::read_system_conf()?; diff --git a/transports/noise/Cargo.toml b/transports/noise/Cargo.toml index 484d9a3773b2..355858e82fe1 100644 --- a/transports/noise/Cargo.toml +++ b/transports/noise/Cargo.toml @@ -22,7 +22,7 @@ quick-protobuf = "0.8" rand = "0.8.3" sha2 = "0.10.7" static_assertions = "1" -thiserror = "1.0.40" +thiserror = "1.0.43" x25519-dalek = "1.1.0" zeroize = "1" diff --git a/transports/noise/tests/webtransport_certhashes.rs b/transports/noise/tests/webtransport_certhashes.rs index 41341fe8655f..95ce0bf58db1 100644 --- a/transports/noise/tests/webtransport_certhashes.rs +++ b/transports/noise/tests/webtransport_certhashes.rs @@ -40,9 +40,10 @@ fn webtransport_server_empty_certhashes() { // Invalid case, because a MITM attacker may strip certificates of the server. let Err(noise::Error::UnknownWebTransportCerthashes(expected, received)) = - handshake_with_certhashes(vec![certhash1, certhash2], vec![]) else { - panic!("unexpected result"); - }; + handshake_with_certhashes(vec![certhash1, certhash2], vec![]) + else { + panic!("unexpected result"); + }; assert_eq!(expected, HashSet::from([certhash1, certhash2])); assert_eq!(received, HashSet::new()); @@ -68,9 +69,10 @@ fn webtransport_server_uninit_certhashes() { // Invalid case, because a MITM attacker may strip certificates of the server. let Err(noise::Error::UnknownWebTransportCerthashes(expected, received)) = - handshake_with_certhashes(vec![certhash1, certhash2], None) else { - panic!("unexpected result"); - }; + handshake_with_certhashes(vec![certhash1, certhash2], None) + else { + panic!("unexpected result"); + }; assert_eq!(expected, HashSet::from([certhash1, certhash2])); assert_eq!(received, HashSet::new()); @@ -81,9 +83,10 @@ fn webtransport_different_server_certhashes() { let (certhash1, certhash2, certhash3) = certhashes(); let Err(noise::Error::UnknownWebTransportCerthashes(expected, received)) = - handshake_with_certhashes(vec![certhash1, certhash3], vec![certhash1, certhash2]) else { - panic!("unexpected result"); - }; + handshake_with_certhashes(vec![certhash1, certhash3], vec![certhash1, certhash2]) + else { + panic!("unexpected result"); + }; assert_eq!(expected, HashSet::from([certhash1, certhash3])); assert_eq!(received, HashSet::from([certhash1, certhash2])); @@ -94,9 +97,10 @@ fn webtransport_superset_of_certhashes() { let (certhash1, certhash2, _) = certhashes(); let Err(noise::Error::UnknownWebTransportCerthashes(expected, received)) = - handshake_with_certhashes(vec![certhash1, certhash2], vec![certhash1]) else { - panic!("unexpected result"); - }; + handshake_with_certhashes(vec![certhash1, certhash2], vec![certhash1]) + else { + panic!("unexpected result"); + }; assert_eq!(expected, HashSet::from([certhash1, certhash2])); assert_eq!(received, HashSet::from([certhash1])); diff --git a/transports/pnet/Cargo.toml b/transports/pnet/Cargo.toml index 986ff9dd3f3c..b74c4ef29a73 100644 --- a/transports/pnet/Cargo.toml +++ b/transports/pnet/Cargo.toml @@ -16,7 +16,7 @@ log = "0.4.19" salsa20 = "0.10" sha3 = "0.10" rand = "0.8" -pin-project = "1.1.0" +pin-project = "1.1.2" [dev-dependencies] libp2p-core = { workspace = true } @@ -27,7 +27,7 @@ libp2p-tcp = { workspace = true, features = ["tokio"] } libp2p-websocket = { workspace = true } libp2p-yamux = { workspace = true } quickcheck = { workspace = true } -tokio = { version = "1.28.2", features = ["full"] } +tokio = { version = "1.29.1", features = ["full"] } # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/transports/quic/Cargo.toml b/transports/quic/Cargo.toml index 2655b34e83a0..338fa8147b7f 100644 --- a/transports/quic/Cargo.toml +++ b/transports/quic/Cargo.toml @@ -22,8 +22,8 @@ parking_lot = "0.12.0" quinn-proto = { version = "0.10.1", default-features = false, features = ["tls-rustls"] } rand = "0.8.5" rustls = { version = "0.21.2", default-features = false } -thiserror = "1.0.40" -tokio = { version = "1.28.2", default-features = false, features = ["net", "rt", "time"], optional = true } +thiserror = "1.0.43" +tokio = { version = "1.29.1", default-features = false, features = ["net", "rt", "time"], optional = true } [features] tokio = ["dep:tokio", "if-watch/tokio"] @@ -44,7 +44,7 @@ libp2p-noise = { workspace = true } libp2p-tcp = { workspace = true, features = ["async-io"] } libp2p-yamux = { workspace = true } quickcheck = "1" -tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread", "time"] } +tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread", "time"] } [[test]] name = "stream_compliance" diff --git a/transports/quic/src/transport.rs b/transports/quic/src/transport.rs index afdaf86cdf44..84f9e479ee85 100644 --- a/transports/quic/src/transport.rs +++ b/transports/quic/src/transport.rs @@ -747,7 +747,7 @@ fn is_quic_addr(addr: &Multiaddr, support_draft_29: bool) -> bool { matches!(third, QuicV1) } && matches!(fourth, Some(P2p(_)) | None) - && matches!(fifth, None) + && fifth.is_none() } /// Turns an IP address and port into the corresponding QUIC multiaddr. diff --git a/transports/tcp/Cargo.toml b/transports/tcp/Cargo.toml index 6207460311b5..aaf409169dcc 100644 --- a/transports/tcp/Cargo.toml +++ b/transports/tcp/Cargo.toml @@ -15,12 +15,12 @@ async-io = { version = "1.13.0", optional = true } futures = "0.3.28" futures-timer = "3.0" if-watch = "3.0.1" -libc = "0.2.145" +libc = "0.2.147" libp2p-core = { workspace = true } libp2p-identity = { workspace = true } log = "0.4.19" socket2 = { version = "0.5.3", features = ["all"] } -tokio = { version = "1.28.2", default-features = false, features = ["net"], optional = true } +tokio = { version = "1.29.1", default-features = false, features = ["net"], optional = true } [features] tokio = ["dep:tokio", "if-watch/tokio"] @@ -28,7 +28,7 @@ async-io = ["dep:async-io", "if-watch/smol"] [dev-dependencies] async-std = { version = "1.6.5", features = ["attributes"] } -tokio = { version = "1.28.2", default-features = false, features = ["full"] } +tokio = { version = "1.29.1", default-features = false, features = ["full"] } env_logger = "0.10.0" # Passing arguments to the docsrs builder in order to properly document cfg's. diff --git a/transports/tls/Cargo.toml b/transports/tls/Cargo.toml index 731fd7508ef8..c793201f3951 100644 --- a/transports/tls/Cargo.toml +++ b/transports/tls/Cargo.toml @@ -15,7 +15,7 @@ libp2p-core = { workspace = true } libp2p-identity = { workspace = true } rcgen = "0.10.0" ring = "0.16.20" -thiserror = "1.0.40" +thiserror = "1.0.43" webpki = { version = "0.22.0", features = ["std"] } x509-parser = "0.15.0" yasna = "0.5.2" @@ -33,7 +33,7 @@ libp2p-core = { workspace = true } libp2p-identity = { workspace = true, features = ["ed25519", "rsa", "secp256k1", "ecdsa"] } libp2p-swarm = { workspace = true } libp2p-yamux = { workspace = true } -tokio = { version = "1.28.2", features = ["full"] } +tokio = { version = "1.29.1", features = ["full"] } # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling diff --git a/transports/uds/Cargo.toml b/transports/uds/Cargo.toml index 1b33ba65aff1..66c412430ad3 100644 --- a/transports/uds/Cargo.toml +++ b/transports/uds/Cargo.toml @@ -15,7 +15,7 @@ async-std = { version = "1.6.2", optional = true } libp2p-core = { workspace = true } log = "0.4.19" futures = "0.3.28" -tokio = { version = "1.28", default-features = false, features = ["net"], optional = true } +tokio = { version = "1.29", default-features = false, features = ["net"], optional = true } [dev-dependencies] tempfile = "3.5" diff --git a/transports/uds/src/lib.rs b/transports/uds/src/lib.rs index 78a6b0f05b83..3cd71552d187 100644 --- a/transports/uds/src/lib.rs +++ b/transports/uds/src/lib.rs @@ -97,6 +97,7 @@ macro_rules! codegen { addr: Multiaddr, ) -> Result<(), TransportError> { if let Ok(path) = multiaddr_to_path(&addr) { + #[allow(clippy::redundant_closure_call)] let listener = $build_listener(path) .map_err(Err) .map_ok(move |listener| { diff --git a/transports/webrtc/CHANGELOG.md b/transports/webrtc/CHANGELOG.md index e8c37cbf061a..0eb395574386 100644 --- a/transports/webrtc/CHANGELOG.md +++ b/transports/webrtc/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.6.0-alpha + +- Update `webrtc` dependency to `v0.8.0`. + See [PR 4099]. + +[PR 4099]: https://github.com/libp2p/rust-libp2p/pull/4099 + ## 0.5.0-alpha - Raise MSRV to 1.65. diff --git a/transports/webrtc/Cargo.toml b/transports/webrtc/Cargo.toml index 273b9c5d80a1..60ac73f44415 100644 --- a/transports/webrtc/Cargo.toml +++ b/transports/webrtc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-webrtc" -version = "0.5.0-alpha" +version = "0.6.0-alpha" authors = ["Parity Technologies "] description = "WebRTC transport for libp2p" repository = "https://github.com/libp2p/rust-libp2p" @@ -32,7 +32,7 @@ serde = { version = "1.0", features = ["derive"] } stun = "0.4" thiserror = "1" tinytemplate = "1.2" -tokio = { version = "1.28", features = ["net"], optional = true} +tokio = { version = "1.29", features = ["net"], optional = true} tokio-util = { version = "0.7", features = ["compat"], optional = true } webrtc = { version = "0.8.0", optional = true } @@ -46,7 +46,7 @@ env_logger = "0.10" hex-literal = "0.4" libp2p-swarm = { workspace = true, features = ["macros", "tokio"] } libp2p-ping = { workspace = true } -tokio = { version = "1.28", features = ["full"] } +tokio = { version = "1.29", features = ["full"] } unsigned-varint = { version = "0.7", features = ["asynchronous_codec"] } void = "1" quickcheck = "1.0.3" diff --git a/transports/websocket/Cargo.toml b/transports/websocket/Cargo.toml index 469e19edac54..a5559b88a632 100644 --- a/transports/websocket/Cargo.toml +++ b/transports/websocket/Cargo.toml @@ -22,7 +22,7 @@ quicksink = "0.1" rw-stream-sink = { workspace = true } soketto = "0.7.0" url = "2.1" -webpki-roots = "0.23" +webpki-roots = "0.24" [dev-dependencies] libp2p-tcp = { workspace = true, features = ["async-io"] } diff --git a/transports/websocket/src/framed.rs b/transports/websocket/src/framed.rs index b5a0b1b98f1b..fc3f6514f12b 100644 --- a/transports/websocket/src/framed.rs +++ b/transports/websocket/src/framed.rs @@ -57,7 +57,10 @@ pub struct WsConfig { listener_protos: HashMap>, } -impl WsConfig { +impl WsConfig +where + T: Send, +{ /// Create a new websocket transport based on another transport. pub fn new(transport: T) -> Self { WsConfig { diff --git a/transports/webtransport-websys/CHANGELOG.md b/transports/webtransport-websys/CHANGELOG.md new file mode 100644 index 000000000000..13ca1b5ed0a9 --- /dev/null +++ b/transports/webtransport-websys/CHANGELOG.md @@ -0,0 +1,5 @@ +## 0.1.0 + +* Initial implementation of WebTranport transport using web-sys bindings. See [PR 4015]. + +[PR 4015]: https://github.com/libp2p/rust-libp2p/pull/4015 diff --git a/transports/webtransport-websys/Cargo.toml b/transports/webtransport-websys/Cargo.toml new file mode 100644 index 000000000000..ba6baa2efe0e --- /dev/null +++ b/transports/webtransport-websys/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "libp2p-webtransport-websys" +edition = "2021" +rust-version = { workspace = true } +description = "WebTransport for libp2p under WASM environment" +version = "0.1.0" +authors = [ + "Yiannis Marangos ", + "oblique ", +] +license = "MIT" +repository = "https://github.com/libp2p/rust-libp2p" +keywords = ["peer-to-peer", "libp2p", "networking"] +categories = ["network-programming", "asynchronous"] + +[dependencies] +futures = "0.3.28" +js-sys = "0.3.64" +libp2p-core = { workspace = true } +libp2p-identity = { workspace = true } +libp2p-noise = { workspace = true } +log = "0.4.19" +multiaddr = { workspace = true } +multihash = { workspace = true } +send_wrapper = { version = "0.6.0", features = ["futures"] } +thiserror = "1.0.43" +wasm-bindgen = "0.2.87" +wasm-bindgen-futures = "0.4.37" +web-sys = { version = "0.3.64", features = [ + "ReadableStreamDefaultReader", + "WebTransport", + "WebTransportBidirectionalStream", + "WebTransportHash", + "WebTransportOptions", + "WebTransportReceiveStream", + "WebTransportSendStream", + "WritableStreamDefaultWriter", +] } + +[dev-dependencies] +multibase = "0.9.1" + +# Passing arguments to the docsrs builder in order to properly document cfg's. +# More information: https://docs.rs/about/builds#cross-compiling +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] +rustc-args = ["--cfg", "docsrs"] diff --git a/transports/webtransport-websys/src/bindings.rs b/transports/webtransport-websys/src/bindings.rs new file mode 100644 index 000000000000..a8a1469f8ad1 --- /dev/null +++ b/transports/webtransport-websys/src/bindings.rs @@ -0,0 +1,141 @@ +//! This file is an extract from `web-sys` crate. It is a temporary +//! solution until `web_sys::WebTransport` and related structs get stabilized. +//! +//! Only the methods that are used by this crate are extracted. + +#![allow(clippy::all)] +use js_sys::{Object, Promise, Reflect}; +use wasm_bindgen::prelude::*; +use web_sys::{ReadableStream, WritableStream}; + +// WebTransport bindings +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Object, js_name = WebTransport, typescript_type = "WebTransport")] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type WebTransport; + + #[wasm_bindgen(structural, method, getter, js_class = "WebTransport", js_name = ready)] + pub fn ready(this: &WebTransport) -> Promise; + + #[wasm_bindgen(structural, method, getter, js_class = "WebTransport", js_name = closed)] + pub fn closed(this: &WebTransport) -> Promise; + + #[wasm_bindgen(structural, method, getter, js_class = "WebTransport" , js_name = incomingBidirectionalStreams)] + pub fn incoming_bidirectional_streams(this: &WebTransport) -> ReadableStream; + + #[wasm_bindgen(catch, constructor, js_class = "WebTransport")] + pub fn new(url: &str) -> Result; + + #[wasm_bindgen(catch, constructor, js_class = "WebTransport")] + pub fn new_with_options( + url: &str, + options: &WebTransportOptions, + ) -> Result; + + #[wasm_bindgen(method, structural, js_class = "WebTransport", js_name = close)] + pub fn close(this: &WebTransport); + + #[wasm_bindgen (method, structural, js_class = "WebTransport", js_name = createBidirectionalStream)] + pub fn create_bidirectional_stream(this: &WebTransport) -> Promise; +} + +// WebTransportBidirectionalStream bindings +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Object, js_name = WebTransportBidirectionalStream, typescript_type = "WebTransportBidirectionalStream")] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type WebTransportBidirectionalStream; + + #[wasm_bindgen(structural, method, getter, js_class = "WebTransportBidirectionalStream", js_name = readable)] + pub fn readable(this: &WebTransportBidirectionalStream) -> WebTransportReceiveStream; + + #[wasm_bindgen(structural, method, getter, js_class = "WebTransportBidirectionalStream", js_name = writable)] + pub fn writable(this: &WebTransportBidirectionalStream) -> WebTransportSendStream; +} + +// WebTransportReceiveStream bindings +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = ReadableStream, extends = Object, js_name = WebTransportReceiveStream, typescript_type = "WebTransportReceiveStream")] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type WebTransportReceiveStream; +} + +// WebTransportSendStream bindings +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = WritableStream, extends = Object, js_name = WebTransportSendStream, typescript_type = "WebTransportSendStream")] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type WebTransportSendStream; +} + +// WebTransportOptions bindings +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Object, js_name = WebTransportOptions)] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type WebTransportOptions; +} + +impl WebTransportOptions { + pub fn new() -> Self { + #[allow(unused_mut)] + let mut ret: Self = JsCast::unchecked_into(Object::new()); + ret + } + + pub fn server_certificate_hashes(&mut self, val: &JsValue) -> &mut Self { + let r = ::js_sys::Reflect::set( + self.as_ref(), + &JsValue::from("serverCertificateHashes"), + &JsValue::from(val), + ); + debug_assert!( + r.is_ok(), + "setting properties should never fail on our dictionary objects" + ); + let _ = r; + self + } +} + +// WebTransportHash bindings +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Object, js_name = WebTransportHash)] + #[derive(Debug, Clone, PartialEq, Eq)] + pub type WebTransportHash; +} + +impl WebTransportHash { + pub fn new() -> Self { + #[allow(unused_mut)] + let mut ret: Self = JsCast::unchecked_into(Object::new()); + ret + } + + pub fn algorithm(&mut self, val: &str) -> &mut Self { + let r = Reflect::set( + self.as_ref(), + &JsValue::from("algorithm"), + &JsValue::from(val), + ); + debug_assert!( + r.is_ok(), + "setting properties should never fail on our dictionary objects" + ); + let _ = r; + self + } + + pub fn value(&mut self, val: &::js_sys::Object) -> &mut Self { + let r = Reflect::set(self.as_ref(), &JsValue::from("value"), &JsValue::from(val)); + debug_assert!( + r.is_ok(), + "setting properties should never fail on our dictionary objects" + ); + let _ = r; + self + } +} diff --git a/transports/webtransport-websys/src/connection.rs b/transports/webtransport-websys/src/connection.rs new file mode 100644 index 000000000000..9ea1dbefd1c8 --- /dev/null +++ b/transports/webtransport-websys/src/connection.rs @@ -0,0 +1,208 @@ +use futures::FutureExt; +use libp2p_core::muxing::{StreamMuxer, StreamMuxerEvent}; +use libp2p_core::{OutboundUpgrade, UpgradeInfo}; +use libp2p_identity::{Keypair, PeerId}; +use multihash::Multihash; +use send_wrapper::SendWrapper; +use std::collections::HashSet; +use std::future::poll_fn; +use std::pin::Pin; +use std::task::{ready, Context, Poll}; +use wasm_bindgen_futures::JsFuture; +use web_sys::ReadableStreamDefaultReader; + +use crate::bindings::{WebTransport, WebTransportBidirectionalStream}; +use crate::endpoint::Endpoint; +use crate::fused_js_promise::FusedJsPromise; +use crate::utils::{detach_promise, parse_reader_response, to_js_type}; +use crate::{Error, Stream}; + +/// An opened WebTransport connection. +#[derive(Debug)] +pub struct Connection { + // Swarm needs all types to be Send. WASM is single-threaded + // and it is safe to use SendWrapper. + inner: SendWrapper, +} + +#[derive(Debug)] +struct ConnectionInner { + session: WebTransport, + create_stream_promise: FusedJsPromise, + incoming_stream_promise: FusedJsPromise, + incoming_streams_reader: ReadableStreamDefaultReader, + closed: bool, +} + +impl Connection { + pub(crate) fn new(endpoint: &Endpoint) -> Result { + let url = endpoint.url(); + + let session = if endpoint.certhashes.is_empty() { + // Endpoint has CA-signed TLS certificate. + WebTransport::new(&url).map_err(Error::from_js_value)? + } else { + // Endpoint has self-signed TLS certificates. + let opts = endpoint.webtransport_opts(); + WebTransport::new_with_options(&url, &opts).map_err(Error::from_js_value)? + }; + + let incoming_streams = session.incoming_bidirectional_streams(); + let incoming_streams_reader = + to_js_type::(incoming_streams.get_reader())?; + + Ok(Connection { + inner: SendWrapper::new(ConnectionInner { + session, + create_stream_promise: FusedJsPromise::new(), + incoming_stream_promise: FusedJsPromise::new(), + incoming_streams_reader, + closed: false, + }), + }) + } + + pub(crate) async fn authenticate( + &mut self, + keypair: &Keypair, + remote_peer: Option, + certhashes: HashSet>, + ) -> Result { + let fut = SendWrapper::new(self.inner.authenticate(keypair, remote_peer, certhashes)); + fut.await + } +} + +impl ConnectionInner { + /// Authenticates with the server + /// + /// This methods runs the security handshake as descripted + /// in the [spec][1]. It validates the certhashes and peer ID + /// of the server. + /// + /// [1]: https://github.com/libp2p/specs/tree/master/webtransport#security-handshake + async fn authenticate( + &mut self, + keypair: &Keypair, + remote_peer: Option, + certhashes: HashSet>, + ) -> Result { + JsFuture::from(self.session.ready()) + .await + .map_err(Error::from_js_value)?; + + let stream = poll_fn(|cx| self.poll_create_bidirectional_stream(cx)).await?; + let mut noise = libp2p_noise::Config::new(keypair)?; + + if !certhashes.is_empty() { + noise = noise.with_webtransport_certhashes(certhashes); + } + + // We do not use `upgrade::apply_outbound` function because it uses + // `multistream_select` protocol, which is not used by WebTransport spec. + let info = noise.protocol_info().next().unwrap_or_default(); + let (peer_id, _io) = noise.upgrade_outbound(stream, info).await?; + + // TODO: This should be part libp2p-noise + if let Some(expected_peer_id) = remote_peer { + if peer_id != expected_peer_id { + return Err(Error::UnknownRemotePeerId); + } + } + + Ok(peer_id) + } + + /// Initiates and polls a promise from `create_bidirectional_stream`. + fn poll_create_bidirectional_stream( + &mut self, + cx: &mut Context, + ) -> Poll> { + // Create bidirectional stream + let val = ready!(self + .create_stream_promise + .maybe_init(|| self.session.create_bidirectional_stream()) + .poll_unpin(cx)) + .map_err(Error::from_js_value)?; + + let bidi_stream = to_js_type::(val)?; + let stream = Stream::new(bidi_stream)?; + + Poll::Ready(Ok(stream)) + } + + /// Polls for incoming stream from `incoming_bidirectional_streams` reader. + fn poll_incoming_bidirectional_streams( + &mut self, + cx: &mut Context, + ) -> Poll> { + // Read the next incoming stream from the JS channel + let val = ready!(self + .incoming_stream_promise + .maybe_init(|| self.incoming_streams_reader.read()) + .poll_unpin(cx)) + .map_err(Error::from_js_value)?; + + let val = parse_reader_response(&val) + .map_err(Error::from_js_value)? + .ok_or_else(|| Error::JsError("incoming_bidirectional_streams closed".to_string()))?; + + let bidi_stream = to_js_type::(val)?; + let stream = Stream::new(bidi_stream)?; + + Poll::Ready(Ok(stream)) + } + + /// Closes the session. + /// + /// This closes the streams also and they will return an error + /// when they will be used. + fn close_session(&mut self) { + if !self.closed { + detach_promise(self.incoming_streams_reader.cancel()); + self.session.close(); + self.closed = true; + } + } +} + +impl Drop for ConnectionInner { + fn drop(&mut self) { + self.close_session(); + } +} + +/// WebTransport native multiplexing +impl StreamMuxer for Connection { + type Substream = Stream; + type Error = Error; + + fn poll_inbound( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.inner.poll_incoming_bidirectional_streams(cx) + } + + fn poll_outbound( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.inner.poll_create_bidirectional_stream(cx) + } + + fn poll_close( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + self.inner.close_session(); + Poll::Ready(Ok(())) + } + + fn poll( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Pending + } +} diff --git a/transports/webtransport-websys/src/endpoint.rs b/transports/webtransport-websys/src/endpoint.rs new file mode 100644 index 000000000000..0bff1ed6186a --- /dev/null +++ b/transports/webtransport-websys/src/endpoint.rs @@ -0,0 +1,227 @@ +use js_sys::{Array, Uint8Array}; +use libp2p_identity::PeerId; +use multiaddr::{Multiaddr, Protocol}; +use multihash::Multihash; +use std::collections::HashSet; + +use crate::bindings::{WebTransportHash, WebTransportOptions}; +use crate::Error; + +pub(crate) struct Endpoint { + pub(crate) host: String, + pub(crate) port: u16, + pub(crate) is_ipv6: bool, + pub(crate) certhashes: HashSet>, + pub(crate) remote_peer: Option, +} + +impl Endpoint { + pub(crate) fn from_multiaddr(addr: &Multiaddr) -> Result { + let mut host = None; + let mut port = None; + let mut found_quic = false; + let mut found_webtransport = false; + let mut certhashes = HashSet::new(); + let mut remote_peer = None; + let mut is_ipv6 = false; + + for proto in addr.iter() { + match proto { + Protocol::Ip4(addr) => { + if host.is_some() { + return Err(Error::InvalidMultiaddr("More than one host definitions")); + } + + host = Some(addr.to_string()); + } + Protocol::Ip6(addr) => { + if host.is_some() { + return Err(Error::InvalidMultiaddr("More than one host definitions")); + } + + is_ipv6 = true; + host = Some(addr.to_string()); + } + Protocol::Dns(domain) | Protocol::Dns4(domain) | Protocol::Dns6(domain) => { + if port.is_some() { + return Err(Error::InvalidMultiaddr("More than one host definitions")); + } + + host = Some(domain.to_string()) + } + Protocol::Dnsaddr(_) => { + return Err(Error::InvalidMultiaddr( + "/dnsaddr not supported from within a browser", + )); + } + Protocol::Udp(p) => { + if port.is_some() { + return Err(Error::InvalidMultiaddr("More than one port definitions")); + } + + port = Some(p); + } + Protocol::Quic | Protocol::QuicV1 => { + if host.is_none() || port.is_none() { + return Err(Error::InvalidMultiaddr( + "No host and port definition before /quic/webtransport", + )); + } + + found_quic = true; + } + Protocol::WebTransport => { + if !found_quic { + return Err(Error::InvalidMultiaddr( + "/quic is not found before /webtransport", + )); + } + + found_webtransport = true; + } + Protocol::Certhash(hash) => { + if !found_webtransport { + return Err(Error::InvalidMultiaddr( + "/certhashes must be after /quic/found_webtransport", + )); + } + + certhashes.insert(hash); + } + Protocol::P2p(peer) => { + if remote_peer.is_some() { + return Err(Error::InvalidMultiaddr("More than one peer definitions")); + } + + remote_peer = Some(peer); + } + _ => {} + } + } + + if !found_quic || !found_webtransport { + return Err(Error::InvalidMultiaddr( + "Not a /quic/webtransport multiaddr", + )); + } + + let host = host.ok_or_else(|| Error::InvalidMultiaddr("Host is not defined"))?; + let port = port.ok_or_else(|| Error::InvalidMultiaddr("Port is not defined"))?; + + Ok(Endpoint { + host, + port, + is_ipv6, + certhashes, + remote_peer, + }) + } + + pub(crate) fn url(&self) -> String { + let host = &self.host; + let port = self.port; + + if self.is_ipv6 { + format!("https://[{host}]:{port}/.well-known/libp2p-webtransport?type=noise") + } else { + format!("https://{host}:{port}/.well-known/libp2p-webtransport?type=noise") + } + } + + pub(crate) fn webtransport_opts(&self) -> WebTransportOptions { + let mut opts = WebTransportOptions::new(); + let hashes = Array::new(); + + for hash in &self.certhashes { + let digest = Uint8Array::from(hash.digest()); + + let mut jshash = WebTransportHash::new(); + jshash.algorithm("sha-256").value(&digest); + + hashes.push(&jshash); + } + + opts.server_certificate_hashes(&hashes); + + opts + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + fn multihash_from_str(s: &str) -> Multihash<64> { + let (_base, bytes) = multibase::decode(s).unwrap(); + Multihash::from_bytes(&bytes).unwrap() + } + + #[test] + fn valid_webtransport_multiaddr() { + let addr = Multiaddr::from_str("/ip4/127.0.0.1/udp/44874/quic-v1/webtransport/certhash/uEiCaDd1Ca1A8IVJ3hsIxIyi11cwxaDKqzVrBkGJbKZU5ng/certhash/uEiDv-VGW8oXxui_G_Kqp-87YjvET-Hr2qYAMYPePJDcsjQ/p2p/12D3KooWR7EfNv5SLtgjMRjUwR8AvNu3hP4fLrtSa9fmHHXKYWNG").unwrap(); + let endpoint = Endpoint::from_multiaddr(&addr).unwrap(); + + assert_eq!(endpoint.host, "127.0.0.1"); + assert_eq!(endpoint.port, 44874); + assert_eq!(endpoint.certhashes.len(), 2); + + assert!(endpoint.certhashes.contains(&multihash_from_str( + "uEiCaDd1Ca1A8IVJ3hsIxIyi11cwxaDKqzVrBkGJbKZU5ng" + ))); + + assert!(endpoint.certhashes.contains(&multihash_from_str( + "uEiDv-VGW8oXxui_G_Kqp-87YjvET-Hr2qYAMYPePJDcsjQ" + ))); + + assert_eq!( + endpoint.remote_peer.unwrap(), + PeerId::from_str("12D3KooWR7EfNv5SLtgjMRjUwR8AvNu3hP4fLrtSa9fmHHXKYWNG").unwrap() + ); + + assert_eq!( + endpoint.url(), + "https://127.0.0.1:44874/.well-known/libp2p-webtransport?type=noise" + ); + } + + #[test] + fn valid_webtransport_multiaddr_without_certhashes() { + let addr = Multiaddr::from_str("/ip4/127.0.0.1/udp/44874/quic-v1/webtransport/p2p/12D3KooWR7EfNv5SLtgjMRjUwR8AvNu3hP4fLrtSa9fmHHXKYWNG").unwrap(); + let endpoint = Endpoint::from_multiaddr(&addr).unwrap(); + + assert_eq!(endpoint.host, "127.0.0.1"); + assert_eq!(endpoint.port, 44874); + assert_eq!(endpoint.certhashes.len(), 0); + assert_eq!( + endpoint.remote_peer.unwrap(), + PeerId::from_str("12D3KooWR7EfNv5SLtgjMRjUwR8AvNu3hP4fLrtSa9fmHHXKYWNG").unwrap() + ); + } + + #[test] + fn ipv6_webtransport() { + let addr = Multiaddr::from_str("/ip6/::1/udp/44874/quic-v1/webtransport/certhash/uEiCaDd1Ca1A8IVJ3hsIxIyi11cwxaDKqzVrBkGJbKZU5ng/certhash/uEiDv-VGW8oXxui_G_Kqp-87YjvET-Hr2qYAMYPePJDcsjQ/p2p/12D3KooWR7EfNv5SLtgjMRjUwR8AvNu3hP4fLrtSa9fmHHXKYWNG").unwrap(); + let endpoint = Endpoint::from_multiaddr(&addr).unwrap(); + + assert_eq!(endpoint.host, "::1"); + assert_eq!(endpoint.port, 44874); + assert_eq!( + endpoint.url(), + "https://[::1]:44874/.well-known/libp2p-webtransport?type=noise" + ); + } + + #[test] + fn dns_webtransport() { + let addr = Multiaddr::from_str("/dns/libp2p.io/udp/44874/quic-v1/webtransport/certhash/uEiCaDd1Ca1A8IVJ3hsIxIyi11cwxaDKqzVrBkGJbKZU5ng/certhash/uEiDv-VGW8oXxui_G_Kqp-87YjvET-Hr2qYAMYPePJDcsjQ/p2p/12D3KooWR7EfNv5SLtgjMRjUwR8AvNu3hP4fLrtSa9fmHHXKYWNG").unwrap(); + let endpoint = Endpoint::from_multiaddr(&addr).unwrap(); + + assert_eq!(endpoint.host, "libp2p.io"); + assert_eq!(endpoint.port, 44874); + assert_eq!( + endpoint.url(), + "https://libp2p.io:44874/.well-known/libp2p-webtransport?type=noise" + ); + } +} diff --git a/transports/webtransport-websys/src/error.rs b/transports/webtransport-websys/src/error.rs new file mode 100644 index 000000000000..ad85cab75370 --- /dev/null +++ b/transports/webtransport-websys/src/error.rs @@ -0,0 +1,36 @@ +use wasm_bindgen::{JsCast, JsValue}; + +/// Errors that may happen on the [`Transport`](crate::Transport) or the +/// [`Connection`](crate::Connection). +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Invalid multiaddr: {0}")] + InvalidMultiaddr(&'static str), + + #[error("Noise authentication failed")] + Noise(#[from] libp2p_noise::Error), + + #[error("JavaScript error: {0}")] + JsError(String), + + #[error("JavaScript typecasting failed")] + JsCastFailed, + + #[error("Unknown remote peer ID")] + UnknownRemotePeerId, +} + +impl Error { + pub(crate) fn from_js_value(value: JsValue) -> Self { + let s = if value.is_instance_of::() { + js_sys::Error::from(value) + .to_string() + .as_string() + .unwrap_or_else(|| "Unknown error".to_string()) + } else { + "Unknown error".to_string() + }; + + Error::JsError(s) + } +} diff --git a/transports/webtransport-websys/src/fused_js_promise.rs b/transports/webtransport-websys/src/fused_js_promise.rs new file mode 100644 index 000000000000..0ba846501c2d --- /dev/null +++ b/transports/webtransport-websys/src/fused_js_promise.rs @@ -0,0 +1,58 @@ +use futures::FutureExt; +use js_sys::Promise; +use std::future::Future; +use std::pin::Pin; +use std::task::{ready, Context, Poll}; +use wasm_bindgen::JsValue; +use wasm_bindgen_futures::JsFuture; + +/// Convenient wrapper to poll a promise to completion. +/// +/// # Panics +/// +/// Panics if polled and promise is not initialized. Use `maybe_init` if unsure. +#[derive(Debug)] +pub(crate) struct FusedJsPromise { + promise: Option, +} + +impl FusedJsPromise { + /// Creates new uninitialized promise. + pub(crate) fn new() -> Self { + FusedJsPromise { promise: None } + } + + /// Initialize promise if needed + pub(crate) fn maybe_init(&mut self, init: F) -> &mut Self + where + F: FnOnce() -> Promise, + { + if self.promise.is_none() { + self.promise = Some(JsFuture::from(init())); + } + + self + } + + /// Checks if promise is already running + pub(crate) fn is_active(&self) -> bool { + self.promise.is_some() + } +} + +impl Future for FusedJsPromise { + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let val = ready!(self + .promise + .as_mut() + .expect("FusedJsPromise not initialized") + .poll_unpin(cx)); + + // Future finished, drop it + self.promise.take(); + + Poll::Ready(val) + } +} diff --git a/transports/webtransport-websys/src/lib.rs b/transports/webtransport-websys/src/lib.rs new file mode 100644 index 000000000000..f9c59694fa31 --- /dev/null +++ b/transports/webtransport-websys/src/lib.rs @@ -0,0 +1,15 @@ +//! Libp2p WebTransport built on [web-sys](https://rustwasm.github.io/wasm-bindgen/web-sys/index.html) + +mod bindings; +mod connection; +mod endpoint; +mod error; +mod fused_js_promise; +mod stream; +mod transport; +mod utils; + +pub use self::connection::Connection; +pub use self::error::Error; +pub use self::stream::Stream; +pub use self::transport::{Config, Transport}; diff --git a/transports/webtransport-websys/src/stream.rs b/transports/webtransport-websys/src/stream.rs new file mode 100644 index 000000000000..ba4238ac8146 --- /dev/null +++ b/transports/webtransport-websys/src/stream.rs @@ -0,0 +1,228 @@ +use futures::{AsyncRead, AsyncWrite, FutureExt}; +use js_sys::Uint8Array; +use send_wrapper::SendWrapper; +use std::io; +use std::pin::Pin; +use std::task::ready; +use std::task::{Context, Poll}; +use web_sys::{ReadableStreamDefaultReader, WritableStreamDefaultWriter}; + +use crate::bindings::WebTransportBidirectionalStream; +use crate::fused_js_promise::FusedJsPromise; +use crate::utils::{detach_promise, parse_reader_response, to_io_error, to_js_type}; +use crate::Error; + +/// A stream on a connection. +#[derive(Debug)] +pub struct Stream { + // Swarm needs all types to be Send. WASM is single-threaded + // and it is safe to use SendWrapper. + inner: SendWrapper, +} + +#[derive(Debug)] +struct StreamInner { + reader: ReadableStreamDefaultReader, + reader_read_promise: FusedJsPromise, + read_leftovers: Option, + writer: WritableStreamDefaultWriter, + writer_state: StreamState, + writer_ready_promise: FusedJsPromise, + writer_closed_promise: FusedJsPromise, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum StreamState { + Open, + Closing, + Closed, +} + +impl Stream { + pub(crate) fn new(bidi_stream: WebTransportBidirectionalStream) -> Result { + let recv_stream = bidi_stream.readable(); + let send_stream = bidi_stream.writable(); + + let reader = to_js_type::(recv_stream.get_reader())?; + let writer = send_stream.get_writer().map_err(Error::from_js_value)?; + + Ok(Stream { + inner: SendWrapper::new(StreamInner { + reader, + reader_read_promise: FusedJsPromise::new(), + read_leftovers: None, + writer, + writer_state: StreamState::Open, + writer_ready_promise: FusedJsPromise::new(), + writer_closed_promise: FusedJsPromise::new(), + }), + }) + } +} + +impl StreamInner { + fn poll_reader_read(&mut self, cx: &mut Context) -> Poll>> { + let val = ready!(self + .reader_read_promise + .maybe_init(|| self.reader.read()) + .poll_unpin(cx)) + .map_err(to_io_error)?; + + let val = parse_reader_response(&val) + .map_err(to_io_error)? + .map(Uint8Array::from); + + Poll::Ready(Ok(val)) + } + + fn poll_read(&mut self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + // If we have leftovers from a previous read, then use them. + // Otherwise read new data. + let data = match self.read_leftovers.take() { + Some(data) => data, + None => { + match ready!(self.poll_reader_read(cx))? { + Some(data) => data, + // EOF + None => return Poll::Ready(Ok(0)), + } + } + }; + + if data.byte_length() == 0 { + return Poll::Ready(Ok(0)); + } + + let out_len = data.byte_length().min(buf.len() as u32); + data.slice(0, out_len).copy_to(&mut buf[..out_len as usize]); + + let leftovers = data.slice(out_len, data.byte_length()); + + if leftovers.byte_length() > 0 { + self.read_leftovers = Some(leftovers); + } + + Poll::Ready(Ok(out_len as usize)) + } + + fn poll_writer_ready(&mut self, cx: &mut Context) -> Poll> { + if self.writer_state != StreamState::Open { + return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into())); + } + + let desired_size = self + .writer + .desired_size() + .map_err(to_io_error)? + .map(|n| n.trunc() as i64) + .unwrap_or(0); + + // We need to poll if the queue is full or if the promise was already activated. + // + // NOTE: `desired_size` can be negative if we overcommit messages to the queue. + if desired_size <= 0 || self.writer_ready_promise.is_active() { + ready!(self + .writer_ready_promise + .maybe_init(|| self.writer.ready()) + .poll_unpin(cx)) + .map_err(to_io_error)?; + } + + Poll::Ready(Ok(())) + } + + fn poll_write(&mut self, cx: &mut Context, buf: &[u8]) -> Poll> { + ready!(self.poll_writer_ready(cx))?; + + let len = buf.len() as u32; + let data = Uint8Array::new_with_length(len); + data.copy_from(buf); + + detach_promise(self.writer.write_with_chunk(&data)); + + Poll::Ready(Ok(len as usize)) + } + + fn poll_flush(&mut self, cx: &mut Context) -> Poll> { + if self.writer_state == StreamState::Open { + // Writer has queue size of 1, so as soon it is ready, self means the + // messages were flushed. + self.poll_writer_ready(cx) + } else { + debug_assert!( + false, + "libp2p_webtransport_websys::Stream: poll_flush called after poll_close" + ); + Poll::Ready(Ok(())) + } + } + + fn poll_writer_close(&mut self, cx: &mut Context) -> Poll> { + match self.writer_state { + StreamState::Open => { + self.writer_state = StreamState::Closing; + + // Initiate close + detach_promise(self.writer.close()); + + // Assume closed on error + let _ = ready!(self + .writer_closed_promise + .maybe_init(|| self.writer.closed()) + .poll_unpin(cx)); + + self.writer_state = StreamState::Closed; + } + StreamState::Closing => { + // Assume closed on error + let _ = ready!(self.writer_closed_promise.poll_unpin(cx)); + self.writer_state = StreamState::Closed; + } + StreamState::Closed => {} + } + + Poll::Ready(Ok(())) + } +} + +impl Drop for StreamInner { + fn drop(&mut self) { + // Close writer. + // + // We choose to use `close()` instead of `abort()`, because + // abort was causing some side effects on the WebTransport + // layer and connection was lost. + detach_promise(self.writer.close()); + + // Cancel any ongoing reads. + detach_promise(self.reader.cancel()); + } +} + +impl AsyncRead for Stream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + self.inner.poll_read(cx, buf) + } +} + +impl AsyncWrite for Stream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context, + buf: &[u8], + ) -> Poll> { + self.inner.poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.inner.poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.inner.poll_writer_close(cx) + } +} diff --git a/transports/webtransport-websys/src/transport.rs b/transports/webtransport-websys/src/transport.rs new file mode 100644 index 000000000000..dcb3639a1942 --- /dev/null +++ b/transports/webtransport-websys/src/transport.rs @@ -0,0 +1,103 @@ +use futures::future::FutureExt; +use libp2p_core::muxing::StreamMuxerBox; +use libp2p_core::transport::{Boxed, ListenerId, Transport as _, TransportError, TransportEvent}; +use libp2p_identity::{Keypair, PeerId}; +use multiaddr::Multiaddr; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use crate::endpoint::Endpoint; +use crate::Connection; +use crate::Error; + +/// Config for the [`Transport`]. +pub struct Config { + keypair: Keypair, +} + +/// A WebTransport [`Transport`](libp2p_core::Transport) that works with `web-sys`. +pub struct Transport { + config: Config, +} + +impl Config { + /// Constructs a new configuration for the [`Transport`]. + pub fn new(keypair: &Keypair) -> Self { + Config { + keypair: keypair.to_owned(), + } + } +} + +impl Transport { + /// Constructs a new `Transport` with the given [`Config`]. + pub fn new(config: Config) -> Transport { + Transport { config } + } + + /// Wraps `Transport` in [`Boxed`] and makes it ready to be consumed by + /// SwarmBuilder. + pub fn boxed(self) -> Boxed<(PeerId, StreamMuxerBox)> { + self.map(|(peer_id, muxer), _| (peer_id, StreamMuxerBox::new(muxer))) + .boxed() + } +} + +impl libp2p_core::Transport for Transport { + type Output = (PeerId, Connection); + type Error = Error; + type ListenerUpgrade = Pin> + Send>>; + type Dial = Pin> + Send>>; + + fn listen_on( + &mut self, + _id: ListenerId, + addr: Multiaddr, + ) -> Result<(), TransportError> { + Err(TransportError::MultiaddrNotSupported(addr)) + } + + fn remove_listener(&mut self, _id: ListenerId) -> bool { + false + } + + fn dial(&mut self, addr: Multiaddr) -> Result> { + let endpoint = Endpoint::from_multiaddr(&addr).map_err(|e| match e { + e @ Error::InvalidMultiaddr(_) => { + log::warn!("{}", e); + TransportError::MultiaddrNotSupported(addr) + } + e => TransportError::Other(e), + })?; + + let mut session = Connection::new(&endpoint).map_err(TransportError::Other)?; + let keypair = self.config.keypair.clone(); + + Ok(async move { + let peer_id = session + .authenticate(&keypair, endpoint.remote_peer, endpoint.certhashes) + .await?; + Ok((peer_id, session)) + } + .boxed()) + } + + fn dial_as_listener( + &mut self, + addr: Multiaddr, + ) -> Result> { + Err(TransportError::MultiaddrNotSupported(addr)) + } + + fn poll( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Pending + } + + fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { + None + } +} diff --git a/transports/webtransport-websys/src/utils.rs b/transports/webtransport-websys/src/utils.rs new file mode 100644 index 000000000000..fcde226be872 --- /dev/null +++ b/transports/webtransport-websys/src/utils.rs @@ -0,0 +1,76 @@ +use js_sys::{Promise, Reflect}; +use send_wrapper::SendWrapper; +use std::io; +use wasm_bindgen::{JsCast, JsValue}; + +use crate::Error; + +/// Properly detach a promise. +/// +/// A promise always runs in the background, however if you don't await it, +/// or specify a `catch` handler before you drop it, it might cause some side +/// effects. This function avoids any side effects. +// +// Ref: https://github.com/typescript-eslint/typescript-eslint/blob/391a6702c0a9b5b3874a7a27047f2a721f090fb6/packages/eslint-plugin/docs/rules/no-floating-promises.md +pub(crate) fn detach_promise(promise: Promise) { + type Closure = wasm_bindgen::closure::Closure; + static mut DO_NOTHING: Option> = None; + + // Allocate Closure only once and reuse it + let do_nothing = unsafe { + if DO_NOTHING.is_none() { + let cb = Closure::new(|_| {}); + DO_NOTHING = Some(SendWrapper::new(cb)); + } + + DO_NOTHING.as_deref().unwrap() + }; + + // Avoid having "floating" promise and ignore any errors. + // After `catch` promise is allowed to be dropped. + let _ = promise.catch(do_nothing); +} + +/// Typecasts a JavaScript type. +/// +/// Returns a `Ok(value)` casted to the requested type. +/// +/// If the underlying value is an error and the requested +/// type is not, then `Err(Error::JsError)` is returned. +/// +/// If the underlying value can not be casted to the requested type and +/// is not an error, then `Err(Error::JsCastFailed)` is returned. +pub(crate) fn to_js_type(value: impl Into) -> Result +where + T: JsCast + From, +{ + let value = value.into(); + + if value.has_type::() { + Ok(value.unchecked_into()) + } else if value.has_type::() { + Err(Error::from_js_value(value)) + } else { + Err(Error::JsCastFailed) + } +} + +/// Parse reponse from `ReadableStreamDefaultReader::read`. +// +// Ref: https://streams.spec.whatwg.org/#default-reader-prototype +pub(crate) fn parse_reader_response(resp: &JsValue) -> Result, JsValue> { + let value = Reflect::get(resp, &JsValue::from_str("value"))?; + let done = Reflect::get(resp, &JsValue::from_str("done"))? + .as_bool() + .unwrap_or_default(); + + if value.is_undefined() || done { + Ok(None) + } else { + Ok(Some(value)) + } +} + +pub(crate) fn to_io_error(value: JsValue) -> io::Error { + io::Error::new(io::ErrorKind::Other, Error::from_js_value(value)) +} diff --git a/wasm-tests/README.md b/wasm-tests/README.md new file mode 100644 index 000000000000..1d0902b106c4 --- /dev/null +++ b/wasm-tests/README.md @@ -0,0 +1,11 @@ +# Dependencies + +Before you run the tests you need to install the following: + +* Chrome or Chromium +* chromedriver (major version must be the same as Chrome's) +* wasm-pack + +# Run tests + +Just call `run-all.sh` or `run.sh` in the test directory you are interested. diff --git a/wasm-tests/run-all.sh b/wasm-tests/run-all.sh new file mode 100755 index 000000000000..77b896a167da --- /dev/null +++ b/wasm-tests/run-all.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +# cd to this script directory +cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1 + +./webtransport-tests/run.sh diff --git a/wasm-tests/webtransport-tests/Cargo.toml b/wasm-tests/webtransport-tests/Cargo.toml new file mode 100644 index 000000000000..6c564281274b --- /dev/null +++ b/wasm-tests/webtransport-tests/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "webtransport-tests" +version = "0.1.0" +edition = "2021" +license = "MIT" +publish = false + +[dependencies] +futures = "0.3.28" +getrandom = { version = "0.2.9", features = ["js"] } +libp2p-core = { workspace = true } +libp2p-identity = { workspace = true } +libp2p-noise = { workspace = true } +libp2p-webtransport-websys = { workspace = true } +multiaddr = { workspace = true } +multihash = { workspace = true } +wasm-bindgen = "0.2.87" +wasm-bindgen-futures = "0.4.37" +wasm-bindgen-test = "0.3.37" +web-sys = { version = "0.3.64", features = ["Response", "Window"] } diff --git a/wasm-tests/webtransport-tests/README.md b/wasm-tests/webtransport-tests/README.md new file mode 100644 index 000000000000..b57a159176b3 --- /dev/null +++ b/wasm-tests/webtransport-tests/README.md @@ -0,0 +1,27 @@ +# Manually run tests + +First you need to build and start the echo-server: + +``` +docker build -t webtransport-echo-server echo-server +docker run -it --rm --network=host webtransport-echo-server +``` + +On another terminal run: + +``` +wasm-pack test --chrome +``` + +Navigate with your browser at http://127.0.0.1:8000. + +You can also run the tests on a headless browser: + +``` +wasm-pack test --chrome --headless +``` + +> **Note:** For headless tests your Chrome browser needs to be compatible +> with chromedriver (i.e. they must have the same major version). +> +> You may need to define the path of chromedriver with `--chromedriver=/path/to/chromedriver`. diff --git a/wasm-tests/webtransport-tests/echo-server/.gitignore b/wasm-tests/webtransport-tests/echo-server/.gitignore new file mode 100644 index 000000000000..e831a850ae03 --- /dev/null +++ b/wasm-tests/webtransport-tests/echo-server/.gitignore @@ -0,0 +1 @@ +/echo-server diff --git a/wasm-tests/webtransport-tests/echo-server/Dockerfile b/wasm-tests/webtransport-tests/echo-server/Dockerfile new file mode 100644 index 000000000000..f498e2baa1b3 --- /dev/null +++ b/wasm-tests/webtransport-tests/echo-server/Dockerfile @@ -0,0 +1,9 @@ +# syntax=docker/dockerfile:1.5-labs +FROM docker.io/library/golang:1.20 AS builder +WORKDIR /workspace +ADD . . +RUN CGO_ENABLED=0 go build . + +FROM scratch +COPY --from=builder /workspace/echo-server / +ENTRYPOINT ["/echo-server"] diff --git a/wasm-tests/webtransport-tests/echo-server/go.mod b/wasm-tests/webtransport-tests/echo-server/go.mod new file mode 100644 index 000000000000..5856827d798e --- /dev/null +++ b/wasm-tests/webtransport-tests/echo-server/go.mod @@ -0,0 +1,64 @@ +module echo-server + +go 1.20 + +require ( + github.com/libp2p/go-libp2p v0.27.5 + github.com/multiformats/go-multiaddr v0.9.0 +) + +require ( + github.com/benbjohnson/clock v1.3.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/flynn/noise v1.0.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gopacket v1.1.19 // indirect + github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/klauspost/compress v1.16.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-netroute v0.2.1 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.8.1 // indirect + github.com/multiformats/go-multihash v0.2.1 // indirect + github.com/multiformats/go-multistream v0.4.1 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/onsi/ginkgo/v2 v2.9.2 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qtls-go1-19 v0.3.2 // indirect + github.com/quic-go/qtls-go1-20 v0.2.2 // indirect + github.com/quic-go/quic-go v0.33.0 // indirect + github.com/quic-go/webtransport-go v0.5.2 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.8.0 // indirect + golang.org/x/tools v0.7.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + lukechampine.com/blake3 v1.1.7 // indirect +) diff --git a/wasm-tests/webtransport-tests/echo-server/go.sum b/wasm-tests/webtransport-tests/echo-server/go.sum new file mode 100644 index 000000000000..ab2832bce751 --- /dev/null +++ b/wasm-tests/webtransport-tests/echo-server/go.sum @@ -0,0 +1,344 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= +github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= +github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= +github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= +github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-libp2p v0.27.5 h1:KwA7pXKXpz8hG6Cr1fMA7UkgleogcwQj0sxl5qquWRg= +github.com/libp2p/go-libp2p v0.27.5/go.mod h1:oMfQGTb9CHnrOuSM6yMmyK2lXz3qIhnkn2+oK3B1Y2g= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= +github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= +github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ= +github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.8.1 h1:ycepHwavHafh3grIbR1jIXnKCsFm0fqsfEOsJ8NtKE8= +github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= +github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= +github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= +github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U= +github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= +github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= +github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= +github.com/quic-go/webtransport-go v0.5.2 h1:GA6Bl6oZY+g/flt00Pnu0XtivSD8vukOu3lYhJjnGEk= +github.com/quic-go/webtransport-go v0.5.2/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= +lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/wasm-tests/webtransport-tests/echo-server/main.go b/wasm-tests/webtransport-tests/echo-server/main.go new file mode 100644 index 000000000000..def4151bd1b6 --- /dev/null +++ b/wasm-tests/webtransport-tests/echo-server/main.go @@ -0,0 +1,99 @@ +package main + +import ( + "context" + "crypto/rand" + "fmt" + "io" + "net/http" + + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/transport" + "github.com/libp2p/go-libp2p/p2p/transport/quicreuse" + webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport" + "github.com/multiformats/go-multiaddr" +) + +// This provides a way for test cases to discover the WebTransport address +func addrReporter(ma multiaddr.Multiaddr) { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + h := w.Header() + h.Add("Access-Control-Allow-Origin", "*") + h.Add("Cross-Origin-Resource-Policy", "cross-origin") + h.Add("Content-Type", "text/plain; charset=utf-8") + + fmt.Fprint(w, ma.String()) + }) + + http.ListenAndServe(":4455", nil) +} + +func serveConn(conn transport.CapableConn) { + go func() { + for { + stream, err := conn.OpenStream(context.Background()) + if err != nil { + break; + } + + // Stream is a local operation until data is send + // on the stream. We send a single byte to fully + // initiate the stream. + // + // Ref: https://github.com/libp2p/go-libp2p/issues/2343 + stream.Write([]byte("1")) + + go io.Copy(stream, stream) + } + }() + + for { + stream, err := conn.AcceptStream() + if err != nil { + break + } + + go io.Copy(stream, stream) + } +} + +func main() { + priv, pub, err := crypto.GenerateEd25519Key(rand.Reader) + if err != nil { + panic(err) + } + + peerId, err := peer.IDFromPublicKey(pub) + if err != nil { + panic(err) + } + + connManager, err := quicreuse.NewConnManager([32]byte{}) + if err != nil { + panic(err) + } + + transport, err := webtransport.New(priv, nil, connManager, nil, nil); + if err != nil { + panic(err) + } + + listener, err := transport.Listen(multiaddr.StringCast("/ip4/127.0.0.1/udp/0/quic-v1/webtransport")) + if err != nil { + panic(err) + } + + addr := listener.Multiaddr().Encapsulate(multiaddr.StringCast("/p2p/" + peerId.String())) + + go addrReporter(addr) + + for { + conn, err := listener.Accept() + if err != nil { + panic(nil) + } + + go serveConn(conn) + } +} diff --git a/wasm-tests/webtransport-tests/run.sh b/wasm-tests/webtransport-tests/run.sh new file mode 100755 index 000000000000..1819fc977706 --- /dev/null +++ b/wasm-tests/webtransport-tests/run.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Prefer podman over docker since it doesn't require root privileges +if command -v podman > /dev/null; then + docker=podman +else + docker=docker +fi + +# cd to this script directory +cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1 + +# Print the directory for debugging +echo "Tests: $PWD" + +# Build and run echo-server +$docker build -t webtransport-echo-server echo-server || exit 1 +id="$($docker run -d --network=host webtransport-echo-server)" || exit 1 + +# Run tests +wasm-pack test --chrome --headless +exit_code=$? + +# Remove echo-server container +$docker rm -f "$id" + +# Propagate wasm-pack's exit code +exit $exit_code diff --git a/wasm-tests/webtransport-tests/src/lib.rs b/wasm-tests/webtransport-tests/src/lib.rs new file mode 100644 index 000000000000..0ec2f0bcb037 --- /dev/null +++ b/wasm-tests/webtransport-tests/src/lib.rs @@ -0,0 +1,382 @@ +use futures::channel::oneshot; +use futures::{AsyncReadExt, AsyncWriteExt}; +use getrandom::getrandom; +use libp2p_core::{StreamMuxer, Transport as _}; +use libp2p_identity::{Keypair, PeerId}; +use libp2p_noise as noise; +use libp2p_webtransport_websys::{Config, Connection, Error, Stream, Transport}; +use multiaddr::{Multiaddr, Protocol}; +use multihash::Multihash; +use std::future::poll_fn; +use std::pin::Pin; +use wasm_bindgen::JsCast; +use wasm_bindgen_futures::{spawn_local, JsFuture}; +use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure}; +use web_sys::{window, Response}; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +async fn single_conn_single_stream() { + let mut conn = new_connection_to_echo_server().await; + let mut stream = create_stream(&mut conn).await; + + send_recv(&mut stream).await; +} + +#[wasm_bindgen_test] +async fn single_conn_single_stream_incoming() { + let mut conn = new_connection_to_echo_server().await; + let mut stream = incoming_stream(&mut conn).await; + + send_recv(&mut stream).await; +} + +#[wasm_bindgen_test] +async fn single_conn_multiple_streams() { + let mut conn = new_connection_to_echo_server().await; + let mut tasks = Vec::new(); + let mut streams = Vec::new(); + + for i in 0..30 { + let stream = if i % 2 == 0 { + create_stream(&mut conn).await + } else { + incoming_stream(&mut conn).await + }; + + streams.push(stream); + } + + for stream in streams { + tasks.push(send_recv_task(stream)); + } + + futures::future::try_join_all(tasks).await.unwrap(); +} + +#[wasm_bindgen_test] +async fn multiple_conn_multiple_streams() { + let mut tasks = Vec::new(); + let mut conns = Vec::new(); + + for _ in 0..10 { + let mut conn = new_connection_to_echo_server().await; + let mut streams = Vec::new(); + + for i in 0..10 { + let stream = if i % 2 == 0 { + create_stream(&mut conn).await + } else { + incoming_stream(&mut conn).await + }; + + streams.push(stream); + } + + // If `conn` gets drop then its streams will close. + // Keep it alive by moving it to the outer scope. + conns.push(conn); + + for stream in streams { + tasks.push(send_recv_task(stream)); + } + } + + futures::future::try_join_all(tasks).await.unwrap(); +} + +#[wasm_bindgen_test] +async fn multiple_conn_multiple_streams_sequential() { + for _ in 0..10 { + let mut conn = new_connection_to_echo_server().await; + + for i in 0..10 { + let mut stream = if i % 2 == 0 { + create_stream(&mut conn).await + } else { + incoming_stream(&mut conn).await + }; + + send_recv(&mut stream).await; + } + } +} + +#[wasm_bindgen_test] +async fn read_leftovers() { + let mut conn = new_connection_to_echo_server().await; + let mut stream = create_stream(&mut conn).await; + + // Test that stream works + send_recv(&mut stream).await; + + stream.write_all(b"hello").await.unwrap(); + + let mut buf = [0u8; 3]; + + // Read first half + let len = stream.read(&mut buf[..]).await.unwrap(); + assert_eq!(len, 3); + assert_eq!(&buf[..len], b"hel"); + + // Read second half + let len = stream.read(&mut buf[..]).await.unwrap(); + assert_eq!(len, 2); + assert_eq!(&buf[..len], b"lo"); +} + +#[wasm_bindgen_test] +async fn allow_read_after_closing_writer() { + let mut conn = new_connection_to_echo_server().await; + let mut stream = create_stream(&mut conn).await; + + // Test that stream works + send_recv(&mut stream).await; + + // Write random data + let mut send_buf = [0u8; 1024]; + getrandom(&mut send_buf).unwrap(); + stream.write_all(&send_buf).await.unwrap(); + + // Close writer by calling AsyncWrite::poll_close + stream.close().await.unwrap(); + + // Make sure writer is closed + stream.write_all(b"1").await.unwrap_err(); + + // We should be able to read + let mut recv_buf = [0u8; 1024]; + stream.read_exact(&mut recv_buf).await.unwrap(); + + assert_eq!(send_buf, recv_buf); +} + +#[wasm_bindgen_test] +async fn poll_outbound_error_after_connection_close() { + let mut conn = new_connection_to_echo_server().await; + + // Make sure that poll_outbound works well before closing the connection + let mut stream = create_stream(&mut conn).await; + send_recv(&mut stream).await; + drop(stream); + + poll_fn(|cx| Pin::new(&mut conn).poll_close(cx)) + .await + .unwrap(); + + poll_fn(|cx| Pin::new(&mut conn).poll_outbound(cx)) + .await + .expect_err("poll_outbound error after conn closed"); +} + +#[wasm_bindgen_test] +async fn poll_inbound_error_after_connection_close() { + let mut conn = new_connection_to_echo_server().await; + + // Make sure that poll_inbound works well before closing the connection + let mut stream = incoming_stream(&mut conn).await; + send_recv(&mut stream).await; + drop(stream); + + poll_fn(|cx| Pin::new(&mut conn).poll_close(cx)) + .await + .unwrap(); + + poll_fn(|cx| Pin::new(&mut conn).poll_inbound(cx)) + .await + .expect_err("poll_inbound error after conn closed"); +} + +#[wasm_bindgen_test] +async fn read_error_after_connection_drop() { + let mut conn = new_connection_to_echo_server().await; + let mut stream = create_stream(&mut conn).await; + + send_recv(&mut stream).await; + drop(conn); + + let mut buf = [0u8; 16]; + stream + .read(&mut buf) + .await + .expect_err("read error after conn drop"); +} + +#[wasm_bindgen_test] +async fn read_error_after_connection_close() { + let mut conn = new_connection_to_echo_server().await; + let mut stream = create_stream(&mut conn).await; + + send_recv(&mut stream).await; + + poll_fn(|cx| Pin::new(&mut conn).poll_close(cx)) + .await + .unwrap(); + + let mut buf = [0u8; 16]; + stream + .read(&mut buf) + .await + .expect_err("read error after conn drop"); +} + +#[wasm_bindgen_test] +async fn write_error_after_connection_drop() { + let mut conn = new_connection_to_echo_server().await; + let mut stream = create_stream(&mut conn).await; + + send_recv(&mut stream).await; + drop(conn); + + let buf = [0u8; 16]; + stream + .write(&buf) + .await + .expect_err("write error after conn drop"); +} + +#[wasm_bindgen_test] +async fn write_error_after_connection_close() { + let mut conn = new_connection_to_echo_server().await; + let mut stream = create_stream(&mut conn).await; + + send_recv(&mut stream).await; + + poll_fn(|cx| Pin::new(&mut conn).poll_close(cx)) + .await + .unwrap(); + + let buf = [0u8; 16]; + stream + .write(&buf) + .await + .expect_err("write error after conn drop"); +} + +#[wasm_bindgen_test] +async fn connect_without_peer_id() { + let mut addr = fetch_server_addr().await; + let keypair = Keypair::generate_ed25519(); + + // Remove peer id + addr.pop(); + + let mut transport = Transport::new(Config::new(&keypair)); + transport.dial(addr).unwrap().await.unwrap(); +} + +#[wasm_bindgen_test] +async fn error_on_unknown_peer_id() { + let mut addr = fetch_server_addr().await; + let keypair = Keypair::generate_ed25519(); + + // Remove peer id + addr.pop(); + + // Add an unknown one + addr.push(Protocol::P2p(PeerId::random())); + + let mut transport = Transport::new(Config::new(&keypair)); + let e = transport.dial(addr.clone()).unwrap().await.unwrap_err(); + assert!(matches!(e, Error::UnknownRemotePeerId)); +} + +#[wasm_bindgen_test] +async fn error_on_unknown_certhash() { + let mut addr = fetch_server_addr().await; + let keypair = Keypair::generate_ed25519(); + + // Remove peer id + let peer_id = addr.pop().unwrap(); + + // Add unknown certhash + addr.push(Protocol::Certhash(Multihash::wrap(1, b"1").unwrap())); + + // Add peer id back + addr.push(peer_id); + + let mut transport = Transport::new(Config::new(&keypair)); + let e = transport.dial(addr.clone()).unwrap().await.unwrap_err(); + assert!(matches!( + e, + Error::Noise(noise::Error::UnknownWebTransportCerthashes(..)) + )); +} + +async fn new_connection_to_echo_server() -> Connection { + let addr = fetch_server_addr().await; + let keypair = Keypair::generate_ed25519(); + + let mut transport = Transport::new(Config::new(&keypair)); + + let (_peer_id, conn) = transport.dial(addr).unwrap().await.unwrap(); + + conn +} + +/// Helper that returns the multiaddress of echo-server +/// +/// It fetches the multiaddress via HTTP request to +/// 127.0.0.1:4455. +async fn fetch_server_addr() -> Multiaddr { + let url = "http://127.0.0.1:4455/"; + let window = window().expect("failed to get browser window"); + + let value = JsFuture::from(window.fetch_with_str(url)) + .await + .expect("fetch failed"); + let resp = value.dyn_into::().expect("cast failed"); + + let text = resp.text().expect("text failed"); + let text = JsFuture::from(text).await.expect("text promise failed"); + + text.as_string() + .filter(|s| !s.is_empty()) + .expect("response not a text") + .parse() + .unwrap() +} + +async fn create_stream(conn: &mut Connection) -> Stream { + poll_fn(|cx| Pin::new(&mut *conn).poll_outbound(cx)) + .await + .unwrap() +} + +async fn incoming_stream(conn: &mut Connection) -> Stream { + let mut stream = poll_fn(|cx| Pin::new(&mut *conn).poll_inbound(cx)) + .await + .unwrap(); + + // For the stream to be initiated `echo-server` sends a single byte + let mut buf = [0u8; 1]; + stream.read_exact(&mut buf).await.unwrap(); + + stream +} + +fn send_recv_task(mut steam: Stream) -> oneshot::Receiver<()> { + let (tx, rx) = oneshot::channel(); + + spawn_local(async move { + send_recv(&mut steam).await; + tx.send(()).unwrap(); + }); + + rx +} + +async fn send_recv(stream: &mut Stream) { + let mut send_buf = [0u8; 1024]; + let mut recv_buf = [0u8; 1024]; + + for _ in 0..30 { + getrandom(&mut send_buf).unwrap(); + + stream.write_all(&send_buf).await.unwrap(); + stream.read_exact(&mut recv_buf).await.unwrap(); + + assert_eq!(send_buf, recv_buf); + } +}