From 414a8fc2eda3bb72e30cefdba628cf6c361cd6e1 Mon Sep 17 00:00:00 2001
From: Aaro Altonen <48052676+altonen@users.noreply.github.com>
Date: Tue, 25 Jun 2024 16:51:05 +0300
Subject: [PATCH] Upgrade libp2p to 0.52.4 (#1631)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Upgrade libp2p to 0.52.4, including a fix:

* Set Kademlia to server mode
(https://github.com/paritytech/substrate/pull/14703)

### TODO
- [x] Fix 3 zombienet tests failing:
  - [x] `zombienet-substrate-0002-validators-warp-sync`
- [ ]
~`zombienet-polkadot-functional-0005-parachains-disputes-past-session`~
The test is also flaky in other PRs and is not required for CI to
succeed.
  - [x] `zombienet-polkadot-functional-0009-approval-voting-coalescing`
- [x] Uncomment and update to the actual libp2p API tests in
[`substrate/client/network/src/protocol/notifications/handler.rs`](https://github.com/paritytech/polkadot-sdk/blob/7331f1796f1a0b0e9fb0cd7bf441239ad9664595/substrate/client/network/src/protocol/notifications/handler.rs#L1009).
- [x] When upgrading `multihash` crate as part of libp2p upgrade to
version v0.19.1, uncomment the conversion code at
https://github.com/paritytech/polkadot-sdk/blob/7547c4942a887029c11cbcfd5103f6d8315ab95c/substrate/client/network/types/src/multihash.rs#L159
- [x] Perform a burn-in.

---------

Co-authored-by: Anton <anton.kalyaev@gmail.com>
Co-authored-by: command-bot <>
Co-authored-by: Dmitry Markin <dmitry@markin.tech>
Co-authored-by: Bastian Köcher <git@kchr.de>
---
 Cargo.lock                                    | 766 ++++++++++--------
 Cargo.toml                                    |  10 +-
 prdoc/pr_1631.prdoc                           |  39 +
 .../client/authority-discovery/Cargo.toml     |   7 +-
 .../client/authority-discovery/src/service.rs |   2 +-
 .../client/authority-discovery/src/worker.rs  |  12 +-
 .../src/worker/addr_cache.rs                  |  16 +-
 substrate/client/network-gossip/Cargo.toml    |   1 -
 substrate/client/network-gossip/src/bridge.rs |   3 +-
 substrate/client/network-gossip/src/lib.rs    |  10 +-
 .../network-gossip/src/state_machine.rs       |   2 +-
 substrate/client/network/src/behaviour.rs     |  18 +-
 substrate/client/network/src/config.rs        |   8 +-
 substrate/client/network/src/discovery.rs     | 147 ++--
 .../client/network/src/litep2p/service.rs     |  16 +-
 substrate/client/network/src/peer_info.rs     |  92 ++-
 substrate/client/network/src/protocol.rs      |  19 +-
 .../src/protocol/notifications/behaviour.rs   |  37 +-
 .../src/protocol/notifications/handler.rs     |  85 +-
 .../src/protocol/notifications/tests.rs       |  16 +-
 .../src/protocol/notifications/upgrade.rs     |   1 +
 .../protocol/notifications/upgrade/collec.rs  |  12 +-
 .../notifications/upgrade/notifications.rs    | 228 +++---
 .../client/network/src/request_responses.rs   |  49 +-
 substrate/client/network/src/service.rs       | 146 ++--
 substrate/client/network/src/transport.rs     |   4 +-
 substrate/client/network/src/types.rs         |   8 +-
 substrate/client/network/statement/Cargo.toml |   1 -
 .../client/network/transactions/Cargo.toml    |   1 -
 substrate/client/network/types/Cargo.toml     |   5 +-
 .../client/network/types/src/multiaddr.rs     |  13 +
 .../network/types/src/multiaddr/protocol.rs   | 112 +++
 .../client/network/types/src/multihash.rs     |  30 +-
 substrate/client/offchain/Cargo.toml          |   1 -
 substrate/client/telemetry/src/transport.rs   |   2 +-
 35 files changed, 1128 insertions(+), 791 deletions(-)
 create mode 100644 prdoc/pr_1631.prdoc

diff --git a/Cargo.lock b/Cargo.lock
index 8052bcceee6..6f1e91f7b02 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1076,7 +1076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
 dependencies = [
  "concurrent-queue",
- "event-listener",
+ "event-listener 2.5.3",
  "futures-core",
 ]
 
@@ -1086,11 +1086,11 @@ version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb"
 dependencies = [
- "async-lock",
+ "async-lock 2.8.0",
  "async-task",
  "concurrent-queue",
  "fastrand 1.9.0",
- "futures-lite",
+ "futures-lite 1.13.0",
  "slab",
 ]
 
@@ -1100,10 +1100,10 @@ version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
 dependencies = [
- "async-lock",
+ "async-lock 2.8.0",
  "autocfg",
  "blocking",
- "futures-lite",
+ "futures-lite 1.13.0",
 ]
 
 [[package]]
@@ -1114,10 +1114,10 @@ checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
 dependencies = [
  "async-channel",
  "async-executor",
- "async-io",
- "async-lock",
+ "async-io 1.13.0",
+ "async-lock 2.8.0",
  "blocking",
- "futures-lite",
+ "futures-lite 1.13.0",
  "once_cell",
 ]
 
@@ -1127,27 +1127,57 @@ version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
 dependencies = [
- "async-lock",
+ "async-lock 2.8.0",
  "autocfg",
  "cfg-if",
  "concurrent-queue",
- "futures-lite",
+ "futures-lite 1.13.0",
  "log",
  "parking",
- "polling",
+ "polling 2.8.0",
  "rustix 0.37.23",
  "slab",
  "socket2 0.4.9",
  "waker-fn",
 ]
 
+[[package]]
+name = "async-io"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964"
+dependencies = [
+ "async-lock 3.4.0",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite 2.3.0",
+ "parking",
+ "polling 3.4.0",
+ "rustix 0.38.21",
+ "slab",
+ "tracing",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "async-lock"
 version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
 dependencies = [
- "event-listener",
+ "event-listener 2.5.3",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
+dependencies = [
+ "event-listener 5.2.0",
+ "event-listener-strategy",
+ "pin-project-lite 0.2.12",
 ]
 
 [[package]]
@@ -1156,10 +1186,10 @@ version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f"
 dependencies = [
- "async-io",
+ "async-io 1.13.0",
  "autocfg",
  "blocking",
- "futures-lite",
+ "futures-lite 1.13.0",
 ]
 
 [[package]]
@@ -1168,13 +1198,13 @@ version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9"
 dependencies = [
- "async-io",
- "async-lock",
+ "async-io 1.13.0",
+ "async-lock 2.8.0",
  "autocfg",
  "blocking",
  "cfg-if",
- "event-listener",
- "futures-lite",
+ "event-listener 2.5.3",
+ "futures-lite 1.13.0",
  "rustix 0.37.23",
  "signal-hook",
  "windows-sys 0.48.0",
@@ -1189,13 +1219,13 @@ dependencies = [
  "async-attributes",
  "async-channel",
  "async-global-executor",
- "async-io",
- "async-lock",
+ "async-io 1.13.0",
+ "async-lock 2.8.0",
  "crossbeam-utils",
  "futures-channel",
  "futures-core",
  "futures-io",
- "futures-lite",
+ "futures-lite 1.13.0",
  "gloo-timers",
  "kv-log-macro",
  "log",
@@ -1271,6 +1301,17 @@ version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
 
+[[package]]
+name = "attohttpc"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2"
+dependencies = [
+ "http",
+ "log",
+ "url",
+]
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -1603,11 +1644,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65"
 dependencies = [
  "async-channel",
- "async-lock",
+ "async-lock 2.8.0",
  "async-task",
  "atomic-waker",
  "fastrand 1.9.0",
- "futures-lite",
+ "futures-lite 1.13.0",
  "log",
 ]
 
@@ -5334,6 +5375,27 @@ version = "2.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
 
+[[package]]
+name = "event-listener"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite 0.2.12",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
+dependencies = [
+ "event-listener 5.2.0",
+ "pin-project-lite 0.2.12",
+]
+
 [[package]]
 name = "exit-future"
 version = "0.2.0"
@@ -5574,7 +5636,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
 dependencies = [
  "crc32fast",
- "libz-sys",
  "miniz_oxide",
 ]
 
@@ -5939,7 +6000,7 @@ dependencies = [
  "frame-support-procedural-tools",
  "itertools 0.11.0",
  "macro_magic",
- "proc-macro-warning",
+ "proc-macro-warning 1.0.0",
  "proc-macro2 1.0.82",
  "quote 1.0.35",
  "regex",
@@ -6141,6 +6202,16 @@ dependencies = [
  "futures-util",
 ]
 
+[[package]]
+name = "futures-bounded"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b07bbbe7d7e78809544c6f718d875627addc73a7c3582447abc052cd3dc67e0"
+dependencies = [
+ "futures-timer",
+ "futures-util",
+]
+
 [[package]]
 name = "futures-channel"
 version = "0.3.30"
@@ -6190,6 +6261,16 @@ dependencies = [
  "waker-fn",
 ]
 
+[[package]]
+name = "futures-lite"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
+dependencies = [
+ "futures-core",
+ "pin-project-lite 0.2.12",
+]
+
 [[package]]
 name = "futures-macro"
 version = "0.3.30"
@@ -6203,13 +6284,12 @@ dependencies = [
 
 [[package]]
 name = "futures-rustls"
-version = "0.22.2"
+version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd"
+checksum = "35bd3cf68c183738046838e300353e4716c674dc5e56890de4826801a6622a28"
 dependencies = [
  "futures-io",
- "rustls 0.20.8",
- "webpki",
+ "rustls 0.21.7",
 ]
 
 [[package]]
@@ -6744,7 +6824,7 @@ dependencies = [
  "http",
  "hyper",
  "log",
- "rustls 0.21.6",
+ "rustls 0.21.7",
  "rustls-native-certs 0.6.3",
  "tokio",
  "tokio-rustls 0.24.1",
@@ -6796,21 +6876,21 @@ dependencies = [
 
 [[package]]
 name = "if-addrs"
-version = "0.7.0"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9"
+checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a"
 dependencies = [
  "libc",
- "winapi",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
 name = "if-watch"
-version = "3.0.1"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f"
+checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e"
 dependencies = [
- "async-io",
+ "async-io 2.3.3",
  "core-foundation",
  "fnv",
  "futures",
@@ -6820,7 +6900,26 @@ dependencies = [
  "rtnetlink",
  "system-configuration",
  "tokio",
- "windows 0.34.0",
+ "windows 0.51.1",
+]
+
+[[package]]
+name = "igd-next"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "064d90fec10d541084e7b39ead8875a5a80d9114a2b18791565253bae25f49e4"
+dependencies = [
+ "async-trait",
+ "attohttpc",
+ "bytes",
+ "futures",
+ "http",
+ "hyper",
+ "log",
+ "rand 0.8.5",
+ "tokio",
+ "url",
+ "xmltree",
 ]
 
 [[package]]
@@ -7037,13 +7136,13 @@ dependencies = [
  "curl",
  "curl-sys",
  "encoding_rs",
- "event-listener",
- "futures-lite",
+ "event-listener 2.5.3",
+ "futures-lite 1.13.0",
  "http",
  "log",
  "mime",
  "once_cell",
- "polling",
+ "polling 2.8.0",
  "slab",
  "sluice",
  "tracing",
@@ -7458,11 +7557,12 @@ dependencies = [
 
 [[package]]
 name = "libp2p"
-version = "0.51.4"
+version = "0.52.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f35eae38201a993ece6bdc823292d6abd1bffed1c4d0f4a3517d2bd8e1d917fe"
+checksum = "e94495eb319a85b70a68b85e2389a95bb3555c71c49025b78c691a854a7e6464"
 dependencies = [
  "bytes",
+ "either",
  "futures",
  "futures-timer",
  "getrandom 0.2.10",
@@ -7482,18 +7582,21 @@ dependencies = [
  "libp2p-request-response",
  "libp2p-swarm",
  "libp2p-tcp",
+ "libp2p-upnp",
  "libp2p-wasm-ext",
  "libp2p-websocket",
  "libp2p-yamux",
- "multiaddr",
+ "multiaddr 0.18.1",
  "pin-project",
+ "rw-stream-sink",
+ "thiserror",
 ]
 
 [[package]]
 name = "libp2p-allow-block-list"
-version = "0.1.1"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "510daa05efbc25184458db837f6f9a5143888f1caa742426d92e1833ddd38a50"
+checksum = "55b46558c5c0bf99d3e2a1a38fd54ff5476ca66dd1737b12466a1824dd219311"
 dependencies = [
  "libp2p-core",
  "libp2p-identity",
@@ -7503,9 +7606,9 @@ dependencies = [
 
 [[package]]
 name = "libp2p-connection-limits"
-version = "0.1.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4caa33f1d26ed664c4fe2cca81a08c8e07d4c1c04f2f4ac7655c2dd85467fda0"
+checksum = "2f5107ad45cb20b2f6c3628c7b6014b996fcb13a88053f4569c872c6e30abf58"
 dependencies = [
  "libp2p-core",
  "libp2p-identity",
@@ -7515,9 +7618,9 @@ dependencies = [
 
 [[package]]
 name = "libp2p-core"
-version = "0.39.2"
+version = "0.40.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2"
+checksum = "dd44289ab25e4c9230d9246c475a22241e301b23e8f4061d3bdef304a1a99713"
 dependencies = [
  "either",
  "fnv",
@@ -7526,8 +7629,8 @@ dependencies = [
  "instant",
  "libp2p-identity",
  "log",
- "multiaddr",
- "multihash 0.17.0",
+ "multiaddr 0.18.1",
+ "multihash 0.19.1",
  "multistream-select",
  "once_cell",
  "parking_lot 0.12.1",
@@ -7543,33 +7646,36 @@ dependencies = [
 
 [[package]]
 name = "libp2p-dns"
-version = "0.39.0"
+version = "0.40.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "146ff7034daae62077c415c2376b8057368042df6ab95f5432ad5e88568b1554"
+checksum = "e6a18db73084b4da2871438f6239fef35190b05023de7656e877c18a00541a3b"
 dependencies = [
+ "async-trait",
  "futures",
  "libp2p-core",
+ "libp2p-identity",
  "log",
  "parking_lot 0.12.1",
  "smallvec",
- "trust-dns-resolver 0.22.0",
+ "trust-dns-resolver",
 ]
 
 [[package]]
 name = "libp2p-identify"
-version = "0.42.2"
+version = "0.43.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5455f472243e63b9c497ff320ded0314254a9eb751799a39c283c6f20b793f3c"
+checksum = "45a96638a0a176bec0a4bcaebc1afa8cf909b114477209d7456ade52c61cd9cd"
 dependencies = [
  "asynchronous-codec",
  "either",
  "futures",
+ "futures-bounded",
  "futures-timer",
  "libp2p-core",
  "libp2p-identity",
  "libp2p-swarm",
  "log",
- "lru 0.10.1",
+ "lru 0.12.3",
  "quick-protobuf",
  "quick-protobuf-codec",
  "smallvec",
@@ -7579,27 +7685,27 @@ dependencies = [
 
 [[package]]
 name = "libp2p-identity"
-version = "0.1.3"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "276bb57e7af15d8f100d3c11cbdd32c6752b7eef4ba7a18ecf464972c07abcce"
+checksum = "999ec70441b2fb35355076726a6bc466c932e9bdc66f6a11c6c0aa17c7ab9be0"
 dependencies = [
- "bs58 0.4.0",
+ "bs58 0.5.0",
  "ed25519-dalek 2.1.1",
- "log",
- "multiaddr",
- "multihash 0.17.0",
+ "hkdf",
+ "multihash 0.19.1",
  "quick-protobuf",
  "rand 0.8.5",
  "sha2 0.10.8",
  "thiserror",
+ "tracing",
  "zeroize",
 ]
 
 [[package]]
 name = "libp2p-kad"
-version = "0.43.3"
+version = "0.44.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39d5ef876a2b2323d63c258e63c2f8e36f205fe5a11f0b3095d59635650790ff"
+checksum = "16ea178dabba6dde6ffc260a8e0452ccdc8f79becf544946692fff9d412fc29d"
 dependencies = [
  "arrayvec 0.7.4",
  "asynchronous-codec",
@@ -7614,6 +7720,7 @@ dependencies = [
  "libp2p-swarm",
  "log",
  "quick-protobuf",
+ "quick-protobuf-codec",
  "rand 0.8.5",
  "sha2 0.10.8",
  "smallvec",
@@ -7625,9 +7732,9 @@ dependencies = [
 
 [[package]]
 name = "libp2p-mdns"
-version = "0.43.1"
+version = "0.44.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19983e1f949f979a928f2c603de1cf180cc0dc23e4ac93a62651ccb18341460b"
+checksum = "42a2567c305232f5ef54185e9604579a894fd0674819402bb0ac0246da82f52a"
 dependencies = [
  "data-encoding",
  "futures",
@@ -7638,7 +7745,7 @@ dependencies = [
  "log",
  "rand 0.8.5",
  "smallvec",
- "socket2 0.4.9",
+ "socket2 0.5.6",
  "tokio",
  "trust-dns-proto 0.22.0",
  "void",
@@ -7646,30 +7753,35 @@ dependencies = [
 
 [[package]]
 name = "libp2p-metrics"
-version = "0.12.0"
+version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a42ec91e227d7d0dafa4ce88b333cdf5f277253873ab087555c92798db2ddd46"
+checksum = "239ba7d28f8d0b5d77760dc6619c05c7e88e74ec8fbbe97f856f20a56745e620"
 dependencies = [
+ "instant",
  "libp2p-core",
  "libp2p-identify",
+ "libp2p-identity",
  "libp2p-kad",
  "libp2p-ping",
  "libp2p-swarm",
+ "once_cell",
  "prometheus-client",
 ]
 
 [[package]]
 name = "libp2p-noise"
-version = "0.42.2"
+version = "0.43.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c3673da89d29936bc6435bafc638e2f184180d554ce844db65915113f86ec5e"
+checksum = "d2eeec39ad3ad0677551907dd304b2f13f17208ccebe333bef194076cd2e8921"
 dependencies = [
  "bytes",
- "curve25519-dalek 3.2.0",
+ "curve25519-dalek 4.1.3",
  "futures",
  "libp2p-core",
  "libp2p-identity",
  "log",
+ "multiaddr 0.18.1",
+ "multihash 0.19.1",
  "once_cell",
  "quick-protobuf",
  "rand 0.8.5",
@@ -7677,21 +7789,22 @@ dependencies = [
  "snow",
  "static_assertions",
  "thiserror",
- "x25519-dalek 1.1.1",
+ "x25519-dalek",
  "zeroize",
 ]
 
 [[package]]
 name = "libp2p-ping"
-version = "0.42.0"
+version = "0.43.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e57759c19c28a73ef1eb3585ca410cefb72c1a709fcf6de1612a378e4219202"
+checksum = "e702d75cd0827dfa15f8fd92d15b9932abe38d10d21f47c50438c71dd1b5dae3"
 dependencies = [
  "either",
  "futures",
  "futures-timer",
  "instant",
  "libp2p-core",
+ "libp2p-identity",
  "libp2p-swarm",
  "log",
  "rand 0.8.5",
@@ -7700,9 +7813,9 @@ dependencies = [
 
 [[package]]
 name = "libp2p-quic"
-version = "0.7.0-alpha.3"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6b26abd81cd2398382a1edfe739b539775be8a90fa6914f39b2ab49571ec735"
+checksum = "130d451d83f21b81eb7b35b360bc7972aeafb15177784adc56528db082e6b927"
 dependencies = [
  "bytes",
  "futures",
@@ -7713,18 +7826,20 @@ dependencies = [
  "libp2p-tls",
  "log",
  "parking_lot 0.12.1",
- "quinn-proto",
+ "quinn 0.10.2",
  "rand 0.8.5",
- "rustls 0.20.8",
+ "ring 0.16.20",
+ "rustls 0.21.7",
+ "socket2 0.5.6",
  "thiserror",
  "tokio",
 ]
 
 [[package]]
 name = "libp2p-request-response"
-version = "0.24.1"
+version = "0.25.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ffdb374267d42dc5ed5bc53f6e601d4a64ac5964779c6e40bb9e4f14c1e30d5"
+checksum = "d8e3b4d67870478db72bac87bfc260ee6641d0734e0e3e275798f089c3fecfd4"
 dependencies = [
  "async-trait",
  "futures",
@@ -7732,15 +7847,17 @@ dependencies = [
  "libp2p-core",
  "libp2p-identity",
  "libp2p-swarm",
+ "log",
  "rand 0.8.5",
  "smallvec",
+ "void",
 ]
 
 [[package]]
 name = "libp2p-swarm"
-version = "0.42.2"
+version = "0.43.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "903b3d592d7694e56204d211f29d31bc004be99386644ba8731fc3e3ef27b296"
+checksum = "580189e0074af847df90e75ef54f3f30059aedda37ea5a1659e8b9fca05c0141"
 dependencies = [
  "either",
  "fnv",
@@ -7751,6 +7868,8 @@ dependencies = [
  "libp2p-identity",
  "libp2p-swarm-derive",
  "log",
+ "multistream-select",
+ "once_cell",
  "rand 0.8.5",
  "smallvec",
  "tokio",
@@ -7759,36 +7878,39 @@ dependencies = [
 
 [[package]]
 name = "libp2p-swarm-derive"
-version = "0.32.0"
+version = "0.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fba456131824ab6acd4c7bf61e9c0f0a3014b5fc9868ccb8e10d344594cdc4f"
+checksum = "c4d5ec2a3df00c7836d7696c136274c9c59705bac69133253696a6c932cd1d74"
 dependencies = [
  "heck 0.4.1",
+ "proc-macro-warning 0.4.2",
+ "proc-macro2 1.0.82",
  "quote 1.0.35",
- "syn 1.0.109",
+ "syn 2.0.61",
 ]
 
 [[package]]
 name = "libp2p-tcp"
-version = "0.39.0"
+version = "0.40.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33d33698596d7722d85d3ab0c86c2c322254fce1241e91208e3679b4eb3026cf"
+checksum = "b558dd40d1bcd1aaaed9de898e9ec6a436019ecc2420dd0016e712fbb61c5508"
 dependencies = [
  "futures",
  "futures-timer",
  "if-watch",
  "libc",
  "libp2p-core",
+ "libp2p-identity",
  "log",
- "socket2 0.4.9",
+ "socket2 0.5.6",
  "tokio",
 ]
 
 [[package]]
 name = "libp2p-tls"
-version = "0.1.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781"
+checksum = "8218d1d5482b122ccae396bbf38abdcb283ecc96fa54760e1dfd251f0546ac61"
 dependencies = [
  "futures",
  "futures-rustls",
@@ -7796,51 +7918,68 @@ dependencies = [
  "libp2p-identity",
  "rcgen",
  "ring 0.16.20",
- "rustls 0.20.8",
+ "rustls 0.21.7",
+ "rustls-webpki 0.101.4",
  "thiserror",
- "webpki",
- "x509-parser 0.14.0",
+ "x509-parser",
  "yasna",
 ]
 
+[[package]]
+name = "libp2p-upnp"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82775a47b34f10f787ad3e2a22e2c1541e6ebef4fe9f28f3ac553921554c94c1"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "igd-next",
+ "libp2p-core",
+ "libp2p-swarm",
+ "log",
+ "tokio",
+ "void",
+]
+
 [[package]]
 name = "libp2p-wasm-ext"
-version = "0.39.0"
+version = "0.40.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77dff9d32353a5887adb86c8afc1de1a94d9e8c3bc6df8b2201d7cdf5c848f43"
+checksum = "1e5d8e3a9e07da0ef5b55a9f26c009c8fb3c725d492d8bb4b431715786eea79c"
 dependencies = [
  "futures",
  "js-sys",
  "libp2p-core",
- "parity-send-wrapper",
+ "send_wrapper",
  "wasm-bindgen",
  "wasm-bindgen-futures",
 ]
 
 [[package]]
 name = "libp2p-websocket"
-version = "0.41.0"
+version = "0.42.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "111273f7b3d3510524c752e8b7a5314b7f7a1fee7e68161c01a7d72cbb06db9f"
+checksum = "3facf0691bab65f571bc97c6c65ffa836248ca631d631b7691ac91deb7fceb5f"
 dependencies = [
  "either",
  "futures",
  "futures-rustls",
  "libp2p-core",
+ "libp2p-identity",
  "log",
  "parking_lot 0.12.1",
  "quicksink",
  "rw-stream-sink",
  "soketto",
  "url",
- "webpki-roots 0.22.6",
+ "webpki-roots",
 ]
 
 [[package]]
 name = "libp2p-yamux"
-version = "0.43.1"
+version = "0.44.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd21d950662700a385d4c6d68e2f5f54d778e97068cdd718522222ef513bda"
+checksum = "8eedcb62824c4300efb9cfd4e2a6edaf3ca097b9e68b36dabe45a44469fd6a85"
 dependencies = [
  "futures",
  "libp2p-core",
@@ -8022,7 +8161,7 @@ dependencies = [
  "indexmap 2.2.3",
  "libc",
  "mockall 0.12.1",
- "multiaddr",
+ "multiaddr 0.17.1",
  "multihash 0.17.0",
  "network-interface",
  "nohash-hasher",
@@ -8030,11 +8169,11 @@ dependencies = [
  "pin-project",
  "prost 0.11.9",
  "prost-build 0.11.9",
- "quinn",
+ "quinn 0.9.4",
  "rand 0.8.5",
  "rcgen",
  "ring 0.16.20",
- "rustls 0.20.8",
+ "rustls 0.20.9",
  "serde",
  "sha2 0.10.8",
  "simple-dns",
@@ -8049,13 +8188,13 @@ dependencies = [
  "tokio-tungstenite",
  "tokio-util",
  "tracing",
- "trust-dns-resolver 0.23.2",
+ "trust-dns-resolver",
  "uint",
  "unsigned-varint",
  "url",
  "webpki",
- "x25519-dalek 2.0.0",
- "x509-parser 0.15.1",
+ "x25519-dalek",
+ "x509-parser",
  "yasna",
  "zeroize",
 ]
@@ -8091,18 +8230,18 @@ dependencies = [
 
 [[package]]
 name = "lru"
-version = "0.10.1"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670"
-dependencies = [
- "hashbrown 0.13.2",
-]
+checksum = "eedb2bdbad7e0634f83989bf596f497b070130daaa398ab22d84c39e266deec5"
 
 [[package]]
 name = "lru"
-version = "0.11.0"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eedb2bdbad7e0634f83989bf596f497b070130daaa398ab22d84c39e266deec5"
+checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
+dependencies = [
+ "hashbrown 0.14.3",
+]
 
 [[package]]
 name = "lru-cache"
@@ -8601,6 +8740,25 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "multiaddr"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b852bc02a2da5feed68cd14fa50d0774b92790a5bdbfa932a813926c8472070"
+dependencies = [
+ "arrayref",
+ "byteorder",
+ "data-encoding",
+ "libp2p-identity",
+ "multibase",
+ "multihash 0.19.1",
+ "percent-encoding",
+ "serde",
+ "static_assertions",
+ "unsigned-varint",
+ "url",
+]
+
 [[package]]
 name = "multibase"
 version = "0.9.1"
@@ -8623,7 +8781,7 @@ dependencies = [
  "blake3",
  "core2",
  "digest 0.10.7",
- "multihash-derive 0.8.0",
+ "multihash-derive",
  "sha2 0.10.8",
  "sha3",
  "unsigned-varint",
@@ -8640,7 +8798,7 @@ dependencies = [
  "blake3",
  "core2",
  "digest 0.10.7",
- "multihash-derive 0.8.0",
+ "multihash-derive",
  "sha2 0.10.8",
  "sha3",
  "unsigned-varint",
@@ -8656,26 +8814,6 @@ dependencies = [
  "unsigned-varint",
 ]
 
-[[package]]
-name = "multihash-codetable"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6d815ecb3c8238d00647f8630ede7060a642c9f704761cd6082cb4028af6935"
-dependencies = [
- "blake2b_simd",
- "blake2s_simd",
- "blake3",
- "core2",
- "digest 0.10.7",
- "multihash-derive 0.9.0",
- "ripemd",
- "serde",
- "sha1",
- "sha2 0.10.8",
- "sha3",
- "strobe-rs",
-]
-
 [[package]]
 name = "multihash-derive"
 version = "0.8.0"
@@ -8690,31 +8828,6 @@ dependencies = [
  "synstructure",
 ]
 
-[[package]]
-name = "multihash-derive"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "890e72cb7396cb99ed98c1246a97b243cc16394470d94e0bc8b0c2c11d84290e"
-dependencies = [
- "core2",
- "multihash 0.19.1",
- "multihash-derive-impl",
-]
-
-[[package]]
-name = "multihash-derive-impl"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d38685e08adb338659871ecfc6ee47ba9b22dcc8abcf6975d379cc49145c3040"
-dependencies = [
- "proc-macro-crate 1.3.1",
- "proc-macro-error",
- "proc-macro2 1.0.82",
- "quote 1.0.35",
- "syn 1.0.109",
- "synstructure",
-]
-
 [[package]]
 name = "multimap"
 version = "0.8.3"
@@ -8723,9 +8836,9 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
 
 [[package]]
 name = "multistream-select"
-version = "0.12.1"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a"
+checksum = "ea0df8e5eec2298a62b326ee4f0d7fe1a6b90a09dfcf9df37b38f947a8c42f19"
 dependencies = [
  "bytes",
  "futures",
@@ -12058,12 +12171,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "parity-send-wrapper"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f"
-
 [[package]]
 name = "parity-util-mem"
 version = "0.12.0"
@@ -14969,6 +15076,20 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "polling"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30054e72317ab98eddd8561db0f6524df3367636884b7b21b703e4b280a84a14"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "pin-project-lite 0.2.12",
+ "rustix 0.38.21",
+ "tracing",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "poly1305"
 version = "0.8.0"
@@ -15191,6 +15312,17 @@ version = "0.5.20+deprecated"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
 
+[[package]]
+name = "proc-macro-warning"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e"
+dependencies = [
+ "proc-macro2 1.0.82",
+ "quote 1.0.35",
+ "syn 2.0.61",
+]
+
 [[package]]
 name = "proc-macro-warning"
 version = "1.0.0"
@@ -15262,9 +15394,9 @@ dependencies = [
 
 [[package]]
 name = "prometheus-client"
-version = "0.19.0"
+version = "0.21.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d6fa99d535dd930d1249e6c79cb3c2915f9172a540fe2b02a4c8f9ca954721e"
+checksum = "3c99afa9a01501019ac3a14d71d9f94050346f55ca471ce90c799a15c58f61e2"
 dependencies = [
  "dtoa",
  "itoa",
@@ -15494,9 +15626,9 @@ dependencies = [
 
 [[package]]
 name = "quick-protobuf-codec"
-version = "0.1.0"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1693116345026436eb2f10b677806169c1a1260c1c60eaaffe3fb5a29ae23d8b"
+checksum = "f8ededb1cd78531627244d51dd0c7139fbe736c7d57af0092a76f0ffb2f56e98"
 dependencies = [
  "asynchronous-codec",
  "bytes",
@@ -15546,27 +15678,45 @@ checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e"
 dependencies = [
  "bytes",
  "pin-project-lite 0.2.12",
- "quinn-proto",
- "quinn-udp",
+ "quinn-proto 0.9.6",
+ "quinn-udp 0.3.2",
  "rustc-hash",
- "rustls 0.20.8",
+ "rustls 0.20.9",
  "thiserror",
  "tokio",
  "tracing",
  "webpki",
 ]
 
+[[package]]
+name = "quinn"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75"
+dependencies = [
+ "bytes",
+ "futures-io",
+ "pin-project-lite 0.2.12",
+ "quinn-proto 0.10.6",
+ "quinn-udp 0.4.1",
+ "rustc-hash",
+ "rustls 0.21.7",
+ "thiserror",
+ "tokio",
+ "tracing",
+]
+
 [[package]]
 name = "quinn-proto"
-version = "0.9.5"
+version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c956be1b23f4261676aed05a0046e204e8a6836e50203902683a718af0797989"
+checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863"
 dependencies = [
  "bytes",
  "rand 0.8.5",
  "ring 0.16.20",
  "rustc-hash",
- "rustls 0.20.8",
+ "rustls 0.20.9",
  "slab",
  "thiserror",
  "tinyvec",
@@ -15574,6 +15724,23 @@ dependencies = [
  "webpki",
 ]
 
+[[package]]
+name = "quinn-proto"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a"
+dependencies = [
+ "bytes",
+ "rand 0.8.5",
+ "ring 0.16.20",
+ "rustc-hash",
+ "rustls 0.21.7",
+ "slab",
+ "thiserror",
+ "tinyvec",
+ "tracing",
+]
+
 [[package]]
 name = "quinn-udp"
 version = "0.3.2"
@@ -15581,12 +15748,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4"
 dependencies = [
  "libc",
- "quinn-proto",
+ "quinn-proto 0.9.6",
  "socket2 0.4.9",
  "tracing",
  "windows-sys 0.42.0",
 ]
 
+[[package]]
+name = "quinn-udp"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7"
+dependencies = [
+ "bytes",
+ "libc",
+ "socket2 0.5.6",
+ "tracing",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "quote"
 version = "0.6.13"
@@ -16034,7 +16214,7 @@ dependencies = [
  "once_cell",
  "percent-encoding",
  "pin-project-lite 0.2.12",
- "rustls 0.21.6",
+ "rustls 0.21.7",
  "rustls-pemfile 1.0.3",
  "serde",
  "serde_json",
@@ -16046,7 +16226,7 @@ dependencies = [
  "wasm-bindgen",
  "wasm-bindgen-futures",
  "web-sys",
- "webpki-roots 0.25.2",
+ "webpki-roots",
  "winreg",
 ]
 
@@ -16115,15 +16295,6 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
-[[package]]
-name = "ripemd"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f"
-dependencies = [
- "digest 0.10.7",
-]
-
 [[package]]
 name = "rle-decode-fast"
 version = "1.0.3"
@@ -16564,11 +16735,10 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.20.8"
+version = "0.20.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
+checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
 dependencies = [
- "log",
  "ring 0.16.20",
  "sct",
  "webpki",
@@ -16576,9 +16746,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.21.6"
+version = "0.21.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb"
+checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
 dependencies = [
  "log",
  "ring 0.16.20",
@@ -16702,9 +16872,9 @@ dependencies = [
 
 [[package]]
 name = "rw-stream-sink"
-version = "0.3.0"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04"
+checksum = "d8c9026ff5d2f23da5e45bbc283f156383001bfb09c4e44256d02c1a685fe9a1"
 dependencies = [
  "futures",
  "pin-project",
@@ -16765,8 +16935,7 @@ dependencies = [
  "libp2p",
  "linked_hash_set",
  "log",
- "multihash 0.17.0",
- "multihash-codetable",
+ "multihash 0.19.1",
  "parity-scale-codec",
  "prost 0.12.4",
  "prost-build 0.12.4",
@@ -17472,7 +17641,7 @@ dependencies = [
  "futures-timer",
  "log",
  "mixnet",
- "multiaddr",
+ "multiaddr 0.18.1",
  "parity-scale-codec",
  "parking_lot 0.12.1",
  "sc-client-api",
@@ -17578,7 +17747,6 @@ dependencies = [
  "async-trait",
  "futures",
  "futures-timer",
- "libp2p",
  "log",
  "parity-scale-codec",
  "quickcheck",
@@ -17621,7 +17789,6 @@ dependencies = [
  "array-bytes",
  "async-channel",
  "futures",
- "libp2p",
  "log",
  "parity-scale-codec",
  "sc-network",
@@ -17712,7 +17879,6 @@ version = "0.33.0"
 dependencies = [
  "array-bytes",
  "futures",
- "libp2p",
  "log",
  "parity-scale-codec",
  "sc-network",
@@ -17733,8 +17899,9 @@ dependencies = [
  "ed25519-dalek 2.1.1",
  "libp2p-identity",
  "litep2p",
- "multiaddr",
- "multihash 0.17.0",
+ "log",
+ "multiaddr 0.18.1",
+ "multihash 0.19.1",
  "quickcheck",
  "rand 0.8.5",
  "thiserror",
@@ -17754,7 +17921,6 @@ dependencies = [
  "hyper",
  "hyper-rustls",
  "lazy_static",
- "libp2p",
  "log",
  "num_cpus",
  "once_cell",
@@ -18571,6 +18737,12 @@ dependencies = [
  "pest",
 ]
 
+[[package]]
+name = "send_wrapper"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
+
 [[package]]
 name = "separator"
 version = "0.4.1"
@@ -18982,12 +19154,12 @@ dependencies = [
  "async-channel",
  "async-executor",
  "async-fs",
- "async-io",
- "async-lock",
+ "async-io 1.13.0",
+ "async-lock 2.8.0",
  "async-net",
  "async-process",
  "blocking",
- "futures-lite",
+ "futures-lite 1.13.0",
 ]
 
 [[package]]
@@ -19006,7 +19178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c0bb30cf57b7b5f6109ce17c3164445e2d6f270af2cb48f6e4d31c2967c9a9f5"
 dependencies = [
  "arrayvec 0.7.4",
- "async-lock",
+ "async-lock 2.8.0",
  "atomic-take",
  "base64 0.21.2",
  "bip39",
@@ -19017,9 +19189,9 @@ dependencies = [
  "derive_more",
  "ed25519-zebra",
  "either",
- "event-listener",
+ "event-listener 2.5.3",
  "fnv",
- "futures-lite",
+ "futures-lite 1.13.0",
  "futures-util",
  "hashbrown 0.14.3",
  "hex",
@@ -19049,7 +19221,7 @@ dependencies = [
  "soketto",
  "twox-hash",
  "wasmi 0.31.2",
- "x25519-dalek 2.0.0",
+ "x25519-dalek",
  "zeroize",
 ]
 
@@ -19060,15 +19232,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "256b5bad1d6b49045e95fe87492ce73d5af81545d8b4d8318a872d2007024c33"
 dependencies = [
  "async-channel",
- "async-lock",
+ "async-lock 2.8.0",
  "base64 0.21.2",
  "blake2-rfc",
  "derive_more",
  "either",
- "event-listener",
+ "event-listener 2.5.3",
  "fnv",
  "futures-channel",
- "futures-lite",
+ "futures-lite 1.13.0",
  "futures-util",
  "hashbrown 0.14.3",
  "hex",
@@ -19460,7 +19632,6 @@ checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2"
 dependencies = [
  "base64 0.13.1",
  "bytes",
- "flate2",
  "futures",
  "http",
  "httparse",
@@ -20389,7 +20560,7 @@ dependencies = [
  "sp-runtime",
  "sp-runtime-interface 24.0.0",
  "thiserror",
- "x25519-dalek 2.0.0",
+ "x25519-dalek",
 ]
 
 [[package]]
@@ -20877,19 +21048,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "strobe-rs"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fabb238a1cccccfa4c4fb703670c0d157e1256c1ba695abf1b93bd2bb14bab2d"
-dependencies = [
- "bitflags 1.3.2",
- "byteorder",
- "keccak",
- "subtle 2.5.0",
- "zeroize",
-]
-
 [[package]]
 name = "strsim"
 version = "0.8.0"
@@ -21955,7 +22113,7 @@ version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
 dependencies = [
- "rustls 0.21.6",
+ "rustls 0.21.7",
  "tokio",
 ]
 
@@ -22003,7 +22161,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
 dependencies = [
  "futures-util",
  "log",
- "rustls 0.21.6",
+ "rustls 0.21.7",
  "rustls-native-certs 0.6.3",
  "tokio",
  "tokio-rustls 0.24.1",
@@ -22360,26 +22518,6 @@ dependencies = [
  "url",
 ]
 
-[[package]]
-name = "trust-dns-resolver"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe"
-dependencies = [
- "cfg-if",
- "futures-util",
- "ipconfig",
- "lazy_static",
- "lru-cache",
- "parking_lot 0.12.1",
- "resolv-conf",
- "smallvec",
- "thiserror",
- "tokio",
- "tracing",
- "trust-dns-proto 0.22.0",
-]
-
 [[package]]
 name = "trust-dns-resolver"
 version = "0.23.2"
@@ -22442,7 +22580,7 @@ dependencies = [
  "httparse",
  "log",
  "rand 0.8.5",
- "rustls 0.21.6",
+ "rustls 0.21.7",
  "sha1",
  "thiserror",
  "url",
@@ -23239,15 +23377,6 @@ dependencies = [
  "untrusted 0.9.0",
 ]
 
-[[package]]
-name = "webpki-roots"
-version = "0.22.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
-dependencies = [
- "webpki",
-]
-
 [[package]]
 name = "webpki-roots"
 version = "0.25.2"
@@ -23474,23 +23603,20 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows"
-version = "0.34.0"
+version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
 dependencies = [
- "windows_aarch64_msvc 0.34.0",
- "windows_i686_gnu 0.34.0",
- "windows_i686_msvc 0.34.0",
- "windows_x86_64_gnu 0.34.0",
- "windows_x86_64_msvc 0.34.0",
+ "windows-targets 0.48.5",
 ]
 
 [[package]]
 name = "windows"
-version = "0.48.0"
+version = "0.51.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9"
 dependencies = [
+ "windows-core 0.51.1",
  "windows-targets 0.48.5",
 ]
 
@@ -23500,10 +23626,19 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
 dependencies = [
- "windows-core",
+ "windows-core 0.52.0",
  "windows-targets 0.52.0",
 ]
 
+[[package]]
+name = "windows-core"
+version = "0.51.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
 [[package]]
 name = "windows-core"
 version = "0.52.0"
@@ -23618,12 +23753,6 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
 
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.34.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
-
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.42.2"
@@ -23642,12 +23771,6 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
 
-[[package]]
-name = "windows_i686_gnu"
-version = "0.34.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
-
 [[package]]
 name = "windows_i686_gnu"
 version = "0.42.2"
@@ -23666,12 +23789,6 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
 
-[[package]]
-name = "windows_i686_msvc"
-version = "0.34.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
-
 [[package]]
 name = "windows_i686_msvc"
 version = "0.42.2"
@@ -23690,12 +23807,6 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
 
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.34.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
-
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.42.2"
@@ -23732,12 +23843,6 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
 
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.34.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
-
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.42.2"
@@ -23784,17 +23889,6 @@ dependencies = [
  "tap",
 ]
 
-[[package]]
-name = "x25519-dalek"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
-dependencies = [
- "curve25519-dalek 3.2.0",
- "rand_core 0.5.1",
- "zeroize",
-]
-
 [[package]]
 name = "x25519-dalek"
 version = "2.0.0"
@@ -23807,24 +23901,6 @@ dependencies = [
  "zeroize",
 ]
 
-[[package]]
-name = "x509-parser"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8"
-dependencies = [
- "asn1-rs",
- "base64 0.13.1",
- "data-encoding",
- "der-parser",
- "lazy_static",
- "nom",
- "oid-registry",
- "rusticata-macros",
- "thiserror",
- "time",
-]
-
 [[package]]
 name = "x509-parser"
 version = "0.15.1"
@@ -24045,16 +24121,32 @@ dependencies = [
  "xcm-simulator",
 ]
 
+[[package]]
+name = "xml-rs"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
+
+[[package]]
+name = "xmltree"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb"
+dependencies = [
+ "xml-rs",
+]
+
 [[package]]
 name = "yamux"
-version = "0.10.2"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5"
+checksum = "9ed0164ae619f2dc144909a9f082187ebb5893693d8c0196e8085283ccd4b776"
 dependencies = [
  "futures",
  "log",
  "nohash-hasher",
  "parking_lot 0.12.1",
+ "pin-project",
  "rand 0.8.5",
  "static_assertions",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 60459500a89..7eea70c31aa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -805,8 +805,8 @@ landlock = { version = "0.3.0" }
 lazy_static = { version = "1.4.0" }
 libc = { version = "0.2.153" }
 libfuzzer-sys = { version = "0.4" }
-libp2p = { version = "0.51.4" }
-libp2p-identity = { version = "0.1.3" }
+libp2p = { version = "0.52.4" }
+libp2p-identity = { version = "0.2.3" }
 libsecp256k1 = { version = "0.7.0", default-features = false }
 linked-hash-map = { version = "0.5.4" }
 linked_hash_set = { version = "0.1.4" }
@@ -831,10 +831,10 @@ mmr-gadget = { path = "substrate/client/merkle-mountain-range", default-features
 mmr-lib = { version = "0.5.2", package = "ckb-merkle-mountain-range" }
 mmr-rpc = { path = "substrate/client/merkle-mountain-range/rpc", default-features = false }
 mockall = { version = "0.11.3" }
-multiaddr = { version = "0.17.1" }
-multihash = { version = "0.17.0", default-features = false }
+multiaddr = { version = "0.18.1" }
+multihash = { version = "0.19.1", default-features = false }
 multihash-codetable = { version = "0.1.1" }
-multistream-select = { version = "0.12.1" }
+multistream-select = { version = "0.13.0" }
 names = { version = "0.14.0", default-features = false }
 nix = { version = "0.28.0" }
 node-cli = { path = "substrate/bin/node/cli", package = "staging-node-cli" }
diff --git a/prdoc/pr_1631.prdoc b/prdoc/pr_1631.prdoc
new file mode 100644
index 00000000000..f73d0096855
--- /dev/null
+++ b/prdoc/pr_1631.prdoc
@@ -0,0 +1,39 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: Upgrade libp2p to 0.52.4
+
+doc:
+  - audience: [Node Dev, Node Operator]
+    description: |
+      Upgrade libp2p from 0.51.4 to 0.52.4
+
+crates:
+  - name: sc-authority-discovery
+    bump: minor
+  - name: sc-cli
+    bump: minor
+  - name: sc-mixnet
+    bump: minor
+  - name: sc-network
+    bump: minor
+  - name: sc-network-gossip
+    bump: minor
+  - name: sc-network-common
+    bump: minor
+  - name: sc-network-light
+    bump: minor
+  - name: sc-network-statement
+    bump: minor
+  - name: sc-network-sync
+    bump: minor
+  - name: sc-network-test
+    bump: minor
+  - name: sc-network-transactions
+    bump: minor
+  - name: sc-network-types
+    bump: minor
+  - name: sc-offchain
+    bump: major
+  - name: sc-telemetry
+    bump: major
diff --git a/substrate/client/authority-discovery/Cargo.toml b/substrate/client/authority-discovery/Cargo.toml
index 4dfbe7f24ac..309c9c542a0 100644
--- a/substrate/client/authority-discovery/Cargo.toml
+++ b/substrate/client/authority-discovery/Cargo.toml
@@ -25,7 +25,7 @@ futures = { workspace = true }
 futures-timer = { workspace = true }
 ip_network = { workspace = true }
 libp2p = { features = ["ed25519", "kad"], workspace = true }
-multihash = { features = ["sha2", "std"], workspace = true }
+multihash = { workspace = true }
 linked_hash_set = { workspace = true }
 log = { workspace = true, default-features = true }
 prost = { workspace = true }
@@ -42,11 +42,6 @@ sp-core = { workspace = true, default-features = true }
 sp-keystore = { workspace = true, default-features = true }
 sp-runtime = { workspace = true, default-features = true }
 async-trait = { workspace = true }
-multihash-codetable = { features = [
-	"digest",
-	"serde",
-	"sha2",
-], workspace = true }
 
 [dev-dependencies]
 quickcheck = { workspace = true }
diff --git a/substrate/client/authority-discovery/src/service.rs b/substrate/client/authority-discovery/src/service.rs
index 60c7a2b9903..852d3ab80c9 100644
--- a/substrate/client/authority-discovery/src/service.rs
+++ b/substrate/client/authority-discovery/src/service.rs
@@ -55,7 +55,7 @@ impl Service {
 	/// [`crate::Worker`] failed.
 	///
 	/// Note: [`Multiaddr`]s returned always include a [`PeerId`] via a
-	/// [`libp2p::core::multiaddr::Protocol::P2p`] component. Equality of
+	/// [`sc_network_types::multiaddr::Protocol::P2p`] component. Equality of
 	/// [`PeerId`]s across [`Multiaddr`]s returned by a single call is not
 	/// enforced today, given that there are still authorities out there
 	/// publishing the addresses of their sentry nodes on the DHT. In the future
diff --git a/substrate/client/authority-discovery/src/worker.rs b/substrate/client/authority-discovery/src/worker.rs
index f20cf6aa212..b81b10b3ae0 100644
--- a/substrate/client/authority-discovery/src/worker.rs
+++ b/substrate/client/authority-discovery/src/worker.rs
@@ -45,10 +45,7 @@ use sc_network::{
 	event::DhtEvent, multiaddr, KademliaKey, Multiaddr, NetworkDHTProvider, NetworkSigner,
 	NetworkStateInfo,
 };
-use sc_network_types::{
-	multihash::{Code, Multihash},
-	PeerId,
-};
+use sc_network_types::{multihash::Code, PeerId};
 use schema::PeerSignature;
 use sp_api::{ApiError, ProvideRuntimeApi};
 use sp_authority_discovery::{
@@ -247,14 +244,14 @@ where
 		};
 
 		let public_addresses = {
-			let local_peer_id: Multihash = network.local_peer_id().into();
+			let local_peer_id = network.local_peer_id();
 
 			config
 				.public_addresses
 				.into_iter()
 				.map(|mut address| {
 					if let Some(multiaddr::Protocol::P2p(peer_id)) = address.iter().last() {
-						if peer_id != local_peer_id {
+						if peer_id != *local_peer_id.as_ref() {
 							error!(
 								target: LOG_TARGET,
 								"Discarding invalid local peer ID in public address {address}.",
@@ -401,10 +398,9 @@ where
 		);
 
 		// The address must include the local peer id.
-		let local_peer_id: Multihash = local_peer_id.into();
 		addresses
 			.into_iter()
-			.map(move |a| a.with(multiaddr::Protocol::P2p(local_peer_id)))
+			.map(move |a| a.with(multiaddr::Protocol::P2p(*local_peer_id.as_ref())))
 	}
 
 	/// Publish own public addresses.
diff --git a/substrate/client/authority-discovery/src/worker/addr_cache.rs b/substrate/client/authority-discovery/src/worker/addr_cache.rs
index 77cdfbd4f15..13bb990bf8b 100644
--- a/substrate/client/authority-discovery/src/worker/addr_cache.rs
+++ b/substrate/client/authority-discovery/src/worker/addr_cache.rs
@@ -177,7 +177,7 @@ mod tests {
 	use super::*;
 
 	use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
-	use sc_network_types::multihash::Multihash;
+	use sc_network_types::multihash::{Code, Multihash};
 
 	use sp_authority_discovery::{AuthorityId, AuthorityPair};
 	use sp_core::crypto::Pair;
@@ -198,10 +198,9 @@ mod tests {
 	impl Arbitrary for TestMultiaddr {
 		fn arbitrary(g: &mut Gen) -> Self {
 			let seed = (0..32).map(|_| u8::arbitrary(g)).collect::<Vec<_>>();
-			let peer_id = PeerId::from_multihash(
-				Multihash::wrap(multihash::Code::Sha2_256.into(), &seed).unwrap(),
-			)
-			.unwrap();
+			let peer_id =
+				PeerId::from_multihash(Multihash::wrap(Code::Sha2_256.into(), &seed).unwrap())
+					.unwrap();
 			let multiaddr = "/ip6/2001:db8:0:0:0:0:0:2/tcp/30333"
 				.parse::<Multiaddr>()
 				.unwrap()
@@ -217,10 +216,9 @@ mod tests {
 	impl Arbitrary for TestMultiaddrsSamePeerCombo {
 		fn arbitrary(g: &mut Gen) -> Self {
 			let seed = (0..32).map(|_| u8::arbitrary(g)).collect::<Vec<_>>();
-			let peer_id = PeerId::from_multihash(
-				Multihash::wrap(multihash::Code::Sha2_256.into(), &seed).unwrap(),
-			)
-			.unwrap();
+			let peer_id =
+				PeerId::from_multihash(Multihash::wrap(Code::Sha2_256.into(), &seed).unwrap())
+					.unwrap();
 			let multiaddr1 = "/ip6/2001:db8:0:0:0:0:0:2/tcp/30333"
 				.parse::<Multiaddr>()
 				.unwrap()
diff --git a/substrate/client/network-gossip/Cargo.toml b/substrate/client/network-gossip/Cargo.toml
index 6a2b3e3ef78..f1441e4a1cf 100644
--- a/substrate/client/network-gossip/Cargo.toml
+++ b/substrate/client/network-gossip/Cargo.toml
@@ -20,7 +20,6 @@ targets = ["x86_64-unknown-linux-gnu"]
 ahash = { workspace = true }
 futures = { workspace = true }
 futures-timer = { workspace = true }
-libp2p = { workspace = true }
 log = { workspace = true, default-features = true }
 schnellru = { workspace = true }
 tracing = { workspace = true, default-features = true }
diff --git a/substrate/client/network-gossip/src/bridge.rs b/substrate/client/network-gossip/src/bridge.rs
index cd344d9196d..414da9b2a58 100644
--- a/substrate/client/network-gossip/src/bridge.rs
+++ b/substrate/client/network-gossip/src/bridge.rs
@@ -348,7 +348,7 @@ impl<B: BlockT> futures::future::FusedFuture for GossipEngine<B> {
 #[cfg(test)]
 mod tests {
 	use super::*;
-	use crate::{multiaddr::Multiaddr, ValidationResult, ValidatorContext};
+	use crate::{ValidationResult, ValidatorContext};
 	use codec::{DecodeAll, Encode};
 	use futures::{
 		channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender},
@@ -363,6 +363,7 @@ mod tests {
 	};
 	use sc_network_common::role::ObservedRole;
 	use sc_network_sync::SyncEventStream;
+	use sc_network_types::multiaddr::Multiaddr;
 	use sp_runtime::{
 		testing::H256,
 		traits::{Block as BlockT, NumberFor},
diff --git a/substrate/client/network-gossip/src/lib.rs b/substrate/client/network-gossip/src/lib.rs
index e04ea2a91e7..20d9922200c 100644
--- a/substrate/client/network-gossip/src/lib.rs
+++ b/substrate/client/network-gossip/src/lib.rs
@@ -67,9 +67,12 @@ pub use self::{
 	validator::{DiscardAll, MessageIntent, ValidationResult, Validator, ValidatorContext},
 };
 
-use sc_network::{multiaddr, types::ProtocolName, NetworkBlock, NetworkEventStream, NetworkPeers};
+use sc_network::{types::ProtocolName, NetworkBlock, NetworkEventStream, NetworkPeers};
 use sc_network_sync::SyncEventStream;
-use sc_network_types::PeerId;
+use sc_network_types::{
+	multiaddr::{Multiaddr, Protocol},
+	PeerId,
+};
 use sp_runtime::traits::{Block as BlockT, NumberFor};
 use std::iter;
 
@@ -80,8 +83,7 @@ mod validator;
 /// Abstraction over a network.
 pub trait Network<B: BlockT>: NetworkPeers + NetworkEventStream {
 	fn add_set_reserved(&self, who: PeerId, protocol: ProtocolName) {
-		let addr =
-			iter::once(multiaddr::Protocol::P2p(who.into())).collect::<multiaddr::Multiaddr>();
+		let addr = Multiaddr::empty().with(Protocol::P2p(*who.as_ref()));
 		let result = self.add_peers_to_reserved_set(protocol, iter::once(addr).collect());
 		if let Err(err) = result {
 			log::error!(target: "gossip", "add_set_reserved failed: {}", err);
diff --git a/substrate/client/network-gossip/src/state_machine.rs b/substrate/client/network-gossip/src/state_machine.rs
index 016afa95ece..ac3f7a1b8c7 100644
--- a/substrate/client/network-gossip/src/state_machine.rs
+++ b/substrate/client/network-gossip/src/state_machine.rs
@@ -542,12 +542,12 @@ impl Metrics {
 #[cfg(test)]
 mod tests {
 	use super::*;
-	use crate::multiaddr::Multiaddr;
 	use futures::prelude::*;
 	use sc_network::{
 		config::MultiaddrWithPeerId, event::Event, service::traits::NotificationEvent, MessageSink,
 		NetworkBlock, NetworkEventStream, NetworkPeers, ReputationChange,
 	};
+	use sc_network_types::multiaddr::Multiaddr;
 	use sp_runtime::{
 		testing::{Block as RawBlock, ExtrinsicWrapper, H256},
 		traits::NumberFor,
diff --git a/substrate/client/network/src/behaviour.rs b/substrate/client/network/src/behaviour.rs
index 68deac0f47b..68816a10980 100644
--- a/substrate/client/network/src/behaviour.rs
+++ b/substrate/client/network/src/behaviour.rs
@@ -31,8 +31,8 @@ use crate::{
 
 use futures::channel::oneshot;
 use libp2p::{
-	core::Multiaddr, identify::Info as IdentifyInfo, identity::PublicKey, kad::RecordKey,
-	swarm::NetworkBehaviour, PeerId,
+	connection_limits::ConnectionLimits, core::Multiaddr, identify::Info as IdentifyInfo,
+	identity::PublicKey, kad::RecordKey, swarm::NetworkBehaviour, PeerId, StreamProtocol,
 };
 
 use parking_lot::Mutex;
@@ -47,8 +47,10 @@ pub use crate::request_responses::{InboundFailure, OutboundFailure, ResponseFail
 
 /// General behaviour of the network. Combines all protocols together.
 #[derive(NetworkBehaviour)]
-#[behaviour(out_event = "BehaviourOut")]
+#[behaviour(to_swarm = "BehaviourOut")]
 pub struct Behaviour<B: BlockT> {
+	/// Connection limits.
+	connection_limits: libp2p::connection_limits::Behaviour,
 	/// All the substrate-specific protocols.
 	substrate: Protocol<B>,
 	/// Periodically pings and identifies the nodes we are connected to, and store information in a
@@ -180,6 +182,7 @@ impl<B: BlockT> Behaviour<B> {
 		request_response_protocols: Vec<ProtocolConfig>,
 		peer_store_handle: Arc<dyn PeerStoreProvider>,
 		external_addresses: Arc<Mutex<HashSet<Multiaddr>>>,
+		connection_limits: ConnectionLimits,
 	) -> Result<Self, request_responses::RegisterError> {
 		Ok(Self {
 			substrate,
@@ -193,6 +196,7 @@ impl<B: BlockT> Behaviour<B> {
 				request_response_protocols.into_iter(),
 				peer_store_handle,
 			)?,
+			connection_limits: libp2p::connection_limits::Behaviour::new(connection_limits),
 		})
 	}
 
@@ -267,7 +271,7 @@ impl<B: BlockT> Behaviour<B> {
 	pub fn add_self_reported_address_to_dht(
 		&mut self,
 		peer_id: &PeerId,
-		supported_protocols: &[impl AsRef<[u8]>],
+		supported_protocols: &[StreamProtocol],
 		addr: Multiaddr,
 	) {
 		self.discovery.add_self_reported_address(peer_id, supported_protocols, addr);
@@ -376,3 +380,9 @@ impl From<DiscoveryOut> for BehaviourOut {
 		}
 	}
 }
+
+impl From<void::Void> for BehaviourOut {
+	fn from(e: void::Void) -> Self {
+		void::unreachable(e)
+	}
+}
diff --git a/substrate/client/network/src/config.rs b/substrate/client/network/src/config.rs
index 100a1e9dfb3..e939558b20b 100644
--- a/substrate/client/network/src/config.rs
+++ b/substrate/client/network/src/config.rs
@@ -114,13 +114,13 @@ pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> {
 
 /// Splits a Multiaddress into a Multiaddress and PeerId.
 pub fn parse_addr(mut addr: Multiaddr) -> Result<(PeerId, Multiaddr), ParseErr> {
-	let who = match addr.pop() {
-		Some(multiaddr::Protocol::P2p(key)) =>
-			PeerId::from_multihash(key).map_err(|_| ParseErr::InvalidPeerId)?,
+	let multihash = match addr.pop() {
+		Some(multiaddr::Protocol::P2p(multihash)) => multihash,
 		_ => return Err(ParseErr::PeerIdMissing),
 	};
+	let peer_id = PeerId::from_multihash(multihash).map_err(|_| ParseErr::InvalidPeerId)?;
 
-	Ok((who, addr))
+	Ok((peer_id, addr))
 }
 
 /// Address of a node, including its identity.
diff --git a/substrate/client/network/src/discovery.rs b/substrate/client/network/src/discovery.rs
index 2c788ec713f..3145b891a8d 100644
--- a/substrate/client/network/src/discovery.rs
+++ b/substrate/client/network/src/discovery.rs
@@ -55,20 +55,20 @@ use ip_network::IpNetwork;
 use libp2p::{
 	core::{Endpoint, Multiaddr},
 	kad::{
-		handler::KademliaHandler,
+		self,
 		record::store::{MemoryStore, RecordStore},
-		GetClosestPeersError, GetRecordOk, Kademlia, KademliaBucketInserts, KademliaConfig,
-		KademliaEvent, QueryId, QueryResult, Quorum, Record, RecordKey,
+		Behaviour as Kademlia, BucketInserts, Config as KademliaConfig, Event as KademliaEvent,
+		GetClosestPeersError, GetRecordOk, QueryId, QueryResult, Quorum, Record, RecordKey,
 	},
 	mdns::{self, tokio::Behaviour as TokioMdns},
 	multiaddr::Protocol,
 	swarm::{
 		behaviour::{
 			toggle::{Toggle, ToggleConnectionHandler},
-			DialFailure, FromSwarm, NewExternalAddr,
+			DialFailure, ExternalAddrConfirmed, FromSwarm,
 		},
-		ConnectionDenied, ConnectionId, DialError, NetworkBehaviour, PollParameters, THandler,
-		THandlerInEvent, THandlerOutEvent, ToSwarm,
+		ConnectionDenied, ConnectionId, DialError, NetworkBehaviour, PollParameters,
+		StreamProtocol, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm,
 	},
 	PeerId,
 };
@@ -105,8 +105,8 @@ pub struct DiscoveryConfig {
 	discovery_only_if_under_num: u64,
 	enable_mdns: bool,
 	kademlia_disjoint_query_paths: bool,
-	kademlia_protocol: Vec<u8>,
-	kademlia_legacy_protocol: Vec<u8>,
+	kademlia_protocol: Option<StreamProtocol>,
+	kademlia_legacy_protocol: Option<StreamProtocol>,
 	kademlia_replication_factor: NonZeroUsize,
 }
 
@@ -122,8 +122,8 @@ impl DiscoveryConfig {
 			discovery_only_if_under_num: std::u64::MAX,
 			enable_mdns: false,
 			kademlia_disjoint_query_paths: false,
-			kademlia_protocol: Vec::new(),
-			kademlia_legacy_protocol: Vec::new(),
+			kademlia_protocol: None,
+			kademlia_legacy_protocol: None,
 			kademlia_replication_factor: NonZeroUsize::new(DEFAULT_KADEMLIA_REPLICATION_FACTOR)
 				.expect("value is a constant; constant is non-zero; qed."),
 		}
@@ -179,8 +179,8 @@ impl DiscoveryConfig {
 		fork_id: Option<&str>,
 		protocol_id: &ProtocolId,
 	) -> &mut Self {
-		self.kademlia_protocol = kademlia_protocol_name(genesis_hash, fork_id);
-		self.kademlia_legacy_protocol = legacy_kademlia_protocol_name(protocol_id);
+		self.kademlia_protocol = Some(kademlia_protocol_name(genesis_hash, fork_id));
+		self.kademlia_legacy_protocol = Some(legacy_kademlia_protocol_name(protocol_id));
 		self
 	}
 
@@ -213,26 +213,31 @@ impl DiscoveryConfig {
 			kademlia_replication_factor,
 		} = self;
 
-		let kademlia = if !kademlia_protocol.is_empty() {
+		let kademlia = if let Some(ref kademlia_protocol) = kademlia_protocol {
 			let mut config = KademliaConfig::default();
 
 			config.set_replication_factor(kademlia_replication_factor);
 			// Populate kad with both the legacy and the new protocol names.
 			// Remove the legacy protocol:
 			// https://github.com/paritytech/polkadot-sdk/issues/504
-			let kademlia_protocols = [kademlia_protocol.clone(), kademlia_legacy_protocol];
+			let kademlia_protocols = if let Some(legacy_protocol) = kademlia_legacy_protocol {
+				vec![kademlia_protocol.clone(), legacy_protocol]
+			} else {
+				vec![kademlia_protocol.clone()]
+			};
 			config.set_protocol_names(kademlia_protocols.into_iter().map(Into::into).collect());
 
-			config.set_record_filtering(libp2p::kad::KademliaStoreInserts::FilterBoth);
+			config.set_record_filtering(libp2p::kad::StoreInserts::FilterBoth);
 
 			// By default Kademlia attempts to insert all peers into its routing table once a
 			// dialing attempt succeeds. In order to control which peer is added, disable the
 			// auto-insertion and instead add peers manually.
-			config.set_kbucket_inserts(KademliaBucketInserts::Manual);
+			config.set_kbucket_inserts(BucketInserts::Manual);
 			config.disjoint_query_paths(kademlia_disjoint_query_paths);
 
 			let store = MemoryStore::new(local_peer_id);
 			let mut kad = Kademlia::with_config(local_peer_id, store, config);
+			kad.set_mode(Some(kad::Mode::Server));
 
 			for (peer_id, addr) in &permanent_addresses {
 				kad.add_address(peer_id, addr.clone());
@@ -323,7 +328,7 @@ pub struct DiscoveryBehaviour {
 	///
 	/// Remove when all nodes are upgraded to genesis hash and fork ID-based Kademlia:
 	/// <https://github.com/paritytech/polkadot-sdk/issues/504>.
-	kademlia_protocol: Vec<u8>,
+	kademlia_protocol: Option<StreamProtocol>,
 }
 
 impl DiscoveryBehaviour {
@@ -369,7 +374,7 @@ impl DiscoveryBehaviour {
 	pub fn add_self_reported_address(
 		&mut self,
 		peer_id: &PeerId,
-		supported_protocols: &[impl AsRef<[u8]>],
+		supported_protocols: &[StreamProtocol],
 		addr: Multiaddr,
 	) {
 		if let Some(kademlia) = self.kademlia.as_mut() {
@@ -386,10 +391,12 @@ impl DiscoveryBehaviour {
 			// Extract the chain-based Kademlia protocol from `kademlia.protocol_name()`
 			// when all nodes are upgraded to genesis hash and fork ID-based Kademlia:
 			// https://github.com/paritytech/polkadot-sdk/issues/504.
-			if !supported_protocols
-				.iter()
-				.any(|p| p.as_ref() == self.kademlia_protocol.as_slice())
-			{
+			if !supported_protocols.iter().any(|p| {
+				p == self
+					.kademlia_protocol
+					.as_ref()
+					.expect("kademlia protocol was checked above to be enabled; qed")
+			}) {
 				trace!(
 					target: "sub-libp2p",
 					"Ignoring self-reported address {} from {} as remote node is not part of the \
@@ -503,7 +510,7 @@ impl DiscoveryBehaviour {
 #[derive(Debug)]
 pub enum DiscoveryOut {
 	/// A connection to a peer has been established but the peer has not been
-	/// added to the routing table because [`KademliaBucketInserts::Manual`] is
+	/// added to the routing table because [`BucketInserts::Manual`] is
 	/// configured. If the peer is to be included in the routing table, it must
 	/// be explicitly added via
 	/// [`DiscoveryBehaviour::add_self_reported_address`].
@@ -552,8 +559,9 @@ pub enum DiscoveryOut {
 }
 
 impl NetworkBehaviour for DiscoveryBehaviour {
-	type ConnectionHandler = ToggleConnectionHandler<KademliaHandler<QueryId>>;
-	type OutEvent = DiscoveryOut;
+	type ConnectionHandler =
+		ToggleConnectionHandler<<Kademlia<MemoryStore> as NetworkBehaviour>::ConnectionHandler>;
+	type ToSwarm = DiscoveryOut;
 
 	fn handle_established_inbound_connection(
 		&mut self,
@@ -689,11 +697,11 @@ impl NetworkBehaviour for DiscoveryBehaviour {
 			FromSwarm::ListenerError(e) => {
 				self.kademlia.on_swarm_event(FromSwarm::ListenerError(e));
 			},
-			FromSwarm::ExpiredExternalAddr(e) => {
+			FromSwarm::ExternalAddrExpired(e) => {
 				// We intentionally don't remove the element from `known_external_addresses` in
 				// order to not print the log line again.
 
-				self.kademlia.on_swarm_event(FromSwarm::ExpiredExternalAddr(e));
+				self.kademlia.on_swarm_event(FromSwarm::ExternalAddrExpired(e));
 			},
 			FromSwarm::NewListener(e) => {
 				self.kademlia.on_swarm_event(FromSwarm::NewListener(e));
@@ -701,8 +709,18 @@ impl NetworkBehaviour for DiscoveryBehaviour {
 			FromSwarm::ExpiredListenAddr(e) => {
 				self.kademlia.on_swarm_event(FromSwarm::ExpiredListenAddr(e));
 			},
-			FromSwarm::NewExternalAddr(e @ NewExternalAddr { addr }) => {
-				let new_addr = addr.clone().with(Protocol::P2p(self.local_peer_id.into()));
+			FromSwarm::NewExternalAddrCandidate(e) => {
+				self.kademlia.on_swarm_event(FromSwarm::NewExternalAddrCandidate(e));
+			},
+			FromSwarm::AddressChange(e) => {
+				self.kademlia.on_swarm_event(FromSwarm::AddressChange(e));
+			},
+			FromSwarm::NewListenAddr(e) => {
+				self.kademlia.on_swarm_event(FromSwarm::NewListenAddr(e));
+				self.mdns.on_swarm_event(FromSwarm::NewListenAddr(e));
+			},
+			FromSwarm::ExternalAddrConfirmed(e @ ExternalAddrConfirmed { addr }) => {
+				let new_addr = addr.clone().with(Protocol::P2p(self.local_peer_id));
 
 				if Self::can_add_to_dht(addr) {
 					// NOTE: we might re-discover the same address multiple times
@@ -716,14 +734,7 @@ impl NetworkBehaviour for DiscoveryBehaviour {
 					}
 				}
 
-				self.kademlia.on_swarm_event(FromSwarm::NewExternalAddr(e));
-			},
-			FromSwarm::AddressChange(e) => {
-				self.kademlia.on_swarm_event(FromSwarm::AddressChange(e));
-			},
-			FromSwarm::NewListenAddr(e) => {
-				self.kademlia.on_swarm_event(FromSwarm::NewListenAddr(e));
-				self.mdns.on_swarm_event(FromSwarm::NewListenAddr(e));
+				self.kademlia.on_swarm_event(FromSwarm::ExternalAddrConfirmed(e));
 			},
 		}
 	}
@@ -741,7 +752,7 @@ impl NetworkBehaviour for DiscoveryBehaviour {
 		&mut self,
 		cx: &mut Context,
 		params: &mut impl PollParameters,
-	) -> Poll<ToSwarm<Self::OutEvent, THandlerInEvent<Self>>> {
+	) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
 		// Immediately process the content of `discovered`.
 		if let Some(ev) = self.pending_events.pop_front() {
 			return Poll::Ready(ToSwarm::GenerateEvent(ev))
@@ -963,10 +974,17 @@ impl NetworkBehaviour for DiscoveryBehaviour {
 				ToSwarm::Dial { opts } => return Poll::Ready(ToSwarm::Dial { opts }),
 				ToSwarm::NotifyHandler { peer_id, handler, event } =>
 					return Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event }),
-				ToSwarm::ReportObservedAddr { address, score } =>
-					return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }),
 				ToSwarm::CloseConnection { peer_id, connection } =>
 					return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }),
+				ToSwarm::NewExternalAddrCandidate(observed) =>
+					return Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)),
+				ToSwarm::ExternalAddrConfirmed(addr) =>
+					return Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)),
+				ToSwarm::ExternalAddrExpired(addr) =>
+					return Poll::Ready(ToSwarm::ExternalAddrExpired(addr)),
+				ToSwarm::ListenOn { opts } => return Poll::Ready(ToSwarm::ListenOn { opts }),
+				ToSwarm::RemoveListener { id } =>
+					return Poll::Ready(ToSwarm::RemoveListener { id }),
 			}
 		}
 
@@ -979,8 +997,9 @@ impl NetworkBehaviour for DiscoveryBehaviour {
 							continue
 						}
 
-						self.pending_events
-							.extend(list.map(|(peer_id, _)| DiscoveryOut::Discovered(peer_id)));
+						self.pending_events.extend(
+							list.into_iter().map(|(peer_id, _)| DiscoveryOut::Discovered(peer_id)),
+						);
 						if let Some(ev) = self.pending_events.pop_front() {
 							return Poll::Ready(ToSwarm::GenerateEvent(ev))
 						}
@@ -990,13 +1009,19 @@ impl NetworkBehaviour for DiscoveryBehaviour {
 				ToSwarm::Dial { .. } => {
 					unreachable!("mDNS never dials!");
 				},
-				ToSwarm::NotifyHandler { event, .. } => match event {}, /* `event` is an */
-				// enum with no
-				// variant
-				ToSwarm::ReportObservedAddr { address, score } =>
-					return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }),
+				// `event` is an enum with no variant
+				ToSwarm::NotifyHandler { event, .. } => match event {},
 				ToSwarm::CloseConnection { peer_id, connection } =>
 					return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }),
+				ToSwarm::NewExternalAddrCandidate(observed) =>
+					return Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)),
+				ToSwarm::ExternalAddrConfirmed(addr) =>
+					return Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)),
+				ToSwarm::ExternalAddrExpired(addr) =>
+					return Poll::Ready(ToSwarm::ExternalAddrExpired(addr)),
+				ToSwarm::ListenOn { opts } => return Poll::Ready(ToSwarm::ListenOn { opts }),
+				ToSwarm::RemoveListener { id } =>
+					return Poll::Ready(ToSwarm::RemoveListener { id }),
 			}
 		}
 
@@ -1005,21 +1030,24 @@ impl NetworkBehaviour for DiscoveryBehaviour {
 }
 
 /// Legacy (fallback) Kademlia protocol name based on `protocol_id`.
-fn legacy_kademlia_protocol_name(id: &ProtocolId) -> Vec<u8> {
-	let mut v = vec![b'/'];
-	v.extend_from_slice(id.as_ref().as_bytes());
-	v.extend_from_slice(b"/kad");
-	v
+fn legacy_kademlia_protocol_name(id: &ProtocolId) -> StreamProtocol {
+	let name = format!("/{}/kad", id.as_ref());
+	StreamProtocol::try_from_owned(name).expect("protocol name is valid. qed")
 }
 
 /// Kademlia protocol name based on `genesis_hash` and `fork_id`.
-fn kademlia_protocol_name<Hash: AsRef<[u8]>>(genesis_hash: Hash, fork_id: Option<&str>) -> Vec<u8> {
+fn kademlia_protocol_name<Hash: AsRef<[u8]>>(
+	genesis_hash: Hash,
+	fork_id: Option<&str>,
+) -> StreamProtocol {
 	let genesis_hash_hex = bytes2hex("", genesis_hash.as_ref());
-	if let Some(fork_id) = fork_id {
-		format!("/{}/{}/kad", genesis_hash_hex, fork_id).as_bytes().into()
+	let name = if let Some(fork_id) = fork_id {
+		format!("/{genesis_hash_hex}/{fork_id}/kad")
 	} else {
-		format!("/{}/kad", genesis_hash_hex).as_bytes().into()
-	}
+		format!("/{genesis_hash_hex}/kad")
+	};
+
+	StreamProtocol::try_from_owned(name).expect("protocol name is valid. qed")
 }
 
 #[cfg(test)]
@@ -1036,7 +1064,7 @@ mod tests {
 		},
 		identity::Keypair,
 		noise,
-		swarm::{Executor, Swarm, SwarmBuilder, SwarmEvent},
+		swarm::{Executor, Swarm, SwarmEvent},
 		yamux, Multiaddr,
 	};
 	use sp_core::hash::H256;
@@ -1082,7 +1110,8 @@ mod tests {
 				};
 
 				let runtime = tokio::runtime::Runtime::new().unwrap();
-				let mut swarm = SwarmBuilder::with_executor(
+				#[allow(deprecated)]
+				let mut swarm = libp2p::swarm::SwarmBuilder::with_executor(
 					transport,
 					behaviour,
 					keypair.public().to_peer_id(),
diff --git a/substrate/client/network/src/litep2p/service.rs b/substrate/client/network/src/litep2p/service.rs
index 8f36b0828bd..20bf6cd12d2 100644
--- a/substrate/client/network/src/litep2p/service.rs
+++ b/substrate/client/network/src/litep2p/service.rs
@@ -264,8 +264,20 @@ impl NetworkStatusProvider for Litep2pNetworkService {
 	async fn network_state(&self) -> Result<NetworkState, ()> {
 		Ok(NetworkState {
 			peer_id: self.local_peer_id.to_base58(),
-			listened_addresses: self.listen_addresses.read().iter().cloned().collect(),
-			external_addresses: self.external_addresses.read().iter().cloned().collect(),
+			listened_addresses: self
+				.listen_addresses
+				.read()
+				.iter()
+				.cloned()
+				.map(|a| Multiaddr::from(a).into())
+				.collect(),
+			external_addresses: self
+				.external_addresses
+				.read()
+				.iter()
+				.cloned()
+				.map(|a| Multiaddr::from(a).into())
+				.collect(),
 			connected_peers: HashMap::new(),
 			not_connected_peers: HashMap::new(),
 			// TODO: Check what info we can include here.
diff --git a/substrate/client/network/src/peer_info.rs b/substrate/client/network/src/peer_info.rs
index 2735bd873db..21eeea6bcc0 100644
--- a/substrate/client/network/src/peer_info.rs
+++ b/substrate/client/network/src/peer_info.rs
@@ -31,14 +31,15 @@ use libp2p::{
 		Info as IdentifyInfo,
 	},
 	identity::PublicKey,
-	ping::{Behaviour as Ping, Config as PingConfig, Event as PingEvent, Success as PingSuccess},
+	ping::{Behaviour as Ping, Config as PingConfig, Event as PingEvent},
 	swarm::{
 		behaviour::{
-			AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm,
-			ListenFailure,
+			AddressChange, ConnectionClosed, ConnectionEstablished, DialFailure,
+			ExternalAddrConfirmed, FromSwarm, ListenFailure,
 		},
-		ConnectionDenied, ConnectionHandler, ConnectionId, IntoConnectionHandlerSelect,
-		NetworkBehaviour, PollParameters, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm,
+		ConnectionDenied, ConnectionHandler, ConnectionHandlerSelect, ConnectionId,
+		NetworkBehaviour, NewExternalAddrCandidate, PollParameters, THandler, THandlerInEvent,
+		THandlerOutEvent, ToSwarm,
 	},
 	Multiaddr, PeerId,
 };
@@ -47,7 +48,7 @@ use parking_lot::Mutex;
 use smallvec::SmallVec;
 
 use std::{
-	collections::{hash_map::Entry, HashSet},
+	collections::{hash_map::Entry, HashSet, VecDeque},
 	pin::Pin,
 	sync::Arc,
 	task::{Context, Poll},
@@ -71,6 +72,8 @@ pub struct PeerInfoBehaviour {
 	garbage_collect: Pin<Box<dyn Stream<Item = ()> + Send>>,
 	/// Record keeping of external addresses. Data is queried by the `NetworkService`.
 	external_addresses: ExternalAddresses,
+	/// Pending events to emit to [`Swarm`](libp2p::swarm::Swarm).
+	pending_actions: VecDeque<ToSwarm<PeerInfoEvent, THandlerInEvent<PeerInfoBehaviour>>>,
 }
 
 /// Information about a node we're connected to.
@@ -134,6 +137,7 @@ impl PeerInfoBehaviour {
 			nodes_info: FnvHashMap::default(),
 			garbage_collect: Box::pin(interval(GARBAGE_COLLECT_INTERVAL)),
 			external_addresses: ExternalAddresses { addresses: external_addresses },
+			pending_actions: Default::default(),
 		}
 	}
 
@@ -148,13 +152,18 @@ impl PeerInfoBehaviour {
 
 	/// Inserts a ping time in the cache. Has no effect if we don't have any entry for that node,
 	/// which shouldn't happen.
-	fn handle_ping_report(&mut self, peer_id: &PeerId, ping_time: Duration) {
-		trace!(target: "sub-libp2p", "Ping time with {:?}: {:?}", peer_id, ping_time);
+	fn handle_ping_report(
+		&mut self,
+		peer_id: &PeerId,
+		ping_time: Duration,
+		connection: ConnectionId,
+	) {
+		trace!(target: "sub-libp2p", "Ping time with {:?} via {:?}: {:?}", peer_id, connection, ping_time);
 		if let Some(entry) = self.nodes_info.get_mut(peer_id) {
 			entry.latest_ping = Some(ping_time);
 		} else {
 			error!(target: "sub-libp2p",
-				"Received ping from node we're not connected to {:?}", peer_id);
+				"Received ping from node we're not connected to {:?} via {:?}", peer_id, connection);
 		}
 	}
 
@@ -208,11 +217,11 @@ pub enum PeerInfoEvent {
 }
 
 impl NetworkBehaviour for PeerInfoBehaviour {
-	type ConnectionHandler = IntoConnectionHandlerSelect<
+	type ConnectionHandler = ConnectionHandlerSelect<
 		<Ping as NetworkBehaviour>::ConnectionHandler,
 		<Identify as NetworkBehaviour>::ConnectionHandler,
 	>;
-	type OutEvent = PeerInfoEvent;
+	type ToSwarm = PeerInfoEvent;
 
 	fn handle_pending_inbound_connection(
 		&mut self,
@@ -378,9 +387,9 @@ impl NetworkBehaviour for PeerInfoBehaviour {
 				self.ping.on_swarm_event(FromSwarm::ListenerError(e));
 				self.identify.on_swarm_event(FromSwarm::ListenerError(e));
 			},
-			FromSwarm::ExpiredExternalAddr(e) => {
-				self.ping.on_swarm_event(FromSwarm::ExpiredExternalAddr(e));
-				self.identify.on_swarm_event(FromSwarm::ExpiredExternalAddr(e));
+			FromSwarm::ExternalAddrExpired(e) => {
+				self.ping.on_swarm_event(FromSwarm::ExternalAddrExpired(e));
+				self.identify.on_swarm_event(FromSwarm::ExternalAddrExpired(e));
 			},
 			FromSwarm::NewListener(e) => {
 				self.ping.on_swarm_event(FromSwarm::NewListener(e));
@@ -391,10 +400,23 @@ impl NetworkBehaviour for PeerInfoBehaviour {
 				self.identify.on_swarm_event(FromSwarm::ExpiredListenAddr(e));
 				self.external_addresses.remove(e.addr);
 			},
-			FromSwarm::NewExternalAddr(e) => {
-				self.ping.on_swarm_event(FromSwarm::NewExternalAddr(e));
-				self.identify.on_swarm_event(FromSwarm::NewExternalAddr(e));
-				self.external_addresses.add(e.addr.clone());
+			FromSwarm::NewExternalAddrCandidate(e @ NewExternalAddrCandidate { addr }) => {
+				self.ping.on_swarm_event(FromSwarm::NewExternalAddrCandidate(e));
+				self.identify.on_swarm_event(FromSwarm::NewExternalAddrCandidate(e));
+
+				// Manually confirm all external address candidates.
+				// TODO: consider adding [AutoNAT protocol](https://docs.rs/libp2p/0.52.3/libp2p/autonat/index.html)
+				// (must go through the polkadot protocol spec) or implemeting heuristics for
+				// approving external address candidates. This can be done, for example, by
+				// approving only addresses reported by multiple peers.
+				// See also https://github.com/libp2p/rust-libp2p/pull/4721 introduced
+				// in libp2p v0.53 for heuristics approach.
+				self.pending_actions.push_back(ToSwarm::ExternalAddrConfirmed(addr.clone()));
+			},
+			FromSwarm::ExternalAddrConfirmed(e @ ExternalAddrConfirmed { addr }) => {
+				self.ping.on_swarm_event(FromSwarm::ExternalAddrConfirmed(e));
+				self.identify.on_swarm_event(FromSwarm::ExternalAddrConfirmed(e));
+				self.external_addresses.add(addr.clone());
 			},
 			FromSwarm::AddressChange(e @ AddressChange { peer_id, old, new, .. }) => {
 				self.ping.on_swarm_event(FromSwarm::AddressChange(e));
@@ -437,13 +459,17 @@ impl NetworkBehaviour for PeerInfoBehaviour {
 		&mut self,
 		cx: &mut Context,
 		params: &mut impl PollParameters,
-	) -> Poll<ToSwarm<Self::OutEvent, THandlerInEvent<Self>>> {
+	) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
+		if let Some(event) = self.pending_actions.pop_front() {
+			return Poll::Ready(event)
+		}
+
 		loop {
 			match self.ping.poll(cx, params) {
 				Poll::Pending => break,
 				Poll::Ready(ToSwarm::GenerateEvent(ev)) => {
-					if let PingEvent { peer, result: Ok(PingSuccess::Ping { rtt }) } = ev {
-						self.handle_ping_report(&peer, rtt)
+					if let PingEvent { peer, result: Ok(rtt), connection } = ev {
+						self.handle_ping_report(&peer, rtt, connection)
 					}
 				},
 				Poll::Ready(ToSwarm::Dial { opts }) => return Poll::Ready(ToSwarm::Dial { opts }),
@@ -453,10 +479,18 @@ impl NetworkBehaviour for PeerInfoBehaviour {
 						handler,
 						event: Either::Left(event),
 					}),
-				Poll::Ready(ToSwarm::ReportObservedAddr { address, score }) =>
-					return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }),
 				Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }) =>
 					return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }),
+				Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)) =>
+					return Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)),
+				Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)) =>
+					return Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)),
+				Poll::Ready(ToSwarm::ExternalAddrExpired(addr)) =>
+					return Poll::Ready(ToSwarm::ExternalAddrExpired(addr)),
+				Poll::Ready(ToSwarm::ListenOn { opts }) =>
+					return Poll::Ready(ToSwarm::ListenOn { opts }),
+				Poll::Ready(ToSwarm::RemoveListener { id }) =>
+					return Poll::Ready(ToSwarm::RemoveListener { id }),
 			}
 		}
 
@@ -482,10 +516,18 @@ impl NetworkBehaviour for PeerInfoBehaviour {
 						handler,
 						event: Either::Right(event),
 					}),
-				Poll::Ready(ToSwarm::ReportObservedAddr { address, score }) =>
-					return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }),
 				Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }) =>
 					return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }),
+				Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)) =>
+					return Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)),
+				Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)) =>
+					return Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)),
+				Poll::Ready(ToSwarm::ExternalAddrExpired(addr)) =>
+					return Poll::Ready(ToSwarm::ExternalAddrExpired(addr)),
+				Poll::Ready(ToSwarm::ListenOn { opts }) =>
+					return Poll::Ready(ToSwarm::ListenOn { opts }),
+				Poll::Ready(ToSwarm::RemoveListener { id }) =>
+					return Poll::Ready(ToSwarm::RemoveListener { id }),
 			}
 		}
 
diff --git a/substrate/client/network/src/protocol.rs b/substrate/client/network/src/protocol.rs
index 2e57ff1b6a8..977c4c4de66 100644
--- a/substrate/client/network/src/protocol.rs
+++ b/substrate/client/network/src/protocol.rs
@@ -163,9 +163,6 @@ impl<B: BlockT> Protocol<B> {
 	pub fn disconnect_peer(&mut self, peer_id: &PeerId, protocol_name: ProtocolName) {
 		if let Some(position) = self.notification_protocols.iter().position(|p| *p == protocol_name)
 		{
-			// Note: no need to remove a peer from `self.peers` if we are dealing with sync
-			// protocol, because it will be done when handling
-			// `NotificationsOut::CustomProtocolClosed`.
 			self.behaviour.disconnect_peer(peer_id, SetId::from(position));
 		} else {
 			warn!(target: "sub-libp2p", "disconnect_peer() with invalid protocol name")
@@ -229,7 +226,7 @@ pub enum CustomMessageOutcome {
 
 impl<B: BlockT> NetworkBehaviour for Protocol<B> {
 	type ConnectionHandler = <Notifications as NetworkBehaviour>::ConnectionHandler;
-	type OutEvent = CustomMessageOutcome;
+	type ToSwarm = CustomMessageOutcome;
 
 	fn handle_established_inbound_connection(
 		&mut self,
@@ -290,17 +287,25 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
 		&mut self,
 		cx: &mut std::task::Context,
 		params: &mut impl PollParameters,
-	) -> Poll<ToSwarm<Self::OutEvent, THandlerInEvent<Self>>> {
+	) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
 		let event = match self.behaviour.poll(cx, params) {
 			Poll::Pending => return Poll::Pending,
 			Poll::Ready(ToSwarm::GenerateEvent(ev)) => ev,
 			Poll::Ready(ToSwarm::Dial { opts }) => return Poll::Ready(ToSwarm::Dial { opts }),
 			Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event }) =>
 				return Poll::Ready(ToSwarm::NotifyHandler { peer_id, handler, event }),
-			Poll::Ready(ToSwarm::ReportObservedAddr { address, score }) =>
-				return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }),
 			Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }) =>
 				return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }),
+			Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)) =>
+				return Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)),
+			Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)) =>
+				return Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)),
+			Poll::Ready(ToSwarm::ExternalAddrExpired(addr)) =>
+				return Poll::Ready(ToSwarm::ExternalAddrExpired(addr)),
+			Poll::Ready(ToSwarm::ListenOn { opts }) =>
+				return Poll::Ready(ToSwarm::ListenOn { opts }),
+			Poll::Ready(ToSwarm::RemoveListener { id }) =>
+				return Poll::Ready(ToSwarm::RemoveListener { id }),
 		};
 
 		let outcome = match event {
diff --git a/substrate/client/network/src/protocol/notifications/behaviour.rs b/substrate/client/network/src/protocol/notifications/behaviour.rs
index 03ba437a667..cb4f089995e 100644
--- a/substrate/client/network/src/protocol/notifications/behaviour.rs
+++ b/substrate/client/network/src/protocol/notifications/behaviour.rs
@@ -1198,7 +1198,7 @@ impl Notifications {
 
 impl NetworkBehaviour for Notifications {
 	type ConnectionHandler = NotifsHandler;
-	type OutEvent = NotificationsOut;
+	type ToSwarm = NotificationsOut;
 
 	fn handle_pending_inbound_connection(
 		&mut self,
@@ -1678,10 +1678,11 @@ impl NetworkBehaviour for Notifications {
 			FromSwarm::ListenerClosed(_) => {},
 			FromSwarm::ListenFailure(_) => {},
 			FromSwarm::ListenerError(_) => {},
-			FromSwarm::ExpiredExternalAddr(_) => {},
+			FromSwarm::ExternalAddrExpired(_) => {},
 			FromSwarm::NewListener(_) => {},
 			FromSwarm::ExpiredListenAddr(_) => {},
-			FromSwarm::NewExternalAddr(_) => {},
+			FromSwarm::NewExternalAddrCandidate(_) => {},
+			FromSwarm::ExternalAddrConfirmed(_) => {},
 			FromSwarm::AddressChange(_) => {},
 			FromSwarm::NewListenAddr(_) => {},
 		}
@@ -2239,7 +2240,7 @@ impl NetworkBehaviour for Notifications {
 		&mut self,
 		cx: &mut Context,
 		_params: &mut impl PollParameters,
-	) -> Poll<ToSwarm<Self::OutEvent, THandlerInEvent<Self>>> {
+	) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
 		if let Some(event) = self.events.pop_front() {
 			return Poll::Ready(event)
 		}
@@ -2382,7 +2383,6 @@ mod tests {
 		protocol::notifications::handler::tests::*,
 		protocol_controller::{IncomingIndex, ProtoSetConfig, ProtocolController},
 	};
-	use libp2p::swarm::AddressRecord;
 	use sc_utils::mpsc::tracing_unbounded;
 	use std::{collections::HashSet, iter};
 
@@ -2402,31 +2402,14 @@ mod tests {
 	}
 
 	#[derive(Clone)]
-	struct MockPollParams {
-		peer_id: PeerId,
-		addr: Multiaddr,
-	}
+	struct MockPollParams {}
 
 	impl PollParameters for MockPollParams {
 		type SupportedProtocolsIter = std::vec::IntoIter<Vec<u8>>;
-		type ListenedAddressesIter = std::vec::IntoIter<Multiaddr>;
-		type ExternalAddressesIter = std::vec::IntoIter<AddressRecord>;
 
 		fn supported_protocols(&self) -> Self::SupportedProtocolsIter {
 			vec![].into_iter()
 		}
-
-		fn listened_addresses(&self) -> Self::ListenedAddressesIter {
-			vec![self.addr.clone()].into_iter()
-		}
-
-		fn external_addresses(&self) -> Self::ExternalAddressesIter {
-			vec![].into_iter()
-		}
-
-		fn local_peer_id(&self) -> &PeerId {
-			&self.peer_id
-		}
 	}
 
 	fn development_notifs(
@@ -3331,7 +3314,7 @@ mod tests {
 
 		notif.on_swarm_event(FromSwarm::DialFailure(libp2p::swarm::behaviour::DialFailure {
 			peer_id: Some(peer),
-			error: &libp2p::swarm::DialError::Banned,
+			error: &libp2p::swarm::DialError::Aborted,
 			connection_id: ConnectionId::new_unchecked(1337),
 		}));
 
@@ -3877,7 +3860,7 @@ mod tests {
 		let now = Instant::now();
 		notif.on_swarm_event(FromSwarm::DialFailure(libp2p::swarm::behaviour::DialFailure {
 			peer_id: Some(peer),
-			error: &libp2p::swarm::DialError::Banned,
+			error: &libp2p::swarm::DialError::Aborted,
 			connection_id: ConnectionId::new_unchecked(0),
 		}));
 
@@ -4003,7 +3986,7 @@ mod tests {
 		assert!(notif.peers.get(&(peer, set_id)).is_some());
 
 		if tokio::time::timeout(Duration::from_secs(5), async {
-			let mut params = MockPollParams { peer_id: PeerId::random(), addr: Multiaddr::empty() };
+			let mut params = MockPollParams {};
 
 			loop {
 				futures::future::poll_fn(|cx| {
@@ -4115,7 +4098,7 @@ mod tests {
 		// verify that the code continues to keep the peer disabled by resetting the timer
 		// after the first one expired.
 		if tokio::time::timeout(Duration::from_secs(5), async {
-			let mut params = MockPollParams { peer_id: PeerId::random(), addr: Multiaddr::empty() };
+			let mut params = MockPollParams {};
 
 			loop {
 				futures::future::poll_fn(|cx| {
diff --git a/substrate/client/network/src/protocol/notifications/handler.rs b/substrate/client/network/src/protocol/notifications/handler.rs
index cb09583b73a..967ef614c55 100644
--- a/substrate/client/network/src/protocol/notifications/handler.rs
+++ b/substrate/client/network/src/protocol/notifications/handler.rs
@@ -75,8 +75,8 @@ use futures::{
 use libp2p::{
 	core::ConnectedPoint,
 	swarm::{
-		handler::ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, KeepAlive,
-		NegotiatedSubstream, SubstreamProtocol,
+		handler::ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, KeepAlive, Stream,
+		SubstreamProtocol,
 	},
 	PeerId,
 };
@@ -199,7 +199,7 @@ enum State {
 	/// emitted.
 	OpenDesiredByRemote {
 		/// Substream opened by the remote and that hasn't been accepted/rejected yet.
-		in_substream: NotificationsInSubstream<NegotiatedSubstream>,
+		in_substream: NotificationsInSubstream<Stream>,
 
 		/// See [`State::Closed::pending_opening`].
 		pending_opening: bool,
@@ -212,7 +212,7 @@ enum State {
 	/// be emitted when transitioning to respectively [`State::Open`] or [`State::Closed`].
 	Opening {
 		/// Substream opened by the remote. If `Some`, has been accepted.
-		in_substream: Option<NotificationsInSubstream<NegotiatedSubstream>>,
+		in_substream: Option<NotificationsInSubstream<Stream>>,
 		/// Is the connection inbound.
 		inbound: bool,
 	},
@@ -236,14 +236,14 @@ enum State {
 		/// Always `Some` on transition to [`State::Open`]. Switched to `None` only if the remote
 		/// closed the substream. If `None`, a [`NotifsHandlerOut::CloseDesired`] event has been
 		/// emitted.
-		out_substream: Option<NotificationsOutSubstream<NegotiatedSubstream>>,
+		out_substream: Option<NotificationsOutSubstream<Stream>>,
 
 		/// Substream opened by the remote.
 		///
 		/// Contrary to the `out_substream` field, operations continue as normal even if the
 		/// substream has been closed by the remote. A `None` is treated the same way as if there
 		/// was an idle substream.
-		in_substream: Option<NotificationsInSubstream<NegotiatedSubstream>>,
+		in_substream: Option<NotificationsInSubstream<Stream>>,
 	},
 }
 
@@ -481,8 +481,8 @@ pub enum NotifsHandlerError {
 }
 
 impl ConnectionHandler for NotifsHandler {
-	type InEvent = NotifsHandlerIn;
-	type OutEvent = NotifsHandlerOut;
+	type FromBehaviour = NotifsHandlerIn;
+	type ToBehaviour = NotifsHandlerOut;
 	type Error = NotifsHandlerError;
 	type InboundProtocol = UpgradeCollec<NotificationsIn>;
 	type OutboundProtocol = NotificationsOut;
@@ -517,7 +517,7 @@ impl ConnectionHandler for NotifsHandler {
 
 				match protocol_info.state {
 					State::Closed { pending_opening } => {
-						self.events_queue.push_back(ConnectionHandlerEvent::Custom(
+						self.events_queue.push_back(ConnectionHandlerEvent::NotifyBehaviour(
 							NotifsHandlerOut::OpenDesiredByRemote {
 								protocol_index,
 								handshake: in_substream_open.handshake,
@@ -586,7 +586,7 @@ impl ConnectionHandler for NotifsHandler {
 							in_substream: in_substream.take(),
 						};
 
-						self.events_queue.push_back(ConnectionHandlerEvent::Custom(
+						self.events_queue.push_back(ConnectionHandlerEvent::NotifyBehaviour(
 							NotifsHandlerOut::OpenResultOk {
 								protocol_index,
 								negotiated_fallback: new_open.negotiated_fallback,
@@ -600,6 +600,8 @@ impl ConnectionHandler for NotifsHandler {
 				}
 			},
 			ConnectionEvent::AddressChange(_address_change) => {},
+			ConnectionEvent::LocalProtocolsChange(_) => {},
+			ConnectionEvent::RemoteProtocolsChange(_) => {},
 			ConnectionEvent::DialUpgradeError(dial_upgrade_error) => match self.protocols
 				[dial_upgrade_error.info]
 				.state
@@ -614,7 +616,7 @@ impl ConnectionHandler for NotifsHandler {
 					self.protocols[dial_upgrade_error.info].state =
 						State::Closed { pending_opening: false };
 
-					self.events_queue.push_back(ConnectionHandlerEvent::Custom(
+					self.events_queue.push_back(ConnectionHandlerEvent::NotifyBehaviour(
 						NotifsHandlerOut::OpenResultErr { protocol_index: dial_upgrade_error.info },
 					));
 				},
@@ -701,7 +703,7 @@ impl ConnectionHandler for NotifsHandler {
 						self.protocols[protocol_index].state =
 							State::Closed { pending_opening: true };
 
-						self.events_queue.push_back(ConnectionHandlerEvent::Custom(
+						self.events_queue.push_back(ConnectionHandlerEvent::NotifyBehaviour(
 							NotifsHandlerOut::OpenResultErr { protocol_index },
 						));
 					},
@@ -711,7 +713,7 @@ impl ConnectionHandler for NotifsHandler {
 					State::Closed { .. } => {},
 				}
 
-				self.events_queue.push_back(ConnectionHandlerEvent::Custom(
+				self.events_queue.push_back(ConnectionHandlerEvent::NotifyBehaviour(
 					NotifsHandlerOut::CloseResult { protocol_index },
 				));
 			},
@@ -726,9 +728,11 @@ impl ConnectionHandler for NotifsHandler {
 
 		// A grace period of `INITIAL_KEEPALIVE_TIME` must be given to leave time for the remote
 		// to express desire to open substreams.
+		#[allow(deprecated)]
 		KeepAlive::Until(self.when_connection_open + INITIAL_KEEPALIVE_TIME)
 	}
 
+	#[allow(deprecated)]
 	fn poll(
 		&mut self,
 		cx: &mut Context,
@@ -736,7 +740,7 @@ impl ConnectionHandler for NotifsHandler {
 		ConnectionHandlerEvent<
 			Self::OutboundProtocol,
 			Self::OutboundOpenInfo,
-			Self::OutEvent,
+			Self::ToBehaviour,
 			Self::Error,
 		>,
 	> {
@@ -755,6 +759,7 @@ impl ConnectionHandler for NotifsHandler {
 					// Only proceed with `out_substream.poll_ready_unpin` if there is an element
 					// available in `notifications_sink_rx`. This avoids waking up the task when
 					// a substream is ready to send if there isn't actually something to send.
+					#[allow(deprecated)]
 					match Pin::new(&mut *notifications_sink_rx).as_mut().poll_peek(cx) {
 						Poll::Ready(Some(&NotificationsSinkMessage::ForceClose)) =>
 							return Poll::Ready(ConnectionHandlerEvent::Close(
@@ -808,7 +813,7 @@ impl ConnectionHandler for NotifsHandler {
 						Poll::Ready(Err(_)) => {
 							*out_substream = None;
 							let event = NotifsHandlerOut::CloseDesired { protocol_index };
-							return Poll::Ready(ConnectionHandlerEvent::Custom(event))
+							return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(event))
 						},
 					};
 				},
@@ -830,11 +835,14 @@ impl ConnectionHandler for NotifsHandler {
 				State::Opening { in_substream: None, .. } => {},
 
 				State::Open { in_substream: in_substream @ Some(_), .. } =>
-					match Stream::poll_next(Pin::new(in_substream.as_mut().unwrap()), cx) {
+					match futures::prelude::stream::Stream::poll_next(
+						Pin::new(in_substream.as_mut().unwrap()),
+						cx,
+					) {
 						Poll::Pending => {},
 						Poll::Ready(Some(Ok(message))) => {
 							let event = NotifsHandlerOut::Notification { protocol_index, message };
-							return Poll::Ready(ConnectionHandlerEvent::Custom(event))
+							return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(event))
 						},
 						Poll::Ready(None) | Poll::Ready(Some(Err(_))) => *in_substream = None,
 					},
@@ -846,7 +854,7 @@ impl ConnectionHandler for NotifsHandler {
 						Poll::Ready(Err(_)) => {
 							self.protocols[protocol_index].state =
 								State::Closed { pending_opening: *pending_opening };
-							return Poll::Ready(ConnectionHandlerEvent::Custom(
+							return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(
 								NotifsHandlerOut::CloseDesired { protocol_index },
 							))
 						},
@@ -880,8 +888,8 @@ pub mod tests {
 	use asynchronous_codec::Framed;
 	use libp2p::{
 		core::muxing::SubstreamBox,
-		swarm::{handler, ConnectionHandlerUpgrErr},
-		Multiaddr,
+		swarm::handler::{self, StreamUpgradeError},
+		Multiaddr, Stream,
 	};
 	use multistream_select::{dialer_select_proto, listener_select_proto, Negotiated, Version};
 	use std::{
@@ -972,6 +980,7 @@ pub mod tests {
 			.await
 		}
 	}
+
 	struct MockSubstream {
 		pub rx: mpsc::Receiver<Vec<u8>>,
 		pub tx: mpsc::Sender<Vec<u8>>,
@@ -991,18 +1000,35 @@ pub mod tests {
 		}
 
 		/// Create new negotiated substream pair.
-		pub async fn negotiated() -> (Negotiated<SubstreamBox>, Negotiated<SubstreamBox>) {
+		pub async fn negotiated() -> (Stream, Stream) {
 			let (socket1, socket2) = Self::new();
 			let socket1 = SubstreamBox::new(socket1);
 			let socket2 = SubstreamBox::new(socket2);
 
-			let protos = vec![b"/echo/1.0.0", b"/echo/2.5.0"];
+			let protos = vec!["/echo/1.0.0", "/echo/2.5.0"];
 			let (res1, res2) = tokio::join!(
 				dialer_select_proto(socket1, protos.clone(), Version::V1),
 				listener_select_proto(socket2, protos),
 			);
 
-			(res1.unwrap().1, res2.unwrap().1)
+			(Self::stream_new(res1.unwrap().1), Self::stream_new(res2.unwrap().1))
+		}
+
+		/// Unsafe substitute for `Stream::new` private constructor.
+		fn stream_new(stream: Negotiated<SubstreamBox>) -> Stream {
+			// Static asserts to make sure this doesn't break.
+			const _: () = {
+				assert!(
+					core::mem::size_of::<Stream>() ==
+						core::mem::size_of::<Negotiated<SubstreamBox>>()
+				);
+				assert!(
+					core::mem::align_of::<Stream>() ==
+						core::mem::align_of::<Negotiated<SubstreamBox>>()
+				);
+			};
+
+			unsafe { core::mem::transmute(stream) }
 		}
 	}
 
@@ -1504,7 +1530,7 @@ pub mod tests {
 
 		// inject dial failure to an already closed substream and verify outbound state is reset
 		handler.on_connection_event(handler::ConnectionEvent::DialUpgradeError(
-			handler::DialUpgradeError { info: 0, error: ConnectionHandlerUpgrErr::Timeout },
+			handler::DialUpgradeError { info: 0, error: StreamUpgradeError::Timeout },
 		));
 		assert!(std::matches!(
 			handler.protocols[0].state,
@@ -1574,7 +1600,7 @@ pub mod tests {
 
 		// inject dial failure to an already closed substream and verify outbound state is reset
 		handler.on_connection_event(handler::ConnectionEvent::DialUpgradeError(
-			handler::DialUpgradeError { info: 0, error: ConnectionHandlerUpgrErr::Timeout },
+			handler::DialUpgradeError { info: 0, error: StreamUpgradeError::Timeout },
 		));
 		assert!(std::matches!(
 			handler.protocols[0].state,
@@ -1610,6 +1636,7 @@ pub mod tests {
 		notifications_sink.send_sync_notification(vec![1, 3, 3, 9]);
 		notifications_sink.send_sync_notification(vec![1, 3, 4, 0]);
 
+		#[allow(deprecated)]
 		futures::future::poll_fn(|cx| {
 			assert!(std::matches!(
 				handler.poll(cx),
@@ -1648,15 +1675,15 @@ pub mod tests {
 		futures::future::poll_fn(|cx| {
 			assert!(std::matches!(
 				handler.poll(cx),
-				Poll::Ready(ConnectionHandlerEvent::Custom(
+				Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(
 					NotifsHandlerOut::OpenDesiredByRemote { protocol_index: 0, .. },
 				))
 			));
 			assert!(std::matches!(
 				handler.poll(cx),
-				Poll::Ready(ConnectionHandlerEvent::Custom(NotifsHandlerOut::CloseDesired {
-					protocol_index: 0
-				},))
+				Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(
+					NotifsHandlerOut::CloseDesired { protocol_index: 0 },
+				))
 			));
 			Poll::Ready(())
 		})
diff --git a/substrate/client/network/src/protocol/notifications/tests.rs b/substrate/client/network/src/protocol/notifications/tests.rs
index a72b5b4a674..90c9cc5b7cd 100644
--- a/substrate/client/network/src/protocol/notifications/tests.rs
+++ b/substrate/client/network/src/protocol/notifications/tests.rs
@@ -33,9 +33,8 @@ use libp2p::{
 	core::{transport::MemoryTransport, upgrade, Endpoint},
 	identity, noise,
 	swarm::{
-		behaviour::FromSwarm, ConnectionDenied, ConnectionId, Executor, NetworkBehaviour,
-		PollParameters, Swarm, SwarmBuilder, SwarmEvent, THandler, THandlerInEvent,
-		THandlerOutEvent, ToSwarm,
+		self, behaviour::FromSwarm, ConnectionDenied, ConnectionId, Executor, NetworkBehaviour,
+		PollParameters, Swarm, SwarmEvent, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm,
 	},
 	yamux, Multiaddr, PeerId, Transport,
 };
@@ -141,13 +140,12 @@ fn build_nodes() -> (Swarm<CustomProtoWithAddr>, Swarm<CustomProtoWithAddr>) {
 			}
 		});
 
-		let mut swarm = SwarmBuilder::with_executor(
+		let mut swarm = Swarm::new(
 			transport,
 			behaviour,
 			keypairs[index].public().to_peer_id(),
-			TokioExecutor(runtime),
-		)
-		.build();
+			swarm::Config::with_executor(TokioExecutor(runtime)),
+		);
 		swarm.listen_on(addrs[index].clone()).unwrap();
 		out.push(swarm);
 	}
@@ -183,7 +181,7 @@ impl std::ops::DerefMut for CustomProtoWithAddr {
 
 impl NetworkBehaviour for CustomProtoWithAddr {
 	type ConnectionHandler = <Notifications as NetworkBehaviour>::ConnectionHandler;
-	type OutEvent = <Notifications as NetworkBehaviour>::OutEvent;
+	type ToSwarm = <Notifications as NetworkBehaviour>::ToSwarm;
 
 	fn handle_pending_inbound_connection(
 		&mut self,
@@ -261,7 +259,7 @@ impl NetworkBehaviour for CustomProtoWithAddr {
 		&mut self,
 		cx: &mut Context,
 		params: &mut impl PollParameters,
-	) -> Poll<ToSwarm<Self::OutEvent, THandlerInEvent<Self>>> {
+	) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
 		let _ = self.peer_store_future.poll_unpin(cx);
 		let _ = self.protocol_controller_future.poll_unpin(cx);
 		self.inner.poll(cx, params)
diff --git a/substrate/client/network/src/protocol/notifications/upgrade.rs b/substrate/client/network/src/protocol/notifications/upgrade.rs
index 8fd837f949d..72e0c2d1039 100644
--- a/substrate/client/network/src/protocol/notifications/upgrade.rs
+++ b/substrate/client/network/src/protocol/notifications/upgrade.rs
@@ -20,6 +20,7 @@
 pub(crate) use self::notifications::{
 	NotificationsInOpen, NotificationsInSubstreamHandshake, NotificationsOutOpen,
 };
+
 pub use self::{
 	collec::UpgradeCollec,
 	notifications::{
diff --git a/substrate/client/network/src/protocol/notifications/upgrade/collec.rs b/substrate/client/network/src/protocol/notifications/upgrade/collec.rs
index 33c090ae50e..ab0f87215cc 100644
--- a/substrate/client/network/src/protocol/notifications/upgrade/collec.rs
+++ b/substrate/client/network/src/protocol/notifications/upgrade/collec.rs
@@ -17,7 +17,7 @@
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
 use futures::prelude::*;
-use libp2p::core::upgrade::{InboundUpgrade, ProtocolName, UpgradeInfo};
+use libp2p::core::upgrade::{InboundUpgrade, UpgradeInfo};
 use std::{
 	pin::Pin,
 	task::{Context, Poll},
@@ -75,9 +75,9 @@ where
 #[derive(Debug, Clone, PartialEq)]
 pub struct ProtoNameWithUsize<T>(T, usize);
 
-impl<T: ProtocolName> ProtocolName for ProtoNameWithUsize<T> {
-	fn protocol_name(&self) -> &[u8] {
-		self.0.protocol_name()
+impl<T: AsRef<str>> AsRef<str> for ProtoNameWithUsize<T> {
+	fn as_ref(&self) -> &str {
+		self.0.as_ref()
 	}
 }
 
@@ -103,13 +103,13 @@ impl<T: Future<Output = Result<O, E>>, O, E> Future for FutWithUsize<T> {
 mod tests {
 	use super::*;
 	use crate::types::ProtocolName as ProtoName;
-	use libp2p::core::upgrade::{ProtocolName, UpgradeInfo};
+	use libp2p::core::upgrade::UpgradeInfo;
 
 	// TODO: move to mocks
 	mockall::mock! {
 		pub ProtocolUpgrade<T> {}
 
-		impl<T: Clone + ProtocolName> UpgradeInfo for ProtocolUpgrade<T> {
+		impl<T: Clone + AsRef<str>> UpgradeInfo for ProtocolUpgrade<T> {
 			type Info = T;
 			type InfoIter = vec::IntoIter<T>;
 			fn protocol_info(&self) -> vec::IntoIter<T>;
diff --git a/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs b/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs
index c760b7a963f..a8a9e453a7b 100644
--- a/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs
+++ b/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs
@@ -513,45 +513,99 @@ pub enum NotificationsOutError {
 
 #[cfg(test)]
 mod tests {
+	use crate::ProtocolName;
+
 	use super::{
-		NotificationsIn, NotificationsInOpen, NotificationsOut, NotificationsOutError,
-		NotificationsOutOpen,
+		NotificationsHandshakeError, NotificationsIn, NotificationsInOpen,
+		NotificationsInSubstream, NotificationsOut, NotificationsOutError, NotificationsOutOpen,
+		NotificationsOutSubstream,
 	};
-	use futures::{channel::oneshot, future, prelude::*};
-	use libp2p::core::upgrade;
+	use futures::{channel::oneshot, future, prelude::*, SinkExt, StreamExt};
+	use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo};
 	use std::{pin::Pin, task::Poll};
 	use tokio::net::{TcpListener, TcpStream};
 	use tokio_util::compat::TokioAsyncReadCompatExt;
 
+	/// Opens a substream to the given address, negotiates the protocol, and returns the substream
+	/// along with the handshake message.
+	async fn dial(
+		addr: std::net::SocketAddr,
+		handshake: impl Into<Vec<u8>>,
+	) -> Result<
+		(
+			Vec<u8>,
+			NotificationsOutSubstream<
+				multistream_select::Negotiated<tokio_util::compat::Compat<TcpStream>>,
+			>,
+		),
+		NotificationsHandshakeError,
+	> {
+		let socket = TcpStream::connect(addr).await.unwrap();
+		let notifs_out = NotificationsOut::new("/test/proto/1", Vec::new(), handshake, 1024 * 1024);
+		let (_, substream) = multistream_select::dialer_select_proto(
+			socket.compat(),
+			notifs_out.protocol_info(),
+			upgrade::Version::V1,
+		)
+		.await
+		.unwrap();
+		let NotificationsOutOpen { handshake, substream, .. } =
+			<NotificationsOut as OutboundUpgrade<_>>::upgrade_outbound(
+				notifs_out,
+				substream,
+				"/test/proto/1".into(),
+			)
+			.await?;
+		Ok((handshake, substream))
+	}
+
+	/// Listens on a localhost, negotiates the protocol, and returns the substream along with the
+	/// handshake message.
+	///
+	/// Also sends the listener address through the given channel.
+	async fn listen_on_localhost(
+		listener_addr_tx: oneshot::Sender<std::net::SocketAddr>,
+	) -> Result<
+		(
+			Vec<u8>,
+			NotificationsInSubstream<
+				multistream_select::Negotiated<tokio_util::compat::Compat<TcpStream>>,
+			>,
+		),
+		NotificationsHandshakeError,
+	> {
+		let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
+		listener_addr_tx.send(listener.local_addr().unwrap()).unwrap();
+
+		let (socket, _) = listener.accept().await.unwrap();
+		let notifs_in = NotificationsIn::new("/test/proto/1", Vec::new(), 1024 * 1024);
+		let (_, substream) =
+			multistream_select::listener_select_proto(socket.compat(), notifs_in.protocol_info())
+				.await
+				.unwrap();
+		let NotificationsInOpen { handshake, substream, .. } =
+			<NotificationsIn as InboundUpgrade<_>>::upgrade_inbound(
+				notifs_in,
+				substream,
+				"/test/proto/1".into(),
+			)
+			.await?;
+		Ok((handshake, substream))
+	}
+
 	#[tokio::test]
 	async fn basic_works() {
-		const PROTO_NAME: &str = "/test/proto/1";
 		let (listener_addr_tx, listener_addr_rx) = oneshot::channel();
 
 		let client = tokio::spawn(async move {
-			let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap();
-			let NotificationsOutOpen { handshake, mut substream, .. } = upgrade::apply_outbound(
-				socket.compat(),
-				NotificationsOut::new(PROTO_NAME, Vec::new(), &b"initial message"[..], 1024 * 1024),
-				upgrade::Version::V1,
-			)
-			.await
-			.unwrap();
+			let (handshake, mut substream) =
+				dial(listener_addr_rx.await.unwrap(), &b"initial message"[..]).await.unwrap();
 
 			assert_eq!(handshake, b"hello world");
 			substream.send(b"test message".to_vec()).await.unwrap();
 		});
 
-		let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
-		listener_addr_tx.send(listener.local_addr().unwrap()).unwrap();
-
-		let (socket, _) = listener.accept().await.unwrap();
-		let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound(
-			socket.compat(),
-			NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024),
-		)
-		.await
-		.unwrap();
+		let (handshake, mut substream) = listen_on_localhost(listener_addr_tx).await.unwrap();
 
 		assert_eq!(handshake, b"initial message");
 		substream.send_handshake(&b"hello world"[..]);
@@ -566,33 +620,17 @@ mod tests {
 	async fn empty_handshake() {
 		// Check that everything still works when the handshake messages are empty.
 
-		const PROTO_NAME: &str = "/test/proto/1";
 		let (listener_addr_tx, listener_addr_rx) = oneshot::channel();
 
 		let client = tokio::spawn(async move {
-			let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap();
-			let NotificationsOutOpen { handshake, mut substream, .. } = upgrade::apply_outbound(
-				socket.compat(),
-				NotificationsOut::new(PROTO_NAME, Vec::new(), vec![], 1024 * 1024),
-				upgrade::Version::V1,
-			)
-			.await
-			.unwrap();
+			let (handshake, mut substream) =
+				dial(listener_addr_rx.await.unwrap(), vec![]).await.unwrap();
 
 			assert!(handshake.is_empty());
 			substream.send(Default::default()).await.unwrap();
 		});
 
-		let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
-		listener_addr_tx.send(listener.local_addr().unwrap()).unwrap();
-
-		let (socket, _) = listener.accept().await.unwrap();
-		let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound(
-			socket.compat(),
-			NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024),
-		)
-		.await
-		.unwrap();
+		let (handshake, mut substream) = listen_on_localhost(listener_addr_tx).await.unwrap();
 
 		assert!(handshake.is_empty());
 		substream.send_handshake(vec![]);
@@ -605,17 +643,10 @@ mod tests {
 
 	#[tokio::test]
 	async fn refused() {
-		const PROTO_NAME: &str = "/test/proto/1";
 		let (listener_addr_tx, listener_addr_rx) = oneshot::channel();
 
 		let client = tokio::spawn(async move {
-			let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap();
-			let outcome = upgrade::apply_outbound(
-				socket.compat(),
-				NotificationsOut::new(PROTO_NAME, Vec::new(), &b"hello"[..], 1024 * 1024),
-				upgrade::Version::V1,
-			)
-			.await;
+			let outcome = dial(listener_addr_rx.await.unwrap(), &b"hello"[..]).await;
 
 			// Despite the protocol negotiation being successfully conducted on the listener
 			// side, we have to receive an error here because the listener didn't send the
@@ -623,17 +654,7 @@ mod tests {
 			assert!(outcome.is_err());
 		});
 
-		let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
-		listener_addr_tx.send(listener.local_addr().unwrap()).unwrap();
-
-		let (socket, _) = listener.accept().await.unwrap();
-		let NotificationsInOpen { handshake, substream, .. } = upgrade::apply_inbound(
-			socket.compat(),
-			NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024),
-		)
-		.await
-		.unwrap();
-
+		let (handshake, substream) = listen_on_localhost(listener_addr_tx).await.unwrap();
 		assert_eq!(handshake, b"hello");
 
 		// We successfully upgrade to the protocol, but then close the substream.
@@ -644,66 +665,29 @@ mod tests {
 
 	#[tokio::test]
 	async fn large_initial_message_refused() {
-		const PROTO_NAME: &str = "/test/proto/1";
 		let (listener_addr_tx, listener_addr_rx) = oneshot::channel();
 
 		let client = tokio::spawn(async move {
-			let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap();
-			let ret = upgrade::apply_outbound(
-				socket.compat(),
-				// We check that an initial message that is too large gets refused.
-				NotificationsOut::new(
-					PROTO_NAME,
-					Vec::new(),
-					(0..32768).map(|_| 0).collect::<Vec<_>>(),
-					1024 * 1024,
-				),
-				upgrade::Version::V1,
-			)
-			.await;
+			let ret =
+				dial(listener_addr_rx.await.unwrap(), (0..32768).map(|_| 0).collect::<Vec<_>>())
+					.await;
 			assert!(ret.is_err());
 		});
 
-		let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
-		listener_addr_tx.send(listener.local_addr().unwrap()).unwrap();
-
-		let (socket, _) = listener.accept().await.unwrap();
-		let ret = upgrade::apply_inbound(
-			socket.compat(),
-			NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024),
-		)
-		.await;
-		assert!(ret.is_err());
-
+		let _ret = listen_on_localhost(listener_addr_tx).await;
 		client.await.unwrap();
 	}
 
 	#[tokio::test]
 	async fn large_handshake_refused() {
-		const PROTO_NAME: &str = "/test/proto/1";
 		let (listener_addr_tx, listener_addr_rx) = oneshot::channel();
 
 		let client = tokio::spawn(async move {
-			let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap();
-			let ret = upgrade::apply_outbound(
-				socket.compat(),
-				NotificationsOut::new(PROTO_NAME, Vec::new(), &b"initial message"[..], 1024 * 1024),
-				upgrade::Version::V1,
-			)
-			.await;
+			let ret = dial(listener_addr_rx.await.unwrap(), &b"initial message"[..]).await;
 			assert!(ret.is_err());
 		});
 
-		let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
-		listener_addr_tx.send(listener.local_addr().unwrap()).unwrap();
-
-		let (socket, _) = listener.accept().await.unwrap();
-		let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound(
-			socket.compat(),
-			NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024),
-		)
-		.await
-		.unwrap();
+		let (handshake, mut substream) = listen_on_localhost(listener_addr_tx).await.unwrap();
 		assert_eq!(handshake, b"initial message");
 
 		// We check that a handshake that is too large gets refused.
@@ -720,10 +704,10 @@ mod tests {
 
 		let client = tokio::spawn(async move {
 			let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap();
-			let NotificationsOutOpen { handshake, .. } = upgrade::apply_outbound(
-				socket.compat(),
+			let NotificationsOutOpen { handshake, .. } = OutboundUpgrade::upgrade_outbound(
 				NotificationsOut::new(PROTO_NAME, Vec::new(), &b"initial message"[..], 1024 * 1024),
-				upgrade::Version::V1,
+				socket.compat(),
+				ProtocolName::Static(PROTO_NAME),
 			)
 			.await
 			.unwrap();
@@ -735,9 +719,10 @@ mod tests {
 		listener_addr_tx.send(listener.local_addr().unwrap()).unwrap();
 
 		let (socket, _) = listener.accept().await.unwrap();
-		let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound(
-			socket.compat(),
+		let NotificationsInOpen { handshake, mut substream, .. } = InboundUpgrade::upgrade_inbound(
 			NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024),
+			socket.compat(),
+			ProtocolName::Static(PROTO_NAME),
 		)
 		.await
 		.unwrap();
@@ -758,13 +743,19 @@ mod tests {
 
 		let client = tokio::spawn(async move {
 			let socket = TcpStream::connect(listener_addr_rx.await.unwrap()).await.unwrap();
-			let NotificationsOutOpen { handshake, mut substream, .. } = upgrade::apply_outbound(
-				socket.compat(),
-				NotificationsOut::new(PROTO_NAME, Vec::new(), &b"initial message"[..], 1024 * 1024),
-				upgrade::Version::V1,
-			)
-			.await
-			.unwrap();
+			let NotificationsOutOpen { handshake, mut substream, .. } =
+				OutboundUpgrade::upgrade_outbound(
+					NotificationsOut::new(
+						PROTO_NAME,
+						Vec::new(),
+						&b"initial message"[..],
+						1024 * 1024,
+					),
+					socket.compat(),
+					ProtocolName::Static(PROTO_NAME),
+				)
+				.await
+				.unwrap();
 
 			assert_eq!(handshake, b"hello world");
 
@@ -786,9 +777,10 @@ mod tests {
 		listener_addr_tx.send(listener.local_addr().unwrap()).unwrap();
 
 		let (socket, _) = listener.accept().await.unwrap();
-		let NotificationsInOpen { handshake, mut substream, .. } = upgrade::apply_inbound(
-			socket.compat(),
+		let NotificationsInOpen { handshake, mut substream, .. } = InboundUpgrade::upgrade_inbound(
 			NotificationsIn::new(PROTO_NAME, Vec::new(), 1024 * 1024),
+			socket.compat(),
+			ProtocolName::Static(PROTO_NAME),
 		)
 		.await
 		.unwrap();
diff --git a/substrate/client/network/src/request_responses.rs b/substrate/client/network/src/request_responses.rs
index fbf050a6571..3671d76ea63 100644
--- a/substrate/client/network/src/request_responses.rs
+++ b/substrate/client/network/src/request_responses.rs
@@ -318,7 +318,6 @@ impl RequestResponsesBehaviour {
 		let mut protocols = HashMap::new();
 		for protocol in list {
 			let mut cfg = Config::default();
-			cfg.set_connection_keep_alive(Duration::from_secs(10));
 			cfg.set_request_timeout(protocol.request_timeout);
 
 			let protocol_support = if protocol.inbound_queue.is_some() {
@@ -327,13 +326,13 @@ impl RequestResponsesBehaviour {
 				ProtocolSupport::Outbound
 			};
 
-			let rq_rp = Behaviour::new(
+			let rq_rp = Behaviour::with_codec(
 				GenericCodec {
 					max_request_size: protocol.max_request_size,
 					max_response_size: protocol.max_response_size,
 				},
-				iter::once(protocol.name.as_bytes().to_vec())
-					.chain(protocol.fallback_names.iter().map(|name| name.as_bytes().to_vec()))
+				iter::once(protocol.name.clone())
+					.chain(protocol.fallback_names)
 					.zip(iter::repeat(protocol_support)),
 				cfg,
 			);
@@ -427,7 +426,7 @@ impl RequestResponsesBehaviour {
 impl NetworkBehaviour for RequestResponsesBehaviour {
 	type ConnectionHandler =
 		MultiHandler<String, <Behaviour<GenericCodec> as NetworkBehaviour>::ConnectionHandler>;
-	type OutEvent = Event;
+	type ToSwarm = Event;
 
 	fn handle_pending_inbound_connection(
 		&mut self,
@@ -543,9 +542,9 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
 				for (p, _) in self.protocols.values_mut() {
 					NetworkBehaviour::on_swarm_event(p, FromSwarm::ListenerError(e));
 				},
-			FromSwarm::ExpiredExternalAddr(e) =>
+			FromSwarm::ExternalAddrExpired(e) =>
 				for (p, _) in self.protocols.values_mut() {
-					NetworkBehaviour::on_swarm_event(p, FromSwarm::ExpiredExternalAddr(e));
+					NetworkBehaviour::on_swarm_event(p, FromSwarm::ExternalAddrExpired(e));
 				},
 			FromSwarm::NewListener(e) =>
 				for (p, _) in self.protocols.values_mut() {
@@ -555,9 +554,13 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
 				for (p, _) in self.protocols.values_mut() {
 					NetworkBehaviour::on_swarm_event(p, FromSwarm::ExpiredListenAddr(e));
 				},
-			FromSwarm::NewExternalAddr(e) =>
+			FromSwarm::NewExternalAddrCandidate(e) =>
 				for (p, _) in self.protocols.values_mut() {
-					NetworkBehaviour::on_swarm_event(p, FromSwarm::NewExternalAddr(e));
+					NetworkBehaviour::on_swarm_event(p, FromSwarm::NewExternalAddrCandidate(e));
+				},
+			FromSwarm::ExternalAddrConfirmed(e) =>
+				for (p, _) in self.protocols.values_mut() {
+					NetworkBehaviour::on_swarm_event(p, FromSwarm::ExternalAddrConfirmed(e));
 				},
 			FromSwarm::AddressChange(e) =>
 				for (p, _) in self.protocols.values_mut() {
@@ -592,7 +595,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
 		&mut self,
 		cx: &mut Context,
 		params: &mut impl PollParameters,
-	) -> Poll<ToSwarm<Self::OutEvent, THandlerInEvent<Self>>> {
+	) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
 		'poll_all: loop {
 			// Poll to see if any response is ready to be sent back.
 			while let Poll::Ready(Some(outcome)) = self.pending_responses.poll_next_unpin(cx) {
@@ -663,10 +666,18 @@ impl NetworkBehaviour for RequestResponsesBehaviour {
 								handler,
 								event: ((*protocol).to_string(), event),
 							}),
-						ToSwarm::ReportObservedAddr { address, score } =>
-							return Poll::Ready(ToSwarm::ReportObservedAddr { address, score }),
 						ToSwarm::CloseConnection { peer_id, connection } =>
 							return Poll::Ready(ToSwarm::CloseConnection { peer_id, connection }),
+						ToSwarm::NewExternalAddrCandidate(observed) =>
+							return Poll::Ready(ToSwarm::NewExternalAddrCandidate(observed)),
+						ToSwarm::ExternalAddrConfirmed(addr) =>
+							return Poll::Ready(ToSwarm::ExternalAddrConfirmed(addr)),
+						ToSwarm::ExternalAddrExpired(addr) =>
+							return Poll::Ready(ToSwarm::ExternalAddrExpired(addr)),
+						ToSwarm::ListenOn { opts } =>
+							return Poll::Ready(ToSwarm::ListenOn { opts }),
+						ToSwarm::RemoveListener { id } =>
+							return Poll::Ready(ToSwarm::RemoveListener { id }),
 					};
 
 					match ev {
@@ -950,7 +961,7 @@ pub struct GenericCodec {
 
 #[async_trait::async_trait]
 impl Codec for GenericCodec {
-	type Protocol = Vec<u8>;
+	type Protocol = ProtocolName;
 	type Request = Vec<u8>;
 	type Response = Result<Vec<u8>, ()>;
 
@@ -1078,7 +1089,7 @@ mod tests {
 		},
 		identity::Keypair,
 		noise,
-		swarm::{Executor, Swarm, SwarmBuilder, SwarmEvent},
+		swarm::{Config as SwarmConfig, Executor, Swarm, SwarmEvent},
 		Multiaddr,
 	};
 	use std::{iter, time::Duration};
@@ -1104,16 +1115,18 @@ mod tests {
 		let behaviour = RequestResponsesBehaviour::new(list, Arc::new(MockPeerStore {})).unwrap();
 
 		let runtime = tokio::runtime::Runtime::new().unwrap();
-		let mut swarm = SwarmBuilder::with_executor(
+
+		let mut swarm = Swarm::new(
 			transport,
 			behaviour,
 			keypair.public().to_peer_id(),
-			TokioExecutor(runtime),
-		)
-		.build();
+			SwarmConfig::with_executor(TokioExecutor(runtime)),
+		);
+
 		let listen_addr: Multiaddr = format!("/memory/{}", rand::random::<u64>()).parse().unwrap();
 
 		swarm.listen_on(listen_addr.clone()).unwrap();
+
 		(swarm, listen_addr)
 	}
 
diff --git a/substrate/client/network/src/service.rs b/substrate/client/network/src/service.rs
index 2cf4564e312..3a685787c48 100644
--- a/substrate/client/network/src/service.rs
+++ b/substrate/client/network/src/service.rs
@@ -61,18 +61,18 @@ use crate::{
 use codec::DecodeAll;
 use either::Either;
 use futures::{channel::oneshot, prelude::*};
-use libp2p::identity::ed25519;
 #[allow(deprecated)]
+use libp2p::swarm::THandlerErr;
 use libp2p::{
-	connection_limits::Exceeded,
+	connection_limits::{ConnectionLimits, Exceeded},
 	core::{upgrade, ConnectedPoint, Endpoint},
 	identify::Info as IdentifyInfo,
+	identity::ed25519,
 	kad::record::Key as KademliaKey,
 	multiaddr::{self, Multiaddr},
-	ping::Failure as PingFailure,
 	swarm::{
-		AddressScore, ConnectionError, ConnectionId, ConnectionLimits, DialError, Executor,
-		ListenError, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent, THandlerErr,
+		Config as SwarmConfig, ConnectionError, ConnectionId, DialError, Executor, ListenError,
+		NetworkBehaviour, Swarm, SwarmEvent,
 	},
 	PeerId,
 };
@@ -274,10 +274,6 @@ where
 		let local_identity: ed25519::Keypair = local_identity.into();
 		let local_public: ed25519::PublicKey = local_public.into();
 		let local_peer_id: PeerId = local_peer_id.into();
-		let listen_addresses: Vec<Multiaddr> =
-			network_config.listen_addresses.iter().cloned().map(Into::into).collect();
-		let public_addresses: Vec<Multiaddr> =
-			network_config.public_addresses.iter().cloned().map(Into::into).collect();
 
 		network_config.boot_nodes = network_config
 			.boot_nodes
@@ -559,6 +555,11 @@ where
 					request_response_protocols,
 					Arc::clone(&peer_store_handle),
 					external_addresses.clone(),
+					ConnectionLimits::default()
+						.with_max_established_per_peer(Some(crate::MAX_CONNECTIONS_PER_PEER as u32))
+						.with_max_established_incoming(Some(
+							crate::MAX_CONNECTIONS_ESTABLISHED_INCOMING,
+						)),
 				);
 
 				match result {
@@ -568,37 +569,27 @@ where
 				}
 			};
 
-			let builder = {
+			let swarm = {
 				struct SpawnImpl<F>(F);
 				impl<F: Fn(Pin<Box<dyn Future<Output = ()> + Send>>)> Executor for SpawnImpl<F> {
 					fn exec(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) {
 						(self.0)(f)
 					}
 				}
-				SwarmBuilder::with_executor(
-					transport,
-					behaviour,
-					local_peer_id,
-					SpawnImpl(params.executor),
-				)
+
+				let config = SwarmConfig::with_executor(SpawnImpl(params.executor))
+					.with_substream_upgrade_protocol_override(upgrade::Version::V1)
+					.with_notify_handler_buffer_size(NonZeroUsize::new(32).expect("32 != 0; qed"))
+					// NOTE: 24 is somewhat arbitrary and should be tuned in the future if
+					// necessary. See <https://github.com/paritytech/substrate/pull/6080>
+					.with_per_connection_event_buffer_size(24)
+					.with_max_negotiating_inbound_streams(2048)
+					.with_idle_connection_timeout(Duration::from_secs(10));
+
+				Swarm::new(transport, behaviour, local_peer_id, config)
 			};
-			#[allow(deprecated)]
-			let builder = builder
-				.connection_limits(
-					ConnectionLimits::default()
-						.with_max_established_per_peer(Some(crate::MAX_CONNECTIONS_PER_PEER as u32))
-						.with_max_established_incoming(Some(
-							crate::MAX_CONNECTIONS_ESTABLISHED_INCOMING,
-						)),
-				)
-				.substream_upgrade_protocol_override(upgrade::Version::V1)
-				.notify_handler_buffer_size(NonZeroUsize::new(32).expect("32 != 0; qed"))
-				// NOTE: 24 is somewhat arbitrary and should be tuned in the future if necessary.
-				// See <https://github.com/paritytech/substrate/pull/6080>
-				.per_connection_event_buffer_size(24)
-				.max_negotiating_inbound_streams(2048);
-
-			(builder.build(), Arc::new(Libp2pBandwidthSink { sink: bandwidth }))
+
+			(swarm, Arc::new(Libp2pBandwidthSink { sink: bandwidth }))
 		};
 
 		// Initialize the metrics.
@@ -614,19 +605,15 @@ where
 		};
 
 		// Listen on multiaddresses.
-		for addr in &listen_addresses {
-			if let Err(err) = Swarm::<Behaviour<B>>::listen_on(&mut swarm, addr.clone()) {
+		for addr in &network_config.listen_addresses {
+			if let Err(err) = Swarm::<Behaviour<B>>::listen_on(&mut swarm, addr.clone().into()) {
 				warn!(target: "sub-libp2p", "Can't listen on {} because: {:?}", addr, err)
 			}
 		}
 
 		// Add external addresses.
-		for addr in &public_addresses {
-			Swarm::<Behaviour<B>>::add_external_address(
-				&mut swarm,
-				addr.clone(),
-				AddressScore::Infinite,
-			);
+		for addr in &network_config.public_addresses {
+			Swarm::<Behaviour<B>>::add_external_address(&mut swarm, addr.clone().into());
 		}
 
 		let listen_addresses_set = Arc::new(Mutex::new(HashSet::new()));
@@ -807,7 +794,7 @@ where
 
 		let peer_id = Swarm::<Behaviour<B>>::local_peer_id(swarm).to_base58();
 		let listened_addresses = swarm.listeners().cloned().collect();
-		let external_addresses = swarm.external_addresses().map(|r| &r.addr).cloned().collect();
+		let external_addresses = swarm.external_addresses().cloned().collect();
 
 		NetworkState {
 			peer_id,
@@ -867,8 +854,7 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkService<B, H> {
 			.into_iter()
 			.map(|mut addr| {
 				let peer = match addr.pop() {
-					Some(multiaddr::Protocol::P2p(key)) => PeerId::from_multihash(key)
-						.map_err(|_| "Invalid PeerId format".to_string())?,
+					Some(multiaddr::Protocol::P2p(peer_id)) => peer_id,
 					_ => return Err("Missing PeerId from address".to_string()),
 				};
 
@@ -1492,6 +1478,7 @@ where
 	}
 
 	/// Process the next event coming from `Swarm`.
+	#[allow(deprecated)]
 	fn handle_swarm_event(&mut self, event: SwarmEvent<BehaviourOut, THandlerErr<Behaviour<B>>>) {
 		match event {
 			SwarmEvent::Behaviour(BehaviourOut::InboundRequest { protocol, result, .. }) => {
@@ -1586,9 +1573,11 @@ where
 					listen_addrs.truncate(30);
 				}
 				for addr in listen_addrs {
-					self.network_service
-						.behaviour_mut()
-						.add_self_reported_address_to_dht(&peer_id, &protocols, addr);
+					self.network_service.behaviour_mut().add_self_reported_address_to_dht(
+						&peer_id,
+						&protocols,
+						addr.clone(),
+					);
 				}
 				self.peer_store_handle.add_known_peer(peer_id.into());
 			},
@@ -1705,8 +1694,14 @@ where
 					}
 				}
 			},
-			SwarmEvent::ConnectionClosed { peer_id, cause, endpoint, num_established } => {
-				debug!(target: "sub-libp2p", "Libp2p => Disconnected({:?}, {:?})", peer_id, cause);
+			SwarmEvent::ConnectionClosed {
+				connection_id,
+				peer_id,
+				cause,
+				endpoint,
+				num_established,
+			} => {
+				debug!(target: "sub-libp2p", "Libp2p => Disconnected({peer_id:?} via {connection_id:?}, {cause:?})");
 				if let Some(metrics) = self.metrics.as_ref() {
 					let direction = match endpoint {
 						ConnectedPoint::Dialer { .. } => "out",
@@ -1715,11 +1710,13 @@ where
 					let reason = match cause {
 						Some(ConnectionError::IO(_)) => "transport-error",
 						Some(ConnectionError::Handler(Either::Left(Either::Left(
-							Either::Right(Either::Left(PingFailure::Timeout)),
-						)))) => "ping-timeout",
-						Some(ConnectionError::Handler(Either::Left(Either::Left(
-							Either::Left(NotifsHandlerError::SyncNotificationsClogged),
+							Either::Left(Either::Right(
+								NotifsHandlerError::SyncNotificationsClogged,
+							)),
 						)))) => "sync-notifications-clogged",
+						Some(ConnectionError::Handler(Either::Left(Either::Left(
+							Either::Right(Either::Left(_)),
+						)))) => "ping-timeout",
 						Some(ConnectionError::Handler(_)) => "protocol-error",
 						Some(ConnectionError::KeepAliveTimeout) => "keep-alive-timeout",
 						None => "actively-closed",
@@ -1746,12 +1743,11 @@ where
 				}
 				self.listen_addresses.lock().remove(&address);
 			},
-			SwarmEvent::OutgoingConnectionError { peer_id, error } => {
+			SwarmEvent::OutgoingConnectionError { connection_id, peer_id, error } => {
 				if let Some(peer_id) = peer_id {
 					trace!(
 						target: "sub-libp2p",
-						"Libp2p => Failed to reach {:?}: {}",
-						peer_id, error,
+						"Libp2p => Failed to reach {peer_id:?} via {connection_id:?}: {error}",
 					);
 
 					let not_reported = !self.reported_invalid_boot_nodes.contains(&peer_id);
@@ -1789,12 +1785,9 @@ where
 							} else {
 								None
 							},
-						DialError::ConnectionLimit(_) => Some("limit-reached"),
-						DialError::InvalidPeerId(_) |
-						DialError::WrongPeerId { .. } |
-						DialError::LocalPeerId { .. } => Some("invalid-peer-id"),
+						DialError::LocalPeerId { .. } => Some("local-peer-id"),
+						DialError::WrongPeerId { .. } => Some("invalid-peer-id"),
 						DialError::Transport(_) => Some("transport-error"),
-						DialError::Banned |
 						DialError::NoAddresses |
 						DialError::DialPeerConditionFalse(_) |
 						DialError::Aborted => None, // ignore them
@@ -1804,21 +1797,24 @@ where
 					}
 				}
 			},
-			SwarmEvent::Dialing(peer_id) => {
-				trace!(target: "sub-libp2p", "Libp2p => Dialing({:?})", peer_id)
+			SwarmEvent::Dialing { connection_id, peer_id } => {
+				trace!(target: "sub-libp2p", "Libp2p => Dialing({peer_id:?}) via {connection_id:?}")
 			},
-			SwarmEvent::IncomingConnection { local_addr, send_back_addr } => {
-				trace!(target: "sub-libp2p", "Libp2p => IncomingConnection({},{}))",
-					local_addr, send_back_addr);
+			SwarmEvent::IncomingConnection { connection_id, local_addr, send_back_addr } => {
+				trace!(target: "sub-libp2p", "Libp2p => IncomingConnection({local_addr},{send_back_addr} via {connection_id:?}))");
 				if let Some(metrics) = self.metrics.as_ref() {
 					metrics.incoming_connections_total.inc();
 				}
 			},
-			SwarmEvent::IncomingConnectionError { local_addr, send_back_addr, error } => {
+			SwarmEvent::IncomingConnectionError {
+				connection_id,
+				local_addr,
+				send_back_addr,
+				error,
+			} => {
 				debug!(
 					target: "sub-libp2p",
-					"Libp2p => IncomingConnectionError({},{}): {}",
-					local_addr, send_back_addr, error,
+					"Libp2p => IncomingConnectionError({local_addr},{send_back_addr} via {connection_id:?}): {error}"
 				);
 				if let Some(metrics) = self.metrics.as_ref() {
 					#[allow(deprecated)]
@@ -1829,7 +1825,6 @@ where
 							} else {
 								None
 							},
-						ListenError::ConnectionLimit(_) => Some("limit-reached"),
 						ListenError::WrongPeerId { .. } | ListenError::LocalPeerId { .. } =>
 							Some("invalid-peer-id"),
 						ListenError::Transport(_) => Some("transport-error"),
@@ -1844,17 +1839,6 @@ where
 					}
 				}
 			},
-			#[allow(deprecated)]
-			SwarmEvent::BannedPeer { peer_id, endpoint } => {
-				debug!(
-					target: "sub-libp2p",
-					"Libp2p => BannedPeer({}). Connected via {:?}.",
-					peer_id, endpoint,
-				);
-				if let Some(metrics) = self.metrics.as_ref() {
-					metrics.incoming_connections_errors_total.with_label_values(&["banned"]).inc();
-				}
-			},
 			SwarmEvent::ListenerClosed { reason, addresses, .. } => {
 				if let Some(metrics) = self.metrics.as_ref() {
 					metrics.listeners_local_addresses.sub(addresses.len() as u64);
diff --git a/substrate/client/network/src/transport.rs b/substrate/client/network/src/transport.rs
index 4136b34fc0e..ed7e7c574e1 100644
--- a/substrate/client/network/src/transport.rs
+++ b/substrate/client/network/src/transport.rs
@@ -57,7 +57,7 @@ pub fn build_transport(
 		// Main transport: DNS(TCP)
 		let tcp_config = tcp::Config::new().nodelay(true);
 		let tcp_trans = tcp::tokio::Transport::new(tcp_config.clone());
-		let dns_init = dns::TokioDnsConfig::system(tcp_trans);
+		let dns_init = dns::tokio::Transport::system(tcp_trans);
 
 		Either::Left(if let Ok(dns) = dns_init {
 			// WS + WSS transport
@@ -66,7 +66,7 @@ pub fn build_transport(
 			// unresolved addresses (BUT WSS transport itself needs an instance of DNS transport to
 			// resolve and dial addresses).
 			let tcp_trans = tcp::tokio::Transport::new(tcp_config);
-			let dns_for_wss = dns::TokioDnsConfig::system(tcp_trans)
+			let dns_for_wss = dns::tokio::Transport::system(tcp_trans)
 				.expect("same system_conf & resolver to work");
 			Either::Left(websocket::WsConfig::new(dns_for_wss).or_transport(dns))
 		} else {
diff --git a/substrate/client/network/src/types.rs b/substrate/client/network/src/types.rs
index 25517599469..0652bbcddde 100644
--- a/substrate/client/network/src/types.rs
+++ b/substrate/client/network/src/types.rs
@@ -18,8 +18,6 @@
 
 //! `sc-network` type definitions
 
-use libp2p::core::upgrade;
-
 use std::{
 	borrow::Borrow,
 	fmt,
@@ -94,9 +92,9 @@ impl fmt::Display for ProtocolName {
 	}
 }
 
-impl upgrade::ProtocolName for ProtocolName {
-	fn protocol_name(&self) -> &[u8] {
-		(self as &str).as_bytes()
+impl AsRef<str> for ProtocolName {
+	fn as_ref(&self) -> &str {
+		self as &str
 	}
 }
 
diff --git a/substrate/client/network/statement/Cargo.toml b/substrate/client/network/statement/Cargo.toml
index 55ff3d54bba..4cced49fee7 100644
--- a/substrate/client/network/statement/Cargo.toml
+++ b/substrate/client/network/statement/Cargo.toml
@@ -20,7 +20,6 @@ array-bytes = { workspace = true, default-features = true }
 async-channel = { workspace = true }
 codec = { features = ["derive"], workspace = true, default-features = true }
 futures = { workspace = true }
-libp2p = { workspace = true }
 log = { workspace = true, default-features = true }
 prometheus-endpoint = { workspace = true, default-features = true }
 sc-network-common = { workspace = true, default-features = true }
diff --git a/substrate/client/network/transactions/Cargo.toml b/substrate/client/network/transactions/Cargo.toml
index a561aa2da47..eb907b606d5 100644
--- a/substrate/client/network/transactions/Cargo.toml
+++ b/substrate/client/network/transactions/Cargo.toml
@@ -19,7 +19,6 @@ targets = ["x86_64-unknown-linux-gnu"]
 array-bytes = { workspace = true, default-features = true }
 codec = { features = ["derive"], workspace = true, default-features = true }
 futures = { workspace = true }
-libp2p = { workspace = true }
 log = { workspace = true, default-features = true }
 prometheus-endpoint = { workspace = true, default-features = true }
 sc-network = { workspace = true, default-features = true }
diff --git a/substrate/client/network/types/Cargo.toml b/substrate/client/network/types/Cargo.toml
index b54617eafd0..811ccddbef9 100644
--- a/substrate/client/network/types/Cargo.toml
+++ b/substrate/client/network/types/Cargo.toml
@@ -12,10 +12,11 @@ documentation = "https://docs.rs/sc-network-types"
 [dependencies]
 bs58 = { workspace = true, default-features = true }
 ed25519-dalek = { workspace = true, default-features = true }
-libp2p-identity = { features = ["ed25519", "peerid"], workspace = true }
+libp2p-identity = { features = ["ed25519", "peerid", "rand"], workspace = true }
 litep2p = { workspace = true }
+log = { workspace = true, default-features = true }
 multiaddr = { workspace = true }
-multihash = { features = ["identity", "multihash-impl", "sha2", "std"], workspace = true }
+multihash = { workspace = true }
 rand = { workspace = true, default-features = true }
 thiserror = { workspace = true }
 zeroize = { workspace = true }
diff --git a/substrate/client/network/types/src/multiaddr.rs b/substrate/client/network/types/src/multiaddr.rs
index de317e2aa1c..925e24fe70d 100644
--- a/substrate/client/network/types/src/multiaddr.rs
+++ b/substrate/client/network/types/src/multiaddr.rs
@@ -20,6 +20,7 @@ use litep2p::types::multiaddr::{
 	Error as LiteP2pError, Iter as LiteP2pIter, Multiaddr as LiteP2pMultiaddr,
 	Protocol as LiteP2pProtocol,
 };
+use multiaddr::Multiaddr as LibP2pMultiaddr;
 use std::{
 	fmt::{self, Debug, Display},
 	net::{IpAddr, Ipv4Addr, Ipv6Addr},
@@ -103,6 +104,18 @@ impl From<Multiaddr> for LiteP2pMultiaddr {
 	}
 }
 
+impl From<LibP2pMultiaddr> for Multiaddr {
+	fn from(multiaddr: LibP2pMultiaddr) -> Self {
+		multiaddr.into_iter().map(Into::into).collect()
+	}
+}
+
+impl From<Multiaddr> for LibP2pMultiaddr {
+	fn from(multiaddr: Multiaddr) -> Self {
+		multiaddr.into_iter().map(Into::into).collect()
+	}
+}
+
 impl From<IpAddr> for Multiaddr {
 	fn from(v: IpAddr) -> Multiaddr {
 		match v {
diff --git a/substrate/client/network/types/src/multiaddr/protocol.rs b/substrate/client/network/types/src/multiaddr/protocol.rs
index 2a3b59e6a68..aca3a311368 100644
--- a/substrate/client/network/types/src/multiaddr/protocol.rs
+++ b/substrate/client/network/types/src/multiaddr/protocol.rs
@@ -17,12 +17,18 @@
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
 use crate::multihash::Multihash;
+use libp2p_identity::PeerId;
 use litep2p::types::multiaddr::Protocol as LiteP2pProtocol;
+use multiaddr::Protocol as LibP2pProtocol;
 use std::{
 	borrow::Cow,
+	fmt::{self, Debug, Display},
 	net::{IpAddr, Ipv4Addr, Ipv6Addr},
 };
 
+// Log target for this file.
+const LOG_TARGET: &str = "sub-libp2p";
+
 /// [`Protocol`] describes all possible multiaddress protocols.
 #[derive(PartialEq, Eq, Clone, Debug)]
 pub enum Protocol<'a> {
@@ -60,6 +66,13 @@ pub enum Protocol<'a> {
 	Wss(Cow<'a, str>),
 }
 
+impl<'a> Display for Protocol<'a> {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+		let protocol = LiteP2pProtocol::from(self.clone());
+		Display::fmt(&protocol, f)
+	}
+}
+
 impl<'a> From<IpAddr> for Protocol<'a> {
 	#[inline]
 	fn from(addr: IpAddr) -> Self {
@@ -160,3 +173,102 @@ impl<'a> From<Protocol<'a>> for LiteP2pProtocol<'a> {
 		}
 	}
 }
+
+impl<'a> From<LibP2pProtocol<'a>> for Protocol<'a> {
+	fn from(protocol: LibP2pProtocol<'a>) -> Self {
+		match protocol {
+			LibP2pProtocol::Dccp(port) => Protocol::Dccp(port),
+			LibP2pProtocol::Dns(str) => Protocol::Dns(str),
+			LibP2pProtocol::Dns4(str) => Protocol::Dns4(str),
+			LibP2pProtocol::Dns6(str) => Protocol::Dns6(str),
+			LibP2pProtocol::Dnsaddr(str) => Protocol::Dnsaddr(str),
+			LibP2pProtocol::Http => Protocol::Http,
+			LibP2pProtocol::Https => Protocol::Https,
+			LibP2pProtocol::Ip4(ipv4_addr) => Protocol::Ip4(ipv4_addr),
+			LibP2pProtocol::Ip6(ipv6_addr) => Protocol::Ip6(ipv6_addr),
+			LibP2pProtocol::P2pWebRtcDirect => Protocol::P2pWebRtcDirect,
+			LibP2pProtocol::P2pWebRtcStar => Protocol::P2pWebRtcStar,
+			LibP2pProtocol::Certhash(multihash) => Protocol::Certhash(multihash.into()),
+			LibP2pProtocol::P2pWebSocketStar => Protocol::P2pWebSocketStar,
+			LibP2pProtocol::Memory(port) => Protocol::Memory(port),
+			LibP2pProtocol::Onion(str, port) => Protocol::Onion(str, port),
+			LibP2pProtocol::Onion3(addr) => Protocol::Onion3(Cow::Owned(*addr.hash()), addr.port()),
+			LibP2pProtocol::P2p(peer_id) => Protocol::P2p((*peer_id.as_ref()).into()),
+			LibP2pProtocol::P2pCircuit => Protocol::P2pCircuit,
+			LibP2pProtocol::Quic => Protocol::Quic,
+			LibP2pProtocol::QuicV1 => Protocol::QuicV1,
+			LibP2pProtocol::Sctp(port) => Protocol::Sctp(port),
+			LibP2pProtocol::Tcp(port) => Protocol::Tcp(port),
+			LibP2pProtocol::Tls => Protocol::Tls,
+			LibP2pProtocol::Noise => Protocol::Noise,
+			LibP2pProtocol::Udp(port) => Protocol::Udp(port),
+			LibP2pProtocol::Udt => Protocol::Udt,
+			LibP2pProtocol::Unix(str) => Protocol::Unix(str),
+			LibP2pProtocol::Utp => Protocol::Utp,
+			LibP2pProtocol::Ws(str) => Protocol::Ws(str),
+			LibP2pProtocol::Wss(str) => Protocol::Wss(str),
+			protocol => {
+				log::error!(
+					target: LOG_TARGET,
+					"Got unsupported multiaddr protocol '{}'",
+					protocol.tag(),
+				);
+				// Strictly speaking, this conversion is incorrect. But making protocol conversion
+				// fallible would significantly complicate the client code. As DCCP transport is not
+				// used by substrate, this conversion should be safe.
+				// Also, as of `multiaddr-18.1`, all enum variants are actually covered.
+				Protocol::Dccp(0)
+			},
+		}
+	}
+}
+
+impl<'a> From<Protocol<'a>> for LibP2pProtocol<'a> {
+	fn from(protocol: Protocol<'a>) -> Self {
+		match protocol {
+			Protocol::Dccp(port) => LibP2pProtocol::Dccp(port),
+			Protocol::Dns(str) => LibP2pProtocol::Dns(str),
+			Protocol::Dns4(str) => LibP2pProtocol::Dns4(str),
+			Protocol::Dns6(str) => LibP2pProtocol::Dns6(str),
+			Protocol::Dnsaddr(str) => LibP2pProtocol::Dnsaddr(str),
+			Protocol::Http => LibP2pProtocol::Http,
+			Protocol::Https => LibP2pProtocol::Https,
+			Protocol::Ip4(ipv4_addr) => LibP2pProtocol::Ip4(ipv4_addr),
+			Protocol::Ip6(ipv6_addr) => LibP2pProtocol::Ip6(ipv6_addr),
+			Protocol::P2pWebRtcDirect => LibP2pProtocol::P2pWebRtcDirect,
+			Protocol::P2pWebRtcStar => LibP2pProtocol::P2pWebRtcStar,
+			// Protocol #280 is called `WebRTC` in multiaddr-17.0 and `WebRTCDirect` in
+			// multiaddr-18.1.
+			Protocol::WebRTC => LibP2pProtocol::WebRTCDirect,
+			Protocol::Certhash(multihash) => LibP2pProtocol::Certhash(multihash.into()),
+			Protocol::P2pWebSocketStar => LibP2pProtocol::P2pWebSocketStar,
+			Protocol::Memory(port) => LibP2pProtocol::Memory(port),
+			Protocol::Onion(str, port) => LibP2pProtocol::Onion(str, port),
+			Protocol::Onion3(str, port) => LibP2pProtocol::Onion3((str.into_owned(), port).into()),
+			Protocol::P2p(multihash) =>
+				LibP2pProtocol::P2p(PeerId::from_multihash(multihash.into()).unwrap_or_else(|_| {
+					// This is better than making conversion fallible and complicating the
+					// client code.
+					log::error!(
+						target: LOG_TARGET,
+						"Received multiaddr with p2p multihash which is not a valid \
+						 peer_id. Replacing with random peer_id."
+					);
+					PeerId::random()
+				})),
+			Protocol::P2pCircuit => LibP2pProtocol::P2pCircuit,
+			Protocol::Quic => LibP2pProtocol::Quic,
+			Protocol::QuicV1 => LibP2pProtocol::QuicV1,
+			Protocol::Sctp(port) => LibP2pProtocol::Sctp(port),
+			Protocol::Tcp(port) => LibP2pProtocol::Tcp(port),
+			Protocol::Tls => LibP2pProtocol::Tls,
+			Protocol::Noise => LibP2pProtocol::Noise,
+			Protocol::Udp(port) => LibP2pProtocol::Udp(port),
+			Protocol::Udt => LibP2pProtocol::Udt,
+			Protocol::Unix(str) => LibP2pProtocol::Unix(str),
+			Protocol::Utp => LibP2pProtocol::Utp,
+			Protocol::Ws(str) => LibP2pProtocol::Ws(str),
+			Protocol::Wss(str) => LibP2pProtocol::Wss(str),
+		}
+	}
+}
diff --git a/substrate/client/network/types/src/multihash.rs b/substrate/client/network/types/src/multihash.rs
index 91f5b6353a7..321211c598d 100644
--- a/substrate/client/network/types/src/multihash.rs
+++ b/substrate/client/network/types/src/multihash.rs
@@ -156,22 +156,20 @@ impl From<Multihash> for LiteP2pMultihash {
 	}
 }
 
-// TODO: uncomment this after upgrading `multihash` crate to v0.19.1.
-//
-// impl From<multihash::Multihash<64>> for Multihash {
-// 	fn from(generic: multihash::MultihashGeneric<64>) -> Self {
-// 		LiteP2pMultihash::wrap(generic.code(), generic.digest())
-// 			.expect("both have size 64; qed")
-// 			.into()
-// 	}
-// }
-//
-// impl From<Multihash> for multihash::Multihash<64> {
-// 	fn from(multihash: Multihash) -> Self {
-// 		multihash::Multihash::<64>::wrap(multihash.code(), multihash.digest())
-// 			.expect("both have size 64; qed")
-// 	}
-// }
+impl From<multihash::Multihash<64>> for Multihash {
+	fn from(generic: multihash::Multihash<64>) -> Self {
+		LiteP2pMultihash::wrap(generic.code(), generic.digest())
+			.expect("both have size 64; qed")
+			.into()
+	}
+}
+
+impl From<Multihash> for multihash::Multihash<64> {
+	fn from(multihash: Multihash) -> Self {
+		multihash::Multihash::<64>::wrap(multihash.code(), multihash.digest())
+			.expect("both have size 64; qed")
+	}
+}
 
 #[cfg(test)]
 mod tests {
diff --git a/substrate/client/offchain/Cargo.toml b/substrate/client/offchain/Cargo.toml
index be816239306..5046808f013 100644
--- a/substrate/client/offchain/Cargo.toml
+++ b/substrate/client/offchain/Cargo.toml
@@ -24,7 +24,6 @@ futures = { workspace = true }
 futures-timer = { workspace = true }
 hyper = { features = ["http2", "stream"], workspace = true, default-features = true }
 hyper-rustls = { features = ["http2"], workspace = true }
-libp2p = { workspace = true }
 num_cpus = { workspace = true }
 once_cell = { workspace = true }
 parking_lot = { workspace = true, default-features = true }
diff --git a/substrate/client/telemetry/src/transport.rs b/substrate/client/telemetry/src/transport.rs
index a82626caac2..ca6ceecbed6 100644
--- a/substrate/client/telemetry/src/transport.rs
+++ b/substrate/client/telemetry/src/transport.rs
@@ -31,7 +31,7 @@ const CONNECT_TIMEOUT: Duration = Duration::from_secs(20);
 pub(crate) fn initialize_transport() -> Result<WsTrans, io::Error> {
 	let transport = {
 		let tcp_transport = libp2p::tcp::tokio::Transport::new(libp2p::tcp::Config::new());
-		let inner = libp2p::dns::TokioDnsConfig::system(tcp_transport)?;
+		let inner = libp2p::dns::tokio::Transport::system(tcp_transport)?;
 		libp2p::websocket::framed::WsConfig::new(inner).and_then(|connec, _| {
 			let connec = connec
 				.with(|item| {
-- 
GitLab