diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 6c43a31d2c1..293445706bb 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -12,7 +12,6 @@ updates:
hickory-dns:
patterns:
- "hickory-*"
- - "async-std-resolver"
opentelemetry:
patterns:
- "opentelemetry*"
diff --git a/.github/mergify.yml b/.github/mergify.yml
index 0d519b38a94..5bbca7d970b 100644
--- a/.github/mergify.yml
+++ b/.github/mergify.yml
@@ -1,3 +1,6 @@
+merge_queue:
+ max_parallel_checks: 1
+
pull_request_rules:
- name: Ask to resolve conflict
conditions:
diff --git a/.github/workflows/cache-factory.yml b/.github/workflows/cache-factory.yml
index 2176fb937db..4533df0e80d 100644
--- a/.github/workflows/cache-factory.yml
+++ b/.github/workflows/cache-factory.yml
@@ -18,11 +18,11 @@ jobs:
make_stable_rust_cache:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
shared-key: stable-cache
diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml
index 405d0a2f799..7a83100f853 100644
--- a/.github/workflows/cargo-audit.yml
+++ b/.github/workflows/cargo-audit.yml
@@ -8,7 +8,7 @@ jobs:
audit:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a9834ee7423..e217e8b59a9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,12 +18,7 @@ env:
jobs:
test:
name: Test ${{ matrix.crate }}
- runs-on: ${{ fromJSON(
- github.repository == 'libp2p/rust-libp2p' && (
- (contains(fromJSON('["libp2p-webrtc", "libp2p"]'), matrix.crate) && '["self-hosted", "linux", "x64", "2xlarge"]') ||
- (contains(fromJSON('["libp2p-quic", "libp2p-perf"]'), matrix.crate) && '["self-hosted", "linux", "x64", "xlarge"]') ||
- '["self-hosted", "linux", "x64", "large"]'
- ) || '"ubuntu-latest"') }}
+ runs-on: ubuntu-latest
timeout-minutes: 10
needs: gather_published_crates
strategy:
@@ -33,7 +28,7 @@ jobs:
env:
CRATE: ${{ matrix.crate }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
@@ -41,7 +36,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
shared-key: stable-cache
save-if: false
@@ -109,7 +104,7 @@ jobs:
name: Run all WASM tests
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
@@ -142,7 +137,7 @@ jobs:
os: windows-latest
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
@@ -150,7 +145,7 @@ jobs:
- uses: r7kamura/rust-problem-matchers@9fe7ca9f6550e5d6358e179d451cc25ea6b54f98 #v1.5.0
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
key: ${{ matrix.target }}
save-if: ${{ github.ref == 'refs/heads/master' }}
@@ -161,7 +156,7 @@ jobs:
name: Compile with MSRV
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Extract MSRV from workspace manifest
shell: bash
@@ -175,7 +170,7 @@ jobs:
- uses: r7kamura/rust-problem-matchers@9fe7ca9f6550e5d6358e179d451cc25ea6b54f98 #v1.5.0
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
save-if: ${{ github.ref == 'refs/heads/master' }}
@@ -188,15 +183,14 @@ jobs:
matrix:
include:
- features: "mdns tcp dns tokio"
- - features: "mdns tcp dns async-std"
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: r7kamura/rust-problem-matchers@9fe7ca9f6550e5d6358e179d451cc25ea6b54f98 #v1.5.0
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
key: ${{ matrix.features }}
save-if: ${{ github.ref == 'refs/heads/master' }}
@@ -207,13 +201,13 @@ jobs:
name: Check rustdoc intra-doc links
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: r7kamura/rust-problem-matchers@9fe7ca9f6550e5d6358e179d451cc25ea6b54f98 #v1.5.0
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
save-if: ${{ github.ref == 'refs/heads/master' }}
@@ -230,7 +224,7 @@ jobs:
beta,
]
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@master
with:
@@ -239,7 +233,7 @@ jobs:
- uses: r7kamura/rust-problem-matchers@9fe7ca9f6550e5d6358e179d451cc25ea6b54f98 #v1.5.0
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
save-if: ${{ github.ref == 'refs/heads/master' }}
@@ -249,13 +243,13 @@ jobs:
name: IPFS Integration tests
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: r7kamura/rust-problem-matchers@9fe7ca9f6550e5d6358e179d451cc25ea6b54f98 #v1.5.0
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
save-if: ${{ github.ref == 'refs/heads/master' }}
@@ -268,13 +262,13 @@ jobs:
examples:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- uses: r7kamura/rust-problem-matchers@9fe7ca9f6550e5d6358e179d451cc25ea6b54f98 #v1.5.0
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
shared-key: stable-cache
save-if: false
@@ -308,14 +302,14 @@ jobs:
# https://github.com/obi1kenobi/cargo-semver-checks/issues/589
RUSTFLAGS: ''
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: obi1kenobi/cargo-semver-checks-action@v2
- run: cargo semver-checks
rustfmt:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@nightly
with:
@@ -329,7 +323,7 @@ jobs:
manifest_lint:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
@@ -350,7 +344,7 @@ jobs:
outputs:
members: ${{ steps.cargo-metadata.outputs.members }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
@@ -363,9 +357,9 @@ jobs:
name: Check for changes in proto files
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
- run: cargo install --version 0.10.0 pb-rs --locked
@@ -390,14 +384,14 @@ jobs:
name: Ensure that `Cargo.lock` is up-to-date
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+ - uses: actions/checkout@v5
+ - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
- run: cargo metadata --locked --format-version=1 > /dev/null
cargo-deny:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check advisories bans licenses sources
diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
index 5cbfc20d69d..103dd3e057b 100644
--- a/.github/workflows/docker-image.yml
+++ b/.github/workflows/docker-image.yml
@@ -11,7 +11,7 @@ jobs:
server:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: docker/login-action@v3
with:
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index e2bac78c006..f6783ce11f6 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Install nightly toolchain
run: rustup toolchain install nightly
- name: Build Documentation
@@ -23,7 +23,7 @@ jobs:
echo "" > target/doc/index.html
cp -r target/doc/* ./host-docs
- name: Upload documentation
- uses: actions/upload-pages-artifact@v3.0.1
+ uses: actions/upload-pages-artifact@v4.0.0
with:
path: "host-docs/"
diff --git a/.github/workflows/interop-test.yml b/.github/workflows/interop-test.yml
index 406bea27205..c5dbe2323ac 100644
--- a/.github/workflows/interop-test.yml
+++ b/.github/workflows/interop-test.yml
@@ -13,12 +13,12 @@ jobs:
run-transport-interop:
name: Run transport interoperability tests
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
- runs-on: ${{ fromJSON(github.repository == 'libp2p/rust-libp2p' && '["self-hosted", "linux", "x64", "4xlarge"]' || '"ubuntu-latest"') }}
+ runs-on: ubuntu-latest
strategy:
matrix:
flavour: [chromium, native]
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: docker/setup-buildx-action@v3
@@ -47,7 +47,7 @@ jobs:
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ${{ fromJSON(github.repository == 'libp2p/rust-libp2p' && '["self-hosted", "linux", "x64", "4xlarge"]' || '"ubuntu-latest"') }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- uses: docker/setup-buildx-action@v3
- name: Build image
run: docker buildx build --load -t rust-libp2p-head . -f hole-punching-tests/Dockerfile
diff --git a/.gitignore b/.gitignore
index eb5a316cbd1..1b2924b6333 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
target
+.idea/
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 22c82b5f793..2a1ceb8a7e3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -52,18 +52,6 @@ dependencies = [
"subtle",
]
-[[package]]
-name = "ahash"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
-dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
- "zerocopy 0.7.35",
-]
-
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -73,12 +61,6 @@ dependencies = [
"memchr",
]
-[[package]]
-name = "allocator-api2"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
-
[[package]]
name = "anes"
version = "0.1.6"
@@ -232,27 +214,6 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247"
-[[package]]
-name = "async-attributes"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
-dependencies = [
- "quote",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "async-channel"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
-dependencies = [
- "concurrent-queue",
- "event-listener 2.5.3",
- "futures-core",
-]
-
[[package]]
name = "async-channel"
version = "2.3.1"
@@ -265,45 +226,6 @@ dependencies = [
"pin-project-lite",
]
-[[package]]
-name = "async-executor"
-version = "1.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
-dependencies = [
- "async-task",
- "concurrent-queue",
- "fastrand",
- "futures-lite",
- "slab",
-]
-
-[[package]]
-name = "async-fs"
-version = "2.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
-dependencies = [
- "async-lock",
- "blocking",
- "futures-lite",
-]
-
-[[package]]
-name = "async-global-executor"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
-dependencies = [
- "async-channel 2.3.1",
- "async-executor",
- "async-io",
- "async-lock",
- "blocking",
- "futures-lite",
- "once_cell",
-]
-
[[package]]
name = "async-io"
version = "2.4.0"
@@ -329,113 +251,11 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
dependencies = [
- "event-listener 5.4.0",
+ "event-listener",
"event-listener-strategy",
"pin-project-lite",
]
-[[package]]
-name = "async-net"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
-dependencies = [
- "async-io",
- "blocking",
- "futures-lite",
-]
-
-[[package]]
-name = "async-process"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
-dependencies = [
- "async-channel 2.3.1",
- "async-io",
- "async-lock",
- "async-signal",
- "async-task",
- "blocking",
- "cfg-if",
- "event-listener 5.4.0",
- "futures-lite",
- "rustix 0.38.44",
- "tracing",
-]
-
-[[package]]
-name = "async-recursion"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.100",
-]
-
-[[package]]
-name = "async-signal"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
-dependencies = [
- "async-io",
- "async-lock",
- "atomic-waker",
- "cfg-if",
- "futures-core",
- "futures-io",
- "rustix 0.38.44",
- "signal-hook-registry",
- "slab",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "async-std"
-version = "1.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24"
-dependencies = [
- "async-attributes",
- "async-channel 1.9.0",
- "async-global-executor",
- "async-io",
- "async-lock",
- "async-process",
- "crossbeam-utils",
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-lite",
- "gloo-timers 0.3.0",
- "kv-log-macro",
- "log",
- "memchr",
- "once_cell",
- "pin-project-lite",
- "pin-utils",
- "slab",
- "wasm-bindgen-futures",
-]
-
-[[package]]
-name = "async-std-resolver"
-version = "0.25.0-alpha.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4abef525d07400182ad6d48b3097d8e88a40aed1f3a6e80f221b2b002cfad608"
-dependencies = [
- "async-std",
- "async-trait",
- "futures-io",
- "futures-util",
- "hickory-resolver",
- "pin-utils",
- "socket2",
-]
-
[[package]]
name = "async-stream"
version = "0.3.6"
@@ -458,12 +278,6 @@ dependencies = [
"syn 2.0.100",
]
-[[package]]
-name = "async-task"
-version = "4.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
-
[[package]]
name = "async-trait"
version = "0.1.88"
@@ -687,16 +501,26 @@ dependencies = [
]
[[package]]
-name = "blocking"
-version = "1.6.1"
+name = "borsh"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea"
+checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f"
dependencies = [
- "async-channel 2.3.1",
- "async-task",
- "futures-io",
- "futures-lite",
- "piper",
+ "borsh-derive",
+ "cfg_aliases",
+]
+
+[[package]]
+name = "borsh-derive"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c"
+dependencies = [
+ "once_cell",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
]
[[package]]
@@ -716,7 +540,7 @@ dependencies = [
"tokio",
"tokio-util",
"tower 0.4.13",
- "tower-http",
+ "tower-http 0.5.2",
"tracing",
"tracing-subscriber",
"tracing-wasm",
@@ -922,7 +746,7 @@ version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
dependencies = [
- "heck",
+ "heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.100",
@@ -1066,6 +890,12 @@ dependencies = [
"itertools 0.10.5",
]
+[[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
@@ -1160,7 +990,10 @@ dependencies = [
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
+ "group",
+ "rand_core 0.6.4",
"rustc_version",
+ "serde",
"subtle",
"zeroize",
]
@@ -1199,7 +1032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18e4fdb82bd54a12e42fb58a800dcae6b9e13982238ce2296dc3570b92148e1f"
dependencies = [
"data-encoding",
- "syn 1.0.109",
+ "syn 2.0.100",
]
[[package]]
@@ -1317,6 +1150,12 @@ dependencies = [
"tracing-subscriber",
]
+[[package]]
+name = "doc-comment"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9"
+
[[package]]
name = "dtoa"
version = "1.0.10"
@@ -1355,7 +1194,6 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
dependencies = [
"curve25519-dalek",
"ed25519",
- "rand_core 0.6.4",
"serde",
"sha2",
"subtle",
@@ -1404,7 +1242,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
dependencies = [
- "heck",
+ "heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.100",
@@ -1459,12 +1297,6 @@ dependencies = [
"windows-sys 0.59.0",
]
-[[package]]
-name = "event-listener"
-version = "2.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
-
[[package]]
name = "event-listener"
version = "5.4.0"
@@ -1482,7 +1314,7 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
- "event-listener 5.4.0",
+ "event-listener",
"pin-project-lite",
]
@@ -1621,10 +1453,7 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
dependencies = [
- "fastrand",
"futures-core",
- "futures-io",
- "parking",
"pin-project-lite",
]
@@ -1668,7 +1497,7 @@ version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
dependencies = [
- "gloo-timers 0.2.6",
+ "gloo-timers",
"send_wrapper 0.4.0",
]
@@ -1795,8 +1624,8 @@ dependencies = [
"aho-corasick",
"bstr",
"log",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -1811,18 +1640,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "gloo-timers"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
-dependencies = [
- "futures-channel",
- "futures-core",
- "js-sys",
- "wasm-bindgen",
-]
-
[[package]]
name = "group"
version = "0.13.0"
@@ -1846,7 +1663,7 @@ dependencies = [
"futures-core",
"futures-sink",
"http 1.3.1",
- "indexmap 2.9.0",
+ "indexmap 2.13.0",
"slab",
"tokio",
"tokio-util",
@@ -1871,33 +1688,34 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
-version = "0.14.5"
+version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
dependencies = [
- "ahash",
+ "foldhash",
]
[[package]]
name = "hashbrown"
-version = "0.15.2"
+version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
-dependencies = [
- "allocator-api2",
- "equivalent",
- "foldhash",
-]
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "hashlink"
-version = "0.9.1"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
dependencies = [
- "hashbrown 0.14.5",
+ "hashbrown 0.15.2",
]
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
[[package]]
name = "heck"
version = "0.5.0"
@@ -1942,11 +1760,10 @@ checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f"
[[package]]
name = "hickory-proto"
-version = "0.25.0-alpha.5"
+version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d00147af6310f4392a31680db52a3ed45a2e0f68eb18e8c3fe5537ecc96d9e2"
+checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502"
dependencies = [
- "async-recursion",
"async-trait",
"cfg-if",
"data-encoding",
@@ -1958,7 +1775,8 @@ dependencies = [
"ipnet",
"once_cell",
"rand 0.9.0",
- "socket2",
+ "ring",
+ "socket2 0.5.9",
"thiserror 2.0.12",
"tinyvec",
"tokio",
@@ -1968,9 +1786,9 @@ dependencies = [
[[package]]
name = "hickory-resolver"
-version = "0.25.0-alpha.5"
+version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5762f69ebdbd4ddb2e975cd24690bf21fe6b2604039189c26acddbc427f12887"
+checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a"
dependencies = [
"cfg-if",
"futures-util",
@@ -2097,13 +1915,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
-version = "1.6.0"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
+checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
dependencies = [
+ "atomic-waker",
"bytes",
"futures-channel",
- "futures-util",
+ "futures-core",
"h2",
"http 1.3.1",
"http-body",
@@ -2111,6 +1930,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
+ "pin-utils",
"smallvec",
"tokio",
"want",
@@ -2131,7 +1951,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
- "webpki-roots",
+ "webpki-roots 0.26.8",
]
[[package]]
@@ -2165,22 +1985,28 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.11"
+version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
+checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
dependencies = [
+ "base64",
"bytes",
"futures-channel",
+ "futures-core",
"futures-util",
"http 1.3.1",
"http-body",
"hyper",
+ "ipnet",
"libc",
+ "percent-encoding",
"pin-project-lite",
- "socket2",
+ "socket2 0.6.0",
+ "system-configuration",
"tokio",
"tower-service",
"tracing",
+ "windows-registry",
]
[[package]]
@@ -2360,7 +2186,6 @@ dependencies = [
"netlink-proto",
"netlink-sys",
"rtnetlink",
- "smol",
"system-configuration",
"tokio",
"windows 0.53.0",
@@ -2399,12 +2224,12 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.9.0"
+version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
- "hashbrown 0.15.2",
+ "hashbrown 0.16.1",
]
[[package]]
@@ -2467,7 +2292,7 @@ dependencies = [
"serde_json",
"thirtyfour",
"tokio",
- "tower-http",
+ "tower-http 0.5.2",
"tracing",
"tracing-subscriber",
"wasm-bindgen",
@@ -2482,7 +2307,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
dependencies = [
- "socket2",
+ "socket2 0.5.9",
"widestring",
"windows-sys 0.48.0",
"winreg",
@@ -2517,6 +2342,16 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
+[[package]]
+name = "iri-string"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
[[package]]
name = "is-terminal"
version = "0.4.16"
@@ -2543,6 +2378,15 @@ dependencies = [
"either",
]
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "itertools"
version = "0.14.0"
@@ -2627,15 +2471,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "kv-log-macro"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
-dependencies = [
- "log",
-]
-
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -2644,15 +2479,14 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.171"
+version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libp2p"
-version = "0.56.0"
+version = "0.56.1"
dependencies = [
- "async-std",
"bytes",
"either",
"futures",
@@ -2674,7 +2508,6 @@ dependencies = [
"libp2p-metrics",
"libp2p-mplex",
"libp2p-noise",
- "libp2p-peer-store",
"libp2p-ping",
"libp2p-plaintext",
"libp2p-pnet",
@@ -2702,7 +2535,7 @@ dependencies = [
[[package]]
name = "libp2p-allow-block-list"
-version = "0.5.0"
+version = "0.6.0"
dependencies = [
"libp2p-core",
"libp2p-identity",
@@ -2714,7 +2547,7 @@ dependencies = [
[[package]]
name = "libp2p-autonat"
-version = "0.14.1"
+version = "0.15.0"
dependencies = [
"async-trait",
"asynchronous-codec",
@@ -2741,7 +2574,7 @@ dependencies = [
[[package]]
name = "libp2p-connection-limits"
-version = "0.5.1"
+version = "0.6.0"
dependencies = [
"libp2p-core",
"libp2p-identify",
@@ -2757,7 +2590,7 @@ dependencies = [
[[package]]
name = "libp2p-core"
-version = "0.43.1"
+version = "0.43.2"
dependencies = [
"either",
"fnv",
@@ -2783,13 +2616,14 @@ dependencies = [
[[package]]
name = "libp2p-dcutr"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"asynchronous-codec",
"either",
"futures",
"futures-bounded",
"futures-timer",
+ "hashlink",
"libp2p-core",
"libp2p-identify",
"libp2p-identity",
@@ -2799,7 +2633,6 @@ dependencies = [
"libp2p-swarm-test",
"libp2p-tcp",
"libp2p-yamux",
- "lru",
"quick-protobuf",
"quick-protobuf-codec",
"thiserror 2.0.12",
@@ -2813,8 +2646,6 @@ dependencies = [
name = "libp2p-dns"
version = "0.44.0"
dependencies = [
- "async-std",
- "async-std-resolver",
"async-trait",
"futures",
"hickory-resolver",
@@ -2829,7 +2660,7 @@ dependencies = [
[[package]]
name = "libp2p-floodsub"
-version = "0.46.1"
+version = "0.47.0"
dependencies = [
"asynchronous-codec",
"bytes",
@@ -2849,9 +2680,9 @@ dependencies = [
[[package]]
name = "libp2p-gossipsub"
-version = "0.49.0"
+version = "0.50.0"
dependencies = [
- "async-channel 2.3.1",
+ "async-channel",
"asynchronous-codec",
"base64",
"byteorder",
@@ -2905,7 +2736,7 @@ dependencies = [
[[package]]
name = "libp2p-identity"
-version = "0.2.11"
+version = "0.2.13"
dependencies = [
"asn1_der",
"bs58",
@@ -2925,6 +2756,7 @@ dependencies = [
"serde",
"serde_json",
"sha2",
+ "tari_crypto",
"thiserror 2.0.12",
"tracing",
"zeroize",
@@ -2932,7 +2764,7 @@ dependencies = [
[[package]]
name = "libp2p-kad"
-version = "0.47.1"
+version = "0.49.0"
dependencies = [
"asynchronous-codec",
"bytes",
@@ -2976,7 +2808,7 @@ dependencies = [
"libp2p-swarm-test",
"rand 0.8.5",
"smallvec",
- "socket2",
+ "socket2 0.6.0",
"tokio",
"tracing",
"tracing-subscriber",
@@ -2984,7 +2816,7 @@ dependencies = [
[[package]]
name = "libp2p-memory-connection-limits"
-version = "0.4.0"
+version = "0.5.0"
dependencies = [
"libp2p-core",
"libp2p-identify",
@@ -3000,7 +2832,7 @@ dependencies = [
[[package]]
name = "libp2p-metrics"
-version = "0.17.0"
+version = "0.17.1"
dependencies = [
"futures",
"libp2p-core",
@@ -3080,12 +2912,12 @@ dependencies = [
name = "libp2p-peer-store"
version = "0.1.0"
dependencies = [
+ "hashlink",
"libp2p",
"libp2p-core",
"libp2p-identity",
"libp2p-swarm",
"libp2p-swarm-test",
- "lru",
"serde_json",
"tokio",
]
@@ -3118,7 +2950,7 @@ dependencies = [
[[package]]
name = "libp2p-ping"
-version = "0.46.0"
+version = "0.47.0"
dependencies = [
"futures",
"futures-timer",
@@ -3190,7 +3022,7 @@ dependencies = [
"rand 0.8.5",
"ring",
"rustls",
- "socket2",
+ "socket2 0.6.0",
"thiserror 2.0.12",
"tokio",
"tracing",
@@ -3199,7 +3031,7 @@ dependencies = [
[[package]]
name = "libp2p-relay"
-version = "0.20.0"
+version = "0.21.1"
dependencies = [
"asynchronous-codec",
"bytes",
@@ -3220,6 +3052,7 @@ dependencies = [
"rand 0.8.5",
"static_assertions",
"thiserror 2.0.12",
+ "tokio",
"tracing",
"tracing-subscriber",
"web-time 1.1.0",
@@ -3227,7 +3060,7 @@ dependencies = [
[[package]]
name = "libp2p-rendezvous"
-version = "0.16.1"
+version = "0.17.0"
dependencies = [
"async-trait",
"asynchronous-codec",
@@ -3251,7 +3084,7 @@ dependencies = [
[[package]]
name = "libp2p-request-response"
-version = "0.28.1"
+version = "0.29.0"
dependencies = [
"anyhow",
"async-trait",
@@ -3292,7 +3125,7 @@ dependencies = [
[[package]]
name = "libp2p-stream"
-version = "0.3.0-alpha.1"
+version = "0.4.0-alpha"
dependencies = [
"futures",
"libp2p-core",
@@ -3309,13 +3142,13 @@ dependencies = [
name = "libp2p-swarm"
version = "0.47.0"
dependencies = [
- "async-std",
"criterion",
"either",
"fnv",
"futures",
"futures-timer",
"getrandom 0.2.15",
+ "hashlink",
"libp2p-core",
"libp2p-identify",
"libp2p-identity",
@@ -3325,7 +3158,6 @@ dependencies = [
"libp2p-swarm-derive",
"libp2p-swarm-test",
"libp2p-yamux",
- "lru",
"multistream-select",
"quickcheck-ext",
"rand 0.8.5",
@@ -3342,7 +3174,7 @@ dependencies = [
name = "libp2p-swarm-derive"
version = "0.35.1"
dependencies = [
- "heck",
+ "heck 0.5.0",
"quote",
"syn 2.0.100",
]
@@ -3365,16 +3197,14 @@ dependencies = [
[[package]]
name = "libp2p-tcp"
-version = "0.43.0"
+version = "0.44.1"
dependencies = [
- "async-io",
- "async-std",
"futures",
"futures-timer",
"if-watch",
"libc",
"libp2p-core",
- "socket2",
+ "socket2 0.6.0",
"tokio",
"tracing",
"tracing-subscriber",
@@ -3403,9 +3233,8 @@ dependencies = [
[[package]]
name = "libp2p-uds"
-version = "0.42.0"
+version = "0.43.1"
dependencies = [
- "async-std",
"futures",
"libp2p-core",
"tempfile",
@@ -3415,7 +3244,7 @@ dependencies = [
[[package]]
name = "libp2p-upnp"
-version = "0.4.1"
+version = "0.6.0"
dependencies = [
"futures",
"futures-timer",
@@ -3428,7 +3257,7 @@ dependencies = [
[[package]]
name = "libp2p-webrtc"
-version = "0.9.0-alpha"
+version = "0.9.0-alpha.2"
dependencies = [
"async-trait",
"futures",
@@ -3495,7 +3324,7 @@ dependencies = [
[[package]]
name = "libp2p-websocket"
-version = "0.45.1"
+version = "0.45.2"
dependencies = [
"either",
"futures",
@@ -3513,7 +3342,7 @@ dependencies = [
"tokio",
"tracing",
"url",
- "webpki-roots",
+ "webpki-roots 0.26.8",
]
[[package]]
@@ -3536,7 +3365,7 @@ dependencies = [
[[package]]
name = "libp2p-webtransport-websys"
-version = "0.5.1"
+version = "0.5.2"
dependencies = [
"futures",
"js-sys",
@@ -3612,9 +3441,6 @@ name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
-dependencies = [
- "value-bag",
-]
[[package]]
name = "loom"
@@ -3629,22 +3455,13 @@ dependencies = [
"tracing-subscriber",
]
-[[package]]
-name = "lru"
-version = "0.12.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
-dependencies = [
- "hashbrown 0.15.2",
-]
-
[[package]]
name = "matchers"
-version = "0.1.0"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
- "regex-automata 0.1.10",
+ "regex-automata",
]
[[package]]
@@ -3688,6 +3505,18 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "merlin"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
+dependencies = [
+ "byteorder",
+ "keccak",
+ "rand_core 0.6.4",
+ "zeroize",
+]
+
[[package]]
name = "metrics-example"
version = "0.1.0"
@@ -3778,18 +3607,21 @@ dependencies = [
[[package]]
name = "multiaddr"
-version = "0.18.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961"
+version = "0.18.3"
dependencies = [
"arrayref",
+ "bincode",
"byteorder",
+ "bytes",
"data-encoding",
"libp2p-identity",
"multibase",
"multihash",
"percent-encoding",
+ "quickcheck",
+ "rand 0.9.0",
"serde",
+ "serde_json",
"static_assertions",
"unsigned-varint",
"url",
@@ -3824,7 +3656,6 @@ dependencies = [
name = "multistream-select"
version = "0.13.0"
dependencies = [
- "async-std",
"bytes",
"futures",
"futures_ringbuf",
@@ -3832,6 +3663,8 @@ dependencies = [
"quickcheck-ext",
"rw-stream-sink",
"smallvec",
+ "tokio",
+ "tokio-util",
"tracing",
"tracing-subscriber",
"unsigned-varint",
@@ -3911,7 +3744,6 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23"
dependencies = [
- "async-io",
"bytes",
"futures",
"libc",
@@ -3959,12 +3791,11 @@ dependencies = [
[[package]]
name = "nu-ansi-term"
-version = "0.46.0"
+version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
- "overload",
- "winapi",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -4043,6 +3874,10 @@ name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+dependencies = [
+ "critical-section",
+ "portable-atomic",
+]
[[package]]
name = "oorandom"
@@ -4108,7 +3943,7 @@ checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a"
dependencies = [
"futures-core",
"futures-sink",
- "indexmap 2.9.0",
+ "indexmap 2.13.0",
"js-sys",
"once_cell",
"pin-project-lite",
@@ -4253,12 +4088,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
[[package]]
name = "p256"
version = "0.13.2"
@@ -4385,17 +4214,6 @@ dependencies = [
"tracing-subscriber",
]
-[[package]]
-name = "piper"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
-dependencies = [
- "atomic-waker",
- "fastrand",
- "futures-io",
-]
-
[[package]]
name = "pkcs8"
version = "0.10.2"
@@ -4505,7 +4323,7 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
- "zerocopy 0.8.24",
+ "zerocopy",
]
[[package]]
@@ -4517,6 +4335,15 @@ dependencies = [
"elliptic-curve",
]
+[[package]]
+name = "proc-macro-crate"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
+dependencies = [
+ "toml_edit 0.23.10+spec-1.0.0",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.94"
@@ -4528,9 +4355,9 @@ dependencies = [
[[package]]
name = "prometheus-client"
-version = "0.23.1"
+version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf41c1a7c32ed72abe5082fb19505b969095c12da9f5732a4bc9878757fd087c"
+checksum = "e4500adecd7af8e0e9f4dbce15cfee07ce913fbf6ad605cc468b83f2d531ee94"
dependencies = [
"dtoa",
"itoa",
@@ -4540,9 +4367,9 @@ dependencies = [
[[package]]
name = "prometheus-client-derive-encode"
-version = "0.4.2"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8"
+checksum = "9adf1691c04c0a5ff46ff8f262b58beb07b0dbb61f96f9f54f6cbd82106ed87f"
dependencies = [
"proc-macro2",
"quote",
@@ -4628,7 +4455,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
- "socket2",
+ "socket2 0.5.9",
"thiserror 2.0.12",
"tokio",
"tracing",
@@ -4664,7 +4491,7 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
- "socket2",
+ "socket2 0.5.9",
"tracing",
"windows-sys 0.59.0",
]
@@ -4716,7 +4543,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
- "zerocopy 0.8.24",
+ "zerocopy",
]
[[package]]
@@ -4866,17 +4693,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
-]
-
-[[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",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -4887,15 +4705,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.8.5",
+ "regex-syntax",
]
-[[package]]
-name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
[[package]]
name = "regex-syntax"
version = "0.8.5"
@@ -4926,15 +4738,14 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.12.15"
+version = "0.12.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
+checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
- "futures-util",
"h2",
"http 1.3.1",
"http-body",
@@ -4943,34 +4754,30 @@ dependencies = [
"hyper-rustls",
"hyper-tls",
"hyper-util",
- "ipnet",
"js-sys",
"log",
"mime",
"native-tls",
- "once_cell",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
- "rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
- "system-configuration",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tower 0.5.2",
+ "tower-http 0.6.7",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "webpki-roots",
- "windows-registry",
+ "webpki-roots 1.0.4",
]
[[package]]
@@ -5054,7 +4861,6 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0"
dependencies = [
- "async-global-executor",
"futures",
"log",
"netlink-packet-core",
@@ -5188,15 +4994,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "rustls-pemfile"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
-dependencies = [
- "rustls-pki-types",
-]
-
[[package]]
name = "rustls-pki-types"
version = "1.11.0"
@@ -5350,18 +5147,28 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.219"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.219"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -5374,7 +5181,7 @@ version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
- "indexmap 2.9.0",
+ "indexmap 2.13.0",
"itoa",
"memchr",
"ryu",
@@ -5514,29 +5321,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
-name = "smol"
-version = "2.0.2"
+name = "smol_str"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
dependencies = [
- "async-channel 2.3.1",
- "async-executor",
- "async-fs",
- "async-io",
- "async-lock",
- "async-net",
- "async-process",
- "blocking",
- "futures-lite",
+ "serde",
]
[[package]]
-name = "smol_str"
-version = "0.2.2"
+name = "snafu"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6"
dependencies = [
- "serde",
+ "doc-comment",
+ "snafu-derive",
+]
+
+[[package]]
+name = "snafu-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
]
[[package]]
@@ -5566,6 +5378,16 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "socket2"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
[[package]]
name = "soketto"
version = "0.8.1"
@@ -5647,7 +5469,7 @@ version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
dependencies = [
- "heck",
+ "heck 0.5.0",
"proc-macro2",
"quote",
"rustversion",
@@ -5777,6 +5599,62 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790"
+[[package]]
+name = "tari_bulletproofs_plus"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98e43bc4d522de252647e34e72aef4d5e33f845a6acc23fb7b1f4e877a7f9053"
+dependencies = [
+ "blake2",
+ "byteorder",
+ "curve25519-dalek",
+ "digest",
+ "ff",
+ "itertools 0.12.1",
+ "merlin",
+ "once_cell",
+ "rand_core 0.6.4",
+ "serde",
+ "sha3",
+ "thiserror-no-std",
+ "zeroize",
+]
+
+[[package]]
+name = "tari_crypto"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b74b1ee79be37e04fcdb307b2809e658f19e05f31a2b5263c128fc3a5e2dee16"
+dependencies = [
+ "blake2",
+ "borsh",
+ "curve25519-dalek",
+ "digest",
+ "log",
+ "merlin",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+ "serde",
+ "sha3",
+ "snafu",
+ "subtle",
+ "tari_bulletproofs_plus",
+ "tari_utilities",
+ "zeroize",
+]
+
+[[package]]
+name = "tari_utilities"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "539470532a8ca1a8a1aa26f6240586f7d0f7d90ab94ae67f092bcd75a1bb4060"
+dependencies = [
+ "generic-array",
+ "snafu",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "tempfile"
version = "3.19.1"
@@ -5809,7 +5687,7 @@ dependencies = [
"base64",
"futures",
"http 1.3.1",
- "indexmap 2.9.0",
+ "indexmap 2.13.0",
"parking_lot",
"paste",
"reqwest",
@@ -5876,6 +5754,26 @@ dependencies = [
"syn 2.0.100",
]
+[[package]]
+name = "thiserror-impl-no-std"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "thiserror-no-std"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea"
+dependencies = [
+ "thiserror-impl-no-std",
+]
+
[[package]]
name = "thread_local"
version = "1.1.8"
@@ -5987,7 +5885,7 @@ dependencies = [
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
- "socket2",
+ "socket2 0.5.9",
"tokio-macros",
"windows-sys 0.52.0",
]
@@ -6056,8 +5954,8 @@ checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
dependencies = [
"serde",
"serde_spanned",
- "toml_datetime",
- "toml_edit",
+ "toml_datetime 0.6.8",
+ "toml_edit 0.22.24",
]
[[package]]
@@ -6069,16 +5967,46 @@ dependencies = [
"serde",
]
+[[package]]
+name = "toml_datetime"
+version = "0.7.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
+dependencies = [
+ "serde_core",
+]
+
[[package]]
name = "toml_edit"
version = "0.22.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
dependencies = [
- "indexmap 2.9.0",
+ "indexmap 2.13.0",
"serde",
"serde_spanned",
- "toml_datetime",
+ "toml_datetime 0.6.8",
+ "winnow",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.23.10+spec-1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
+dependencies = [
+ "indexmap 2.13.0",
+ "toml_datetime 0.7.5+spec-1.1.0",
+ "toml_parser",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.6+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
+dependencies = [
"winnow",
]
@@ -6103,7 +6031,7 @@ dependencies = [
"percent-encoding",
"pin-project",
"prost",
- "socket2",
+ "socket2 0.5.9",
"tokio",
"tokio-stream",
"tower 0.4.13",
@@ -6173,6 +6101,24 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "tower-http"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456"
+dependencies = [
+ "bitflags 2.9.0",
+ "bytes",
+ "futures-util",
+ "http 1.3.1",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower 0.5.2",
+ "tower-layer",
+ "tower-service",
+]
+
[[package]]
name = "tower-layer"
version = "0.3.3"
@@ -6267,14 +6213,14 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
-version = "0.3.19"
+version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
- "regex",
+ "regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
@@ -6452,12 +6398,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
-[[package]]
-name = "value-bag"
-version = "1.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5"
-
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -6664,6 +6604,15 @@ dependencies = [
"rustls-pki-types",
]
+[[package]]
+name = "webpki-roots"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "webrtc"
version = "0.12.0"
@@ -6794,7 +6743,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6dfe9686c6c9c51428da4de415cb6ca2dc0591ce2b63212e23fd9cccf0e316b"
dependencies = [
"log",
- "socket2",
+ "socket2 0.5.9",
"thiserror 1.0.69",
"tokio",
"webrtc-util",
@@ -6921,7 +6870,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -7316,9 +7265,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "winnow"
-version = "0.7.6"
+version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
+checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
dependencies = [
"memchr",
]
@@ -7480,33 +7429,13 @@ dependencies = [
"synstructure",
]
-[[package]]
-name = "zerocopy"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
-dependencies = [
- "zerocopy-derive 0.7.35",
-]
-
[[package]]
name = "zerocopy"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
- "zerocopy-derive 0.8.24",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.100",
+ "zerocopy-derive",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 5f6059fcbb8..c20bc781dfa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -74,51 +74,50 @@ rust-version = "1.83.0"
edition = "2021"
[workspace.dependencies]
-libp2p = { version = "0.56.0", path = "libp2p" }
-libp2p-allow-block-list = { version = "0.5.0", path = "misc/allow-block-list" }
-libp2p-autonat = { version = "0.14.1", path = "protocols/autonat" }
-libp2p-connection-limits = { version = "0.5.1", path = "misc/connection-limits" }
-libp2p-core = { version = "0.43.1", path = "core" }
-libp2p-dcutr = { version = "0.13.0", path = "protocols/dcutr" }
+libp2p = { version = "0.56.1", path = "libp2p" }
+libp2p-allow-block-list = { version = "0.6.0", path = "misc/allow-block-list" }
+libp2p-autonat = { version = "0.15.0", path = "protocols/autonat" }
+libp2p-connection-limits = { version = "0.6.0", path = "misc/connection-limits" }
+libp2p-core = { version = "0.43.2", path = "core" }
+libp2p-dcutr = { version = "0.14.0", path = "protocols/dcutr" }
libp2p-dns = { version = "0.44.0", path = "transports/dns" }
-libp2p-floodsub = { version = "0.46.1", path = "protocols/floodsub" }
-libp2p-gossipsub = { version = "0.49.0", path = "protocols/gossipsub" }
+libp2p-floodsub = { version = "0.47.0", path = "protocols/floodsub" }
+libp2p-gossipsub = { version = "0.50.0", path = "protocols/gossipsub" }
libp2p-identify = { version = "0.47.0", path = "protocols/identify" }
-libp2p-identity = { version = "0.2.11" }
-libp2p-kad = { version = "0.47.1", path = "protocols/kad" }
+libp2p-identity = { version = "0.2.13", path = "identity" }
+libp2p-kad = { version = "0.49.0", path = "protocols/kad" }
libp2p-mdns = { version = "0.48.0", path = "protocols/mdns" }
-libp2p-memory-connection-limits = { version = "0.4.0", path = "misc/memory-connection-limits" }
-libp2p-metrics = { version = "0.17.0", path = "misc/metrics" }
+libp2p-memory-connection-limits = { version = "0.5.0", path = "misc/memory-connection-limits" }
+libp2p-metrics = { version = "0.17.1", path = "misc/metrics" }
libp2p-mplex = { version = "0.43.1", path = "muxers/mplex" }
libp2p-noise = { version = "0.46.1", path = "transports/noise" }
libp2p-peer-store = { version = "0.1.0", path = "misc/peer-store" }
libp2p-perf = { version = "0.4.0", path = "protocols/perf" }
-libp2p-ping = { version = "0.46.0", path = "protocols/ping" }
+libp2p-ping = { version = "0.47.0", path = "protocols/ping" }
libp2p-plaintext = { version = "0.43.0", path = "transports/plaintext" }
libp2p-pnet = { version = "0.26.0", path = "transports/pnet" }
libp2p-quic = { version = "0.13.0", path = "transports/quic" }
-libp2p-relay = { version = "0.20.0", path = "protocols/relay" }
-libp2p-rendezvous = { version = "0.16.1", path = "protocols/rendezvous" }
-libp2p-request-response = { version = "0.28.1", path = "protocols/request-response" }
+libp2p-relay = { version = "0.21.1", path = "protocols/relay" }
+libp2p-rendezvous = { version = "0.17.0", path = "protocols/rendezvous" }
+libp2p-request-response = { version = "0.29.0", path = "protocols/request-response" }
libp2p-server = { version = "0.12.7", path = "misc/server" }
-libp2p-stream = { version = "0.3.0-alpha.1", path = "protocols/stream" }
+libp2p-stream = { version = "0.4.0-alpha", path = "protocols/stream" }
libp2p-swarm = { version = "0.47.0", path = "swarm" }
libp2p-swarm-derive = { version = "=0.35.1", path = "swarm-derive" } # `libp2p-swarm-derive` may not be compatible with different `libp2p-swarm` non-breaking releases. E.g. `libp2p-swarm` might introduce a new enum variant `FromSwarm` (which is `#[non-exhaustive]`) in a non-breaking release. Older versions of `libp2p-swarm-derive` would not forward this enum variant within the `NetworkBehaviour` hierarchy. Thus the version pinning is required.
libp2p-swarm-test = { version = "0.6.0", path = "swarm-test" }
-libp2p-tcp = { version = "0.43.0", path = "transports/tcp" }
+libp2p-tcp = { version = "0.44.1", path = "transports/tcp" }
libp2p-tls = { version = "0.6.2", path = "transports/tls" }
-libp2p-uds = { version = "0.42.0", path = "transports/uds" }
-libp2p-upnp = { version = "0.4.1", path = "protocols/upnp" }
-libp2p-webrtc = { version = "0.9.0-alpha", path = "transports/webrtc" }
+libp2p-uds = { version = "0.43.1", path = "transports/uds" }
+libp2p-upnp = { version = "0.6.0", path = "protocols/upnp" }
+libp2p-webrtc = { version = "0.9.0-alpha.2", path = "transports/webrtc" }
libp2p-webrtc-utils = { version = "0.4.0", path = "misc/webrtc-utils" }
libp2p-webrtc-websys = { version = "0.4.0", path = "transports/webrtc-websys" }
-libp2p-websocket = { version = "0.45.1", path = "transports/websocket" }
+libp2p-websocket = { version = "0.45.2", path = "transports/websocket" }
libp2p-websocket-websys = { version = "0.5.0", path = "transports/websocket-websys" }
-libp2p-webtransport-websys = { version = "0.5.1", path = "transports/webtransport-websys" }
+libp2p-webtransport-websys = { version = "0.5.2", path = "transports/webtransport-websys" }
libp2p-yamux = { version = "0.47.0", path = "muxers/yamux" }
# External dependencies
-async-std-resolver = { version = "0.25.0-alpha.4", default-features = false }
asynchronous-codec = { version = "0.7.0" }
env_logger = "0.11"
futures = "0.3.30"
@@ -126,12 +125,13 @@ futures-bounded = { version = "0.2.4" }
futures-rustls = { version = "0.26.0", default-features = false }
getrandom = "0.2"
if-watch = "3.2.1"
-hickory-proto = { version = "0.25.0-alpha.4", default-features = false }
-hickory-resolver = { version = "0.25.0-alpha.4", default-features = false }
-multiaddr = "0.18.1"
+hickory-proto = { version = "0.25.2", default-features = false }
+hickory-resolver = { version = "0.25.2", default-features = false }
+#multiaddr = "0.18.1"
+multiaddr = { path = "multiaddr", version = "0.18.3" }
multihash = "0.19.1"
multistream-select = { version = "0.13.0", path = "misc/multistream-select" }
-prometheus-client = "0.23"
+prometheus-client = "0.24"
quick-protobuf-codec = { version = "0.3.1", path = "misc/quick-protobuf-codec" }
quickcheck = { package = "quickcheck-ext", path = "misc/quickcheck-ext" }
rcgen = "0.13"
@@ -143,7 +143,7 @@ tracing = "0.1.41"
tracing-subscriber = "0.3.19"
unsigned-varint = { version = "0.8.0" }
web-time = "1.1.0"
-hashlink = "0.9.0"
+hashlink = "0.10.0"
[patch.crates-io]
diff --git a/README.md b/README.md
index 8840b37f2a9..788ae301048 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,12 @@
This repository is the central place for Rust development of the [libp2p](https://libp2p.io) spec.
+## Tari fork
+- Add ristretto/Schnorr identity support
+- multiaddr moved into this repo to avoid dependency issues
+- update hickory_resolver (support for tokio resolver only)
+
+
## Getting started
- **Main documentation** can be found on https://docs.rs/libp2p.
@@ -105,3 +111,4 @@ used by [Polkadot](https://www.parity.io/technologies/polkadot/).
- [Swarm NL](https://github.com/algorealmInc/SwarmNL) - A library that makes it easy to configure the networking requirements for any distributed application.
- [Taple](https://github.com/opencanarias/taple-core) - Sustainable DLT for asset and process traceability by [OpenCanarias](https://www.opencanarias.com/en/).
- [Ceylon](https://github.com/ceylonai/ceylon) - A Multi-Agent System (MAS) Development Framework.
+- [Fungi](https://github.com/enbop/fungi) - A platform built for seamless multi-device integration.
diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md
index 90d26e75608..45aa3a01a88 100644
--- a/core/CHANGELOG.md
+++ b/core/CHANGELOG.md
@@ -1,3 +1,16 @@
+## 0.43.2
+
+- Add `*_interop` methods to `PeerRecord` for cross-implementation compatibility with Go and JavaScript libp2p.
+ - `PeerRecord::new_interop()` - Create peer records using standard format
+ - `PeerRecord::from_signed_envelope_interop()` - Verify peer records using standard format
+
+ The standard format uses libp2p-peer-record domain and multicodec identifier (0x0301) for interoperability.
+ Existing methods (`new()`, `from_signed_envelope()`) maintain backward compatibility with legacy Rust libp2p format.
+
+ Use the `*_interop` variants when exchanging peer records with non-Rust libp2p implementations.
+
+ See [PR 6230](https://github.com/libp2p/rust-libp2p/pull/6230).
+
## 0.43.1
- Remove `once_cell` dependency.
See [PR 5913](https://github.com/libp2p/rust-libp2p/pull/5913)
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 8c6018adb16..374877281ee 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -3,7 +3,7 @@ name = "libp2p-core"
edition.workspace = true
rust-version = { workspace = true }
description = "Core traits and structs of libp2p"
-version = "0.43.1"
+version = "0.43.2"
authors = ["Parity Technologies "]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
@@ -16,7 +16,8 @@ fnv = "1.0"
futures = { workspace = true, features = ["executor", "thread-pool"] }
futures-timer = "3"
web-time = { workspace = true }
-libp2p-identity = { workspace = true, features = ["peerid", "ed25519"] }
+#libp2p-identity = { workspace = true, features = ["peerid", "ed25519"] }
+libp2p-identity = { path = "../identity", features = ["peerid", "ed25519"] }
multiaddr = { workspace = true }
multihash = { workspace = true }
multistream-select = { workspace = true }
diff --git a/core/src/peer_record.rs b/core/src/peer_record.rs
index 9c6b7f73f05..3ab880effe4 100644
--- a/core/src/peer_record.rs
+++ b/core/src/peer_record.rs
@@ -4,13 +4,30 @@ use web_time::SystemTime;
use crate::{proto, signed_envelope, signed_envelope::SignedEnvelope, DecodeError, Multiaddr};
-const PAYLOAD_TYPE: &str = "/libp2p/routing-state-record";
-const DOMAIN_SEP: &str = "libp2p-routing-state";
+// Legacy constants for backward compatibility with existing Rust libp2p deployments.
+const LEGACY_PAYLOAD_TYPE: &str = "/libp2p/routing-state-record";
+const LEGACY_DOMAIN_SEP: &str = "libp2p-routing-state";
+
+// Standard constants for cross-implementation compatibility with Go/JS libp2p.
+// Defined in https://github.com/multiformats/multicodec/blob/master/table.csv
+// and https://github.com/libp2p/specs/blob/master/RFC/0002-signed-envelopes.md.
+const STANDARD_PAYLOAD_TYPE: &[u8] = &[0x03, 0x01];
+const STANDARD_DOMAIN_SEP: &str = "libp2p-peer-record";
/// Represents a peer routing record.
///
/// Peer records are designed to be distributable and carry a signature by being wrapped in a signed
/// envelope. For more information see RFC0003 of the libp2p specifications:
+///
+/// ## Cross-Implementation Compatibility
+///
+/// This implementation provides two formats:
+/// - **Legacy format** (default methods): Compatible with existing Rust libp2p deployments.
+/// - **Standard format** (`*_interop` methods): Compatible with Go and JavaScript implementations.
+///
+/// Use the `*_interop` variants (e.g., [`PeerRecord::new_interop`],
+/// [`PeerRecord::from_signed_envelope_interop`]) when you need to exchange peer records with
+/// non-Rust libp2p implementations.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct PeerRecord {
peer_id: PeerId,
@@ -25,15 +42,43 @@ pub struct PeerRecord {
}
impl PeerRecord {
- /// Attempt to re-construct a [`PeerRecord`] from a [`SignedEnvelope`].
+ /// Attempt to re-construct a [`PeerRecord`] from a [`SignedEnvelope`] using legacy format.
+ ///
+ /// Uses the legacy routing-state-record format for backward compatibility with existing
+ /// Rust libp2p deployments.
///
/// If this function succeeds, the [`SignedEnvelope`] contained a peer record with a valid
/// signature and can hence be considered authenticated.
+ ///
+ /// For cross-implementation compatibility with Go/JS libp2p, use
+ /// [`Self::from_signed_envelope_interop`].
pub fn from_signed_envelope(envelope: SignedEnvelope) -> Result {
+ Self::from_signed_envelope_impl(envelope, LEGACY_DOMAIN_SEP, LEGACY_PAYLOAD_TYPE.as_bytes())
+ }
+
+ /// Attempt to re-construct a [`PeerRecord`] from a [`SignedEnvelope`] using standard interop
+ /// format.
+ ///
+ /// Uses the standard libp2p-peer-record format for cross-implementation compatibility
+ /// with Go and JavaScript libp2p implementations.
+ ///
+ /// If this function succeeds, the [`SignedEnvelope`] contained a peer record with a valid
+ /// signature and can hence be considered authenticated.
+ pub fn from_signed_envelope_interop(
+ envelope: SignedEnvelope,
+ ) -> Result {
+ Self::from_signed_envelope_impl(envelope, STANDARD_DOMAIN_SEP, STANDARD_PAYLOAD_TYPE)
+ }
+
+ fn from_signed_envelope_impl(
+ envelope: SignedEnvelope,
+ domain: &str,
+ payload_type: &[u8],
+ ) -> Result {
use quick_protobuf::MessageRead;
let (payload, signing_key) =
- envelope.payload_and_signing_key(String::from(DOMAIN_SEP), PAYLOAD_TYPE.as_bytes())?;
+ envelope.payload_and_signing_key(String::from(domain), payload_type)?;
let mut reader = BytesReader::from_bytes(payload);
let record = proto::PeerRecord::from_reader(&mut reader, payload).map_err(DecodeError)?;
@@ -58,11 +103,43 @@ impl PeerRecord {
})
}
- /// Construct a new [`PeerRecord`] by authenticating the provided addresses with the given key.
+ /// Construct a new [`PeerRecord`] by authenticating the provided addresses with the given key
+ /// using legacy format.
+ ///
+ /// Uses the legacy routing-state-record format for backward compatibility with existing
+ /// Rust libp2p deployments.
///
/// This is the same key that is used for authenticating every libp2p connection of your
/// application, i.e. what you use when setting up your [`crate::transport::Transport`].
+ ///
+ /// For cross-implementation compatibility with Go/JS libp2p, use [`Self::new_interop`].
pub fn new(key: &Keypair, addresses: Vec) -> Result {
+ Self::new_impl(
+ key,
+ addresses,
+ LEGACY_DOMAIN_SEP,
+ LEGACY_PAYLOAD_TYPE.as_bytes(),
+ )
+ }
+
+ /// Construct a new [`PeerRecord`] by authenticating the provided addresses with the given key
+ /// using standard interop format.
+ ///
+ /// Uses the standard libp2p-peer-record format for cross-implementation compatibility
+ /// with Go and JavaScript libp2p implementations.
+ ///
+ /// This is the same key that is used for authenticating every libp2p connection of your
+ /// application, i.e. what you use when setting up your [`crate::transport::Transport`].
+ pub fn new_interop(key: &Keypair, addresses: Vec) -> Result {
+ Self::new_impl(key, addresses, STANDARD_DOMAIN_SEP, STANDARD_PAYLOAD_TYPE)
+ }
+
+ fn new_impl(
+ key: &Keypair,
+ addresses: Vec,
+ domain: &str,
+ payload_type: &[u8],
+ ) -> Result {
use quick_protobuf::MessageWrite;
let seq = SystemTime::now()
@@ -92,12 +169,8 @@ impl PeerRecord {
buf
};
- let envelope = SignedEnvelope::new(
- key,
- String::from(DOMAIN_SEP),
- PAYLOAD_TYPE.as_bytes().to_vec(),
- payload,
- )?;
+ let envelope =
+ SignedEnvelope::new(key, String::from(domain), payload_type.to_vec(), payload)?;
Ok(Self {
peer_id,
@@ -154,7 +227,7 @@ mod tests {
const HOME: &str = "/ip4/127.0.0.1/tcp/1337";
#[test]
- fn roundtrip_envelope() {
+ fn roundtrip_envelope_legacy() {
let key = Keypair::generate_ed25519();
let record = PeerRecord::new(&key, vec![HOME.parse().unwrap()]).unwrap();
@@ -166,7 +239,19 @@ mod tests {
}
#[test]
- fn mismatched_signature() {
+ fn roundtrip_envelope_interop() {
+ let key = Keypair::generate_ed25519();
+
+ let record = PeerRecord::new_interop(&key, vec![HOME.parse().unwrap()]).unwrap();
+
+ let envelope = record.to_signed_envelope();
+ let reconstructed = PeerRecord::from_signed_envelope_interop(envelope).unwrap();
+
+ assert_eq!(reconstructed, record)
+ }
+
+ #[test]
+ fn mismatched_signature_legacy() {
use quick_protobuf::MessageWrite;
let addr: Multiaddr = HOME.parse().unwrap();
@@ -195,8 +280,8 @@ mod tests {
SignedEnvelope::new(
&identity_b,
- String::from(DOMAIN_SEP),
- PAYLOAD_TYPE.as_bytes().to_vec(),
+ String::from(LEGACY_DOMAIN_SEP),
+ LEGACY_PAYLOAD_TYPE.as_bytes().to_vec(),
payload,
)
.unwrap()
@@ -207,4 +292,47 @@ mod tests {
Err(FromEnvelopeError::MismatchedSignature)
));
}
+
+ #[test]
+ fn mismatched_signature_interop() {
+ use quick_protobuf::MessageWrite;
+
+ let addr: Multiaddr = HOME.parse().unwrap();
+
+ let envelope = {
+ let identity_a = Keypair::generate_ed25519();
+ let identity_b = Keypair::generate_ed25519();
+
+ let payload = {
+ let record = proto::PeerRecord {
+ peer_id: identity_a.public().to_peer_id().to_bytes(),
+ seq: 0,
+ addresses: vec![proto::AddressInfo {
+ multiaddr: addr.to_vec(),
+ }],
+ };
+
+ let mut buf = Vec::with_capacity(record.get_size());
+ let mut writer = Writer::new(&mut buf);
+ record
+ .write_message(&mut writer)
+ .expect("Encoding to succeed");
+
+ buf
+ };
+
+ SignedEnvelope::new(
+ &identity_b,
+ String::from(STANDARD_DOMAIN_SEP),
+ STANDARD_PAYLOAD_TYPE.to_vec(),
+ payload,
+ )
+ .unwrap()
+ };
+
+ assert!(matches!(
+ PeerRecord::from_signed_envelope_interop(envelope),
+ Err(FromEnvelopeError::MismatchedSignature)
+ ));
+ }
}
diff --git a/core/src/transport.rs b/core/src/transport.rs
index 58a9c2a9557..de6ec279468 100644
--- a/core/src/transport.rs
+++ b/core/src/transport.rs
@@ -62,7 +62,7 @@ static NEXT_LISTENER_ID: AtomicUsize = AtomicUsize::new(1);
pub enum PortUse {
/// Always allocate a new port for the dial.
New,
- /// Best effor reusing of an existing port.
+ /// Best effort reusing of an existing port.
///
/// If there is no listener present that can be used to dial, a new port is allocated.
#[default]
diff --git a/deny.toml b/deny.toml
index f8485cdb1a3..7a6475b5998 100644
--- a/deny.toml
+++ b/deny.toml
@@ -39,6 +39,7 @@ allow = [
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
+ "CDLA-Permissive-2.0",
"ISC",
"MIT",
"MPL-2.0",
diff --git a/examples/README.md b/examples/README.md
index b1fb9f1f104..2e397960b76 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -24,19 +24,26 @@ Each example includes its own README.md file with specific instructions on how t
## Individual libp2p features
-- [Chat](./chat) A basic chat application demonstrating libp2p and the mDNS and Gossipsub protocols.
-- [Distributed key-value store](./distributed-key-value-store) A basic key value store demonstrating libp2p and the mDNS and Kademlia protocol.
+- [Chat](./chat) A basic chat application demonstrating libp2p and the [mDNS] and [Gossipsub] protocols.
+- [Distributed key-value store](./distributed-key-value-store) A basic key value store demonstrating libp2p and the [mDNS] and [Kademlia] protocol.
- [File sharing application](./file-sharing) Basic file sharing application with peers either providing or locating and getting files by name.
- While obviously showcasing how to build a basic file sharing application with the Kademlia and
- Request-Response protocol, the actual goal of this example is **to show how to integrate
+ While obviously showcasing how to build a basic file sharing application with the [Kademlia] and
+ [Request-Response] protocol, the actual goal of this example is **to show how to integrate
rust-libp2p into a larger application**.
-- [IPFS Kademlia](./ipfs-kad) Demonstrates how to perform Kademlia queries on the IPFS network.
+- [IPFS Kademlia](./ipfs-kad) Demonstrates how to perform [Kademlia] queries on the [IPFS] network.
-- [IPFS Private](./ipfs-private) Implementation using the gossipsub, ping and identify protocols to implement the ipfs private swarms feature.
+- [IPFS Private](./ipfs-private) Implementation using the [Gossipsub], ping and identify protocols to implement the IPFS private swarms feature.
- [Ping](./ping) Small `ping` clone, sending a ping to a peer, expecting a pong as a response. See [tutorial](../libp2p/src/tutorials/ping.rs) for a step-by-step guide building the example.
-- [Rendezvous](./rendezvous) Rendezvous Protocol. See [specs](https://github.com/libp2p/specs/blob/master/rendezvous/README.md).
+- [Rendezvous](./rendezvous) [Rendezvous] Protocol. See [specs](https://github.com/libp2p/specs/blob/master/rendezvous/README.md).
+
+[mDNS]: https://github.com/libp2p/specs/blob/master/discovery/mdns.md
+[Gossipsub]: https://github.com/libp2p/specs/tree/master/pubsub/gossipsub
+[Kademlia]: https://github.com/libp2p/specs/blob/master/kad-dht/README.md
+[Request-Response]: https://en.wikipedia.org/wiki/Request%E2%80%93response
+[IPFS]: https://ipfs.tech/
+[Rendezvous]: https://github.com/libp2p/specs/blob/master/rendezvous/README.md
diff --git a/examples/browser-webrtc/README.md b/examples/browser-webrtc/README.md
index eec2c9c0494..2b53cd4c1d8 100644
--- a/examples/browser-webrtc/README.md
+++ b/examples/browser-webrtc/README.md
@@ -1,11 +1,11 @@
# Rust-libp2p Browser-Server WebRTC Example
This example demonstrates how to use the `libp2p-webrtc-websys` transport library in a browser to ping the WebRTC Server.
-It uses [wasm-pack](https://rustwasm.github.io/docs/wasm-pack/) to build the project for use in the browser.
+It uses [wasm-pack](https://drager.github.io/wasm-pack/) to build the project for use in the browser.
## Running the example
-Ensure you have `wasm-pack` [installed](https://rustwasm.github.io/wasm-pack/).
+Ensure you have `wasm-pack` [installed](https://drager.github.io/wasm-pack/).
1. Build the client library:
```shell
diff --git a/examples/stream/Cargo.toml b/examples/stream/Cargo.toml
index 020ea624b50..79c1ea7380f 100644
--- a/examples/stream/Cargo.toml
+++ b/examples/stream/Cargo.toml
@@ -12,7 +12,7 @@ release = false
anyhow = "1"
futures = { workspace = true }
libp2p = { path = "../../libp2p", features = [ "tokio", "quic"] }
-libp2p-stream = { path = "../../protocols/stream", version = "0.3.0-alpha.1" }
+libp2p-stream = { path = "../../protocols/stream", version = "0.4.0-alpha" }
rand = "0.8"
tokio = { workspace = true, features = ["full"] }
tracing = { workspace = true }
diff --git a/examples/upnp/src/main.rs b/examples/upnp/src/main.rs
index 19de8d773ae..b69600dc836 100644
--- a/examples/upnp/src/main.rs
+++ b/examples/upnp/src/main.rs
@@ -57,8 +57,11 @@ async fn main() -> Result<(), Box> {
loop {
match swarm.select_next_some().await {
SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"),
- SwarmEvent::Behaviour(upnp::Event::NewExternalAddr(addr)) => {
- println!("New external address: {addr}");
+ SwarmEvent::Behaviour(upnp::Event::NewExternalAddr {
+ external_addr,
+ local_addr: _,
+ }) => {
+ println!("New external address: {external_addr}");
}
SwarmEvent::Behaviour(upnp::Event::GatewayNotFound) => {
println!("Gateway does not support UPnP");
diff --git a/identity/CHANGELOG.md b/identity/CHANGELOG.md
index 81ce7ad718b..430d8263e7a 100644
--- a/identity/CHANGELOG.md
+++ b/identity/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.2.13
+
+- Turn the `quick-protobuf` dependency optional to only the features which require it.
+ See [PR 6226](https://github.com/libp2p/rust-libp2p/pull/6226)
+
+## 0.2.12
+
+- Avoid depending on the `rand_core` feature in `ed25519-dalek` crate.
+ See [PR 6070](https://github.com/libp2p/rust-libp2p/pull/6070)
+
## 0.2.11
- Switch from `libsecp256` to `k256` for secp256k1 support.
diff --git a/identity/Cargo.toml b/identity/Cargo.toml
index 366de74eac5..8320872f19d 100644
--- a/identity/Cargo.toml
+++ b/identity/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "libp2p-identity"
-version = "0.2.11"
+version = "0.2.13"
edition = "2021" # MUST NOT inherit from workspace because we don't want to publish breaking changes to `libp2p-identity`.
description = "Data structures and algorithms for identifying peers in libp2p."
rust-version = "1.73.0" # MUST NOT inherit from workspace because we don't want to publish breaking changes to `libp2p-identity`.
@@ -15,12 +15,13 @@ categories = ["cryptography"]
asn1_der = { version = "0.7.6", optional = true }
bs58 = { version = "0.5.1", optional = true }
ed25519-dalek = { version = "2.1", optional = true }
-hkdf = { version = "0.12.4", optional = true }
+tari_crypto = { version = "0.22", optional = true }
+hkdf = { version = "0.12", optional = true }
k256 = { version = "0.13.4", optional = true, features = ["ecdsa", "arithmetic"] }
tracing = { workspace = true }
multihash = { version = "0.19.1", optional = true }
p256 = { version = "0.13", default-features = false, features = ["ecdsa", "std", "pem"], optional = true }
-quick-protobuf = "0.8.1"
+quick-protobuf = { version = "0.8.1", optional = true }
rand = { version = "0.8", optional = true }
sec1 = { version = "0.7", default-features = false, optional = true }
serde = { version = "1", optional = true, features = ["derive"] }
@@ -32,12 +33,13 @@ zeroize = { version = "1.8", optional = true }
ring = { workspace = true, features = ["alloc", "std"], optional = true }
[features]
-secp256k1 = ["dep:k256", "dep:asn1_der", "dep:sha2", "dep:hkdf", "dep:zeroize"]
-ecdsa = ["dep:p256", "dep:zeroize", "dep:sec1", "dep:sha2", "dep:hkdf"]
-rsa = ["dep:ring", "dep:asn1_der", "dep:rand", "dep:zeroize"]
-ed25519 = ["dep:ed25519-dalek", "dep:zeroize", "dep:sha2", "dep:hkdf"]
+secp256k1 = ["dep:k256", "dep:asn1_der", "dep:sha2", "dep:hkdf", "dep:zeroize", "dep:quick-protobuf"]
+ecdsa = ["dep:p256", "dep:zeroize", "dep:sec1", "dep:sha2", "dep:hkdf", "dep:quick-protobuf"]
+rsa = ["dep:ring", "dep:asn1_der", "dep:rand", "dep:zeroize", "dep:quick-protobuf"]
+ed25519 = ["dep:ed25519-dalek", "dep:zeroize", "dep:sha2", "dep:hkdf", "dep:quick-protobuf"]
+sr25519 = ["dep:tari_crypto", "dep:zeroize", "dep:sha2", "dep:hkdf", "dep:rand", "dep:quick-protobuf"]
peerid = ["dep:multihash", "dep:bs58", "dep:thiserror", "dep:sha2", "dep:hkdf"]
-rand = ["dep:rand", "ed25519-dalek?/rand_core"]
+rand = ["dep:rand"]
[dev-dependencies]
quickcheck = { workspace = true }
diff --git a/identity/src/ed25519.rs b/identity/src/ed25519.rs
index 5a1a53dd4af..ff1ff082306 100644
--- a/identity/src/ed25519.rs
+++ b/identity/src/ed25519.rs
@@ -184,8 +184,11 @@ impl SecretKey {
/// Generate a new Ed25519 secret key.
#[cfg(feature = "rand")]
pub fn generate() -> SecretKey {
- let signing = ed25519::SigningKey::generate(&mut rand::rngs::OsRng);
- SecretKey(signing.to_bytes())
+ use rand::RngCore as _;
+
+ let mut secret = ed25519::SecretKey::default();
+ rand::rngs::OsRng.fill_bytes(&mut secret);
+ SecretKey(secret)
}
/// Try to parse an Ed25519 secret key from a byte slice
diff --git a/identity/src/error.rs b/identity/src/error.rs
index 6e8c4d02caa..025b295d3b6 100644
--- a/identity/src/error.rs
+++ b/identity/src/error.rs
@@ -44,6 +44,7 @@ impl DecodingError {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
pub(crate) fn failed_to_parse(what: &'static str, source: S) -> Self
@@ -64,6 +65,7 @@ impl DecodingError {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
pub(crate) fn bad_protobuf(
diff --git a/identity/src/generated/keys.proto b/identity/src/generated/keys.proto
index 5fbeaf8f6e0..b0499a9ecdb 100644
--- a/identity/src/generated/keys.proto
+++ b/identity/src/generated/keys.proto
@@ -7,6 +7,7 @@ enum KeyType {
Ed25519 = 1;
Secp256k1 = 2;
ECDSA = 3;
+ Sr25519 = 4;
}
message PublicKey {
diff --git a/identity/src/generated/keys_proto.rs b/identity/src/generated/keys_proto.rs
index ba15fed5004..2bd1caeca65 100644
--- a/identity/src/generated/keys_proto.rs
+++ b/identity/src/generated/keys_proto.rs
@@ -11,6 +11,7 @@
use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result};
use quick_protobuf::sizeofs::*;
+use zeroize::{Zeroize, ZeroizeOnDrop};
use super::*;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -19,6 +20,7 @@ pub enum KeyType {
Ed25519 = 1,
Secp256k1 = 2,
ECDSA = 3,
+ Sr25519 = 4,
}
impl Default for KeyType {
@@ -34,6 +36,7 @@ impl From for KeyType {
1 => KeyType::Ed25519,
2 => KeyType::Secp256k1,
3 => KeyType::ECDSA,
+ 4 => KeyType::Sr25519,
_ => Self::default(),
}
}
@@ -46,6 +49,7 @@ impl<'a> From<&'a str> for KeyType {
"Ed25519" => KeyType::Ed25519,
"Secp256k1" => KeyType::Secp256k1,
"ECDSA" => KeyType::ECDSA,
+ "Sr25519" => KeyType::Sr25519,
_ => Self::default(),
}
}
diff --git a/identity/src/keypair.rs b/identity/src/keypair.rs
index a1bbba00fa9..eff413ba82b 100644
--- a/identity/src/keypair.rs
+++ b/identity/src/keypair.rs
@@ -22,6 +22,7 @@
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
use quick_protobuf::{BytesReader, Writer};
@@ -32,6 +33,7 @@ use crate::ecdsa;
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
#[cfg(feature = "ed25519")]
@@ -40,16 +42,23 @@ use crate::ed25519;
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
use crate::error::OtherVariantError;
+#[cfg(feature = "sr25519")]
+use crate::sr25519;
#[cfg(any(
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
use crate::proto;
+#[cfg(feature = "sr25519")]
+use tari_crypto::ristretto::RistrettoPublicKey;
+
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
use crate::rsa;
#[cfg(feature = "secp256k1")]
@@ -83,6 +92,9 @@ pub struct Keypair {
#[derive(Debug, Clone)]
#[allow(clippy::large_enum_variant)]
enum KeyPairInner {
+ /// An Sr25519 keypair.
+ #[cfg(feature = "sr25519")]
+ Sr25519(sr25519::Keypair),
/// An Ed25519 keypair.
#[cfg(feature = "ed25519")]
Ed25519(ed25519::Keypair),
@@ -98,6 +110,14 @@ enum KeyPairInner {
}
impl Keypair {
+ /// Generate a new Sr25519 keypair.
+ #[cfg(all(feature = "sr25519", feature = "rand"))]
+ pub fn generate_sr25519() -> Keypair {
+ Keypair {
+ keypair: KeyPairInner::Sr25519(sr25519::Keypair::generate()),
+ }
+ }
+
/// Generate a new Ed25519 keypair.
#[cfg(all(feature = "ed25519", feature = "rand"))]
pub fn generate_ed25519() -> Keypair {
@@ -127,6 +147,11 @@ impl Keypair {
self.try_into()
}
+ #[cfg(feature = "sr25519")]
+ pub fn try_into_sr25519(self) -> Result {
+ self.try_into()
+ }
+
#[cfg(feature = "secp256k1")]
pub fn try_into_secp256k1(self) -> Result {
self.try_into()
@@ -173,11 +198,22 @@ impl Keypair {
})
}
+ #[cfg(feature = "sr25519")]
+ pub fn sr25519_from_bytes(bytes: impl AsMut<[u8]>) -> Result {
+ Ok(Keypair {
+ keypair: KeyPairInner::Sr25519(sr25519::Keypair::from(
+ sr25519::SecretKey::try_from_bytes(bytes)?,
+ )),
+ })
+ }
+
/// Sign a message using the private key of this keypair, producing
/// a signature that can be verified using the corresponding public key.
#[allow(unused_variables)]
pub fn sign(&self, msg: &[u8]) -> Result, SigningError> {
match self.keypair {
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(ref pair) => Ok(pair.sign(msg)),
#[cfg(feature = "ed25519")]
KeyPairInner::Ed25519(ref pair) => Ok(pair.sign(msg)),
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
@@ -192,6 +228,10 @@ impl Keypair {
/// Get the public key of this keypair.
pub fn public(&self) -> PublicKey {
match self.keypair {
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(ref pair) => PublicKey {
+ publickey: PublicKeyInner::Sr25519(pair.public().clone()),
+ },
#[cfg(feature = "ed25519")]
KeyPairInner::Ed25519(ref pair) => PublicKey {
publickey: PublicKeyInner::Ed25519(pair.public()),
@@ -217,6 +257,7 @@ impl Keypair {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
{
@@ -227,6 +268,11 @@ impl Keypair {
Type: proto::KeyType::Ed25519,
Data: data.to_bytes().to_vec(),
},
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(ref data) => proto::PrivateKey {
+ Type: proto::KeyType::Sr25519,
+ Data: data.to_bytes().to_vec(),
+ },
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
KeyPairInner::Rsa(_) => return Err(DecodingError::encoding_unsupported("RSA")),
#[cfg(feature = "secp256k1")]
@@ -252,6 +298,7 @@ impl Keypair {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
)))]
unreachable!()
@@ -264,6 +311,7 @@ impl Keypair {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
{
@@ -272,10 +320,19 @@ impl Keypair {
let mut reader = BytesReader::from_bytes(bytes);
let mut private_key = proto::PrivateKey::from_reader(&mut reader, bytes)
.map_err(|e| DecodingError::bad_protobuf("private key bytes", e))
- .map(zeroize::Zeroizing::new)?;
+ .map(|a| zeroize::Zeroizing::new(a))?;
#[allow(unreachable_code)]
match private_key.Type {
+ proto::KeyType::Sr25519 => {
+ #[cfg(feature = "sr25519")]
+ return sr25519::Keypair::try_from_bytes(&mut private_key.Data).map(|sk| {
+ Keypair {
+ keypair: KeyPairInner::Sr25519(sk),
+ }
+ });
+ Err(DecodingError::missing_feature("sr25519"))
+ }
proto::KeyType::Ed25519 => {
#[cfg(feature = "ed25519")]
return ed25519::Keypair::try_from_bytes(&mut private_key.Data).map(|sk| {
@@ -321,6 +378,7 @@ impl Keypair {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
)))]
unreachable!()
@@ -329,6 +387,8 @@ impl Keypair {
/// Return a [`KeyType`] of the [`Keypair`].
pub fn key_type(&self) -> KeyType {
match self.keypair {
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(_) => KeyType::Sr25519,
#[cfg(feature = "ed25519")]
KeyPairInner::Ed25519(_) => KeyType::Ed25519,
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
@@ -361,6 +421,7 @@ impl Keypair {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
pub fn derive_secret(&self, domain: &[u8]) -> Option<[u8; 32]> {
@@ -377,6 +438,7 @@ impl Keypair {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
)))]
pub fn derive_secret(&self, _: &[u8]) -> Option<[u8; 32]> {
@@ -387,6 +449,8 @@ impl Keypair {
#[allow(dead_code)]
pub(crate) fn secret(&self) -> Option<[u8; 32]> {
match self.keypair {
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(ref inner) => Some(inner.secret().to_bytes()),
#[cfg(feature = "ed25519")]
KeyPairInner::Ed25519(ref inner) => Some(inner.secret().to_bytes()),
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
@@ -423,6 +487,15 @@ impl From for Keypair {
}
}
+#[cfg(feature = "sr25519")]
+impl From for Keypair {
+ fn from(kp: sr25519::Keypair) -> Self {
+ Keypair {
+ keypair: KeyPairInner::Sr25519(kp),
+ }
+ }
+}
+
#[cfg(feature = "secp256k1")]
impl From for Keypair {
fn from(kp: secp256k1::Keypair) -> Self {
@@ -454,6 +527,27 @@ impl TryInto for Keypair {
KeyPairInner::Secp256k1(_) => Err(OtherVariantError::new(crate::KeyType::Secp256k1)),
#[cfg(feature = "ecdsa")]
KeyPairInner::Ecdsa(_) => Err(OtherVariantError::new(crate::KeyType::Ecdsa)),
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(_) => Err(OtherVariantError::new(crate::KeyType::Sr25519)),
+ }
+ }
+}
+
+#[cfg(feature = "sr25519")]
+impl TryInto for Keypair {
+ type Error = OtherVariantError;
+
+ fn try_into(self) -> Result {
+ match self.keypair {
+ KeyPairInner::Sr25519(inner) => Ok(inner),
+ #[cfg(feature = "ed25519")]
+ KeyPairInner::Ed25519(_) => Err(OtherVariantError::new(crate::KeyType::Ed25519)),
+ #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
+ KeyPairInner::Rsa(_) => Err(OtherVariantError::new(crate::KeyType::RSA)),
+ #[cfg(feature = "secp256k1")]
+ KeyPairInner::Secp256k1(_) => Err(OtherVariantError::new(crate::KeyType::Secp256k1)),
+ #[cfg(feature = "ecdsa")]
+ KeyPairInner::Ecdsa(_) => Err(OtherVariantError::new(crate::KeyType::Ecdsa)),
}
}
}
@@ -471,6 +565,8 @@ impl TryInto for Keypair {
KeyPairInner::Rsa(_) => Err(OtherVariantError::new(crate::KeyType::RSA)),
#[cfg(feature = "secp256k1")]
KeyPairInner::Secp256k1(_) => Err(OtherVariantError::new(crate::KeyType::Secp256k1)),
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(_) => Err(OtherVariantError::new(crate::KeyType::Sr25519)),
}
}
}
@@ -482,6 +578,8 @@ impl TryInto for Keypair {
fn try_into(self) -> Result {
match self.keypair {
KeyPairInner::Secp256k1(inner) => Ok(inner),
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(_) => Err(OtherVariantError::new(crate::KeyType::Sr25519)),
#[cfg(feature = "ed25519")]
KeyPairInner::Ed25519(_) => Err(OtherVariantError::new(crate::KeyType::Ed25519)),
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
@@ -499,6 +597,8 @@ impl TryInto for Keypair {
fn try_into(self) -> Result {
match self.keypair {
KeyPairInner::Rsa(inner) => Ok(inner),
+ #[cfg(feature = "sr25519")]
+ KeyPairInner::Sr25519(_) => Err(OtherVariantError::new(crate::KeyType::Sr25519)),
#[cfg(feature = "ed25519")]
KeyPairInner::Ed25519(_) => Err(OtherVariantError::new(crate::KeyType::Ed25519)),
#[cfg(feature = "secp256k1")]
@@ -511,6 +611,8 @@ impl TryInto for Keypair {
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) enum PublicKeyInner {
+ #[cfg(feature = "sr25519")]
+ Sr25519(sr25519::PublicKey),
/// A public Ed25519 key.
#[cfg(feature = "ed25519")]
Ed25519(ed25519::PublicKey),
@@ -540,6 +642,8 @@ impl PublicKey {
#[allow(unused_variables)]
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
match self.publickey {
+ #[cfg(feature = "sr25519")]
+ PublicKeyInner::Sr25519(ref pk) => pk.verify(msg, sig),
#[cfg(feature = "ed25519")]
PublicKeyInner::Ed25519(ref pk) => pk.verify(msg, sig),
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
@@ -556,6 +660,11 @@ impl PublicKey {
self.try_into()
}
+ #[cfg(feature = "sr25519")]
+ pub fn try_into_sr25519(self) -> Result {
+ self.try_into()
+ }
+
#[cfg(feature = "secp256k1")]
pub fn try_into_secp256k1(self) -> Result {
self.try_into()
@@ -571,6 +680,14 @@ impl PublicKey {
self.try_into()
}
+ #[cfg(feature = "sr25519")]
+ pub fn is_eq_sr25519(&self, other: &RistrettoPublicKey) -> bool {
+ match &self.publickey {
+ PublicKeyInner::Sr25519(key) => key.inner_key() == other,
+ _ => false,
+ }
+ }
+
/// Encode the public key into a protobuf structure for storage or
/// exchange with other nodes.
pub fn encode_protobuf(&self) -> Vec {
@@ -578,6 +695,7 @@ impl PublicKey {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
{
@@ -597,6 +715,7 @@ impl PublicKey {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
)))]
unreachable!()
@@ -610,6 +729,7 @@ impl PublicKey {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
{
@@ -626,6 +746,7 @@ impl PublicKey {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
)))]
unreachable!()
@@ -640,6 +761,8 @@ impl PublicKey {
/// Return a [`KeyType`] of the [`PublicKey`].
pub fn key_type(&self) -> KeyType {
match self.publickey {
+ #[cfg(feature = "sr25519")]
+ PublicKeyInner::Sr25519(_) => KeyType::Sr25519,
#[cfg(feature = "ed25519")]
PublicKeyInner::Ed25519(_) => KeyType::Ed25519,
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
@@ -656,6 +779,7 @@ impl PublicKey {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
impl TryFrom for PublicKey {
@@ -663,6 +787,17 @@ impl TryFrom for PublicKey {
fn try_from(pubkey: proto::PublicKey) -> Result {
match pubkey.Type {
+ #[cfg(feature = "sr25519")]
+ proto::KeyType::Sr25519 => Ok(sr25519::PublicKey::try_from_bytes(&pubkey.Data).map(
+ |kp| PublicKey {
+ publickey: PublicKeyInner::Sr25519(kp),
+ },
+ )?),
+ #[cfg(not(feature = "sr25519"))]
+ proto::KeyType::Sr25519 => {
+ tracing::debug!("support for sr25519 was disabled at compile-time");
+ Err(DecodingError::missing_feature("sr25519"))
+ }
#[cfg(feature = "ed25519")]
proto::KeyType::Ed25519 => Ok(ed25519::PublicKey::try_from_bytes(&pubkey.Data).map(
|kp| PublicKey {
@@ -725,6 +860,27 @@ impl TryInto for PublicKey {
PublicKeyInner::Secp256k1(_) => Err(OtherVariantError::new(crate::KeyType::Secp256k1)),
#[cfg(feature = "ecdsa")]
PublicKeyInner::Ecdsa(_) => Err(OtherVariantError::new(crate::KeyType::Ecdsa)),
+ #[cfg(feature = "sr25519")]
+ PublicKeyInner::Sr25519(_) => Err(OtherVariantError::new(crate::KeyType::Sr25519)),
+ }
+ }
+}
+
+#[cfg(feature = "sr25519")]
+impl TryInto for PublicKey {
+ type Error = OtherVariantError;
+
+ fn try_into(self) -> Result {
+ match self.publickey {
+ PublicKeyInner::Sr25519(inner) => Ok(inner),
+ #[cfg(feature = "ed25519")]
+ PublicKeyInner::Ed25519(_) => Err(OtherVariantError::new(crate::KeyType::Ed25519)),
+ #[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
+ PublicKeyInner::Rsa(_) => Err(OtherVariantError::new(crate::KeyType::RSA)),
+ #[cfg(feature = "secp256k1")]
+ PublicKeyInner::Secp256k1(_) => Err(OtherVariantError::new(crate::KeyType::Secp256k1)),
+ #[cfg(feature = "ecdsa")]
+ PublicKeyInner::Ecdsa(_) => Err(OtherVariantError::new(crate::KeyType::Ecdsa)),
}
}
}
@@ -738,6 +894,8 @@ impl TryInto for PublicKey {
PublicKeyInner::Ecdsa(inner) => Ok(inner),
#[cfg(feature = "ed25519")]
PublicKeyInner::Ed25519(_) => Err(OtherVariantError::new(crate::KeyType::Ed25519)),
+ #[cfg(feature = "sr25519")]
+ PublicKeyInner::Sr25519(_) => Err(OtherVariantError::new(crate::KeyType::Sr25519)),
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
PublicKeyInner::Rsa(_) => Err(OtherVariantError::new(crate::KeyType::RSA)),
#[cfg(feature = "secp256k1")]
@@ -755,6 +913,8 @@ impl TryInto for PublicKey {
PublicKeyInner::Secp256k1(inner) => Ok(inner),
#[cfg(feature = "ed25519")]
PublicKeyInner::Ed25519(_) => Err(OtherVariantError::new(crate::KeyType::Ed25519)),
+ #[cfg(feature = "sr25519")]
+ PublicKeyInner::Sr25519(_) => Err(OtherVariantError::new(crate::KeyType::Sr25519)),
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
PublicKeyInner::Rsa(_) => Err(OtherVariantError::new(crate::KeyType::RSA)),
#[cfg(feature = "ecdsa")]
@@ -772,6 +932,8 @@ impl TryInto for PublicKey {
PublicKeyInner::Rsa(inner) => Ok(inner),
#[cfg(feature = "ed25519")]
PublicKeyInner::Ed25519(_) => Err(OtherVariantError::new(crate::KeyType::Ed25519)),
+ #[cfg(feature = "sr25519")]
+ PublicKeyInner::Sr25519(_) => Err(OtherVariantError::new(crate::KeyType::Sr25519)),
#[cfg(feature = "secp256k1")]
PublicKeyInner::Secp256k1(_) => Err(OtherVariantError::new(crate::KeyType::Secp256k1)),
#[cfg(feature = "ecdsa")]
@@ -789,6 +951,15 @@ impl From for PublicKey {
}
}
+#[cfg(feature = "sr25519")]
+impl From for PublicKey {
+ fn from(key: sr25519::PublicKey) -> Self {
+ PublicKey {
+ publickey: PublicKeyInner::Sr25519(key),
+ }
+ }
+}
+
#[cfg(feature = "secp256k1")]
impl From for PublicKey {
fn from(key: secp256k1::PublicKey) -> Self {
diff --git a/identity/src/lib.rs b/identity/src/lib.rs
index 4f4313e8f17..f51e2f6c32b 100644
--- a/identity/src/lib.rs
+++ b/identity/src/lib.rs
@@ -39,6 +39,7 @@
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
mod proto {
@@ -52,6 +53,9 @@ pub mod ecdsa;
#[cfg(feature = "ed25519")]
pub mod ed25519;
+#[cfg(feature = "sr25519")]
+pub mod sr25519;
+
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
pub mod rsa;
@@ -67,6 +71,7 @@ mod peer_id;
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
impl zeroize::Zeroize for proto::PrivateKey {
@@ -79,11 +84,17 @@ impl zeroize::Zeroize for proto::PrivateKey {
feature = "ecdsa",
feature = "secp256k1",
feature = "ed25519",
+ feature = "sr25519",
feature = "rsa"
))]
impl From<&PublicKey> for proto::PublicKey {
fn from(key: &PublicKey) -> Self {
match &key.publickey {
+ #[cfg(feature = "sr25519")]
+ keypair::PublicKeyInner::Sr25519(key) => proto::PublicKey {
+ Type: proto::KeyType::Sr25519,
+ Data: key.to_bytes().to_vec(),
+ },
#[cfg(feature = "ed25519")]
keypair::PublicKeyInner::Ed25519(key) => proto::PublicKey {
Type: proto::KeyType::Ed25519,
@@ -117,6 +128,7 @@ pub use peer_id::{ParseError, PeerId};
#[derive(Debug, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms)]
pub enum KeyType {
+ Sr25519,
Ed25519,
RSA,
Secp256k1,
@@ -126,6 +138,7 @@ pub enum KeyType {
impl std::fmt::Display for KeyType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
+ KeyType::Sr25519 => f.write_str("Sr25519"),
KeyType::Ed25519 => f.write_str("Ed25519"),
KeyType::RSA => f.write_str("RSA"),
KeyType::Secp256k1 => f.write_str("Secp256k1"),
diff --git a/identity/src/sr25519.rs b/identity/src/sr25519.rs
new file mode 100644
index 00000000000..1d18827d0ff
--- /dev/null
+++ b/identity/src/sr25519.rs
@@ -0,0 +1,281 @@
+// Copyright 2019 Parity Technologies (UK) Ltd.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+//! Sr25519 keys.
+
+use super::error::DecodingError;
+use core::fmt;
+use std::fmt::{Display, Formatter};
+use tari_crypto::keys::{PublicKey as _, SecretKey as _};
+use tari_crypto::ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey};
+use tari_crypto::tari_utilities::ByteArray;
+use zeroize::Zeroize;
+
+/// An Sr25519 keypair.
+#[derive(Clone)]
+pub struct Keypair {
+ public: PublicKey,
+ secret: SecretKey,
+}
+
+impl Keypair {
+ /// Generate a new random Sr25519 keypair.
+ #[cfg(feature = "rand")]
+ pub fn generate() -> Keypair {
+ Keypair::from(SecretKey::generate())
+ }
+
+ /// Convert the keypair into a byte array. This is encoded as the secret key only since the public key can be derived from it.
+ /// TODO: this leaks the secret key in stack memory
+ pub fn to_bytes(&self) -> [u8; 32] {
+ self.secret.to_bytes()
+ }
+
+ /// Try to parse a keypair from the [binary format](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5)
+ /// produced by [`Keypair::to_bytes`], zeroing the input on success.
+ ///
+ /// Note that this binary format is the same as `curve25519_dalek`'s compressed bytes
+ pub fn try_from_bytes(kp: &mut [u8]) -> Result {
+ let secret = SecretKey::try_from_bytes(kp)
+ .map_err(|e| DecodingError::failed_to_parse("Sr25519 keypair", e))?;
+
+ Ok(Self::from(secret))
+ }
+
+ /// Sign a message using the private key of this keypair.
+ pub fn sign(&self, msg: &[u8]) -> Vec {
+ let sig = RistrettoSchnorr::sign(&self.secret.0, msg, &mut rand::rngs::OsRng).expect(
+ "SchnorrSignature::sign shouldn't return a Result (Blake2b is hard coded as the hasher)",
+ );
+ let mut buf = vec![0u8; 64];
+ buf[..32].copy_from_slice(sig.get_public_nonce().as_bytes());
+ buf[32..].copy_from_slice(sig.get_signature().as_bytes());
+ buf
+ }
+
+ /// Get the public key of this keypair.
+ pub fn public(&self) -> &PublicKey {
+ &self.public
+ }
+
+ /// Get the secret key of this keypair.
+ pub fn secret(&self) -> &SecretKey {
+ &self.secret
+ }
+}
+
+impl fmt::Debug for Keypair {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Keypair")
+ .field("public", self.public())
+ .finish()
+ }
+}
+
+/// Demote an Sr25519 keypair to a secret key.
+impl From for SecretKey {
+ fn from(kp: Keypair) -> SecretKey {
+ kp.secret
+ }
+}
+
+/// Promote an Sr25519 secret key into a keypair.
+impl From for Keypair {
+ fn from(secret: SecretKey) -> Keypair {
+ Keypair {
+ public: PublicKey(RistrettoPublicKey::from_secret_key(&secret.0)),
+ secret,
+ }
+ }
+}
+
+/// An Sr25519 public key.
+#[derive(Eq, Clone, PartialEq, Hash, PartialOrd, Ord)]
+pub struct PublicKey(RistrettoPublicKey);
+
+impl fmt::Debug for PublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("PublicKey(compressed): ")?;
+ for byte in self.0.as_bytes() {
+ write!(f, "{byte:x}")?;
+ }
+ Ok(())
+ }
+}
+
+impl PublicKey {
+ /// Verify the Sr25519 signature on a message using the public key.
+ pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
+ if sig.len() != 64 {
+ return false;
+ }
+ let nonce = match RistrettoPublicKey::from_canonical_bytes(&sig[..32]) {
+ Ok(n) => n,
+ Err(_) => return false,
+ };
+ let sig = match RistrettoSecretKey::from_canonical_bytes(&sig[32..]) {
+ Ok(s) => s,
+ Err(_) => return false,
+ };
+ RistrettoSchnorr::new(nonce, sig).verify(&self.0, msg)
+ }
+
+ /// Convert the public key to a byte array in compressed form, i.e.
+ /// where one coordinate is represented by a single bit.
+ pub fn to_bytes(&self) -> [u8; 32] {
+ let mut buf = [0u8; 32];
+ buf.copy_from_slice(self.0.as_bytes());
+ buf
+ }
+
+ /// Try to parse a public key from a byte array containing the actual key as produced by `to_bytes`.
+ pub fn try_from_bytes(k: &[u8]) -> Result {
+ let pk = RistrettoPublicKey::from_canonical_bytes(k)
+ // TODO: cant pass ByteArrayError because it doesnt implement std Error
+ .map_err(|e| DecodingError::failed_to_parse("Sr25519 public key", ByteArrayError(e)))?;
+ Ok(PublicKey(pk))
+ }
+
+ pub fn inner_key(&self) -> &RistrettoPublicKey {
+ &self.0
+ }
+}
+
+impl From for PublicKey {
+ fn from(pk: RistrettoPublicKey) -> Self {
+ PublicKey(pk)
+ }
+}
+
+/// An Sr25519 secret key.
+#[derive(Clone)]
+pub struct SecretKey(RistrettoSecretKey);
+
+/// View the bytes of the secret key.
+impl AsRef<[u8]> for SecretKey {
+ fn as_ref(&self) -> &[u8] {
+ self.0.as_bytes()
+ }
+}
+
+impl fmt::Debug for SecretKey {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "SecretKey")
+ }
+}
+
+impl SecretKey {
+ /// Generate a new Sr25519 secret key.
+ #[cfg(feature = "rand")]
+ pub fn generate() -> SecretKey {
+ SecretKey(RistrettoSecretKey::random(&mut rand::rngs::OsRng))
+ }
+
+ /// Try to parse an Sr25519 secret key from a byte slice
+ /// containing the actual key, zeroing the input on success.
+ /// If the bytes do not constitute a valid Sr25519 secret key, an error is
+ /// returned.
+ pub fn try_from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result {
+ let sk_bytes = sk_bytes.as_mut();
+ let secret = RistrettoSecretKey::from_canonical_bytes(&*sk_bytes)
+ .map_err(|e| DecodingError::failed_to_parse("Sr25519 secret key", ByteArrayError(e)))?;
+ sk_bytes.zeroize();
+ Ok(SecretKey(secret))
+ }
+
+ pub fn inner_key(&self) -> &RistrettoSecretKey {
+ &self.0
+ }
+
+ // Not great, leaves the secret key in stack memory (all key types not just Sr25519)
+ pub(crate) fn to_bytes(&self) -> [u8; 32] {
+ let mut buf = [0u8; 32];
+ buf.copy_from_slice(self.0.as_bytes());
+ buf
+ }
+}
+
+impl From for SecretKey {
+ fn from(value: RistrettoSecretKey) -> Self {
+ Self(value)
+ }
+}
+#[derive(Debug)]
+struct ByteArrayError(tari_crypto::tari_utilities::ByteArrayError);
+
+impl Display for ByteArrayError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "ByteArrayError: {}", self.0)
+ }
+}
+
+impl std::error::Error for ByteArrayError {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use quickcheck::*;
+
+ fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool {
+ kp1.public() == kp2.public() && kp1.secret.to_bytes() == kp2.secret.to_bytes()
+ }
+
+ #[test]
+ #[cfg(feature = "rand")]
+ fn sr25519_keypair_encode_decode() {
+ fn prop() -> bool {
+ let kp1 = Keypair::generate();
+ let mut kp1_enc = kp1.to_bytes();
+ let kp2 = Keypair::try_from_bytes(&mut kp1_enc).unwrap();
+ eq_keypairs(&kp1, &kp2) && kp1_enc.iter().all(|b| *b == 0)
+ }
+ QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
+ }
+
+ #[test]
+ #[cfg(feature = "rand")]
+ fn sr25519_keypair_from_secret() {
+ fn prop() -> bool {
+ let kp1 = Keypair::generate();
+ let mut sk = kp1.secret.to_bytes();
+ let kp2 = Keypair::from(SecretKey::try_from_bytes(&mut sk).unwrap());
+ eq_keypairs(&kp1, &kp2) && sk == [0u8; 32]
+ }
+ QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
+ }
+
+ #[test]
+ #[cfg(feature = "rand")]
+ fn sr25519_signature() {
+ let kp = Keypair::generate();
+ let pk = kp.public();
+
+ let msg = "hello world".as_bytes();
+ let sig = kp.sign(msg);
+ assert!(pk.verify(msg, &sig));
+
+ let mut invalid_sig = sig.clone();
+ invalid_sig[3..6].copy_from_slice(&[10, 23, 42]);
+ assert!(!pk.verify(msg, &invalid_sig));
+
+ let invalid_msg = "h3ll0 w0rld".as_bytes();
+ assert!(!pk.verify(invalid_msg, &sig));
+ }
+}
diff --git a/interop-tests/Dockerfile.chromium b/interop-tests/Dockerfile.chromium
index 73a9ab82ee7..ff9284f6da2 100644
--- a/interop-tests/Dockerfile.chromium
+++ b/interop-tests/Dockerfile.chromium
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1.5-labs
FROM rust:1.83 as chef
RUN rustup target add wasm32-unknown-unknown
-RUN wget -q -O- https://github.com/rustwasm/wasm-pack/releases/download/v0.12.1/wasm-pack-v0.12.1-x86_64-unknown-linux-musl.tar.gz | tar -zx -C /usr/local/bin --strip-components 1 --wildcards "wasm-pack-*/wasm-pack"
+RUN wget -q -O- https://github.com/drager/wasm-pack/releases/download/v0.12.1/wasm-pack-v0.12.1-x86_64-unknown-linux-musl.tar.gz | tar -zx -C /usr/local/bin --strip-components 1 --wildcards "wasm-pack-*/wasm-pack"
RUN wget -q -O- https://github.com/WebAssembly/binaryen/releases/download/version_115/binaryen-version_115-x86_64-linux.tar.gz | tar -zx -C /usr/local/bin --strip-components 2 --wildcards "binaryen-version_*/bin/wasm-opt"
RUN wget -q -O- https://github.com/LukeMathWalker/cargo-chef/releases/download/v0.1.62/cargo-chef-x86_64-unknown-linux-gnu.tar.gz | tar -zx -C /usr/local/bin
WORKDIR /app
diff --git a/libp2p/CHANGELOG.md b/libp2p/CHANGELOG.md
index 2562a87da28..5c6c4a6865c 100644
--- a/libp2p/CHANGELOG.md
+++ b/libp2p/CHANGELOG.md
@@ -1,5 +1,12 @@
+## 0.56.1
+
+- Fix `metrics` delegation to gossipsub protocol.
+ See [PR 6180](https://github.com/libp2p/rust-libp2p/pull/6180)
+
## 0.56.0
+- Remove `async-std` support.
+ See [PR 6074](https://github.com/libp2p/rust-libp2p/pull/6074)
- Remove deprecated `Transport::with_bandwidth_logging`,
`SwarmBuilder::with_bandwidth_logging` and `TransportExt`.
See [PR 5766](https://github.com/libp2p/rust-libp2p/pull/5766).
@@ -13,8 +20,14 @@
- Make the `*-websys` variants (`libp2p-webrtc-websys`, `libp2p-websocket-websys`, `libp2p-webtransport-websys`) only available on wasm32 target architecture.
See [PR 5891](https://github.com/libp2p/rust-libp2p/pull/5891).
+- Remove TCP from the `async-std` swarm builder, as `async-std` support was removed from `libp2p-tcp` transport.
+ See [PR 5955](https://github.com/libp2p/rust-libp2p/pull/5955)
+
- Remove QUIC from the `async-std` swarm builder, as `async-std` support was removed from `libp2p-quic` transport.
See [PR 5954](https://github.com/libp2p/rust-libp2p/pull/5954)
+
+- Remove DNS from the `async-std` swarm builder, as `async-std` support was removed from `libp2p-dns` transport.
+ See [PR 5959](https://github.com/libp2p/rust-libp2p/pull/5959)
## 0.55.0
diff --git a/libp2p/Cargo.toml b/libp2p/Cargo.toml
index 0ff11d88787..587360b2336 100644
--- a/libp2p/Cargo.toml
+++ b/libp2p/Cargo.toml
@@ -3,7 +3,7 @@ name = "libp2p"
edition.workspace = true
rust-version = { workspace = true }
description = "Peer-to-peer networking library"
-version = "0.56.0"
+version = "0.56.1"
authors = ["Parity Technologies "]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
@@ -12,7 +12,6 @@ categories = ["network-programming", "asynchronous"]
[features]
full = [
- "async-std",
"autonat",
"cbor",
"dcutr",
@@ -29,7 +28,6 @@ full = [
"memory-connection-limits",
"metrics",
"noise",
- "peer-store",
"ping",
"plaintext",
"pnet",
@@ -53,13 +51,13 @@ full = [
"upnp",
]
-async-std = ["libp2p-swarm/async-std", "libp2p-tcp?/async-io", "libp2p-dns?/async-std"]
autonat = ["dep:libp2p-autonat"]
cbor = ["libp2p-request-response?/cbor"]
dcutr = ["dep:libp2p-dcutr", "libp2p-metrics?/dcutr"]
dns = ["dep:libp2p-dns"]
ecdsa = ["libp2p-identity/ecdsa"]
ed25519 = ["libp2p-identity/ed25519"]
+sr25519 = ["libp2p-identity/sr25519"]
floodsub = ["dep:libp2p-floodsub"]
gossipsub = ["dep:libp2p-gossipsub", "libp2p-metrics?/gossipsub"]
identify = ["dep:libp2p-identify", "libp2p-metrics?/identify"]
@@ -68,9 +66,8 @@ kad = ["dep:libp2p-kad", "libp2p-metrics?/kad"]
macros = ["libp2p-swarm/macros"]
mdns = ["dep:libp2p-mdns"]
memory-connection-limits = ["dep:libp2p-memory-connection-limits"]
-metrics = ["dep:libp2p-metrics"]
+metrics = ["dep:libp2p-metrics", "libp2p-gossipsub?/metrics"]
noise = ["dep:libp2p-noise"]
-peer-store = ["dep:libp2p-peer-store"]
ping = ["dep:libp2p-ping", "libp2p-metrics?/ping"]
plaintext = ["dep:libp2p-plaintext"]
pnet = ["dep:libp2p-pnet"]
@@ -80,7 +77,7 @@ rendezvous = ["dep:libp2p-rendezvous"]
request-response = ["dep:libp2p-request-response"]
rsa = ["libp2p-identity/rsa"]
secp256k1 = ["libp2p-identity/secp256k1"]
-serde = ["libp2p-core/serde", "libp2p-kad?/serde", "libp2p-gossipsub?/serde"]
+serde = ["libp2p-core/serde", "libp2p-kad?/serde", "libp2p-gossipsub?/serde", "libp2p-identity/serde"]
tcp = ["dep:libp2p-tcp"]
tls = ["dep:libp2p-tls"]
tokio = ["libp2p-swarm/tokio", "libp2p-mdns?/tokio", "libp2p-tcp?/tokio", "libp2p-dns?/tokio", "libp2p-quic?/tokio", "libp2p-upnp?/tokio"]
@@ -113,7 +110,6 @@ libp2p-identity = { workspace = true, features = ["rand"] }
libp2p-kad = { workspace = true, optional = true }
libp2p-metrics = { workspace = true, optional = true }
libp2p-noise = { workspace = true, optional = true }
-libp2p-peer-store = { workspace = true, optional = true }
libp2p-ping = { workspace = true, optional = true }
libp2p-plaintext = { workspace = true, optional = true }
libp2p-pnet = { workspace = true, optional = true }
@@ -123,6 +119,7 @@ libp2p-request-response = { workspace = true, optional = true }
libp2p-swarm = { workspace = true }
libp2p-yamux = { workspace = true, optional = true }
multiaddr = { workspace = true }
+
pin-project = "1.0.0"
thiserror = { workspace = true }
@@ -143,7 +140,6 @@ libp2p-upnp = { workspace = true, optional = true }
libp2p-websocket = { workspace = true, optional = true }
[dev-dependencies]
-async-std = { version = "1.6.2", features = ["attributes"] }
tokio = { workspace = true, features = ["io-util", "io-std", "macros", "rt", "rt-multi-thread"] }
libp2p-mplex = { workspace = true }
diff --git a/libp2p/src/builder.rs b/libp2p/src/builder.rs
index 4596fb99b50..95166bd34d4 100644
--- a/libp2p/src/builder.rs
+++ b/libp2p/src/builder.rs
@@ -102,28 +102,6 @@ mod tests {
.build();
}
- #[test]
- #[cfg(all(
- feature = "async-std",
- feature = "tcp",
- feature = "tls",
- feature = "noise",
- feature = "yamux",
- ))]
- fn async_std_tcp() {
- let _ = SwarmBuilder::with_new_identity()
- .with_async_std()
- .with_tcp(
- Default::default(),
- libp2p_tls::Config::new,
- libp2p_yamux::Config::default,
- )
- .unwrap()
- .with_behaviour(|_| libp2p_swarm::dummy::Behaviour)
- .unwrap()
- .build();
- }
-
#[test]
#[cfg(all(feature = "tokio", feature = "quic"))]
fn quic() {
diff --git a/libp2p/src/builder/phase.rs b/libp2p/src/builder/phase.rs
index 78ff49724c0..fa378273630 100644
--- a/libp2p/src/builder/phase.rs
+++ b/libp2p/src/builder/phase.rs
@@ -35,7 +35,7 @@ use super::{
select_muxer::SelectMuxerUpgrade, select_security::SelectSecurityUpgrade, SwarmBuilder,
};
-#[allow(unreachable_pub)]
+#[allow(unreachable_pub, dead_code)]
pub trait IntoSecurityUpgrade {
type Upgrade;
type Error;
@@ -77,7 +77,7 @@ where
}
}
-#[allow(unreachable_pub)]
+#[allow(unreachable_pub, dead_code)]
pub trait IntoMultiplexerUpgrade {
type Upgrade;
diff --git a/libp2p/src/builder/phase/dns.rs b/libp2p/src/builder/phase/dns.rs
index 83653836a34..7e6de8e8a6e 100644
--- a/libp2p/src/builder/phase/dns.rs
+++ b/libp2p/src/builder/phase/dns.rs
@@ -7,27 +7,6 @@ pub struct DnsPhase {
pub(crate) transport: T,
}
-#[cfg(all(not(target_arch = "wasm32"), feature = "async-std", feature = "dns"))]
-impl SwarmBuilder> {
- pub fn with_dns(
- self,
- ) -> Result<
- SwarmBuilder<
- super::provider::AsyncStd,
- WebsocketPhase,
- >,
- std::io::Error,
- > {
- Ok(SwarmBuilder {
- keypair: self.keypair,
- phantom: PhantomData,
- phase: WebsocketPhase {
- transport: libp2p_dns::async_std::Transport::system2(self.phase.transport)?,
- },
- })
- }
-}
-
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio", feature = "dns"))]
impl SwarmBuilder> {
pub fn with_dns(
@@ -49,30 +28,6 @@ impl SwarmBuilder SwarmBuilder> {
- pub fn with_dns_config(
- self,
- cfg: libp2p_dns::ResolverConfig,
- opts: libp2p_dns::ResolverOpts,
- ) -> SwarmBuilder<
- super::provider::AsyncStd,
- WebsocketPhase,
- > {
- SwarmBuilder {
- keypair: self.keypair,
- phantom: PhantomData,
- phase: WebsocketPhase {
- transport: libp2p_dns::async_std::Transport::custom2(
- self.phase.transport,
- cfg,
- opts,
- ),
- },
- }
- }
-}
-
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio", feature = "dns"))]
impl SwarmBuilder> {
pub fn with_dns_config(
diff --git a/libp2p/src/builder/phase/other_transport.rs b/libp2p/src/builder/phase/other_transport.rs
index e77f0d62480..a7a8c6cc83d 100644
--- a/libp2p/src/builder/phase/other_transport.rs
+++ b/libp2p/src/builder/phase/other_transport.rs
@@ -66,22 +66,6 @@ impl
}
// Shortcuts
-#[cfg(all(not(target_arch = "wasm32"), feature = "async-std", feature = "dns"))]
-impl
- SwarmBuilder>
-{
- pub fn with_dns(
- self,
- ) -> Result<
- SwarmBuilder<
- super::provider::AsyncStd,
- WebsocketPhase,
- >,
- std::io::Error,
- > {
- self.without_any_other_transports().with_dns()
- }
-}
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio", feature = "dns"))]
impl
SwarmBuilder>
@@ -98,22 +82,6 @@ impl
self.without_any_other_transports().with_dns()
}
}
-#[cfg(all(not(target_arch = "wasm32"), feature = "async-std", feature = "dns"))]
-impl
- SwarmBuilder>
-{
- pub fn with_dns_config(
- self,
- cfg: libp2p_dns::ResolverConfig,
- opts: libp2p_dns::ResolverOpts,
- ) -> SwarmBuilder<
- super::provider::AsyncStd,
- WebsocketPhase,
- > {
- self.without_any_other_transports()
- .with_dns_config(cfg, opts)
- }
-}
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio", feature = "dns"))]
impl
SwarmBuilder>
diff --git a/libp2p/src/builder/phase/provider.rs b/libp2p/src/builder/phase/provider.rs
index 00a79e14a30..aa99d5518ce 100644
--- a/libp2p/src/builder/phase/provider.rs
+++ b/libp2p/src/builder/phase/provider.rs
@@ -11,10 +11,6 @@ pub enum NoProviderSpecified {}
// Define enums for each of the possible runtime environments. These are used as markers in the
// type-state pattern, allowing compile-time checks for the appropriate environment configuration.
-#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))]
-/// Represents the AsyncStd runtime environment.
-pub enum AsyncStd {}
-
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
/// Represents the Tokio runtime environment.
pub enum Tokio {}
@@ -27,18 +23,6 @@ pub enum WasmBindgen {}
pub struct ProviderPhase {}
impl SwarmBuilder {
- /// Configures the SwarmBuilder to use the AsyncStd runtime.
- /// This method is only available when compiling for non-Wasm
- /// targets with the `async-std` feature enabled.
- #[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))]
- pub fn with_async_std(self) -> SwarmBuilder {
- SwarmBuilder {
- keypair: self.keypair,
- phantom: PhantomData,
- phase: TcpPhase {},
- }
- }
-
/// Configures the SwarmBuilder to use the Tokio runtime.
/// This method is only available when compiling for non-Wasm
/// targets with the `tokio` feature enabled
diff --git a/libp2p/src/builder/phase/quic.rs b/libp2p/src/builder/phase/quic.rs
index ebc0776ff62..b80a712dc07 100644
--- a/libp2p/src/builder/phase/quic.rs
+++ b/libp2p/src/builder/phase/quic.rs
@@ -149,22 +149,6 @@ impl SwarmBuilder SwarmBuilder> {
- pub fn with_dns(
- self,
- ) -> Result<
- SwarmBuilder<
- super::provider::AsyncStd,
- WebsocketPhase,
- >,
- std::io::Error,
- > {
- self.without_quic()
- .without_any_other_transports()
- .with_dns()
- }
-}
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio", feature = "dns"))]
impl SwarmBuilder> {
pub fn with_dns(
@@ -181,21 +165,6 @@ impl SwarmBuilder SwarmBuilder> {
- pub fn with_dns_config(
- self,
- cfg: libp2p_dns::ResolverConfig,
- opts: libp2p_dns::ResolverOpts,
- ) -> SwarmBuilder<
- super::provider::AsyncStd,
- WebsocketPhase,
- > {
- self.without_quic()
- .without_any_other_transports()
- .with_dns_config(cfg, opts)
- }
-}
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio", feature = "dns"))]
impl SwarmBuilder> {
pub fn with_dns_config(
@@ -263,13 +232,7 @@ macro_rules! impl_quic_phase_with_websocket {
}
}
}
-impl_quic_phase_with_websocket!(
- "async-std",
- super::provider::AsyncStd,
- rw_stream_sink::RwStreamSink<
- libp2p_websocket::BytesConnection,
- >
-);
+
impl_quic_phase_with_websocket!(
"tokio",
super::provider::Tokio,
diff --git a/libp2p/src/builder/phase/swarm.rs b/libp2p/src/builder/phase/swarm.rs
index e751ad672e4..79b512a38af 100644
--- a/libp2p/src/builder/phase/swarm.rs
+++ b/libp2p/src/builder/phase/swarm.rs
@@ -42,13 +42,6 @@ macro_rules! impl_with_swarm_config {
};
}
-#[cfg(not(target_arch = "wasm32"))]
-impl_with_swarm_config!(
- "async-std",
- super::provider::AsyncStd,
- libp2p_swarm::Config::with_async_std_executor()
-);
-
#[cfg(not(target_arch = "wasm32"))]
impl_with_swarm_config!(
"tokio",
diff --git a/libp2p/src/builder/phase/tcp.rs b/libp2p/src/builder/phase/tcp.rs
index 44c28d89888..f21ae109300 100644
--- a/libp2p/src/builder/phase/tcp.rs
+++ b/libp2p/src/builder/phase/tcp.rs
@@ -97,7 +97,6 @@ macro_rules! impl_tcp_builder {
};
}
-impl_tcp_builder!("async-std", super::provider::AsyncStd, async_io);
impl_tcp_builder!("tokio", super::provider::Tokio, tokio);
impl SwarmBuilder {
@@ -215,13 +214,6 @@ macro_rules! impl_tcp_phase_with_websocket {
}
}
}
-impl_tcp_phase_with_websocket!(
- "async-std",
- super::provider::AsyncStd,
- rw_stream_sink::RwStreamSink<
- libp2p_websocket::BytesConnection,
- >
-);
impl_tcp_phase_with_websocket!(
"tokio",
super::provider::Tokio,
diff --git a/libp2p/src/builder/phase/websocket.rs b/libp2p/src/builder/phase/websocket.rs
index 51ad0217069..369b101329a 100644
--- a/libp2p/src/builder/phase/websocket.rs
+++ b/libp2p/src/builder/phase/websocket.rs
@@ -115,16 +115,6 @@ macro_rules! impl_websocket_builder {
};
}
-impl_websocket_builder!(
- "async-std",
- super::provider::AsyncStd,
- libp2p_dns::async_std::Transport::system(libp2p_tcp::async_io::Transport::new(
- libp2p_tcp::Config::default(),
- )),
- rw_stream_sink::RwStreamSink<
- libp2p_websocket::BytesConnection,
- >
-);
impl_websocket_builder!(
"tokio",
super::provider::Tokio,
diff --git a/libp2p/src/builder/select_muxer.rs b/libp2p/src/builder/select_muxer.rs
index 93ae0547269..c60798a1ee1 100644
--- a/libp2p/src/builder/select_muxer.rs
+++ b/libp2p/src/builder/select_muxer.rs
@@ -34,6 +34,7 @@ use libp2p_core::{
pub struct SelectMuxerUpgrade(A, B);
impl SelectMuxerUpgrade {
+ #[allow(dead_code)]
pub fn new(a: A, b: B) -> Self {
SelectMuxerUpgrade(a, b)
}
diff --git a/libp2p/src/builder/select_security.rs b/libp2p/src/builder/select_security.rs
index 1ed760feb1b..e28083f73dc 100644
--- a/libp2p/src/builder/select_security.rs
+++ b/libp2p/src/builder/select_security.rs
@@ -42,6 +42,7 @@ impl SelectSecurityUpgrade {
/// Combines two upgrades into an `SelectUpgrade`.
///
/// The protocols supported by the first element have a higher priority.
+ #[allow(dead_code)]
pub fn new(a: A, b: B) -> Self {
SelectSecurityUpgrade(a, b)
}
diff --git a/libp2p/src/lib.rs b/libp2p/src/lib.rs
index 1b02b7523b3..42461f8ef8e 100644
--- a/libp2p/src/lib.rs
+++ b/libp2p/src/lib.rs
@@ -81,9 +81,6 @@ pub use libp2p_metrics as metrics;
#[cfg(feature = "noise")]
#[doc(inline)]
pub use libp2p_noise as noise;
-#[cfg(feature = "peer-store")]
-#[doc(inline)]
-pub use libp2p_peer_store as peer_store;
#[cfg(feature = "ping")]
#[doc(inline)]
pub use libp2p_ping as ping;
diff --git a/misc/allow-block-list/CHANGELOG.md b/misc/allow-block-list/CHANGELOG.md
index be7619269d0..c3d14568003 100644
--- a/misc/allow-block-list/CHANGELOG.md
+++ b/misc/allow-block-list/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.6.0
+
+
+
## 0.5.0
- Add getters & setters for the allowed/blocked peers.
diff --git a/misc/allow-block-list/Cargo.toml b/misc/allow-block-list/Cargo.toml
index 98512d0bf94..80f6fbb218b 100644
--- a/misc/allow-block-list/Cargo.toml
+++ b/misc/allow-block-list/Cargo.toml
@@ -3,7 +3,7 @@ name = "libp2p-allow-block-list"
edition.workspace = true
rust-version = { workspace = true }
description = "Allow/block list connection management for libp2p."
-version = "0.5.0"
+version = "0.6.0"
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
keywords = ["peer-to-peer", "libp2p", "networking"]
diff --git a/misc/connection-limits/CHANGELOG.md b/misc/connection-limits/CHANGELOG.md
index 52e3bcdccb4..5f5f7cb41ee 100644
--- a/misc/connection-limits/CHANGELOG.md
+++ b/misc/connection-limits/CHANGELOG.md
@@ -1,9 +1,11 @@
-## 0.5.1
+## 0.6.0
- Allow setting Peer IDs for bypassing limit check.
Connections to the specified peers won't be counted toward limits.
See [PR 5720](https://github.com/libp2p/rust-libp2p/pull/5720).
+
+
## 0.5.0
- Deprecate `void` crate.
diff --git a/misc/connection-limits/Cargo.toml b/misc/connection-limits/Cargo.toml
index 56f3ab091d2..4e72de4a20c 100644
--- a/misc/connection-limits/Cargo.toml
+++ b/misc/connection-limits/Cargo.toml
@@ -3,7 +3,7 @@ name = "libp2p-connection-limits"
edition.workspace = true
rust-version = { workspace = true }
description = "Connection limits for libp2p."
-version = "0.5.1"
+version = "0.6.0"
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
keywords = ["peer-to-peer", "libp2p", "networking"]
diff --git a/misc/memory-connection-limits/CHANGELOG.md b/misc/memory-connection-limits/CHANGELOG.md
index ed16fa37eb3..f854edba6a4 100644
--- a/misc/memory-connection-limits/CHANGELOG.md
+++ b/misc/memory-connection-limits/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.5.0
+
+
+
## 0.4.0
- Deprecate `void` crate.
diff --git a/misc/memory-connection-limits/Cargo.toml b/misc/memory-connection-limits/Cargo.toml
index dbbaafaf0de..da4df0c5244 100644
--- a/misc/memory-connection-limits/Cargo.toml
+++ b/misc/memory-connection-limits/Cargo.toml
@@ -3,7 +3,7 @@ name = "libp2p-memory-connection-limits"
edition.workspace = true
rust-version = { workspace = true }
description = "Memory usage based connection limits for libp2p."
-version = "0.4.0"
+version = "0.5.0"
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
keywords = ["peer-to-peer", "libp2p", "networking"]
diff --git a/misc/memory-connection-limits/src/lib.rs b/misc/memory-connection-limits/src/lib.rs
index 7905e39ac18..b19a1573fba 100644
--- a/misc/memory-connection-limits/src/lib.rs
+++ b/misc/memory-connection-limits/src/lib.rs
@@ -82,7 +82,7 @@ const MAX_STALE_DURATION: Duration = Duration::from_millis(100);
impl Behaviour {
/// Sets the process memory usage threshold in absolute bytes.
///
- /// New inbound and outbound connections will be denied when the threshold is reached.
+ /// New inbound and outbound connections will be denied when the threshold is exceeded.
pub fn with_max_bytes(max_allowed_bytes: usize) -> Self {
Self {
max_allowed_bytes,
@@ -95,7 +95,7 @@ impl Behaviour {
/// Sets the process memory usage threshold in the percentage of the total physical memory.
///
- /// New inbound and outbound connections will be denied when the threshold is reached.
+ /// New inbound and outbound connections will be denied when the threshold is exceeded.
pub fn with_max_percentage(percentage: f64) -> Self {
use sysinfo::{RefreshKind, System};
diff --git a/misc/memory-connection-limits/tests/max_bytes.rs b/misc/memory-connection-limits/tests/max_bytes.rs
index 442e38bdf1b..16ff8ed3ea0 100644
--- a/misc/memory-connection-limits/tests/max_bytes.rs
+++ b/misc/memory-connection-limits/tests/max_bytes.rs
@@ -31,8 +31,11 @@ use util::*;
#[tokio::test]
async fn max_bytes() {
+ // These tests use connections as unit to test the memory limit.
+ // Each connection consumes approximately 1MB of memory, so we give
+ // one connection as buffer for test stability (CONNECTION_LIMIT - 1) on line 35.
const CONNECTION_LIMIT: usize = 20;
- let max_allowed_bytes = CONNECTION_LIMIT * 1024 * 1024;
+ let max_allowed_bytes = (CONNECTION_LIMIT - 1) * 1024 * 1024;
let mut network = Swarm::new_ephemeral_tokio(|_| TestBehaviour {
connection_limits: Behaviour::with_max_bytes(max_allowed_bytes),
diff --git a/misc/memory-connection-limits/tests/max_percentage.rs b/misc/memory-connection-limits/tests/max_percentage.rs
index cddb29d6964..de1585eb279 100644
--- a/misc/memory-connection-limits/tests/max_percentage.rs
+++ b/misc/memory-connection-limits/tests/max_percentage.rs
@@ -59,7 +59,10 @@ async fn max_percentage() {
// Adds current mem usage to the limit and update
let current_mem = memory_stats::memory_stats().unwrap().physical_mem;
- let max_allowed_bytes = current_mem + CONNECTION_LIMIT * 1024 * 1024;
+ // These tests use connections as unit to test the memory limit.
+ // Each connection consumes approximately 1MB of memory, so we give
+ // one connection as buffer for test stability (CONNECTION_LIMIT - 1) on line 35.
+ let max_allowed_bytes = current_mem + (CONNECTION_LIMIT - 1) * 1024 * 1024;
network.behaviour_mut().connection_limits = Behaviour::with_max_percentage(
max_allowed_bytes as f64 / system_info.total_memory() as f64,
);
diff --git a/misc/metrics/CHANGELOG.md b/misc/metrics/CHANGELOG.md
index 7402b996267..bab690c4d98 100644
--- a/misc/metrics/CHANGELOG.md
+++ b/misc/metrics/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.17.1
+
+- Fix panic in swarm metrics when `ConnectionClosed` events are received for connections that were established before metrics collection started.
+ See [PR 6158](https://github.com/libp2p/rust-libp2p/pull/6158).
+
## 0.17.0
- Update `prometheus-client` to `0.23.0`.
@@ -6,6 +11,8 @@
- Add `ReservationClosed` as a relay metric.
See [PR 5869](https://github.com/libp2p/rust-libp2p/pull/5869).
+
+
## 0.16.0
diff --git a/misc/metrics/Cargo.toml b/misc/metrics/Cargo.toml
index 591a904aee3..32be1e8fdac 100644
--- a/misc/metrics/Cargo.toml
+++ b/misc/metrics/Cargo.toml
@@ -3,7 +3,7 @@ name = "libp2p-metrics"
edition.workspace = true
rust-version = { workspace = true }
description = "Metrics for libp2p"
-version = "0.17.0"
+version = "0.17.1"
authors = ["Max Inden "]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
diff --git a/misc/metrics/src/kad.rs b/misc/metrics/src/kad.rs
index 0a2a8038511..1b4d68f31ca 100644
--- a/misc/metrics/src/kad.rs
+++ b/misc/metrics/src/kad.rs
@@ -318,7 +318,6 @@ struct GetRecordResult {
#[derive(EncodeLabelValue, Hash, Clone, Eq, PartialEq, Debug)]
enum GetRecordError {
NotFound,
- QuorumFailed,
Timeout,
}
@@ -328,9 +327,6 @@ impl From<&libp2p_kad::GetRecordError> for GetRecordResult {
libp2p_kad::GetRecordError::NotFound { .. } => GetRecordResult {
error: GetRecordError::NotFound,
},
- libp2p_kad::GetRecordError::QuorumFailed { .. } => GetRecordResult {
- error: GetRecordError::QuorumFailed,
- },
libp2p_kad::GetRecordError::Timeout { .. } => GetRecordResult {
error: GetRecordError::Timeout,
},
diff --git a/misc/metrics/src/swarm.rs b/misc/metrics/src/swarm.rs
index 6e95d082de6..abd76691979 100644
--- a/misc/metrics/src/swarm.rs
+++ b/misc/metrics/src/swarm.rs
@@ -228,15 +228,20 @@ impl super::Recorder> for Metrics {
},
cause: cause.as_ref().map(Into::into),
};
- self.connections_duration.get_or_create(&labels).observe(
- self.connections
- .lock()
- .expect("lock not to be poisoned")
- .remove(connection_id)
- .expect("closed connection to previously be established")
- .elapsed()
- .as_secs_f64(),
- );
+
+ // Only record connection duration if we have a record of when it was established.
+ // This gracefully handles cases where ConnectionClosed events are received
+ // for connections that were established before metrics collection started.
+ if let Some(established_time) = self
+ .connections
+ .lock()
+ .expect("lock not to be poisoned")
+ .remove(connection_id)
+ {
+ self.connections_duration
+ .get_or_create(&labels)
+ .observe(established_time.elapsed().as_secs_f64());
+ }
}
SwarmEvent::IncomingConnection { send_back_addr, .. } => {
self.connections_incoming
@@ -453,3 +458,90 @@ impl From<&libp2p_swarm::ListenError> for IncomingConnectionError {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use std::time::Duration;
+
+ use libp2p_core::ConnectedPoint;
+ use libp2p_swarm::{ConnectionId, SwarmEvent};
+ use prometheus_client::registry::Registry;
+
+ use super::*;
+ use crate::Recorder;
+
+ #[test]
+ fn test_connection_closed_without_established() {
+ let mut registry = Registry::default();
+ let metrics = Metrics::new(&mut registry);
+
+ // Create a fake ConnectionClosed event for a connection that was never tracked.
+ let connection_id = ConnectionId::new_unchecked(1);
+ let endpoint = ConnectedPoint::Dialer {
+ address: "/ip4/127.0.0.1/tcp/8080".parse().unwrap(),
+ role_override: libp2p_core::Endpoint::Dialer,
+ port_use: libp2p_core::transport::PortUse::New,
+ };
+
+ let event = SwarmEvent::<()>::ConnectionClosed {
+ peer_id: libp2p_identity::PeerId::random(),
+ connection_id,
+ endpoint,
+ num_established: 0,
+ cause: None,
+ };
+
+ // This should NOT panic.
+ metrics.record(&event);
+
+ // Verify that the connections map is still empty (no connection was removed).
+ let connections = metrics.connections.lock().expect("lock not to be poisoned");
+ assert!(connections.is_empty());
+ }
+
+ #[test]
+ fn test_connection_established_then_closed() {
+ let mut registry = Registry::default();
+ let metrics = Metrics::new(&mut registry);
+
+ let connection_id = ConnectionId::new_unchecked(1);
+ let endpoint = ConnectedPoint::Dialer {
+ address: "/ip4/127.0.0.1/tcp/8080".parse().unwrap(),
+ role_override: libp2p_core::Endpoint::Dialer,
+ port_use: libp2p_core::transport::PortUse::New,
+ };
+
+ // First, establish a connection.
+ let established_event = SwarmEvent::<()>::ConnectionEstablished {
+ peer_id: libp2p_identity::PeerId::random(),
+ connection_id,
+ endpoint: endpoint.clone(),
+ num_established: std::num::NonZeroU32::new(1).unwrap(),
+ concurrent_dial_errors: None,
+ established_in: Duration::from_millis(100),
+ };
+
+ metrics.record(&established_event);
+
+ // Verify connection was added.
+ {
+ let connections = metrics.connections.lock().expect("lock not to be poisoned");
+ assert!(connections.contains_key(&connection_id));
+ }
+
+ // Now close the connection.
+ let closed_event = SwarmEvent::<()>::ConnectionClosed {
+ peer_id: libp2p_identity::PeerId::random(),
+ connection_id,
+ endpoint,
+ num_established: 0,
+ cause: None,
+ };
+
+ metrics.record(&closed_event);
+
+ // Verify connection was removed.
+ let connections = metrics.connections.lock().expect("lock not to be poisoned");
+ assert!(!connections.contains_key(&connection_id));
+ }
+}
diff --git a/misc/multistream-select/Cargo.toml b/misc/multistream-select/Cargo.toml
index 1b00218c514..2c2435e4566 100644
--- a/misc/multistream-select/Cargo.toml
+++ b/misc/multistream-select/Cargo.toml
@@ -19,11 +19,12 @@ smallvec = "1.13.2"
unsigned-varint = { workspace = true }
[dev-dependencies]
-async-std = { version = "1.6.2", features = ["attributes"] }
futures_ringbuf = "0.4.0"
quickcheck = { workspace = true }
rw-stream-sink = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
+tokio = { workspace = true, features = ["full"] }
+tokio-util = { version = "0.7", features = ["compat"] }
# 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/src/dialer_select.rs b/misc/multistream-select/src/dialer_select.rs
index 1d13e94910d..2c501a4f2f8 100644
--- a/misc/multistream-select/src/dialer_select.rs
+++ b/misc/multistream-select/src/dialer_select.rs
@@ -208,13 +208,16 @@ where
#[cfg(test)]
mod tests {
+
use std::time::Duration;
- use async_std::{
- future::timeout,
+ use quickcheck::{Arbitrary, Gen, GenRange};
+ use tokio::{
net::{TcpListener, TcpStream},
+ runtime::Runtime,
+ time::timeout,
};
- use quickcheck::{Arbitrary, Gen, GenRange};
+ use tokio_util::compat::TokioAsyncReadCompatExt;
use tracing::metadata::LevelFilter;
use tracing_subscriber::EnvFilter;
@@ -226,7 +229,7 @@ mod tests {
async fn run(version: Version) {
let (client_connection, server_connection) = futures_ringbuf::Endpoint::pair(100, 100);
- let server = async_std::task::spawn(async move {
+ let server = tokio::task::spawn(async move {
let protos = vec!["/proto1", "/proto2"];
let (proto, mut io) = listener_select_proto(server_connection, protos)
.await
@@ -242,7 +245,7 @@ mod tests {
io.flush().await.unwrap();
});
- let client = async_std::task::spawn(async move {
+ let client = tokio::task::spawn(async move {
let protos = vec!["/proto3", "/proto2"];
let (proto, mut io) = dialer_select_proto(client_connection, protos, version)
.await
@@ -258,12 +261,13 @@ mod tests {
assert_eq!(out, b"pong");
});
- server.await;
- client.await;
+ server.await.unwrap();
+ client.await.unwrap();
}
- async_std::task::block_on(run(Version::V1));
- async_std::task::block_on(run(Version::V1Lazy));
+ let rt = Runtime::new().unwrap();
+ rt.block_on(run(Version::V1));
+ rt.block_on(run(Version::V1Lazy));
}
/// Tests the expected behaviour of failed negotiations.
@@ -283,12 +287,13 @@ mod tests {
)
.try_init();
- async_std::task::block_on(async move {
+ let rt = Runtime::new().unwrap();
+ rt.block_on(async move {
let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
let addr = listener.local_addr().unwrap();
- let server = async_std::task::spawn(async move {
- let server_connection = listener.accept().await.unwrap().0;
+ let server = tokio::task::spawn(async move {
+ let server_connection = listener.accept().await.unwrap().0.compat();
let io = match timeout(
Duration::from_secs(2),
@@ -309,8 +314,8 @@ mod tests {
}
});
- let client = async_std::task::spawn(async move {
- let client_connection = TcpStream::connect(addr).await.unwrap();
+ let client = tokio::task::spawn(async move {
+ let client_connection = TcpStream::connect(addr).await.unwrap().compat();
let mut io = match timeout(
Duration::from_secs(2),
@@ -336,8 +341,8 @@ mod tests {
}
});
- server.await;
- client.await;
+ server.await.unwrap();
+ client.await.unwrap();
tracing::info!("---------------------------------------")
});
@@ -348,12 +353,12 @@ mod tests {
.quickcheck(prop as fn(_, _, _, _));
}
- #[async_std::test]
+ #[tokio::test]
async fn v1_lazy_do_not_wait_for_negotiation_on_poll_close() {
let (client_connection, _server_connection) =
futures_ringbuf::Endpoint::pair(1024 * 1024, 1);
- let client = async_std::task::spawn(async move {
+ let client = tokio::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, Version::V1Lazy)
@@ -366,9 +371,12 @@ mod tests {
io.close().await.unwrap();
});
- async_std::future::timeout(Duration::from_secs(10), client)
- .await
- .unwrap();
+ match tokio::time::timeout(Duration::from_secs(10), client).await {
+ Ok(join_result) => join_result.expect("Client task should complete successfully"),
+ Err(_elapsed) => {
+ panic!("Expected the client task to complete before timeout");
+ }
+ }
}
#[derive(Clone, Debug)]
diff --git a/misc/multistream-select/src/length_delimited.rs b/misc/multistream-select/src/length_delimited.rs
index 8062455de46..e9d76331796 100644
--- a/misc/multistream-select/src/length_delimited.rs
+++ b/misc/multistream-select/src/length_delimited.rs
@@ -388,6 +388,7 @@ mod tests {
use futures::{io::Cursor, prelude::*};
use quickcheck::*;
+ use tokio::runtime::Runtime;
use crate::length_delimited::LengthDelimited;
@@ -491,9 +492,10 @@ mod tests {
fn prop(frames: Vec>) -> TestResult {
let (client_connection, server_connection) = futures_ringbuf::Endpoint::pair(100, 100);
- async_std::task::block_on(async move {
+ let rt = Runtime::new().unwrap();
+ rt.block_on(async move {
let expected_frames = frames.clone();
- let server = async_std::task::spawn(async move {
+ let server = tokio::task::spawn(async move {
let mut connec =
rw_stream_sink::RwStreamSink::new(LengthDelimited::new(server_connection));
@@ -510,15 +512,15 @@ mod tests {
}
});
- let client = async_std::task::spawn(async move {
+ let client = tokio::task::spawn(async move {
let mut connec = LengthDelimited::new(client_connection);
for frame in frames {
connec.send(From::from(frame)).await.unwrap();
}
});
- server.await;
- client.await;
+ server.await.unwrap();
+ client.await.unwrap();
});
TestResult::passed()
diff --git a/misc/multistream-select/src/lib.rs b/misc/multistream-select/src/lib.rs
index 96432de6cb0..725c3a0e2ba 100644
--- a/misc/multistream-select/src/lib.rs
+++ b/misc/multistream-select/src/lib.rs
@@ -69,17 +69,19 @@
//! For a dialer:
//!
//! ```no_run
-//! use async_std::net::TcpStream;
//! use futures::prelude::*;
//! use multistream_select::{dialer_select_proto, Version};
+//! use tokio::{net::TcpStream, runtime::Runtime};
+//! use tokio_util::compat::TokioAsyncReadCompatExt;
//!
-//! async_std::task::block_on(async move {
+//! let rt = Runtime::new().unwrap();
+//! rt.block_on(async move {
//! let socket = TcpStream::connect("127.0.0.1:10333").await.unwrap();
+//! let compat_socket = socket.compat();
//!
//! let protos = vec!["/echo/1.0.0", "/echo/2.5.0"];
-//! let (protocol, _io) = dialer_select_proto(socket, protos, Version::V1)
-//! .await
-//! .unwrap();
+//! let result = dialer_select_proto(compat_socket, protos, Version::V1).await;
+//! let (protocol, _io) = result.unwrap();
//!
//! println!("Negotiated protocol: {:?}", protocol);
//! // You can now use `_io` to communicate with the remote.
@@ -116,7 +118,7 @@ pub enum Version {
///
/// This strategy is only applicable for the node with the role of "dialer"
/// in the negotiation and only if the dialer supports just a single
- /// application protocol. In that case the dialer immedidately "settles"
+ /// application protocol. In that case the dialer immediately "settles"
/// on that protocol, buffering the negotiation messages to be sent
/// with the first round of application protocol data (or an attempt
/// is made to read from the `Negotiated` I/O stream).
diff --git a/misc/peer-store/Cargo.toml b/misc/peer-store/Cargo.toml
index 5f6bf598cbf..490facb13fb 100644
--- a/misc/peer-store/Cargo.toml
+++ b/misc/peer-store/Cargo.toml
@@ -9,9 +9,9 @@ publish = false
rust-version.workspace = true
[dependencies]
+hashlink = { workspace = true }
libp2p-core = { workspace = true }
libp2p-swarm = { workspace = true }
-lru = "0.12.3"
libp2p-identity = { workspace = true, optional = true }
[dev-dependencies]
diff --git a/misc/peer-store/src/memory_store.rs b/misc/peer-store/src/memory_store.rs
index 8fbe31c6292..ed996dc6fdd 100644
--- a/misc/peer-store/src/memory_store.rs
+++ b/misc/peer-store/src/memory_store.rs
@@ -9,14 +9,14 @@
//! ```
use std::{
- collections::{HashMap, VecDeque},
+ collections::VecDeque,
num::NonZeroUsize,
task::{Poll, Waker},
};
+use hashlink::LruCache;
use libp2p_core::{Multiaddr, PeerId};
use libp2p_swarm::{behaviour::ConnectionEstablished, DialError, FromSwarm};
-use lru::LruCache;
use super::Store;
@@ -49,10 +49,9 @@ pub enum Event {
/// A in-memory store that uses LRU cache for bounded storage of addresses
/// and a frequency-based ordering of addresses.
-#[derive(Default)]
pub struct MemoryStore {
/// The internal store.
- records: HashMap>,
+ records: LruCache>,
/// Events to emit to [`Behaviour`](crate::Behaviour) and [`Swarm`](libp2p_swarm::Swarm).
pending_events: VecDeque,
/// Config of the store.
@@ -65,8 +64,8 @@ impl MemoryStore {
/// Create a new [`MemoryStore`] with the given config.
pub fn new(config: Config) -> Self {
Self {
+ records: LruCache::new(config.peer_capacity().get()),
config,
- records: HashMap::new(),
pending_events: VecDeque::default(),
waker: None,
}
@@ -96,7 +95,7 @@ impl MemoryStore {
let record = self
.records
.entry(*peer)
- .or_insert(PeerRecord::new(self.config.record_capacity));
+ .or_insert_with(|| PeerRecord::new(self.config.record_capacity));
let is_new = record.add_address(address, is_permanent);
if is_new {
self.push_event_and_wake(Event::PeerAddressAdded {
@@ -137,7 +136,7 @@ impl MemoryStore {
/// Get a reference to a peer's custom data.
pub fn get_custom_data(&self, peer: &PeerId) -> Option<&T> {
- self.records.get(peer).and_then(|r| r.get_custom_data())
+ self.records.peek(peer).and_then(|r| r.get_custom_data())
}
/// Take ownership of the internal data, leaving `None` in its place.
@@ -240,7 +239,7 @@ impl Store for MemoryStore {
}
fn addresses_of_peer(&self, peer: &PeerId) -> Option> {
- self.records.get(peer).map(|record| record.addresses())
+ self.records.peek(peer).map(|record| record.addresses())
}
fn poll(&mut self, cx: &mut std::task::Context<'_>) -> Poll {
@@ -257,6 +256,7 @@ impl Store for MemoryStore {
/// Config for [`MemoryStore`]. The available options are documented via their setters.
#[derive(Debug, Clone)]
pub struct Config {
+ peer_capacity: NonZeroUsize,
record_capacity: NonZeroUsize,
remove_addr_on_dial_error: bool,
}
@@ -264,6 +264,7 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Self {
+ peer_capacity: NonZeroUsize::try_from(1000).expect("1000 > 0"),
record_capacity: NonZeroUsize::try_from(8).expect("8 > 0"),
remove_addr_on_dial_error: true,
}
@@ -271,12 +272,24 @@ impl Default for Config {
}
impl Config {
+ pub fn peer_capacity(&self) -> &NonZeroUsize {
+ &self.peer_capacity
+ }
+ /// The capacity of the address store per peer.
+ ///
+ /// The least recently updated peer will be discarded to make room for a new peer.
+ ///
+ /// `1000` by default.
+ pub fn set_peer_capacity(mut self, capacity: NonZeroUsize) -> Self {
+ self.peer_capacity = capacity;
+ self
+ }
pub fn record_capacity(&self) -> &NonZeroUsize {
&self.record_capacity
}
- /// The capacity of an address store.
+ /// The capacity of the address store per peer.
///
- /// The least active address will be discarded to make room for new address.
+ /// The least active address will be discarded to make room for a new address.
///
/// `8` by default.
pub fn set_record_capacity(mut self, capacity: NonZeroUsize) -> Self {
@@ -317,7 +330,7 @@ pub struct PeerRecord {
impl PeerRecord {
pub(crate) fn new(cap: NonZeroUsize) -> Self {
Self {
- addresses: LruCache::new(cap),
+ addresses: LruCache::new(cap.get()),
custom_data: None,
}
}
@@ -325,7 +338,7 @@ impl PeerRecord {
/// Iterate over all addresses. More recently-used address comes first.
/// Does not change the order.
pub fn addresses(&self) -> impl Iterator- {
- self.addresses.iter().map(|(addr, _)| addr)
+ self.addresses.iter().rev().map(|(addr, _)| addr)
}
/// Update the address in the LRU cache, promote it to the front if it exists,
@@ -335,14 +348,13 @@ impl PeerRecord {
pub fn add_address(&mut self, address: &Multiaddr, is_permanent: bool) -> bool {
if let Some(was_permanent) = self.addresses.get(address) {
if !*was_permanent && is_permanent {
- self.addresses
- .get_or_insert(address.clone(), || is_permanent);
+ self.addresses.insert(address.clone(), is_permanent);
}
- return false;
+ false
+ } else {
+ self.addresses.insert(address.clone(), is_permanent);
+ true
}
- self.addresses
- .get_or_insert(address.clone(), || is_permanent);
- true
}
/// Remove the address in the LRU cache regardless of its position.
@@ -353,7 +365,7 @@ impl PeerRecord {
if !force && self.addresses.peek(address) == Some(&true) {
return false;
}
- self.addresses.pop(address).is_some()
+ self.addresses.remove(address).is_some()
}
pub fn get_custom_data(&self) -> Option<&T> {
@@ -399,14 +411,14 @@ mod test {
store.add_address(&peer, &addr1);
store.add_address(&peer, &addr2);
store.add_address(&peer, &addr3);
- assert!(
+ assert_eq!(
store
.records
.get(&peer)
.expect("peer to be in the store")
.addresses()
- .collect::>()
- == vec![&addr3, &addr2, &addr1]
+ .collect::>(),
+ vec![&addr3, &addr2, &addr1]
);
store.add_address(&peer, &addr1);
assert!(
diff --git a/multiaddr/.gitignore b/multiaddr/.gitignore
new file mode 100644
index 00000000000..5057d9ef273
--- /dev/null
+++ b/multiaddr/.gitignore
@@ -0,0 +1,3 @@
+target
+Cargo.lock
+*.rs.bk
diff --git a/multiaddr/CHANGELOG.md b/multiaddr/CHANGELOG.md
new file mode 100644
index 00000000000..349e539c9e3
--- /dev/null
+++ b/multiaddr/CHANGELOG.md
@@ -0,0 +1,183 @@
+# 0.18.3
+
+- Add `starts_with` on `Multiaddr`. See [PR 119].
+
+[PR 119]: https://github.com/multiformats/rust-multiaddr/pull/119
+
+# 0.18.2
+
+- Implement missing protocols. See [PR 110].
+ [PR 110]: https://github.com/multiformats/rust-multiaddr/pull/110.
+
+- Re-export `libp2p_identity::PeerId`. See [PR 108].
+ [PR 108]: https://github.com/multiformats/rust-multiaddr/pull/108.
+
+- Avoid allocations in Display and Debug of Multiaddr. See [PR 106].
+ [PR 106]: https://github.com/multiformats/rust-multiaddr/pull/106
+
+# 0.18.1
+
+- Add `with_p2p` on `Multiaddr`. See [PR 102].
+
+[PR 102]: https://github.com/multiformats/rust-multiaddr/pull/102
+
+# 0.18.0
+
+- Add `WebTransport` instance for `Multiaddr`. See [PR 70].
+
+- Disable all features of `multihash`. See [PR 77].
+
+- Mark `Protocol` as `#[non_exhaustive]`. See [PR 82].
+
+- Rename `Protocol::WebRTC` to `Protocol::WebRTCDirect`.
+ See [multiformats/multiaddr discussion] for context.
+ Remove deprecated support for `/webrtc` in favor of the existing `/webrtc-direct` string representation.
+ **Note that this is a breaking change.**
+
+- Make `/p2p` typesafe, i.e. have `Protocol::P2p` contain a `PeerId` instead of a `Multihash`.
+ See [PR 83].
+
+[multiformats/multiaddr discussion]: https://github.com/multiformats/multiaddr/pull/150#issuecomment-1468791586
+[PR 70]: https://github.com/multiformats/rust-multiaddr/pull/70
+[PR 77]: https://github.com/multiformats/rust-multiaddr/pull/77
+[PR 82]: https://github.com/multiformats/rust-multiaddr/pull/82
+[PR 83]: https://github.com/multiformats/rust-multiaddr/pull/83
+
+# 0.17.1
+
+- Rename string representation of `WebRTC` protocol from `/webrtc` to `/webrt-direct`.
+ For backwards compatibility `/webrtc` will still be decoded to `Protocol::WebRTC`, but `Protocol::WebRTC` will from now on always be encoded as `/webrtc-direct`.
+ See [multiformats/multiaddr discussion] and [PR 84] for context.
+ ``` rust
+ assert_eq!(
+ Multiaddr::empty().with(Protocol::WebRTC),
+ "/webrtc".parse().unwrap(),
+ );
+ assert_eq!(
+ Multiaddr::empty().with(Protocol::WebRTC),
+ "/webrtc-direct".parse().unwrap(),
+ );
+ assert_eq!(
+ "/webrtc-direct",
+ Multiaddr::empty().with(Protocol::WebRTC).to_string(),
+ );
+ assert_ne!(
+ "/webrtc",
+ Multiaddr::empty().with(Protocol::WebRTC).to_string(),
+ );
+ ```
+
+[PR 84]: https://github.com/multiformats/rust-multiaddr/pull/84
+
+# 0.17.0
+
+- Update to multihash `v0.17`. See [PR 63].
+
+[PR 63]: https://github.com/multiformats/rust-multiaddr/pull/63
+
+# 0.16.0 [2022-11-04]
+
+- Create `protocol_stack` for Multiaddr. See [PR 60].
+
+- Add `QuicV1` instance for `Multiaddr`. See [PR 64].
+
+[PR 60]: https://github.com/multiformats/rust-multiaddr/pull/60
+[PR 64]: https://github.com/multiformats/rust-multiaddr/pull/64
+
+# 0.15.0 [2022-10-20]
+
+- Add `WebRTC` instance for `Multiaddr`. See [PR 59].
+- Add `Certhash` instance for `Multiaddr`. See [PR 59].
+
+- Add support for Noise protocol. See [PR 53].
+
+- Use `multibase` instead of `bs58` for base58 encoding. See [PR 56].
+
+[PR 53]: https://github.com/multiformats/rust-multiaddr/pull/53
+[PR 56]: https://github.com/multiformats/rust-multiaddr/pull/56
+[PR 59]: https://github.com/multiformats/rust-multiaddr/pull/59
+
+# 0.14.0 [2022-02-02]
+
+- Add support for TLS protocol (see [PR 48]).
+
+- Update to `multihash` `v0.15` (see [PR 50]).
+
+- Update to `multihash` `v0.16` (see [PR 51]).
+
+[PR 48]: https://github.com/multiformats/rust-multiaddr/pull/48
+[PR 50]: https://github.com/multiformats/rust-multiaddr/pull/50
+[PR 50]: https://github.com/multiformats/rust-multiaddr/pull/51
+
+# 0.13.0 [2021-07-08]
+
+- Update to multihash v0.14.0 (see [PR 44]).
+
+- Update to rand v0.8.4 (see [PR 45]).
+
+[PR 44]: https://github.com/multiformats/rust-multiaddr/pull/44
+[PR 45]: https://github.com/multiformats/rust-multiaddr/pull/45
+
+# 0.12.0 [2021-05-26]
+
+- Merge [multiaddr] and [parity-multiaddr] (see [PR 40]).
+
+ - Functionality to go from a `u64` to a `multiaddr::Protocol` and back is
+ removed. Please open an issue on [multiaddr] in case this is still needed.
+
+ - Given that `multiaddr::Protocol` now represents both the protocol
+ identifier as well as the protocol data (e.g. protocol identifier `55`
+ (`dns6`) and protocol data `some-domain.example`) `multiaddr::Protocol` is
+ no longer `Copy`.
+
+[multiaddr]: https://github.com/multiformats/rust-multiaddr
+[parity-multiaddr]: https://github.com/libp2p/rust-libp2p/blob/master/misc/multiaddr/
+[PR 40]: https://github.com/multiformats/rust-multiaddr/pull/40
+
+# 0.11.2 [2021-03-17]
+
+- Add `Multiaddr::ends_with()`.
+
+# 0.11.1 [2021-02-15]
+
+- Update dependencies
+
+# 0.11.0 [2021-01-12]
+
+- Update dependencies
+
+# 0.10.1 [2021-01-12]
+
+- Fix compilation with serde-1.0.119.
+ [PR 1912](https://github.com/libp2p/rust-libp2p/pull/1912)
+
+# 0.10.0 [2020-11-25]
+
+- Upgrade multihash to `0.13`.
+
+# 0.9.6 [2020-11-17]
+
+- Move the `from_url` module and functionality behind the `url` feature,
+ enabled by default.
+ [PR 1843](https://github.com/libp2p/rust-libp2p/pull/1843).
+
+# 0.9.5 [2020-11-14]
+
+- Limit initial memory allocation in `visit_seq`.
+ [PR 1833](https://github.com/libp2p/rust-libp2p/pull/1833).
+
+# 0.9.4 [2020-11-09]
+
+- Update dependencies.
+
+# 0.9.3 [2020-10-16]
+
+- Update dependencies.
+
+# 0.9.2 [2020-08-31]
+
+- Add `Ord` instance for `Multiaddr`.
+
+# 0.9.1 [2020-06-22]
+
+- Updated dependencies.
diff --git a/multiaddr/Cargo.toml b/multiaddr/Cargo.toml
new file mode 100644
index 00000000000..1c257b39b00
--- /dev/null
+++ b/multiaddr/Cargo.toml
@@ -0,0 +1,42 @@
+[package]
+authors = ["dignifiedquire ", "Parity Technologies "]
+description = "Implementation of the multiaddr format"
+edition = "2021"
+rust-version = "1.59.0"
+repository = "https://github.com/multiformats/rust-multiaddr"
+keywords = ["multiaddr", "ipfs"]
+license = "MIT"
+name = "multiaddr"
+readme = "README.md"
+version = "0.18.3"
+
+[features]
+default = ["url"]
+
+[dependencies]
+arrayref = "0.3"
+byteorder = "1.5.0"
+bytes = "1.7.2"
+data-encoding = "2.6.0"
+multibase = "0.9.1"
+multihash = "0.19"
+percent-encoding = "2.3.1"
+serde = "1.0.209"
+static_assertions = "1.1"
+unsigned-varint = "0.8"
+url = { version = "2.5.0", optional = true, default-features = false }
+libp2p-identity = { workspace = true, version = "0.2.13", features = ["peerid"] }
+
+[dev-dependencies]
+bincode = "1"
+quickcheck = { version = "1.0.3", default-features = false }
+rand = "0.9.0"
+serde_json = "1.0"
+
+# 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
+
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(nightly)'] }
diff --git a/multiaddr/LICENSE b/multiaddr/LICENSE
new file mode 100644
index 00000000000..233fd7bd7d5
--- /dev/null
+++ b/multiaddr/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (C) 2015-2016 Friedel Ziegelmayer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+Status API Training Shop Blog About Pricing
diff --git a/multiaddr/README.md b/multiaddr/README.md
new file mode 100644
index 00000000000..f4766a24323
--- /dev/null
+++ b/multiaddr/README.md
@@ -0,0 +1,63 @@
+# rust-multiaddr
+
+[](http://ipn.io)
+[](https://github.com/multiformats/multiformats)
+[](https://webchat.freenode.net/?channels=%23ipfs)
+[](https://travis-ci.org/multiformats/rust-multiaddr)
+[](https://codecov.io/github/multiformats/rust-multiaddr?branch=master)
+[](https://docs.rs/crate/multiaddr)
+[](https://crates.io/crates/multiaddr)
+[](https://github.com/RichardLitt/standard-readme)
+
+
+> [multiaddr](https://github.com/multiformats/multiaddr) implementation in Rust.
+
+## Table of Contents
+
+- [Install](#install)
+- [Usage](#usage)
+- [Maintainers](#maintainers)
+- [Contribute](#contribute)
+- [License](#license)
+
+## Install
+
+First add this to your `Cargo.toml`
+
+```toml
+[dependencies]
+multiaddr = "*"
+```
+
+then run `cargo build`.
+
+## Usage
+
+```rust
+extern crate multiaddr;
+
+use multiaddr::{Multiaddr, multiaddr};
+
+let address = "/ip4/127.0.0.1/tcp/1234".parse::().unwrap();
+// or with a macro
+let other = multiaddr!(Ip4([127, 0, 0, 1]), Udp(10500u16), QuicV1);
+
+assert_eq!(address.to_string(), "/ip4/127.0.0.1/tcp/1234");
+assert_eq!(other.to_string(), "/ip4/127.0.0.1/udp/10500/quic-v1");
+```
+
+## Maintainers
+
+Captain: [@dignifiedquire](https://github.com/dignifiedquire).
+
+## Contribute
+
+Contributions welcome. Please check out [the issues](https://github.com/multiformats/rust-multiaddr/issues).
+
+Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
+
+Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
+
+## License
+
+[MIT](LICENSE) © 2015-2017 Friedel Ziegelmeyer
diff --git a/multiaddr/src/errors.rs b/multiaddr/src/errors.rs
new file mode 100644
index 00000000000..d3ded906a9e
--- /dev/null
+++ b/multiaddr/src/errors.rs
@@ -0,0 +1,92 @@
+use std::{error, fmt, io, net, num, str, string};
+use unsigned_varint::decode;
+
+pub type Result = ::std::result::Result;
+
+/// Error types
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum Error {
+ DataLessThanLen,
+ InvalidMultiaddr,
+ InvalidProtocolString,
+ InvalidUvar(decode::Error),
+ ParsingError(Box),
+ UnknownProtocolId(u32),
+ UnknownProtocolString(String),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Error::DataLessThanLen => f.write_str("we have less data than indicated by length"),
+ Error::InvalidMultiaddr => f.write_str("invalid multiaddr"),
+ Error::InvalidProtocolString => f.write_str("invalid protocol string"),
+ Error::InvalidUvar(e) => write!(f, "failed to decode unsigned varint: {e}"),
+ Error::ParsingError(e) => write!(f, "failed to parse: {e}"),
+ Error::UnknownProtocolId(id) => write!(f, "unknown protocol id: {id}"),
+ Error::UnknownProtocolString(string) => {
+ write!(f, "unknown protocol string: {string}")
+ }
+ }
+ }
+}
+
+impl error::Error for Error {
+ #[inline]
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ if let Error::ParsingError(e) = self {
+ Some(&**e)
+ } else {
+ None
+ }
+ }
+}
+
+impl From for Error {
+ fn from(err: io::Error) -> Error {
+ Error::ParsingError(err.into())
+ }
+}
+
+impl From for Error {
+ fn from(err: multihash::Error) -> Error {
+ Error::ParsingError(err.into())
+ }
+}
+
+impl From for Error {
+ fn from(err: multibase::Error) -> Error {
+ Error::ParsingError(err.into())
+ }
+}
+
+impl From for Error {
+ fn from(err: net::AddrParseError) -> Error {
+ Error::ParsingError(err.into())
+ }
+}
+
+impl From for Error {
+ fn from(err: num::ParseIntError) -> Error {
+ Error::ParsingError(err.into())
+ }
+}
+
+impl From for Error {
+ fn from(err: string::FromUtf8Error) -> Error {
+ Error::ParsingError(err.into())
+ }
+}
+
+impl From for Error {
+ fn from(err: str::Utf8Error) -> Error {
+ Error::ParsingError(err.into())
+ }
+}
+
+impl From for Error {
+ fn from(e: decode::Error) -> Error {
+ Error::InvalidUvar(e)
+ }
+}
diff --git a/multiaddr/src/from_url.rs b/multiaddr/src/from_url.rs
new file mode 100644
index 00000000000..ce5ae6d579f
--- /dev/null
+++ b/multiaddr/src/from_url.rs
@@ -0,0 +1,298 @@
+use crate::{Multiaddr, Protocol};
+use std::{error, fmt, iter, net::IpAddr};
+
+/// Attempts to parse an URL into a multiaddress.
+///
+/// This function will return an error if some information in the URL cannot be retained in the
+/// generated multiaddress. This includes a username, password, path (if not supported by the
+/// multiaddr), and query string.
+///
+/// This function is only present if the `url` feature is enabled, and it is
+/// enabled by default.
+///
+/// The supported URL schemes are:
+///
+/// - `ws://example.com/`
+/// - `wss://example.com/`
+/// - `http://example.com/`
+/// - `https://example.com/`
+/// - `unix:/foo/bar`
+///
+/// # Example
+///
+/// ```
+/// let addr = multiaddr::from_url("ws://127.0.0.1:8080/").unwrap();
+/// assert_eq!(addr, "/ip4/127.0.0.1/tcp/8080/ws".parse().unwrap());
+/// ```
+///
+pub fn from_url(url: &str) -> std::result::Result {
+ from_url_inner(url, false)
+}
+
+/// Attempts to parse an URL into a multiaddress. Ignores possible loss of information.
+///
+/// This function is similar to [`from_url`], except that we don't return an error if some
+/// information in the URL cannot be retain in the generated multiaddress.
+///
+/// This function is only present if the `url` feature is enabled, and it is
+/// enabled by default.
+///
+/// # Example
+///
+/// ```
+/// let addr = "ws://user:pass@127.0.0.1:8080/";
+/// assert!(multiaddr::from_url(addr).is_err());
+/// assert!(multiaddr::from_url_lossy(addr).is_ok());
+/// ```
+///
+pub fn from_url_lossy(url: &str) -> std::result::Result {
+ from_url_inner(url, true)
+}
+
+/// Underlying implementation of `from_url` and `from_url_lossy`.
+fn from_url_inner(url: &str, lossy: bool) -> std::result::Result {
+ let url = url::Url::parse(url).map_err(|_| FromUrlErr::BadUrl)?;
+
+ match url.scheme() {
+ // Note: if you add support for a new scheme, please update the documentation as well.
+ "ws" | "wss" | "http" | "https" => from_url_inner_http_ws(url, lossy),
+ "unix" => from_url_inner_path(url, lossy),
+ _ => Err(FromUrlErr::UnsupportedScheme),
+ }
+}
+
+/// Called when `url.scheme()` is an Internet-like URL.
+fn from_url_inner_http_ws(
+ url: url::Url,
+ lossy: bool,
+) -> std::result::Result {
+ let (protocol, lost_path, default_port) = match url.scheme() {
+ "ws" => (Protocol::Ws(url.path().to_owned().into()), false, 80),
+ "wss" => (Protocol::Wss(url.path().to_owned().into()), false, 443),
+ "http" => (Protocol::Http, true, 80),
+ "https" => (Protocol::Https, true, 443),
+ _ => unreachable!("We only call this function for one of the given schemes; qed"),
+ };
+
+ let port = Protocol::Tcp(url.port().unwrap_or(default_port));
+ let ip = if let Some(hostname) = url.host_str() {
+ if let Ok(ip) = hostname.parse::() {
+ Protocol::from(ip)
+ } else {
+ Protocol::Dns(hostname.into())
+ }
+ } else {
+ return Err(FromUrlErr::BadUrl);
+ };
+
+ if !lossy
+ && (!url.username().is_empty()
+ || url.password().is_some()
+ || (lost_path && url.path() != "/" && !url.path().is_empty())
+ || url.query().is_some()
+ || url.fragment().is_some())
+ {
+ return Err(FromUrlErr::InformationLoss);
+ }
+
+ Ok(iter::once(ip)
+ .chain(iter::once(port))
+ .chain(iter::once(protocol))
+ .collect())
+}
+
+/// Called when `url.scheme()` is a path-like URL.
+fn from_url_inner_path(url: url::Url, lossy: bool) -> std::result::Result {
+ let protocol = match url.scheme() {
+ "unix" => Protocol::Unix(url.path().to_owned().into()),
+ _ => unreachable!("We only call this function for one of the given schemes; qed"),
+ };
+
+ if !lossy
+ && (!url.username().is_empty()
+ || url.password().is_some()
+ || url.query().is_some()
+ || url.fragment().is_some())
+ {
+ return Err(FromUrlErr::InformationLoss);
+ }
+
+ Ok(Multiaddr::from(protocol))
+}
+
+/// Error while parsing an URL.
+#[derive(Debug)]
+pub enum FromUrlErr {
+ /// Failed to parse the URL.
+ BadUrl,
+ /// The URL scheme was not recognized.
+ UnsupportedScheme,
+ /// Some information in the URL would be lost. Never returned by `from_url_lossy`.
+ InformationLoss,
+}
+
+impl fmt::Display for FromUrlErr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ FromUrlErr::BadUrl => write!(f, "Bad URL"),
+ FromUrlErr::UnsupportedScheme => write!(f, "Unrecognized URL scheme"),
+ FromUrlErr::InformationLoss => write!(f, "Some information in the URL would be lost"),
+ }
+ }
+}
+
+impl error::Error for FromUrlErr {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn parse_garbage_doesnt_panic() {
+ for _ in 0..50 {
+ let url = (0..16).map(|_| rand::random::()).collect::>();
+ let url = String::from_utf8_lossy(&url);
+ assert!(from_url(&url).is_err());
+ }
+ }
+
+ #[test]
+ fn normal_usage_ws() {
+ let addr = from_url("ws://127.0.0.1:8000").unwrap();
+ assert_eq!(addr, "/ip4/127.0.0.1/tcp/8000/ws".parse().unwrap());
+ }
+
+ #[test]
+ fn normal_usage_wss() {
+ let addr = from_url("wss://127.0.0.1:8000").unwrap();
+ assert_eq!(addr, "/ip4/127.0.0.1/tcp/8000/wss".parse().unwrap());
+ }
+
+ #[test]
+ fn default_ws_port() {
+ let addr = from_url("ws://127.0.0.1").unwrap();
+ assert_eq!(addr, "/ip4/127.0.0.1/tcp/80/ws".parse().unwrap());
+ }
+
+ #[test]
+ fn default_http_port() {
+ let addr = from_url("http://127.0.0.1").unwrap();
+ assert_eq!(addr, "/ip4/127.0.0.1/tcp/80/http".parse().unwrap());
+ }
+
+ #[test]
+ fn default_wss_port() {
+ let addr = from_url("wss://127.0.0.1").unwrap();
+ assert_eq!(addr, "/ip4/127.0.0.1/tcp/443/wss".parse().unwrap());
+ }
+
+ #[test]
+ fn default_https_port() {
+ let addr = from_url("https://127.0.0.1").unwrap();
+ assert_eq!(addr, "/ip4/127.0.0.1/tcp/443/https".parse().unwrap());
+ }
+
+ #[test]
+ fn dns_addr_ws() {
+ let addr = from_url("ws://example.com").unwrap();
+ assert_eq!(addr, "/dns/example.com/tcp/80/ws".parse().unwrap());
+ }
+
+ #[test]
+ fn dns_addr_http() {
+ let addr = from_url("http://example.com").unwrap();
+ assert_eq!(addr, "/dns/example.com/tcp/80/http".parse().unwrap());
+ }
+
+ #[test]
+ fn dns_addr_wss() {
+ let addr = from_url("wss://example.com").unwrap();
+ assert_eq!(addr, "/dns/example.com/tcp/443/wss".parse().unwrap());
+ }
+
+ #[test]
+ fn dns_addr_https() {
+ let addr = from_url("https://example.com").unwrap();
+ assert_eq!(addr, "/dns/example.com/tcp/443/https".parse().unwrap());
+ }
+
+ #[test]
+ fn bad_hostname() {
+ let addr = from_url("wss://127.0.0.1x").unwrap();
+ assert_eq!(addr, "/dns/127.0.0.1x/tcp/443/wss".parse().unwrap());
+ }
+
+ #[test]
+ fn wrong_scheme() {
+ match from_url("foo://127.0.0.1") {
+ Err(FromUrlErr::UnsupportedScheme) => {}
+ _ => panic!(),
+ }
+ }
+
+ #[test]
+ fn dns_and_port() {
+ let addr = from_url("http://example.com:1000").unwrap();
+ assert_eq!(addr, "/dns/example.com/tcp/1000/http".parse().unwrap());
+ }
+
+ #[test]
+ fn username_lossy() {
+ let addr = "http://foo@example.com:1000/";
+ assert!(from_url(addr).is_err());
+ assert!(from_url_lossy(addr).is_ok());
+ assert!(from_url("http://@example.com:1000/").is_ok());
+ }
+
+ #[test]
+ fn password_lossy() {
+ let addr = "http://:bar@example.com:1000/";
+ assert!(from_url(addr).is_err());
+ assert!(from_url_lossy(addr).is_ok());
+ }
+
+ #[test]
+ fn path_lossy() {
+ let addr = "http://example.com:1000/foo";
+ assert!(from_url(addr).is_err());
+ assert!(from_url_lossy(addr).is_ok());
+ }
+
+ #[test]
+ fn fragment_lossy() {
+ let addr = "http://example.com:1000/#foo";
+ assert!(from_url(addr).is_err());
+ assert!(from_url_lossy(addr).is_ok());
+ }
+
+ #[test]
+ fn unix() {
+ let addr = from_url("unix:/foo/bar").unwrap();
+ assert_eq!(addr, Multiaddr::from(Protocol::Unix("/foo/bar".into())));
+ }
+
+ #[test]
+ fn ws_path() {
+ let addr = from_url("ws://1.2.3.4:1000/foo/bar").unwrap();
+ assert_eq!(
+ addr,
+ "/ip4/1.2.3.4/tcp/1000/x-parity-ws/%2ffoo%2fbar"
+ .parse()
+ .unwrap()
+ );
+
+ let addr = from_url("ws://1.2.3.4:1000/").unwrap();
+ assert_eq!(addr, "/ip4/1.2.3.4/tcp/1000/ws".parse().unwrap());
+
+ let addr = from_url("wss://1.2.3.4:1000/foo/bar").unwrap();
+ assert_eq!(
+ addr,
+ "/ip4/1.2.3.4/tcp/1000/x-parity-wss/%2ffoo%2fbar"
+ .parse()
+ .unwrap()
+ );
+
+ let addr = from_url("wss://1.2.3.4:1000").unwrap();
+ assert_eq!(addr, "/ip4/1.2.3.4/tcp/1000/wss".parse().unwrap());
+ }
+}
diff --git a/multiaddr/src/lib.rs b/multiaddr/src/lib.rs
new file mode 100644
index 00000000000..b6b0ad4c9de
--- /dev/null
+++ b/multiaddr/src/lib.rs
@@ -0,0 +1,511 @@
+//! Implementation of [multiaddr](https://github.com/multiformats/multiaddr) in Rust.
+#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
+
+pub use multihash;
+
+mod errors;
+mod onion_addr;
+mod protocol;
+
+#[cfg(feature = "url")]
+mod from_url;
+
+pub use self::errors::{Error, Result};
+pub use self::onion_addr::Onion3Addr;
+pub use self::protocol::Protocol;
+use bytes::{BufMut, Bytes, BytesMut};
+use serde::{
+ de::{self, Error as DeserializerError},
+ Deserialize, Deserializer, Serialize, Serializer,
+};
+use std::{
+ convert::TryFrom,
+ fmt,
+ iter::FromIterator,
+ net::{IpAddr, Ipv4Addr, Ipv6Addr},
+ result::Result as StdResult,
+ str::FromStr,
+};
+
+pub use libp2p_identity::PeerId;
+
+#[cfg(feature = "url")]
+pub use self::from_url::{from_url, from_url_lossy, FromUrlErr};
+
+static_assertions::const_assert! {
+ // This check is most certainly overkill right now, but done here
+ // anyway to ensure the `as u64` casts in this crate are safe.
+ std::mem::size_of::() <= std::mem::size_of::()
+}
+
+/// Representation of a Multiaddr.
+#[allow(clippy::rc_buffer)]
+#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
+pub struct Multiaddr {
+ bytes: Bytes,
+}
+
+impl Multiaddr {
+ /// Create a new, empty multiaddress.
+ pub fn empty() -> Self {
+ Self {
+ bytes: Bytes::new(),
+ }
+ }
+
+ /// Create a new, empty multiaddress with the given capacity.
+ pub fn with_capacity(n: usize) -> Self {
+ Self {
+ bytes: BytesMut::with_capacity(n).freeze(),
+ }
+ }
+
+ /// Return the length in bytes of this multiaddress.
+ pub fn len(&self) -> usize {
+ self.bytes.len()
+ }
+
+ /// Returns true if the length of this multiaddress is 0.
+ pub fn is_empty(&self) -> bool {
+ self.bytes.len() == 0
+ }
+
+ /// Return a copy of this [`Multiaddr`]'s byte representation.
+ pub fn to_vec(&self) -> Vec {
+ Vec::from(&self.bytes[..])
+ }
+
+ /// Adds an already-parsed address component to the end of this multiaddr.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use multiaddr::{Multiaddr, Protocol};
+ ///
+ /// let mut address: Multiaddr = "/ip4/127.0.0.1".parse().unwrap();
+ /// address.push(Protocol::Tcp(10000));
+ /// assert_eq!(address, "/ip4/127.0.0.1/tcp/10000".parse().unwrap());
+ /// ```
+ ///
+ pub fn push(&mut self, p: Protocol<'_>) {
+ let mut bytes = BytesMut::from(std::mem::take(&mut self.bytes));
+ p.write_bytes(&mut (&mut bytes).writer())
+ .expect("Writing to a `BytesMut` never fails.");
+ self.bytes = bytes.freeze();
+ }
+
+ /// Pops the last `Protocol` of this multiaddr, or `None` if the multiaddr is empty.
+ /// ```
+ /// use multiaddr::{Multiaddr, Protocol};
+ ///
+ /// let mut address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap();
+ ///
+ /// assert_eq!(address.pop().unwrap(), Protocol::Sctp(5678));
+ /// assert_eq!(address.pop().unwrap(), Protocol::Udt);
+ /// ```
+ ///
+ pub fn pop<'a>(&mut self) -> Option> {
+ let mut slice = &self.bytes[..]; // the remaining multiaddr slice
+ if slice.is_empty() {
+ return None;
+ }
+ let protocol = loop {
+ let (p, s) = Protocol::from_bytes(slice).expect("`slice` is a valid `Protocol`.");
+ if s.is_empty() {
+ break p.acquire();
+ }
+ slice = s
+ };
+ let remaining_len = self.len() - slice.len();
+ let mut bytes = BytesMut::from(std::mem::take(&mut self.bytes));
+ bytes.truncate(remaining_len);
+ self.bytes = bytes.freeze();
+ Some(protocol)
+ }
+
+ /// Like [`Multiaddr::push`] but consumes `self`.
+ pub fn with(mut self, p: Protocol<'_>) -> Self {
+ let mut bytes = BytesMut::from(std::mem::take(&mut self.bytes));
+ p.write_bytes(&mut (&mut bytes).writer())
+ .expect("Writing to a `BytesMut` never fails.");
+ self.bytes = bytes.freeze();
+ self
+ }
+
+ /// Appends the given [`PeerId`] if not yet present at the end of this multiaddress.
+ ///
+ /// Fails if this address ends in a _different_ [`PeerId`].
+ /// In that case, the original, unmodified address is returned.
+ pub fn with_p2p(self, peer: PeerId) -> std::result::Result {
+ match self.iter().last() {
+ Some(Protocol::P2p(p)) if p == peer => Ok(self),
+ Some(Protocol::P2p(_)) => Err(self),
+ _ => Ok(self.with(Protocol::P2p(peer))),
+ }
+ }
+
+ /// Returns the components of this multiaddress.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use std::net::Ipv4Addr;
+ /// use multiaddr::{Multiaddr, Protocol};
+ ///
+ /// let address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap();
+ ///
+ /// let components = address.iter().collect::>();
+ /// assert_eq!(components[0], Protocol::Ip4(Ipv4Addr::new(127, 0, 0, 1)));
+ /// assert_eq!(components[1], Protocol::Udt);
+ /// assert_eq!(components[2], Protocol::Sctp(5678));
+ /// ```
+ ///
+ pub fn iter(&self) -> Iter<'_> {
+ Iter(&self.bytes)
+ }
+
+ /// Replace a [`Protocol`] at some position in this `Multiaddr`.
+ ///
+ /// The parameter `at` denotes the index of the protocol at which the function
+ /// `by` will be applied to the current protocol, returning an optional replacement.
+ ///
+ /// If `at` is out of bounds or `by` does not yield a replacement value,
+ /// `None` will be returned. Otherwise a copy of this `Multiaddr` with the
+ /// updated `Protocol` at position `at` will be returned.
+ pub fn replace<'a, F>(&self, at: usize, by: F) -> Option
+ where
+ F: FnOnce(&Protocol<'_>) -> Option>,
+ {
+ let mut address = Multiaddr::with_capacity(self.len());
+ let mut fun = Some(by);
+ let mut replaced = false;
+
+ for (i, p) in self.iter().enumerate() {
+ if i == at {
+ let f = fun.take().expect("i == at only happens once");
+ if let Some(q) = f(&p) {
+ address = address.with(q);
+ replaced = true;
+ continue;
+ }
+ return None;
+ }
+ address = address.with(p)
+ }
+
+ if replaced {
+ Some(address)
+ } else {
+ None
+ }
+ }
+
+ /// Checks whether the given `Multiaddr` is a suffix of this `Multiaddr`.
+ pub fn ends_with(&self, other: &Multiaddr) -> bool {
+ let n = self.bytes.len();
+ let m = other.bytes.len();
+ if n < m {
+ return false;
+ }
+ self.bytes[(n - m)..] == other.bytes[..]
+ }
+
+ /// Checks whether the given `Multiaddr` is a prefix of this `Multiaddr`.
+ pub fn starts_with(&self, other: &Multiaddr) -> bool {
+ let n = self.bytes.len();
+ let m = other.bytes.len();
+ if n < m {
+ return false;
+ }
+ self.bytes[..m] == other.bytes[..]
+ }
+
+ /// Returns &str identifiers for the protocol names themselves.
+ /// This omits specific info like addresses, ports, peer IDs, and the like.
+ /// Example: `"/ip4/127.0.0.1/tcp/5001"` would return `["ip4", "tcp"]`
+ pub fn protocol_stack(&self) -> ProtoStackIter {
+ ProtoStackIter { parts: self.iter() }
+ }
+}
+
+impl fmt::Debug for Multiaddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+}
+
+impl fmt::Display for Multiaddr {
+ /// Convert a Multiaddr to a string
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use multiaddr::Multiaddr;
+ ///
+ /// let address: Multiaddr = "/ip4/127.0.0.1/udt".parse().unwrap();
+ /// assert_eq!(address.to_string(), "/ip4/127.0.0.1/udt");
+ /// ```
+ ///
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ for s in self.iter() {
+ s.fmt(f)?;
+ }
+ Ok(())
+ }
+}
+
+impl AsRef<[u8]> for Multiaddr {
+ fn as_ref(&self) -> &[u8] {
+ self.bytes.as_ref()
+ }
+}
+
+impl<'a> IntoIterator for &'a Multiaddr {
+ type Item = Protocol<'a>;
+ type IntoIter = Iter<'a>;
+
+ fn into_iter(self) -> Iter<'a> {
+ Iter(&self.bytes)
+ }
+}
+
+impl<'a> FromIterator> for Multiaddr {
+ fn from_iter(iter: T) -> Self
+ where
+ T: IntoIterator
- >,
+ {
+ let mut bytes = BytesMut::new();
+ for cmp in iter {
+ cmp.write_bytes(&mut (&mut bytes).writer())
+ .expect("Writing to a `BytesMut` never fails.");
+ }
+ Multiaddr {
+ bytes: bytes.freeze(),
+ }
+ }
+}
+
+impl FromStr for Multiaddr {
+ type Err = Error;
+
+ fn from_str(input: &str) -> Result {
+ let mut bytes = BytesMut::new();
+ let mut parts = input.split('/').peekable();
+
+ if Some("") != parts.next() {
+ // A multiaddr must start with `/`
+ return Err(Error::InvalidMultiaddr);
+ }
+
+ while parts.peek().is_some() {
+ let p = Protocol::from_str_parts(&mut parts)?;
+ p.write_bytes(&mut (&mut bytes).writer())
+ .expect("Writing to a `BytesMut` never fails.");
+ }
+
+ Ok(Multiaddr {
+ bytes: bytes.freeze(),
+ })
+ }
+}
+
+/// Iterator over `Multiaddr` [`Protocol`]s.
+pub struct Iter<'a>(&'a [u8]);
+
+impl<'a> Iterator for Iter<'a> {
+ type Item = Protocol<'a>;
+
+ fn next(&mut self) -> Option {
+ if self.0.is_empty() {
+ return None;
+ }
+
+ let (p, next_data) =
+ Protocol::from_bytes(self.0).expect("`Multiaddr` is known to be valid.");
+
+ self.0 = next_data;
+ Some(p)
+ }
+}
+
+/// Iterator over the string identifiers of the protocols (not addrs) in a multiaddr
+pub struct ProtoStackIter<'a> {
+ parts: Iter<'a>,
+}
+
+impl Iterator for ProtoStackIter<'_> {
+ type Item = &'static str;
+ fn next(&mut self) -> Option {
+ self.parts.next().as_ref().map(Protocol::tag)
+ }
+}
+
+impl<'a> From> for Multiaddr {
+ fn from(p: Protocol<'a>) -> Multiaddr {
+ let mut bytes = BytesMut::new();
+ p.write_bytes(&mut (&mut bytes).writer())
+ .expect("Writing to a `BytesMut` never fails.");
+ Multiaddr {
+ bytes: bytes.freeze(),
+ }
+ }
+}
+
+impl From for Multiaddr {
+ fn from(v: IpAddr) -> Multiaddr {
+ match v {
+ IpAddr::V4(a) => a.into(),
+ IpAddr::V6(a) => a.into(),
+ }
+ }
+}
+
+impl From for Multiaddr {
+ fn from(v: Ipv4Addr) -> Multiaddr {
+ Protocol::Ip4(v).into()
+ }
+}
+
+impl From for Multiaddr {
+ fn from(v: Ipv6Addr) -> Multiaddr {
+ Protocol::Ip6(v).into()
+ }
+}
+
+impl TryFrom> for Multiaddr {
+ type Error = Error;
+
+ fn try_from(v: Vec) -> Result {
+ // Check if the argument is a valid `Multiaddr` by reading its protocols.
+ let mut slice = &v[..];
+ while !slice.is_empty() {
+ let (_, s) = Protocol::from_bytes(slice)?;
+ slice = s
+ }
+ Ok(Multiaddr {
+ bytes: Bytes::from(v),
+ })
+ }
+}
+
+impl TryFrom for Multiaddr {
+ type Error = Error;
+
+ fn try_from(s: String) -> Result {
+ s.parse()
+ }
+}
+
+impl<'a> TryFrom<&'a str> for Multiaddr {
+ type Error = Error;
+
+ fn try_from(s: &'a str) -> Result {
+ s.parse()
+ }
+}
+
+impl Serialize for Multiaddr {
+ fn serialize
(&self, serializer: S) -> StdResult
+ where
+ S: Serializer,
+ {
+ if serializer.is_human_readable() {
+ serializer.serialize_str(&self.to_string())
+ } else {
+ serializer.serialize_bytes(self.as_ref())
+ }
+ }
+}
+
+impl<'de> Deserialize<'de> for Multiaddr {
+ fn deserialize(deserializer: D) -> StdResult
+ where
+ D: Deserializer<'de>,
+ {
+ struct Visitor {
+ is_human_readable: bool,
+ }
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = Multiaddr;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("multiaddress")
+ }
+ fn visit_seq>(
+ self,
+ mut seq: A,
+ ) -> StdResult {
+ let mut buf: Vec =
+ Vec::with_capacity(std::cmp::min(seq.size_hint().unwrap_or(0), 4096));
+ while let Some(e) = seq.next_element()? {
+ buf.push(e);
+ }
+ if self.is_human_readable {
+ let s = String::from_utf8(buf).map_err(DeserializerError::custom)?;
+ s.parse().map_err(DeserializerError::custom)
+ } else {
+ Multiaddr::try_from(buf).map_err(DeserializerError::custom)
+ }
+ }
+ fn visit_str(self, v: &str) -> StdResult {
+ v.parse().map_err(DeserializerError::custom)
+ }
+ fn visit_borrowed_str(self, v: &'de str) -> StdResult {
+ self.visit_str(v)
+ }
+ fn visit_string(self, v: String) -> StdResult {
+ self.visit_str(&v)
+ }
+ fn visit_bytes(self, v: &[u8]) -> StdResult {
+ self.visit_byte_buf(v.into())
+ }
+ fn visit_borrowed_bytes(self, v: &'de [u8]) -> StdResult {
+ self.visit_byte_buf(v.into())
+ }
+ fn visit_byte_buf(self, v: Vec) -> StdResult {
+ Multiaddr::try_from(v).map_err(DeserializerError::custom)
+ }
+ }
+
+ if deserializer.is_human_readable() {
+ deserializer.deserialize_str(Visitor {
+ is_human_readable: true,
+ })
+ } else {
+ deserializer.deserialize_bytes(Visitor {
+ is_human_readable: false,
+ })
+ }
+ }
+}
+
+/// Easy way for a user to create a `Multiaddr`.
+///
+/// Example:
+///
+/// ```rust
+/// # use multiaddr::multiaddr;
+/// let addr = multiaddr!(Ip4([127, 0, 0, 1]), Tcp(10500u16));
+/// ```
+///
+/// Each element passed to `multiaddr!` should be a variant of the `Protocol` enum. The
+/// optional parameter is turned into the proper type with the `Into` trait.
+///
+/// For example, `Ip4([127, 0, 0, 1])` works because `Ipv4Addr` implements `From<[u8; 4]>`.
+#[macro_export]
+macro_rules! multiaddr {
+ ($($comp:ident $(($param:expr))*),+) => {
+ {
+ use std::iter;
+ let elem = iter::empty::<$crate::Protocol>();
+ $(
+ let elem = {
+ let cmp = $crate::Protocol::$comp $(( $param.into() ))*;
+ elem.chain(iter::once(cmp))
+ };
+ )+
+ elem.collect::<$crate::Multiaddr>()
+ }
+ }
+}
diff --git a/multiaddr/src/onion_addr.rs b/multiaddr/src/onion_addr.rs
new file mode 100644
index 00000000000..38004523e0b
--- /dev/null
+++ b/multiaddr/src/onion_addr.rs
@@ -0,0 +1,51 @@
+use std::{borrow::Cow, fmt};
+
+/// Represents an Onion v3 address
+#[derive(Clone)]
+pub struct Onion3Addr<'a>(Cow<'a, [u8; 35]>, u16);
+
+impl Onion3Addr<'_> {
+ /// Return the hash of the public key as bytes
+ pub fn hash(&self) -> &[u8; 35] {
+ self.0.as_ref()
+ }
+
+ /// Return the port
+ pub fn port(&self) -> u16 {
+ self.1
+ }
+
+ /// Consume this instance and create an owned version containing the same address
+ pub fn acquire<'b>(self) -> Onion3Addr<'b> {
+ Onion3Addr(Cow::Owned(self.0.into_owned()), self.1)
+ }
+}
+
+impl PartialEq for Onion3Addr<'_> {
+ fn eq(&self, other: &Self) -> bool {
+ self.1 == other.1 && self.0[..] == other.0[..]
+ }
+}
+
+impl Eq for Onion3Addr<'_> {}
+
+impl From<([u8; 35], u16)> for Onion3Addr<'_> {
+ fn from(parts: ([u8; 35], u16)) -> Self {
+ Self(Cow::Owned(parts.0), parts.1)
+ }
+}
+
+impl<'a> From<(&'a [u8; 35], u16)> for Onion3Addr<'a> {
+ fn from(parts: (&'a [u8; 35], u16)) -> Self {
+ Self(Cow::Borrowed(parts.0), parts.1)
+ }
+}
+
+impl fmt::Debug for Onion3Addr<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ f.debug_tuple("Onion3Addr")
+ .field(&format!("{:02x?}", &self.0[..]))
+ .field(&self.1)
+ .finish()
+ }
+}
diff --git a/multiaddr/src/protocol.rs b/multiaddr/src/protocol.rs
new file mode 100644
index 00000000000..f0fa1d40fef
--- /dev/null
+++ b/multiaddr/src/protocol.rs
@@ -0,0 +1,867 @@
+use crate::onion_addr::Onion3Addr;
+use crate::{Error, PeerId, Result};
+use arrayref::array_ref;
+use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
+use data_encoding::BASE32;
+use std::{
+ borrow::Cow,
+ convert::From,
+ fmt,
+ io::{Cursor, Write},
+ net::{IpAddr, Ipv4Addr, Ipv6Addr},
+ str::{self, FromStr},
+};
+use unsigned_varint::{decode, encode};
+
+// All the values are obtained by converting hexadecimal protocol codes to u32.
+// Protocols as well as their corresponding codes are defined in
+// https://github.com/multiformats/multiaddr/blob/master/protocols.csv .
+const DCCP: u32 = 33;
+const DNS: u32 = 53;
+const DNS4: u32 = 54;
+const DNS6: u32 = 55;
+const DNSADDR: u32 = 56;
+const HTTP: u32 = 480;
+const HTTPS: u32 = 443; // Deprecated - alias for /tls/http
+const IP4: u32 = 4;
+const IP6: u32 = 41;
+const P2P_WEBRTC_DIRECT: u32 = 276; // Deprecated
+const P2P_WEBRTC_STAR: u32 = 275; // Deprecated
+const WEBRTC_DIRECT: u32 = 280;
+const CERTHASH: u32 = 466;
+const P2P_WEBSOCKET_STAR: u32 = 479; // Deprecated
+const MEMORY: u32 = 777;
+const ONION: u32 = 444;
+const ONION3: u32 = 445;
+const P2P: u32 = 421;
+const P2P_CIRCUIT: u32 = 290;
+const QUIC: u32 = 460;
+const QUIC_V1: u32 = 461;
+const SCTP: u32 = 132;
+const TCP: u32 = 6;
+const TLS: u32 = 448;
+const NOISE: u32 = 454;
+const UDP: u32 = 273;
+const UDT: u32 = 301;
+const UNIX: u32 = 400;
+const UTP: u32 = 302;
+const WEBTRANSPORT: u32 = 465;
+const WS: u32 = 477;
+const WS_WITH_PATH: u32 = 4770; // Note: not standard
+const WSS: u32 = 478; // Deprecated - alias for /tls/ws
+const WSS_WITH_PATH: u32 = 4780; // Note: not standard
+const IP6ZONE: u32 = 42;
+const IPCIDR: u32 = 43;
+// const IPFS: u32 = 421; // Deprecated
+const GARLIC64: u32 = 446;
+const GARLIC32: u32 = 447;
+const SNI: u32 = 449;
+const P2P_STARDUST: u32 = 277; // Deprecated
+const WEBRTC: u32 = 281;
+const HTTP_PATH: u32 = 481;
+
+/// Type-alias for how multi-addresses use `Multihash`.
+///
+/// The `64` defines the allocation size for the digest within the `Multihash`.
+/// This allows us to use hashes such as SHA512.
+/// In case protocols like `/certhash` ever support hashes larger than that, we will need to update this size here (which will be a breaking change!).
+type Multihash = multihash::Multihash<64>;
+
+const PATH_SEGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
+ .add(b'%')
+ .add(b'/')
+ .add(b'`')
+ .add(b'?')
+ .add(b'{')
+ .add(b'}')
+ .add(b' ')
+ .add(b'"')
+ .add(b'#')
+ .add(b'<')
+ .add(b'>');
+
+/// `Protocol` describes all possible multiaddress protocols.
+///
+/// For `Unix`, `Ws` and `Wss` we use `&str` instead of `Path` to allow
+/// cross-platform usage of `Protocol` since encoding `Paths` to bytes is
+/// platform-specific. This means that the actual validation of paths needs to
+/// happen separately.
+#[derive(PartialEq, Eq, Clone, Debug)]
+#[non_exhaustive]
+pub enum Protocol<'a> {
+ Dccp(u16),
+ Dns(Cow<'a, str>),
+ Dns4(Cow<'a, str>),
+ Dns6(Cow<'a, str>),
+ Dnsaddr(Cow<'a, str>),
+ Http,
+ Https,
+ Ip4(Ipv4Addr),
+ Ip6(Ipv6Addr),
+ P2pWebRtcDirect,
+ P2pWebRtcStar,
+ WebRTCDirect,
+ Certhash(Multihash),
+ P2pWebSocketStar,
+ /// Contains the "port" to contact. Similar to TCP or UDP, 0 means "assign me a port".
+ Memory(u64),
+ Onion(Cow<'a, [u8; 10]>, u16),
+ Onion3(Onion3Addr<'a>),
+ P2p(PeerId),
+ P2pCircuit,
+ Quic,
+ QuicV1,
+ Sctp(u16),
+ Tcp(u16),
+ Tls,
+ Noise,
+ Udp(u16),
+ Udt,
+ Unix(Cow<'a, str>),
+ Utp,
+ WebTransport,
+ Ws(Cow<'a, str>),
+ Wss(Cow<'a, str>),
+ Ip6zone(Cow<'a, str>),
+ Ipcidr(u8),
+ Garlic64(Cow<'a, [u8]>),
+ Garlic32(Cow<'a, [u8]>),
+ Sni(Cow<'a, str>),
+ P2pStardust,
+ WebRTC,
+ HttpPath(Cow<'a, str>),
+}
+
+impl<'a> Protocol<'a> {
+ /// Parse a protocol value from the given iterator of string slices.
+ ///
+ /// The parsing only consumes the minimum amount of string slices necessary to
+ /// produce a well-formed protocol. The same iterator can thus be used to parse
+ /// a sequence of protocols in succession. It is up to client code to check
+ /// that iteration has finished whenever appropriate.
+ pub fn from_str_parts(mut iter: I) -> Result
+ where
+ I: Iterator- ,
+ {
+ match iter.next().ok_or(Error::InvalidProtocolString)? {
+ "ip4" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Ip4(Ipv4Addr::from_str(s)?))
+ }
+ "tcp" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Tcp(s.parse()?))
+ }
+ "tls" => Ok(Protocol::Tls),
+ "noise" => Ok(Protocol::Noise),
+ "udp" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Udp(s.parse()?))
+ }
+ "dccp" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Dccp(s.parse()?))
+ }
+ "ip6" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Ip6(Ipv6Addr::from_str(s)?))
+ }
+ "dns" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Dns(Cow::Borrowed(s)))
+ }
+ "dns4" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Dns4(Cow::Borrowed(s)))
+ }
+ "dns6" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Dns6(Cow::Borrowed(s)))
+ }
+ "dnsaddr" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Dnsaddr(Cow::Borrowed(s)))
+ }
+ "sctp" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Sctp(s.parse()?))
+ }
+ "udt" => Ok(Protocol::Udt),
+ "utp" => Ok(Protocol::Utp),
+ "unix" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Unix(Cow::Borrowed(s)))
+ }
+ "p2p" | "ipfs" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ let decoded = multibase::Base::Base58Btc.decode(s)?;
+ let peer_id =
+ PeerId::from_bytes(&decoded).map_err(|e| Error::ParsingError(Box::new(e)))?;
+ Ok(Protocol::P2p(peer_id))
+ }
+ "http" => Ok(Protocol::Http),
+ "https" => Ok(Protocol::Https),
+ "onion" => iter
+ .next()
+ .ok_or(Error::InvalidProtocolString)
+ .and_then(|s| read_onion(&s.to_uppercase()))
+ .map(|(a, p)| Protocol::Onion(Cow::Owned(a), p)),
+ "onion3" => iter
+ .next()
+ .ok_or(Error::InvalidProtocolString)
+ .and_then(|s| read_onion3(&s.to_uppercase()))
+ .map(|(a, p)| Protocol::Onion3((a, p).into())),
+ "quic" => Ok(Protocol::Quic),
+ "quic-v1" => Ok(Protocol::QuicV1),
+ "webtransport" => Ok(Protocol::WebTransport),
+ "ws" => Ok(Protocol::Ws(Cow::Borrowed("/"))),
+ "wss" => Ok(Protocol::Wss(Cow::Borrowed("/"))),
+ "x-parity-ws" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ let decoded = percent_encoding::percent_decode(s.as_bytes()).decode_utf8()?;
+ Ok(Protocol::Ws(decoded))
+ }
+ "x-parity-wss" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ let decoded = percent_encoding::percent_decode(s.as_bytes()).decode_utf8()?;
+ Ok(Protocol::Wss(decoded))
+ }
+ "p2p-websocket-star" => Ok(Protocol::P2pWebSocketStar),
+ "p2p-webrtc-star" => Ok(Protocol::P2pWebRtcStar),
+ "webrtc-direct" => Ok(Protocol::WebRTCDirect),
+ "certhash" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ let (_base, decoded) = multibase::decode(s)?;
+ Ok(Protocol::Certhash(Multihash::from_bytes(&decoded)?))
+ }
+ "p2p-webrtc-direct" => Ok(Protocol::P2pWebRtcDirect),
+ "p2p-circuit" => Ok(Protocol::P2pCircuit),
+ "memory" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Memory(s.parse()?))
+ }
+ "ip6zone" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Ip6zone(Cow::Borrowed(s)))
+ }
+ "ipcidr" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Ipcidr(s.parse()?))
+ }
+ "garlic64" => {
+ let s = iter
+ .next()
+ .ok_or(Error::InvalidProtocolString)?
+ .replace('-', "+")
+ .replace('~', "/");
+
+ if s.len() < 516 || s.len() > 616 {
+ return Err(Error::InvalidProtocolString);
+ }
+
+ let decoded = multibase::Base::Base64.decode(s)?;
+ Ok(Protocol::Garlic64(Cow::from(decoded)))
+ }
+ "garlic32" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+
+ if s.len() < 55 && s.len() != 52 {
+ return Err(Error::InvalidProtocolString);
+ }
+
+ let decoded = multibase::Base::Base32Lower.decode(s)?;
+ Ok(Protocol::Garlic32(Cow::from(decoded)))
+ }
+ "sni" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ Ok(Protocol::Sni(Cow::Borrowed(s)))
+ }
+ "p2p-stardust" => Ok(Protocol::P2pStardust),
+ "webrtc" => Ok(Protocol::WebRTC),
+ "http-path" => {
+ let s = iter.next().ok_or(Error::InvalidProtocolString)?;
+ let decoded = percent_encoding::percent_decode(s.as_bytes()).decode_utf8()?;
+ Ok(Protocol::HttpPath(decoded))
+ }
+ unknown => Err(Error::UnknownProtocolString(unknown.to_string())),
+ }
+ }
+
+ /// Parse a single `Protocol` value from its byte slice representation,
+ /// returning the protocol as well as the remaining byte slice.
+ pub fn from_bytes(input: &'a [u8]) -> Result<(Self, &'a [u8])> {
+ fn split_at(n: usize, input: &[u8]) -> Result<(&[u8], &[u8])> {
+ if input.len() < n {
+ return Err(Error::DataLessThanLen);
+ }
+ Ok(input.split_at(n))
+ }
+ let (id, input) = decode::u32(input)?;
+ match id {
+ DCCP => {
+ let (data, rest) = split_at(2, input)?;
+ let mut rdr = Cursor::new(data);
+ let num = rdr.read_u16::()?;
+ Ok((Protocol::Dccp(num), rest))
+ }
+ DNS => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Dns(Cow::Borrowed(str::from_utf8(data)?)), rest))
+ }
+ DNS4 => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Dns4(Cow::Borrowed(str::from_utf8(data)?)), rest))
+ }
+ DNS6 => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Dns6(Cow::Borrowed(str::from_utf8(data)?)), rest))
+ }
+ DNSADDR => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((
+ Protocol::Dnsaddr(Cow::Borrowed(str::from_utf8(data)?)),
+ rest,
+ ))
+ }
+ HTTP => Ok((Protocol::Http, input)),
+ HTTPS => Ok((Protocol::Https, input)),
+ IP4 => {
+ let (data, rest) = split_at(4, input)?;
+ Ok((
+ Protocol::Ip4(Ipv4Addr::new(data[0], data[1], data[2], data[3])),
+ rest,
+ ))
+ }
+ IP6 => {
+ let (data, rest) = split_at(16, input)?;
+ let mut rdr = Cursor::new(data);
+ let mut seg = [0_u16; 8];
+
+ for x in seg.iter_mut() {
+ *x = rdr.read_u16::()?;
+ }
+
+ let addr = Ipv6Addr::new(
+ seg[0], seg[1], seg[2], seg[3], seg[4], seg[5], seg[6], seg[7],
+ );
+
+ Ok((Protocol::Ip6(addr), rest))
+ }
+ P2P_WEBRTC_DIRECT => Ok((Protocol::P2pWebRtcDirect, input)),
+ P2P_WEBRTC_STAR => Ok((Protocol::P2pWebRtcStar, input)),
+ WEBRTC_DIRECT => Ok((Protocol::WebRTCDirect, input)),
+ CERTHASH => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Certhash(Multihash::from_bytes(data)?), rest))
+ }
+ P2P_WEBSOCKET_STAR => Ok((Protocol::P2pWebSocketStar, input)),
+ MEMORY => {
+ let (data, rest) = split_at(8, input)?;
+ let mut rdr = Cursor::new(data);
+ let num = rdr.read_u64::()?;
+ Ok((Protocol::Memory(num), rest))
+ }
+ ONION => {
+ let (data, rest) = split_at(12, input)?;
+ let port = BigEndian::read_u16(&data[10..]);
+ Ok((
+ Protocol::Onion(Cow::Borrowed(array_ref!(data, 0, 10)), port),
+ rest,
+ ))
+ }
+ ONION3 => {
+ let (data, rest) = split_at(37, input)?;
+ let port = BigEndian::read_u16(&data[35..]);
+ Ok((
+ Protocol::Onion3((array_ref!(data, 0, 35), port).into()),
+ rest,
+ ))
+ }
+ P2P => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((
+ Protocol::P2p(
+ PeerId::from_bytes(data).map_err(|e| Error::ParsingError(Box::new(e)))?,
+ ),
+ rest,
+ ))
+ }
+ P2P_CIRCUIT => Ok((Protocol::P2pCircuit, input)),
+ QUIC => Ok((Protocol::Quic, input)),
+ QUIC_V1 => Ok((Protocol::QuicV1, input)),
+ SCTP => {
+ let (data, rest) = split_at(2, input)?;
+ let mut rdr = Cursor::new(data);
+ let num = rdr.read_u16::()?;
+ Ok((Protocol::Sctp(num), rest))
+ }
+ TCP => {
+ let (data, rest) = split_at(2, input)?;
+ let mut rdr = Cursor::new(data);
+ let num = rdr.read_u16::()?;
+ Ok((Protocol::Tcp(num), rest))
+ }
+ TLS => Ok((Protocol::Tls, input)),
+ NOISE => Ok((Protocol::Noise, input)),
+ UDP => {
+ let (data, rest) = split_at(2, input)?;
+ let mut rdr = Cursor::new(data);
+ let num = rdr.read_u16::()?;
+ Ok((Protocol::Udp(num), rest))
+ }
+ UDT => Ok((Protocol::Udt, input)),
+ UNIX => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Unix(Cow::Borrowed(str::from_utf8(data)?)), rest))
+ }
+ UTP => Ok((Protocol::Utp, input)),
+ WEBTRANSPORT => Ok((Protocol::WebTransport, input)),
+ WS => Ok((Protocol::Ws(Cow::Borrowed("/")), input)),
+ WS_WITH_PATH => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Ws(Cow::Borrowed(str::from_utf8(data)?)), rest))
+ }
+ WSS => Ok((Protocol::Wss(Cow::Borrowed("/")), input)),
+ WSS_WITH_PATH => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Wss(Cow::Borrowed(str::from_utf8(data)?)), rest))
+ }
+ IP6ZONE => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((
+ Protocol::Ip6zone(Cow::Borrowed(str::from_utf8(data)?)),
+ rest,
+ ))
+ }
+ IPCIDR => {
+ let (data, rest) = split_at(1, input)?;
+ Ok((Protocol::Ipcidr(data[0]), rest))
+ }
+ GARLIC64 => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Garlic64(Cow::Borrowed(data)), rest))
+ }
+ GARLIC32 => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Garlic32(Cow::Borrowed(data)), rest))
+ }
+ SNI => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((Protocol::Sni(Cow::Borrowed(str::from_utf8(data)?)), rest))
+ }
+ P2P_STARDUST => Ok((Protocol::P2pStardust, input)),
+ WEBRTC => Ok((Protocol::WebRTC, input)),
+ HTTP_PATH => {
+ let (n, input) = decode::usize(input)?;
+ let (data, rest) = split_at(n, input)?;
+ Ok((
+ Protocol::HttpPath(Cow::Borrowed(str::from_utf8(data)?)),
+ rest,
+ ))
+ }
+ _ => Err(Error::UnknownProtocolId(id)),
+ }
+ }
+
+ /// Encode this protocol by writing its binary representation into
+ /// the given `Write` impl.
+ pub fn write_bytes(&self, w: &mut W) -> Result<()> {
+ let mut buf = encode::u32_buffer();
+ match self {
+ Protocol::Ip4(addr) => {
+ w.write_all(encode::u32(IP4, &mut buf))?;
+ w.write_all(&addr.octets())?
+ }
+ Protocol::Ip6(addr) => {
+ w.write_all(encode::u32(IP6, &mut buf))?;
+ for &segment in &addr.segments() {
+ w.write_u16::(segment)?
+ }
+ }
+ Protocol::Tcp(port) => {
+ w.write_all(encode::u32(TCP, &mut buf))?;
+ w.write_u16::(*port)?
+ }
+ Protocol::Tls => w.write_all(encode::u32(TLS, &mut buf))?,
+ Protocol::Noise => w.write_all(encode::u32(NOISE, &mut buf))?,
+ Protocol::Udp(port) => {
+ w.write_all(encode::u32(UDP, &mut buf))?;
+ w.write_u16::(*port)?
+ }
+ Protocol::Dccp(port) => {
+ w.write_all(encode::u32(DCCP, &mut buf))?;
+ w.write_u16::(*port)?
+ }
+ Protocol::Sctp(port) => {
+ w.write_all(encode::u32(SCTP, &mut buf))?;
+ w.write_u16::(*port)?
+ }
+ Protocol::Dns(s) => {
+ w.write_all(encode::u32(DNS, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::Dns4(s) => {
+ w.write_all(encode::u32(DNS4, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::Dns6(s) => {
+ w.write_all(encode::u32(DNS6, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::Dnsaddr(s) => {
+ w.write_all(encode::u32(DNSADDR, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::Unix(s) => {
+ w.write_all(encode::u32(UNIX, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::P2p(peer_id) => {
+ w.write_all(encode::u32(P2P, &mut buf))?;
+ let bytes = peer_id.to_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(&bytes)?
+ }
+ Protocol::Onion(addr, port) => {
+ w.write_all(encode::u32(ONION, &mut buf))?;
+ w.write_all(addr.as_ref())?;
+ w.write_u16::(*port)?
+ }
+ Protocol::Onion3(addr) => {
+ w.write_all(encode::u32(ONION3, &mut buf))?;
+ w.write_all(addr.hash().as_ref())?;
+ w.write_u16::(addr.port())?
+ }
+ Protocol::Quic => w.write_all(encode::u32(QUIC, &mut buf))?,
+ Protocol::QuicV1 => w.write_all(encode::u32(QUIC_V1, &mut buf))?,
+ Protocol::Utp => w.write_all(encode::u32(UTP, &mut buf))?,
+ Protocol::Udt => w.write_all(encode::u32(UDT, &mut buf))?,
+ Protocol::Http => w.write_all(encode::u32(HTTP, &mut buf))?,
+ Protocol::Https => w.write_all(encode::u32(HTTPS, &mut buf))?,
+ Protocol::WebTransport => w.write_all(encode::u32(WEBTRANSPORT, &mut buf))?,
+ Protocol::Ws(ref s) if s == "/" => w.write_all(encode::u32(WS, &mut buf))?,
+ Protocol::Ws(s) => {
+ w.write_all(encode::u32(WS_WITH_PATH, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::Wss(ref s) if s == "/" => w.write_all(encode::u32(WSS, &mut buf))?,
+ Protocol::Wss(s) => {
+ w.write_all(encode::u32(WSS_WITH_PATH, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::P2pWebSocketStar => w.write_all(encode::u32(P2P_WEBSOCKET_STAR, &mut buf))?,
+ Protocol::P2pWebRtcStar => w.write_all(encode::u32(P2P_WEBRTC_STAR, &mut buf))?,
+ Protocol::WebRTCDirect => w.write_all(encode::u32(WEBRTC_DIRECT, &mut buf))?,
+ Protocol::Certhash(hash) => {
+ w.write_all(encode::u32(CERTHASH, &mut buf))?;
+ let bytes = hash.to_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(&bytes)?
+ }
+ Protocol::P2pWebRtcDirect => w.write_all(encode::u32(P2P_WEBRTC_DIRECT, &mut buf))?,
+ Protocol::P2pCircuit => w.write_all(encode::u32(P2P_CIRCUIT, &mut buf))?,
+ Protocol::Memory(port) => {
+ w.write_all(encode::u32(MEMORY, &mut buf))?;
+ w.write_u64::(*port)?
+ }
+ Protocol::Ip6zone(zone_id) => {
+ w.write_all(encode::u32(IP6ZONE, &mut buf))?;
+ let bytes = zone_id.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::Ipcidr(mask) => {
+ w.write_all(encode::u32(IPCIDR, &mut buf))?;
+ w.write_u8(*mask)?
+ }
+ Protocol::Garlic64(addr) => {
+ w.write_all(encode::u32(GARLIC64, &mut buf))?;
+ w.write_all(encode::usize(addr.len(), &mut encode::usize_buffer()))?;
+ w.write_all(addr)?
+ }
+ Protocol::Garlic32(addr) => {
+ w.write_all(encode::u32(GARLIC32, &mut buf))?;
+ w.write_all(encode::usize(addr.len(), &mut encode::usize_buffer()))?;
+ w.write_all(addr)?
+ }
+ Protocol::Sni(s) => {
+ w.write_all(encode::u32(SNI, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ Protocol::P2pStardust => w.write_all(encode::u32(P2P_STARDUST, &mut buf))?,
+ Protocol::WebRTC => w.write_all(encode::u32(WEBRTC, &mut buf))?,
+ Protocol::HttpPath(s) => {
+ w.write_all(encode::u32(HTTP_PATH, &mut buf))?;
+ let bytes = s.as_bytes();
+ w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?;
+ w.write_all(bytes)?
+ }
+ }
+ Ok(())
+ }
+
+ /// Turn this `Protocol` into one that owns its data, thus being valid for any lifetime.
+ pub fn acquire<'b>(self) -> Protocol<'b> {
+ use self::Protocol::*;
+ match self {
+ Dccp(a) => Dccp(a),
+ Dns(cow) => Dns(Cow::Owned(cow.into_owned())),
+ Dns4(cow) => Dns4(Cow::Owned(cow.into_owned())),
+ Dns6(cow) => Dns6(Cow::Owned(cow.into_owned())),
+ Dnsaddr(cow) => Dnsaddr(Cow::Owned(cow.into_owned())),
+ Http => Http,
+ Https => Https,
+ Ip4(a) => Ip4(a),
+ Ip6(a) => Ip6(a),
+ P2pWebRtcDirect => P2pWebRtcDirect,
+ P2pWebRtcStar => P2pWebRtcStar,
+ WebRTCDirect => WebRTCDirect,
+ Certhash(hash) => Certhash(hash),
+ P2pWebSocketStar => P2pWebSocketStar,
+ Memory(a) => Memory(a),
+ Onion(addr, port) => Onion(Cow::Owned(addr.into_owned()), port),
+ Onion3(addr) => Onion3(addr.acquire()),
+ P2p(a) => P2p(a),
+ P2pCircuit => P2pCircuit,
+ Quic => Quic,
+ QuicV1 => QuicV1,
+ Sctp(a) => Sctp(a),
+ Tcp(a) => Tcp(a),
+ Tls => Tls,
+ Noise => Noise,
+ Udp(a) => Udp(a),
+ Udt => Udt,
+ Unix(cow) => Unix(Cow::Owned(cow.into_owned())),
+ Utp => Utp,
+ WebTransport => WebTransport,
+ Ws(cow) => Ws(Cow::Owned(cow.into_owned())),
+ Wss(cow) => Wss(Cow::Owned(cow.into_owned())),
+ Ip6zone(cow) => Ip6zone(Cow::Owned(cow.into_owned())),
+ Ipcidr(mask) => Ipcidr(mask),
+ Garlic64(addr) => Garlic64(Cow::Owned(addr.into_owned())),
+ Garlic32(addr) => Garlic32(Cow::Owned(addr.into_owned())),
+ Sni(cow) => Sni(Cow::Owned(cow.into_owned())),
+ P2pStardust => P2pStardust,
+ WebRTC => WebRTC,
+ HttpPath(cow) => HttpPath(Cow::Owned(cow.into_owned())),
+ }
+ }
+
+ pub fn tag(&self) -> &'static str {
+ use self::Protocol::*;
+ match self {
+ Dccp(_) => "dccp",
+ Dns(_) => "dns",
+ Dns4(_) => "dns4",
+ Dns6(_) => "dns6",
+ Dnsaddr(_) => "dnsaddr",
+ Http => "http",
+ Https => "https",
+ Ip4(_) => "ip4",
+ Ip6(_) => "ip6",
+ P2pWebRtcDirect => "p2p-webrtc-direct",
+ P2pWebRtcStar => "p2p-webrtc-star",
+ WebRTCDirect => "webrtc-direct",
+ Certhash(_) => "certhash",
+ P2pWebSocketStar => "p2p-websocket-star",
+ Memory(_) => "memory",
+ Onion(_, _) => "onion",
+ Onion3(_) => "onion3",
+ P2p(_) => "p2p",
+ P2pCircuit => "p2p-circuit",
+ Quic => "quic",
+ QuicV1 => "quic-v1",
+ Sctp(_) => "sctp",
+ Tcp(_) => "tcp",
+ Tls => "tls",
+ Noise => "noise",
+ Udp(_) => "udp",
+ Udt => "udt",
+ Unix(_) => "unix",
+ Utp => "utp",
+ WebTransport => "webtransport",
+ Ws(ref s) if s == "/" => "ws",
+ Ws(_) => "x-parity-ws",
+ Wss(ref s) if s == "/" => "wss",
+ Wss(_) => "x-parity-wss",
+ Ip6zone(_) => "ip6zone",
+ Ipcidr(_) => "ipcidr",
+ Garlic64(_) => "garlic64",
+ Garlic32(_) => "garlic32",
+ Sni(_) => "sni",
+ P2pStardust => "p2p-stardust",
+ WebRTC => "webrtc",
+ HttpPath(_) => "http-path",
+ }
+ }
+}
+
+impl fmt::Display for Protocol<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ use self::Protocol::*;
+ write!(f, "/{}", self.tag())?;
+ match self {
+ Dccp(port) => write!(f, "/{port}"),
+ Dns(s) => write!(f, "/{s}"),
+ Dns4(s) => write!(f, "/{s}"),
+ Dns6(s) => write!(f, "/{s}"),
+ Dnsaddr(s) => write!(f, "/{s}"),
+ Ip4(addr) => write!(f, "/{addr}"),
+ Ip6(addr) => write!(f, "/{addr}"),
+ Certhash(hash) => write!(
+ f,
+ "/{}",
+ multibase::encode(multibase::Base::Base64Url, hash.to_bytes())
+ ),
+ Memory(port) => write!(f, "/{port}"),
+ Onion(addr, port) => {
+ let s = BASE32.encode(addr.as_ref());
+ write!(f, "/{}:{}", s.to_lowercase(), port)
+ }
+ Onion3(addr) => {
+ let s = BASE32.encode(addr.hash());
+ write!(f, "/{}:{}", s.to_lowercase(), addr.port())
+ }
+ P2p(c) => write!(f, "/{}", multibase::Base::Base58Btc.encode(c.to_bytes())),
+ Sctp(port) => write!(f, "/{port}"),
+ Tcp(port) => write!(f, "/{port}"),
+ Udp(port) => write!(f, "/{port}"),
+ Unix(s) => write!(f, "/{s}"),
+ Ws(s) if s != "/" => {
+ let encoded =
+ percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
+ write!(f, "/{encoded}")
+ }
+ Wss(s) if s != "/" => {
+ let encoded =
+ percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
+ write!(f, "/{encoded}")
+ }
+ Ip6zone(zone) => write!(f, "/{zone}"),
+ Ipcidr(mask) => write!(f, "/{mask}"),
+ Garlic64(addr) => write!(
+ f,
+ "/{}",
+ multibase::Base::Base64
+ .encode(addr)
+ .replace('+', "-")
+ .replace('/', "~")
+ ),
+ Garlic32(addr) => write!(f, "/{}", multibase::Base::Base32Lower.encode(addr)),
+ Sni(s) => write!(f, "/{s}"),
+ HttpPath(s) => {
+ let encoded =
+ percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET);
+ write!(f, "/{encoded}")
+ }
+ _ => Ok(()),
+ }
+ }
+}
+
+impl From for Protocol<'_> {
+ #[inline]
+ fn from(addr: IpAddr) -> Self {
+ match addr {
+ IpAddr::V4(addr) => Protocol::Ip4(addr),
+ IpAddr::V6(addr) => Protocol::Ip6(addr),
+ }
+ }
+}
+
+impl From for Protocol<'_> {
+ #[inline]
+ fn from(addr: Ipv4Addr) -> Self {
+ Protocol::Ip4(addr)
+ }
+}
+
+impl From for Protocol<'_> {
+ #[inline]
+ fn from(addr: Ipv6Addr) -> Self {
+ Protocol::Ip6(addr)
+ }
+}
+
+macro_rules! read_onion_impl {
+ ($name:ident, $len:expr, $encoded_len:expr) => {
+ fn $name(s: &str) -> Result<([u8; $len], u16)> {
+ let mut parts = s.split(':');
+
+ // address part (without ".onion")
+ let b32 = parts.next().ok_or(Error::InvalidMultiaddr)?;
+ if b32.len() != $encoded_len {
+ return Err(Error::InvalidMultiaddr);
+ }
+
+ // port number
+ let port = parts
+ .next()
+ .ok_or(Error::InvalidMultiaddr)
+ .and_then(|p| str::parse(p).map_err(From::from))?;
+
+ // port == 0 is not valid for onion
+ if port == 0 {
+ return Err(Error::InvalidMultiaddr);
+ }
+
+ // nothing else expected
+ if parts.next().is_some() {
+ return Err(Error::InvalidMultiaddr);
+ }
+
+ if $len
+ != BASE32
+ .decode_len(b32.len())
+ .map_err(|_| Error::InvalidMultiaddr)?
+ {
+ return Err(Error::InvalidMultiaddr);
+ }
+
+ let mut buf = [0u8; $len];
+ BASE32
+ .decode_mut(b32.as_bytes(), &mut buf)
+ .map_err(|_| Error::InvalidMultiaddr)?;
+
+ Ok((buf, port))
+ }
+ };
+}
+
+// Parse a version 2 onion address and return its binary representation.
+//
+// Format: ":"
+read_onion_impl!(read_onion, 10, 16);
+// Parse a version 3 onion address and return its binary representation.
+//
+// Format: ":"
+read_onion_impl!(read_onion3, 35, 56);
diff --git a/multiaddr/tests/lib.rs b/multiaddr/tests/lib.rs
new file mode 100644
index 00000000000..936809eeee4
--- /dev/null
+++ b/multiaddr/tests/lib.rs
@@ -0,0 +1,833 @@
+use data_encoding::HEXUPPER;
+use multiaddr::*;
+use multihash::Multihash;
+use quickcheck::{Arbitrary, Gen, QuickCheck};
+use std::{
+ borrow::Cow,
+ convert::{TryFrom, TryInto},
+ iter::{self, FromIterator},
+ net::{Ipv4Addr, Ipv6Addr},
+ str::FromStr,
+};
+
+// Property tests
+
+#[test]
+fn to_from_bytes_identity() {
+ fn prop(a: Ma) -> bool {
+ let b = a.0.to_vec();
+ Some(a) == Multiaddr::try_from(b).ok().map(Ma)
+ }
+ QuickCheck::new().quickcheck(prop as fn(Ma) -> bool)
+}
+
+#[test]
+fn to_from_str_identity() {
+ fn prop(a: Ma) -> bool {
+ let b = a.0.to_string();
+ Some(a) == Multiaddr::from_str(&b).ok().map(Ma)
+ }
+ QuickCheck::new().quickcheck(prop as fn(Ma) -> bool)
+}
+
+#[test]
+fn byteswriter() {
+ fn prop(a: Ma, b: Ma) -> bool {
+ let mut x = a.0.clone();
+ for p in b.0.iter() {
+ x = x.with(p)
+ }
+ x.iter()
+ .zip(a.0.iter().chain(b.0.iter()))
+ .all(|(x, y)| x == y)
+ }
+ QuickCheck::new().quickcheck(prop as fn(Ma, Ma) -> bool)
+}
+
+#[test]
+fn push_pop_identity() {
+ fn prop(a: Ma, p: Proto) -> bool {
+ let mut b = a.clone();
+ let q = p.clone();
+ b.0.push(q.0);
+ assert_ne!(a.0, b.0);
+ Some(p.0) == b.0.pop() && a.0 == b.0
+ }
+ QuickCheck::new().quickcheck(prop as fn(Ma, Proto) -> bool)
+}
+
+#[test]
+fn ends_with() {
+ fn prop(Ma(m): Ma) {
+ let n = m.iter().count();
+ for i in 0..n {
+ let suffix = m.iter().skip(i).collect::();
+ assert!(m.ends_with(&suffix));
+ }
+ }
+ QuickCheck::new().quickcheck(prop as fn(_))
+}
+
+#[test]
+fn starts_with() {
+ fn prop(Ma(m): Ma) {
+ let n = m.iter().count();
+ for i in 0..n {
+ let prefix = m.iter().take(i + 1).collect::();
+ assert!(m.starts_with(&prefix));
+ }
+ }
+ QuickCheck::new().quickcheck(prop as fn(_))
+}
+
+// Arbitrary impls
+
+#[derive(PartialEq, Eq, Clone, Hash, Debug)]
+struct Ma(Multiaddr);
+
+impl Arbitrary for Ma {
+ fn arbitrary(g: &mut Gen) -> Self {
+ let iter = (0..u8::arbitrary(g) % 128).map(|_| Proto::arbitrary(g).0);
+ Ma(Multiaddr::from_iter(iter))
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+struct Proto(Protocol<'static>);
+
+impl Proto {
+ const IMPL_VARIANT_COUNT: u8 = 40;
+}
+
+impl Arbitrary for Proto {
+ fn arbitrary(g: &mut Gen) -> Self {
+ use Protocol::*;
+ match u8::arbitrary(g) % Proto::IMPL_VARIANT_COUNT {
+ 0 => Proto(Dccp(Arbitrary::arbitrary(g))),
+ 1 => Proto(Dns(Cow::Owned(SubString::arbitrary(g).0))),
+ 2 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))),
+ 3 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))),
+ 4 => Proto(Dnsaddr(Cow::Owned(SubString::arbitrary(g).0))),
+ 5 => Proto(Http),
+ 6 => Proto(Https),
+ 7 => Proto(Ip4(Ipv4Addr::arbitrary(g))),
+ 8 => Proto(Ip6(Ipv6Addr::arbitrary(g))),
+ 9 => Proto(P2pWebRtcDirect),
+ 10 => Proto(P2pWebRtcStar),
+ 11 => Proto(WebRTCDirect),
+ 12 => Proto(Certhash(Mh::arbitrary(g).0)),
+ 13 => Proto(P2pWebSocketStar),
+ 14 => Proto(Memory(Arbitrary::arbitrary(g))),
+ 15 => {
+ let a = iter::repeat_with(|| u8::arbitrary(g))
+ .take(10)
+ .collect::>()
+ .try_into()
+ .unwrap();
+ Proto(Onion(Cow::Owned(a), std::cmp::max(1, u16::arbitrary(g))))
+ }
+ 16 => {
+ let a: [u8; 35] = iter::repeat_with(|| u8::arbitrary(g))
+ .take(35)
+ .collect::>()
+ .try_into()
+ .unwrap();
+ Proto(Onion3((a, std::cmp::max(1, u16::arbitrary(g))).into()))
+ }
+ 17 => Proto(P2p(PId::arbitrary(g).0)),
+ 18 => Proto(P2pCircuit),
+ 19 => Proto(Quic),
+ 20 => Proto(QuicV1),
+ 21 => Proto(Sctp(Arbitrary::arbitrary(g))),
+ 22 => Proto(Tcp(Arbitrary::arbitrary(g))),
+ 23 => Proto(Tls),
+ 24 => Proto(Noise),
+ 25 => Proto(Udp(Arbitrary::arbitrary(g))),
+ 26 => Proto(Udt),
+ 27 => Proto(Unix(Cow::Owned(SubString::arbitrary(g).0))),
+ 28 => Proto(Utp),
+ 29 => Proto(WebTransport),
+ 30 => Proto(Ws("/".into())),
+ 31 => Proto(Wss("/".into())),
+ 32 => Proto(Ip6zone(Cow::Owned(SubString::arbitrary(g).0))),
+ 33 => Proto(Ipcidr(Arbitrary::arbitrary(g))),
+ 34 => {
+ let len = usize::arbitrary(g) % (462 - 387) + 387;
+ let a = iter::repeat_with(|| u8::arbitrary(g))
+ .take(len)
+ .collect::>();
+ Proto(Garlic64(Cow::Owned(a)))
+ }
+ 35 => {
+ let len = if bool::arbitrary(g) {
+ 32
+ } else {
+ usize::arbitrary(g) % 128 + 35
+ };
+ let a = iter::repeat_with(|| u8::arbitrary(g))
+ .take(len)
+ .collect::>();
+ Proto(Garlic32(Cow::Owned(a)))
+ }
+ 36 => Proto(Sni(Cow::Owned(SubString::arbitrary(g).0))),
+ 37 => Proto(P2pStardust),
+ 38 => Proto(WebRTC),
+ 39 => Proto(HttpPath(Cow::Owned(SubString::arbitrary(g).0))),
+ _ => panic!("outside range"),
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+struct Mh(Multihash<64>);
+
+impl Arbitrary for Mh {
+ fn arbitrary(g: &mut Gen) -> Self {
+ let mut hash: [u8; 32] = [0; 32];
+ hash.fill_with(|| u8::arbitrary(g));
+ Mh(Multihash::wrap(0x0, &hash).expect("The digest size is never too large"))
+ }
+}
+
+#[derive(Clone, Debug)]
+struct PId(PeerId);
+
+impl Arbitrary for PId {
+ fn arbitrary(g: &mut Gen) -> Self {
+ let mh = Mh::arbitrary(g);
+
+ PId(PeerId::from_multihash(mh.0).expect("identity multihash works if digest size < 64"))
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+struct SubString(String); // ASCII string without '/'
+
+impl Arbitrary for SubString {
+ fn arbitrary(g: &mut Gen) -> Self {
+ let mut s = String::arbitrary(g);
+ s.retain(|c| c.is_ascii() && c != '/');
+ SubString(s)
+ }
+}
+
+// other unit tests
+
+fn ma_valid(source: &str, target: &str, protocols: Vec>) {
+ let parsed = source.parse::().unwrap();
+ assert_eq!(HEXUPPER.encode(&parsed.to_vec()[..]), target);
+ assert_eq!(parsed.iter().collect::>(), protocols);
+ assert_eq!(source.parse::().unwrap().to_string(), source);
+ assert_eq!(
+ Multiaddr::try_from(HEXUPPER.decode(target.as_bytes()).unwrap()).unwrap(),
+ parsed
+ );
+}
+
+fn peer_id(s: &str) -> PeerId {
+ s.parse().unwrap()
+}
+
+#[test]
+fn multiaddr_eq() {
+ let m1 = "/ip4/127.0.0.1/udp/1234".parse::().unwrap();
+ let m2 = "/ip4/127.0.0.1/tcp/1234".parse::().unwrap();
+ let m3 = "/ip4/127.0.0.1/tcp/1234".parse::().unwrap();
+
+ assert_ne!(m1, m2);
+ assert_ne!(m2, m1);
+ assert_eq!(m2, m3);
+ assert_eq!(m1, m1);
+}
+
+#[test]
+fn construct_success() {
+ use Protocol::*;
+
+ let local: Ipv4Addr = "127.0.0.1".parse().unwrap();
+ let addr6: Ipv6Addr = "2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095".parse().unwrap();
+
+ ma_valid(
+ "/ip4/1.2.3.4",
+ "0401020304",
+ vec![Ip4("1.2.3.4".parse().unwrap())],
+ );
+ ma_valid(
+ "/ip4/0.0.0.0",
+ "0400000000",
+ vec![Ip4("0.0.0.0".parse().unwrap())],
+ );
+ ma_valid(
+ "/ip6/::1",
+ "2900000000000000000000000000000001",
+ vec![Ip6("::1".parse().unwrap())],
+ );
+ ma_valid(
+ "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
+ "29260100094F819700803ECA6566E80C21",
+ vec![Ip6("2601:9:4f81:9700:803e:ca65:66e8:c21".parse().unwrap())],
+ );
+ ma_valid(
+ "/ip6/fe80::9700:803e:ca65:66e8:c21/ip6zone/wlan0",
+ "29FE80000000009700803ECA6566E80C212A05776C616E30",
+ vec![
+ Ip6("fe80::9700:803e:ca65:66e8:c21".parse().unwrap()),
+ Ip6zone(Cow::Borrowed("wlan0")),
+ ],
+ );
+ ma_valid("/udp/0", "91020000", vec![Udp(0)]);
+ ma_valid("/tcp/0", "060000", vec![Tcp(0)]);
+ ma_valid("/sctp/0", "84010000", vec![Sctp(0)]);
+ ma_valid("/udp/1234", "910204D2", vec![Udp(1234)]);
+ ma_valid("/tcp/1234", "0604D2", vec![Tcp(1234)]);
+ ma_valid("/sctp/1234", "840104D2", vec![Sctp(1234)]);
+ ma_valid("/udp/65535", "9102FFFF", vec![Udp(65535)]);
+ ma_valid("/tcp/65535", "06FFFF", vec![Tcp(65535)]);
+ ma_valid(
+ "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
+ vec![P2p(peer_id(
+ "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ ))],
+ );
+ ma_valid(
+ "/udp/1234/sctp/1234",
+ "910204D2840104D2",
+ vec![Udp(1234), Sctp(1234)],
+ );
+ ma_valid("/udp/1234/udt", "910204D2AD02", vec![Udp(1234), Udt]);
+ ma_valid("/udp/1234/utp", "910204D2AE02", vec![Udp(1234), Utp]);
+ ma_valid("/tcp/1234/http", "0604D2E003", vec![Tcp(1234), Http]);
+ ma_valid(
+ "/tcp/1234/tls/http",
+ "0604D2C003E003",
+ vec![Tcp(1234), Tls, Http],
+ );
+ ma_valid(
+ "/tcp/1234/http/http-path/user",
+ "0604D2E003E1030475736572",
+ vec![Tcp(1234), Http, HttpPath(Cow::Borrowed("user"))],
+ );
+ ma_valid(
+ "/tcp/1234/http/http-path/api%2Fv0%2Flogin",
+ "0604D2E003E1030C6170692F76302F6C6F67696E",
+ vec![Tcp(1234), Http, HttpPath(Cow::Borrowed("api/v0/login"))],
+ );
+ ma_valid(
+ "/tcp/1234/http/http-path/a%2520space",
+ "0604D2E003E10309612532307370616365",
+ vec![Tcp(1234), Http, HttpPath(Cow::Borrowed("a%20space"))],
+ );
+ ma_valid("/tcp/1234/https", "0604D2BB03", vec![Tcp(1234), Https]);
+ ma_valid(
+ "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
+ "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2",
+ vec![
+ P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")),
+ Tcp(1234),
+ ],
+ );
+ ma_valid(
+ "/ip4/127.0.0.1/udp/1234",
+ "047F000001910204D2",
+ vec![Ip4(local), Udp(1234)],
+ );
+ ma_valid(
+ "/ip4/127.0.0.1/udp/0",
+ "047F00000191020000",
+ vec![Ip4(local), Udp(0)],
+ );
+ ma_valid(
+ "/ip4/127.0.0.1/tcp/1234",
+ "047F0000010604D2",
+ vec![Ip4(local), Tcp(1234)],
+ );
+ ma_valid(
+ "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
+ vec![
+ Ip4(local),
+ P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")),
+ ],
+ );
+ ma_valid("/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
+ "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2",
+ vec![Ip4(local), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), Tcp(1234)]);
+ // /unix/a/b/c/d/e,
+ // /unix/stdio,
+ // /ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f,
+ // /ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio
+ ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "29200108A07AC542013AC986FFFE317095061F40DD03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
+ vec![Ip6(addr6), Tcp(8000), Ws("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))
+ ]);
+ ma_valid("/p2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "9302047F000001062382DD03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
+ vec![P2pWebRtcStar, Ip4(local), Tcp(9090), Ws("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))
+ ]);
+ ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/wss/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "29200108A07AC542013AC986FFFE317095061F40DE03A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
+ vec![Ip6(addr6), Tcp(8000), Wss("/".into()), P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]);
+ ma_valid("/ip4/127.0.0.1/tcp/9090/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "047F000001062382A202A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
+ vec![Ip4(local), Tcp(9090), P2pCircuit, P2p(peer_id("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]);
+
+ ma_valid(
+ "/onion/aaimaq4ygg2iegci:80",
+ "BC030010C0439831B48218480050",
+ vec![Onion(
+ Cow::Owned([0, 16, 192, 67, 152, 49, 180, 130, 24, 72]),
+ 80,
+ )],
+ );
+ ma_valid(
+ "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:1234",
+ "BD03ADADEC040BE047F9658668B11A504F3155001F231A37F54C4476C07FB4CC139ED7E30304D2",
+ vec![Onion3(
+ (
+ [
+ 173, 173, 236, 4, 11, 224, 71, 249, 101, 134, 104, 177, 26, 80, 79, 49, 85, 0,
+ 31, 35, 26, 55, 245, 76, 68, 118, 192, 127, 180, 204, 19, 158, 215, 227, 3,
+ ],
+ 1234,
+ )
+ .into(),
+ )],
+ );
+ ma_valid(
+ "/garlic64/jT~IyXaoauTni6N4517EG8mrFUKpy0IlgZh-EY9csMAk82Odatmzr~YTZy8Hv7u~wvkg75EFNOyqb~nAPg-khyp2TS~ObUz8WlqYAM2VlEzJ7wJB91P-cUlKF\
+ 18zSzVoJFmsrcQHZCirSbWoOknS6iNmsGRh5KVZsBEfp1Dg3gwTipTRIx7Vl5Vy~1OSKQVjYiGZS9q8RL0MF~7xFiKxZDLbPxk0AK9TzGGqm~wMTI2HS0Gm4Ycy8LYPVmLvG\
+ onIBYndg2bJC7WLuF6tVjVquiokSVDKFwq70BCUU5AU-EvdOD5KEOAM7mPfw-gJUG4tm1TtvcobrObqoRnmhXPTBTN5H7qDD12AvlwFGnfAlBXjuP4xOUAISL5SRLiulrsMS\
+ iT4GcugSI80mF6sdB0zWRgL1yyvoVWeTBn1TqjO27alr95DGTluuSqrNAxgpQzCKEWAyzrQkBfo2avGAmmz2NaHaAvYbOg0QSJz1PLjv2jdPW~ofiQmrGWM1cd~1cCqAAAA",
+ "BE0383038D3FC8C976A86AE4E78BA378E75EC41BC9AB1542A9CB422581987E118F5CB0C024F3639D6AD9B3AFF613672F07BFBBBFC2F920EF910534ECAA6FF9C03E\
+ 0FA4872A764D2FCE6D4CFC5A5A9800CD95944CC9EF0241F753FE71494A175F334B35682459ACADC4076428AB49B5A83A49D2EA2366B06461E4A559B0111FA750E0D\
+ E0C138A94D1231ED5979572FF53922905636221994BDABC44BD0C17FEF11622B16432DB3F193400AF53CC61AA9BFC0C4C8D874B41A6E18732F0B60F5662EF1A89C8\
+ 0589DD8366C90BB58BB85EAD56356ABA2A244950CA170ABBD01094539014F84BDD383E4A10E00CEE63DFC3E809506E2D9B54EDBDCA1BACE6EAA119E68573D305337\
+ 91FBA830F5D80BE5C051A77C09415E3B8FE3139400848BE5244B8AE96BB0C4A24F819CBA0488F34985EAC741D3359180BD72CAFA1559E4C19F54EA8CEDBB6A5AFDE\
+ 4319396EB92AAB340C60A50CC2284580CB3AD09017E8D9ABC60269B3D8D687680BD86CE834412273D4F2E3BF68DD3D6FE87E2426AC658CD5C77FD5C0AA000000",
+ vec![Garlic64(
+ (
+ &[
+ 141, 63, 200, 201, 118, 168, 106, 228, 231, 139, 163, 120, 231, 94, 196, 27, 201, 171, 21, 66,
+ 169, 203, 66, 37, 129, 152, 126, 17, 143, 92, 176, 192, 36, 243, 99, 157, 106, 217, 179, 175,
+ 246, 19, 103, 47, 7, 191, 187, 191, 194, 249, 32, 239, 145, 5, 52, 236, 170, 111, 249, 192,
+ 62, 15, 164, 135, 42, 118, 77, 47, 206, 109, 76, 252, 90, 90, 152, 0, 205, 149, 148, 76,
+ 201, 239, 2, 65, 247, 83, 254, 113, 73, 74, 23, 95, 51, 75, 53, 104, 36, 89, 172, 173,
+ 196, 7, 100, 40, 171, 73, 181, 168, 58, 73, 210, 234, 35, 102, 176, 100, 97, 228, 165, 89,
+ 176, 17, 31, 167, 80, 224, 222, 12, 19, 138, 148, 209, 35, 30, 213, 151, 149, 114, 255, 83,
+ 146, 41, 5, 99, 98, 33, 153, 75, 218, 188, 68, 189, 12, 23, 254, 241, 22, 34, 177, 100,
+ 50, 219, 63, 25, 52, 0, 175, 83, 204, 97, 170, 155, 252, 12, 76, 141, 135, 75, 65, 166,
+ 225, 135, 50, 240, 182, 15, 86, 98, 239, 26, 137, 200, 5, 137, 221, 131, 102, 201, 11, 181,
+ 139, 184, 94, 173, 86, 53, 106, 186, 42, 36, 73, 80, 202, 23, 10, 187, 208, 16, 148, 83,
+ 144, 20, 248, 75, 221, 56, 62, 74, 16, 224, 12, 238, 99, 223, 195, 232, 9, 80, 110, 45,
+ 155, 84, 237, 189, 202, 27, 172, 230, 234, 161, 25, 230, 133, 115, 211, 5, 51, 121, 31, 186,
+ 131, 15, 93, 128, 190, 92, 5, 26, 119, 192, 148, 21, 227, 184, 254, 49, 57, 64, 8, 72,
+ 190, 82, 68, 184, 174, 150, 187, 12, 74, 36, 248, 25, 203, 160, 72, 143, 52, 152, 94, 172,
+ 116, 29, 51, 89, 24, 11, 215, 44, 175, 161, 85, 158, 76, 25, 245, 78, 168, 206, 219, 182,
+ 165, 175, 222, 67, 25, 57, 110, 185, 42, 171, 52, 12, 96, 165, 12, 194, 40, 69, 128, 203,
+ 58, 208, 144, 23, 232, 217, 171, 198, 2, 105, 179, 216, 214, 135, 104, 11, 216, 108, 232, 52,
+ 65, 34, 115, 212, 242, 227, 191, 104, 221, 61, 111, 232, 126, 36, 38, 172, 101, 140, 213, 199,
+ 127, 213, 192, 170, 0, 0, 0,
+ ]
+ ).into()
+ )],
+ );
+ ma_valid(
+ "/dnsaddr/sjc-1.bootstrap.libp2p.io",
+ "3819736A632D312E626F6F7473747261702E6C69627032702E696F",
+ vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io"))],
+ );
+ ma_valid(
+ "/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
+ "3819736A632D312E626F6F7473747261702E6C69627032702E696F0604D2A50322122006B3608AA000274049EB28AD8E793A26FF6FAB281A7D3BD77CD18EB745DFAABB",
+ vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io")), Tcp(1234), P2p(peer_id("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"))]
+ );
+ ma_valid(
+ "/ip4/127.0.0.1/tcp/127/ws",
+ "047F00000106007FDD03",
+ vec![Ip4(local), Tcp(127), Ws("/".into())],
+ );
+ ma_valid(
+ "/ip4/127.0.0.1/tcp/127/tls",
+ "047F00000106007FC003",
+ vec![Ip4(local), Tcp(127), Tls],
+ );
+ ma_valid(
+ "/ip4/127.0.0.1/tcp/127/tls/ws",
+ "047F00000106007FC003DD03",
+ vec![Ip4(local), Tcp(127), Tls, Ws("/".into())],
+ );
+
+ ma_valid(
+ "/ip4/127.0.0.1/tcp/127/noise",
+ "047F00000106007FC603",
+ vec![Ip4(local), Tcp(127), Noise],
+ );
+
+ ma_valid(
+ "/ip4/127.0.0.1/udp/1234/webrtc-direct",
+ "047F000001910204D29802",
+ vec![Ip4(local), Udp(1234), WebRTCDirect],
+ );
+
+ let (_base, decoded) =
+ multibase::decode("uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g").unwrap();
+ ma_valid(
+ "/ip4/127.0.0.1/udp/1234/webrtc-direct/certhash/uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g",
+ "047F000001910204D29802D203221220C3AB8FF13720E8AD9047DD39466B3C8974E592C2FA383D4A3960714CAEF0C4F2",
+ vec![
+ Ip4(local),
+ Udp(1234),
+ WebRTCDirect,
+ Certhash(Multihash::from_bytes(&decoded).unwrap()),
+ ],
+ );
+
+ ma_valid(
+ "/ip4/127.0.0.1/udp/1234/quic/webtransport",
+ "047F000001910204D2CC03D103",
+ vec![Ip4(local), Udp(1234), Quic, WebTransport],
+ );
+
+ let (_base, decoded) =
+ multibase::decode("uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g").unwrap();
+ ma_valid(
+ "/ip4/127.0.0.1/udp/1234/webtransport/certhash/uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g",
+ "047F000001910204D2D103D203221220C3AB8FF13720E8AD9047DD39466B3C8974E592C2FA383D4A3960714CAEF0C4F2",
+ vec![
+ Ip4(local),
+ Udp(1234),
+ WebTransport,
+ Certhash(Multihash::from_bytes(&decoded).unwrap()),
+ ],
+ );
+}
+
+#[test]
+fn construct_fail() {
+ let addresses = [
+ "/ip4",
+ "/ip4/::1",
+ "/ip4/fdpsofodsajfdoisa",
+ "/ip6",
+ "/ip6/fe80::9700:803e:ca65:66e8:c21/ip6zone",
+ "/udp",
+ "/tcp",
+ "/sctp",
+ "/udp/65536",
+ "/tcp/65536",
+ "/onion/9imaq4ygg2iegci7:80",
+ "/onion/aaimaq4ygg2iegci7:80",
+ "/onion/timaq4ygg2iegci7:0",
+ "/onion/timaq4ygg2iegci7:-1",
+ "/onion/timaq4ygg2iegci7",
+ "/onion/timaq4ygg2iegci@:666",
+ "/onion3/9ww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:80",
+ "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd7:80",
+ "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:0",
+ "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:-1",
+ "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd",
+ "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyy@:666",
+ "/garlic64/jT~",
+ "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu",
+ "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu77",
+ "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu:80",
+ "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzuq:-1",
+ "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu@",
+ "/udp/1234/sctp",
+ "/udp/1234/udt/1234",
+ "/udp/1234/utp/1234",
+ "/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa",
+ "/ip4/127.0.0.1/udp",
+ "/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa",
+ "/ip4/127.0.0.1/tcp",
+ "/ip4/127.0.0.1/p2p",
+ "/ip4/127.0.0.1/p2p/tcp",
+ "/p2p-circuit/50",
+ "/ip4/127.0.0.1/udp/1234/webrtc-direct/certhash",
+ "/ip4/127.0.0.1/udp/1234/webrtc-direct/certhash/b2uaraocy6yrdblb4sfptaddgimjmmp", // 1 character missing from certhash
+ "/tcp/1234/http/http-path/a/b",
+ ];
+
+ for address in &addresses {
+ assert!(
+ address.parse::().is_err(),
+ "{}",
+ address.to_string()
+ );
+ }
+}
+
+#[test]
+fn to_multiaddr() {
+ assert_eq!(
+ Multiaddr::from(Ipv4Addr::new(127, 0, 0, 1)),
+ "/ip4/127.0.0.1".parse().unwrap()
+ );
+ assert_eq!(
+ Multiaddr::from(Ipv6Addr::new(
+ 0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21
+ )),
+ "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse().unwrap()
+ );
+ assert_eq!(
+ Multiaddr::try_from("/ip4/127.0.0.1/tcp/1234".to_string()).unwrap(),
+ "/ip4/127.0.0.1/tcp/1234".parse::().unwrap()
+ );
+ assert_eq!(
+ Multiaddr::try_from("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21").unwrap(),
+ "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21"
+ .parse::()
+ .unwrap()
+ );
+ assert_eq!(
+ Multiaddr::from(Ipv4Addr::new(127, 0, 0, 1)).with(Protocol::Tcp(1234)),
+ "/ip4/127.0.0.1/tcp/1234".parse::().unwrap()
+ );
+ assert_eq!(
+ Multiaddr::from(Ipv6Addr::new(
+ 0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21
+ ))
+ .with(Protocol::Tcp(1234)),
+ "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/tcp/1234"
+ .parse::()
+ .unwrap()
+ );
+}
+
+#[test]
+fn from_bytes_fail() {
+ let bytes = vec![1, 2, 3, 4];
+ assert!(Multiaddr::try_from(bytes).is_err());
+}
+
+#[test]
+fn ser_and_deser_json() {
+ let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0/tls".parse::().unwrap();
+ let serialized = serde_json::to_string(&addr).unwrap();
+ assert_eq!(serialized, "\"/ip4/0.0.0.0/tcp/0/tls\"");
+ let deserialized: Multiaddr = serde_json::from_str(&serialized).unwrap();
+ assert_eq!(addr, deserialized);
+}
+
+#[test]
+fn ser_and_deser_bincode() {
+ let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0/tls".parse::().unwrap();
+ let serialized = bincode::serialize(&addr).unwrap();
+ // compact addressing
+ assert_eq!(
+ serialized,
+ vec![10, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 6, 0, 0, 192, 3]
+ );
+ let deserialized: Multiaddr = bincode::deserialize(&serialized).unwrap();
+ assert_eq!(addr, deserialized);
+}
+
+#[test]
+fn append() {
+ let mut a: Multiaddr = Protocol::Ip4(Ipv4Addr::new(1, 2, 3, 4)).into();
+ a.push(Protocol::Tcp(80));
+ a.push(Protocol::Http);
+
+ let mut i = a.iter();
+ assert_eq!(Some(Protocol::Ip4(Ipv4Addr::new(1, 2, 3, 4))), i.next());
+ assert_eq!(Some(Protocol::Tcp(80)), i.next());
+ assert_eq!(Some(Protocol::Http), i.next());
+ assert_eq!(None, i.next())
+}
+
+fn replace_ip_addr(a: &Multiaddr, p: Protocol<'_>) -> Option {
+ a.replace(0, move |x| match x {
+ Protocol::Ip4(_) | Protocol::Ip6(_) => Some(p),
+ _ => None,
+ })
+}
+
+#[test]
+fn replace_ip4_with_ip4() {
+ let server = multiaddr!(Ip4(Ipv4Addr::LOCALHOST), Tcp(10000u16));
+ let result = replace_ip_addr(&server, Protocol::Ip4([80, 81, 82, 83].into())).unwrap();
+ assert_eq!(result, multiaddr!(Ip4([80, 81, 82, 83]), Tcp(10000u16)))
+}
+
+#[test]
+fn replace_ip6_with_ip4() {
+ let server = multiaddr!(Ip6(Ipv6Addr::LOCALHOST), Tcp(10000u16));
+ let result = replace_ip_addr(&server, Protocol::Ip4([80, 81, 82, 83].into())).unwrap();
+ assert_eq!(result, multiaddr!(Ip4([80, 81, 82, 83]), Tcp(10000u16)))
+}
+
+#[test]
+fn replace_ip4_with_ip6() {
+ let server = multiaddr!(Ip4(Ipv4Addr::LOCALHOST), Tcp(10000u16));
+ let result = replace_ip_addr(&server, "2001:db8::1".parse::().unwrap().into());
+ assert_eq!(
+ result.unwrap(),
+ "/ip6/2001:db8::1/tcp/10000".parse::().unwrap()
+ )
+}
+
+#[test]
+fn unknown_protocol_string() {
+ match "/unknown/1.2.3.4".parse::() {
+ Ok(_) => panic!("The UnknownProtocolString error should be caused"),
+ Err(e) => match e {
+ crate::Error::UnknownProtocolString(protocol) => {
+ assert_eq!(protocol, "unknown")
+ }
+ _ => panic!("The UnknownProtocolString error should be caused"),
+ },
+ }
+}
+
+#[test]
+fn protocol_stack() {
+ let addresses = [
+ "/ip4/0.0.0.0",
+ "/ip6/::1",
+ "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
+ "/ip6/fe80::9700:803e:ca65:66e8:c21/ip6zone/wlan0",
+ "/udp/0",
+ "/tcp/0",
+ "/sctp/0",
+ "/udp/1234",
+ "/tcp/1234",
+ "/sctp/1234",
+ "/udp/65535",
+ "/tcp/65535",
+ "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "/udp/1234/sctp/1234",
+ "/udp/1234/udt",
+ "/udp/1234/utp",
+ "/tcp/1234/http",
+ "/tcp/1234/tls/http",
+ "/tcp/1234/http/http-path/user",
+ "/tcp/1234/http/http-path/api%2Fv1%2Flogin",
+ "/tcp/1234/http/http-path/a%20space",
+ "/tcp/1234/https",
+ "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
+ "/ip4/127.0.0.1/udp/1234",
+ "/ip4/127.0.0.1/udp/0",
+ "/ip4/127.0.0.1/tcp/1234",
+ "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
+ "/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "/p2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095/tcp/8000/wss/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "/ip4/127.0.0.1/tcp/9090/p2p-circuit/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ "/onion/aaimaq4ygg2iegci:80",
+ "/dnsaddr/sjc-1.bootstrap.libp2p.io",
+ "/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
+ "/ip4/127.0.0.1/tcp/127/ws",
+ "/ip4/127.0.0.1/tcp/127/tls",
+ "/ip4/127.0.0.1/tcp/127/tls/ws",
+ "/ip4/127.0.0.1/tcp/127/noise",
+ "/ip4/127.0.0.1/udp/1234/webrtc-direct",
+ ];
+ let argless = std::collections::HashSet::from([
+ "http",
+ "https",
+ "noise",
+ "p2p-circuit",
+ "p2p-webrtc-direct",
+ "p2p-webrtc-star",
+ "p2p-websocket-star",
+ "quic",
+ "quic-v1",
+ "tls",
+ "udt",
+ "utp",
+ "webrtc-direct",
+ "ws",
+ "wss",
+ ]);
+ for addr_str in addresses {
+ let ma = Multiaddr::from_str(addr_str).expect("These are supposed to be valid multiaddrs");
+ let ps: Vec<&str> = ma.protocol_stack().collect();
+ let mut toks: Vec<&str> = addr_str.split('/').collect();
+ assert_eq!("", toks[0]);
+ toks.remove(0);
+ let mut i = 0;
+ while i < toks.len() {
+ let proto_tag = toks[i];
+ i += 1;
+ if argless.contains(proto_tag) {
+ //skip
+ } else {
+ toks.remove(i);
+ }
+ }
+ assert_eq!(ps, toks);
+ }
+}
+
+// Assert all `Protocol` variants are covered
+// in its `Arbitrary` impl.
+#[cfg(nightly)]
+#[test]
+fn arbitrary_impl_for_all_proto_variants() {
+ let variants = core::mem::variant_count::() as u8;
+ assert_eq!(variants, Proto::IMPL_VARIANT_COUNT);
+}
+
+mod multiaddr_with_p2p {
+ use multiaddr::{Multiaddr, PeerId};
+
+ fn test_multiaddr_with_p2p(
+ multiaddr: &str,
+ peer: &str,
+ expected: std::result::Result<&str, &str>,
+ ) {
+ let peer = peer.parse::().unwrap();
+ let expected = expected
+ .map(|a| a.parse::().unwrap())
+ .map_err(|a| a.parse::().unwrap());
+
+ let mut multiaddr = multiaddr.parse::().unwrap();
+ // Testing multiple time to validate idempotence.
+ for _ in 0..3 {
+ let result = multiaddr.with_p2p(peer);
+ assert_eq!(result, expected);
+ multiaddr = result.unwrap_or_else(|addr| addr);
+ }
+ }
+
+ #[test]
+ fn empty_multiaddr() {
+ // Multiaddr is empty -> it should push and return Ok.
+ test_multiaddr_with_p2p(
+ "",
+ "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
+ Ok("/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"),
+ )
+ }
+ #[test]
+ fn non_p2p_terminated() {
+ // Last protocol is not p2p -> it should push and return Ok.
+ test_multiaddr_with_p2p(
+ "/ip4/127.0.0.1",
+ "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
+ Ok("/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"),
+ )
+ }
+
+ #[test]
+ fn p2p_terminated_same_peer() {
+ // Last protocol is p2p and the contained peer matches the provided one -> it should do nothing and return Ok.
+ test_multiaddr_with_p2p(
+ "/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
+ "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
+ Ok("/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"),
+ )
+ }
+
+ #[test]
+ fn p2p_terminated_different_peer() {
+ // Last protocol is p2p but the contained peer does not match the provided one -> it should do nothing and return Err.
+ test_multiaddr_with_p2p(
+ "/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
+ "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
+ Err("/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"),
+ )
+ }
+}
diff --git a/muxers/mplex/Cargo.toml b/muxers/mplex/Cargo.toml
index 4eb0a027363..a16b94df59a 100644
--- a/muxers/mplex/Cargo.toml
+++ b/muxers/mplex/Cargo.toml
@@ -30,7 +30,7 @@ futures = { workspace = true }
libp2p-identity = { workspace = true, features = ["rand"] }
libp2p-muxer-test-harness = { path = "../test-harness" }
libp2p-plaintext = { workspace = true }
-libp2p-tcp = { workspace = true, features = ["async-io"] }
+libp2p-tcp = { workspace = true, features = ["tokio"] }
quickcheck = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
diff --git a/muxers/mplex/benches/split_send_size.rs b/muxers/mplex/benches/split_send_size.rs
index 51f6a85dc66..3210a0d4410 100644
--- a/muxers/mplex/benches/split_send_size.rs
+++ b/muxers/mplex/benches/split_send_size.rs
@@ -186,7 +186,7 @@ fn tcp_transport(split_send_size: usize) -> BenchTransport {
let mut mplex = mplex::Config::default();
mplex.set_split_send_size(split_send_size);
- libp2p_tcp::async_io::Transport::new(libp2p_tcp::Config::default().nodelay(true))
+ libp2p_tcp::tokio::Transport::new(libp2p_tcp::Config::default().nodelay(true))
.upgrade(upgrade::Version::V1)
.authenticate(plaintext::Config::new(
&identity::Keypair::generate_ed25519(),
diff --git a/protocols/autonat/CHANGELOG.md b/protocols/autonat/CHANGELOG.md
index 16319724432..1ef95957e92 100644
--- a/protocols/autonat/CHANGELOG.md
+++ b/protocols/autonat/CHANGELOG.md
@@ -1,8 +1,10 @@
-## 0.14.1
+## 0.15.0
- Fix infinity loop on wrong `nonce` when performing `dial_back`.
See [PR 5848](https://github.com/libp2p/rust-libp2p/pull/5848).
+
+
## 0.14.0
- Verify that an incoming AutoNAT dial comes from a connected peer. See [PR 5597](https://github.com/libp2p/rust-libp2p/pull/5597).
diff --git a/protocols/autonat/Cargo.toml b/protocols/autonat/Cargo.toml
index 61a15eae3bc..9a2f227d412 100644
--- a/protocols/autonat/Cargo.toml
+++ b/protocols/autonat/Cargo.toml
@@ -3,7 +3,7 @@ name = "libp2p-autonat"
edition.workspace = true
rust-version = { workspace = true }
description = "NAT and firewall detection for libp2p"
-version = "0.14.1"
+version = "0.15.0"
authors = [
"David Craven ",
"Elena Frank