diff --git a/.cargo/audit.toml b/.cargo/audit.toml index 06c3e7ceacf..64b8b800948 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -6,4 +6,5 @@ ignore = [ "RUSTSEC-2026-0104", # https://github.com/FuelLabs/fuel-core/issues/3279 "RUSTSEC-2026-0114", # https://github.com/FuelLabs/fuel-core/issues/3293 "RUSTSEC-2026-0119", # https://github.com/FuelLabs/fuel-core/issues/3296 + "RUSTSEC-2026-0118", # https://github.com/FuelLabs/fuel-core/issues/3307 ] diff --git a/.changes/changed/3306.md b/.changes/changed/3306.md new file mode 100644 index 00000000000..2cca2564029 --- /dev/null +++ b/.changes/changed/3306.md @@ -0,0 +1 @@ +Upgrade libp2p to 0.56.0 and prometheus-client to 0.23.0 diff --git a/.typos.toml b/.typos.toml index fb239f83b7e..25e17f70b2d 100644 --- a/.typos.toml +++ b/.typos.toml @@ -6,4 +6,9 @@ extend-ignore-identifiers-re = [ "typ", "aloc", "ALOC" -] \ No newline at end of file +] + +[files] +extend-exclude = [ + "version-compatibility/vendor/**" +] diff --git a/Cargo.lock b/Cargo.lock index 9eebad1720f..7bc4fc28d0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -439,7 +439,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", - "itertools 0.13.0", + "itertools 0.14.0", "serde", "serde_json", "serde_with", @@ -573,7 +573,7 @@ checksum = "e8597d36d546e1dab822345ad563243ec3920e199322cb554ce56c8ef1a1e2e7" dependencies = [ "alloy-json-rpc", "alloy-transport", - "itertools 0.13.0", + "itertools 0.14.0", "reqwest 0.13.2", "serde_json", "tower 0.5.3", @@ -660,7 +660,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -671,7 +671,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -904,9 +904,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "asn1-rs" -version = "0.6.2" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -914,15 +914,15 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 1.0.69", + "thiserror 2.0.18", "time", ] [[package]] name = "asn1-rs-derive" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", @@ -978,17 +978,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.5.0" @@ -1001,46 +990,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - [[package]] name = "async-graphql" version = "7.2.1" @@ -1139,84 +1088,11 @@ version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" -dependencies = [ - "async-channel 2.5.0", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener 5.4.1", - "futures-lite", - "rustix", -] - -[[package]] -name = "async-signal" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-std" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-stream" version = "0.3.6" @@ -1239,12 +1115,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.89" @@ -1296,11 +1166,12 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "attohttpc" -version = "0.24.1" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" dependencies = [ - "http 0.2.12", + "base64 0.22.1", + "http 1.4.0", "log", "url", ] @@ -1576,7 +1447,7 @@ dependencies = [ "http 1.4.0", "p256 0.11.1", "percent-encoding", - "ring 0.17.14", + "ring", "sha2 0.10.9", "subtle", "time", @@ -2263,19 +2134,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel 2.5.0", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "blst" version = "0.3.16" @@ -2669,7 +2527,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -3454,7 +3312,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] @@ -3488,9 +3346,9 @@ dependencies = [ [[package]] name = "der-parser" -version = "9.0.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ "asn1-rs", "displaydoc", @@ -3988,7 +3846,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3997,12 +3855,6 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.1" @@ -4020,7 +3872,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] @@ -4745,8 +4597,9 @@ dependencies = [ "fuel-core-types 0.48.2", "futures", "hex", - "hickory-resolver", + "hickory-resolver 0.24.4", "libp2p", + "libp2p-gossipsub", "libp2p-swarm-test", "parking_lot", "postcard", @@ -4760,7 +4613,6 @@ dependencies = [ "tokio", "tracing", "tracing-attributes", - "void", "yamux 0.13.5", ] @@ -5498,10 +5350,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand", "futures-core", - "futures-io", - "parking", "pin-project-lite", ] @@ -5539,17 +5388,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" -[[package]] -name = "futures-ticker" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9763058047f713632a52e916cc7f6a4b3fc6e9fc1ff8c5b1dc49e5a89041682e" -dependencies = [ - "futures", - "futures-timer", - "instant", -] - [[package]] name = "futures-timer" version = "3.0.3" @@ -5664,18 +5502,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "group" version = "0.12.1" @@ -5823,6 +5649,24 @@ dependencies = [ "serde_core", ] +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "heapless" version = "0.7.17" @@ -5833,7 +5677,7 @@ dependencies = [ "hash32", "rustc_version 0.4.1", "serde", - "spin 0.9.8", + "spin", "stable_deref_trait", ] @@ -5905,7 +5749,6 @@ dependencies = [ "ipnet", "once_cell", "rand 0.8.5", - "socket2 0.5.10", "thiserror 1.0.69", "tinyvec", "tokio", @@ -5913,6 +5756,32 @@ dependencies = [ "url", ] +[[package]] +name = "hickory-proto" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand 0.9.2", + "ring", + "socket2 0.5.10", + "thiserror 2.0.18", + "tinyvec", + "tokio", + "tracing", + "url", +] + [[package]] name = "hickory-resolver" version = "0.24.4" @@ -5921,7 +5790,7 @@ checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" dependencies = [ "cfg-if", "futures-util", - "hickory-proto", + "hickory-proto 0.24.4", "ipconfig", "lru-cache", "once_cell", @@ -5934,6 +5803,27 @@ dependencies = [ "tracing", ] +[[package]] +name = "hickory-resolver" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto 0.25.2", + "ipconfig", + "moka", + "once_cell", + "parking_lot", + "rand 0.9.2", + "resolv-conf", + "smallvec", + "thiserror 2.0.18", + "tokio", + "tracing", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -6337,7 +6227,6 @@ dependencies = [ "netlink-proto", "netlink-sys", "rtnetlink", - "smol", "system-configuration 0.7.0", "tokio", "windows", @@ -6345,18 +6234,20 @@ dependencies = [ [[package]] name = "igd-next" -version = "0.14.3" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064d90fec10d541084e7b39ead8875a5a80d9114a2b18791565253bae25f49e4" +checksum = "516893339c97f6011282d5825ac94fc1c7aad5cad26bdc2d0cee068c0bf97f97" dependencies = [ "async-trait", "attohttpc", "bytes", "futures", - "http 0.2.12", - "hyper 0.14.32", + "http 1.4.0", + "http-body-util", + "hyper 1.8.1", + "hyper-util", "log", - "rand 0.8.5", + "rand 0.9.2", "tokio", "url", "xmltree", @@ -6469,15 +6360,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "integer-encoding" version = "3.0.4" @@ -6521,7 +6403,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6672,15 +6554,6 @@ dependencies = [ "sha3-asm", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lalrpop-util" version = "0.22.2" @@ -6696,7 +6569,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.9.8", + "spin", ] [[package]] @@ -6755,9 +6628,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libp2p" -version = "0.54.1" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbe80f9c7e00526cd6b838075b9c171919404a4732cb2fa8ece0a093223bfc4" +checksum = "ce71348bf5838e46449ae240631117b487073d5f347c06d434caddcb91dceb5a" dependencies = [ "bytes", "either", @@ -6785,38 +6658,36 @@ dependencies = [ "multiaddr", "pin-project", "rw-stream-sink", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] name = "libp2p-allow-block-list" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1027ccf8d70320ed77e984f273bc8ce952f623762cb9bf2d126df73caef8041" +checksum = "d16ccf824ee859ca83df301e1c0205270206223fd4b1f2e512a693e1912a8f4a" dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "void", ] [[package]] name = "libp2p-connection-limits" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d003540ee8baef0d254f7b6bfd79bac3ddf774662ca0abf69186d517ef82ad8" +checksum = "a18b8b607cf3bfa2f8c57db9c7d8569a315d5cc0a282e6bfd5ebfc0a9840b2a0" dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "void", ] [[package]] name = "libp2p-core" -version = "0.42.0" +version = "0.43.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a61f26c83ed111104cd820fe9bc3aaabbac5f1652a1d213ed6e900b7918a1298" +checksum = "249128cd37a2199aff30a7675dffa51caf073b51aa612d2f544b19932b9aebca" dependencies = [ "either", "fnv", @@ -6826,29 +6697,26 @@ dependencies = [ "multiaddr", "multihash", "multistream-select", - "once_cell", "parking_lot", "pin-project", "quick-protobuf", "rand 0.8.5", "rw-stream-sink", - "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", "unsigned-varint 0.8.0", - "void", "web-time", ] [[package]] name = "libp2p-dns" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97f37f30d5c7275db282ecd86e54f29dd2176bd3ac656f06abf43bedb21eb8bd" +checksum = "0b770c1c8476736ca98c578cba4b505104ff8e842c2876b528925f9766379f9a" dependencies = [ "async-trait", "futures", - "hickory-resolver", + "hickory-resolver 0.25.2", "libp2p-core", "libp2p-identity", "parking_lot", @@ -6858,10 +6726,11 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" -version = "0.47.0" +version = "0.49.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4e830fdf24ac8c444c12415903174d506e1e077fbe3875c404a78c5935a8543" +checksum = "a538e571cd38f504f761c61b8f79127489ea7a7d6f05c41ca15d31ffb5726326" dependencies = [ + "async-channel", "asynchronous-codec", "base64 0.22.1", "byteorder", @@ -6869,8 +6738,9 @@ dependencies = [ "either", "fnv", "futures", - "futures-ticker", + "futures-timer", "getrandom 0.2.17", + "hashlink 0.9.1", "hex_fmt", "libp2p-core", "libp2p-identity", @@ -6881,17 +6751,15 @@ dependencies = [ "rand 0.8.5", "regex", "sha2 0.10.9", - "smallvec", "tracing", - "void", "web-time", ] [[package]] name = "libp2p-identify" -version = "0.45.0" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1711b004a273be4f30202778856368683bd9a83c4c7dcc8f848847606831a4e3" +checksum = "8ab792a8b68fdef443a62155b01970c81c3aadab5e659621b063ef252a8e65e8" dependencies = [ "asynchronous-codec", "either", @@ -6901,13 +6769,11 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "lru 0.12.5", "quick-protobuf", "quick-protobuf-codec", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", - "void", ] [[package]] @@ -6932,11 +6798,10 @@ dependencies = [ [[package]] name = "libp2p-kad" -version = "0.46.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced237d0bd84bbebb7c2cad4c073160dacb4fe40534963c32ed6d4c6bb7702a3" +checksum = "13d3fd632a5872ec804d37e7413ceea20588f69d027a0fa3c46f82574f4dee60" dependencies = [ - "arrayvec", "asynchronous-codec", "bytes", "either", @@ -6952,22 +6817,20 @@ dependencies = [ "rand 0.8.5", "sha2 0.10.9", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", - "uint", - "void", + "uint 0.10.0", "web-time", ] [[package]] name = "libp2p-mdns" -version = "0.46.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b8546b6644032565eb29046b42744aee1e9f261ed99671b2c93fb140dba417" +checksum = "c66872d0f1ffcded2788683f76931be1c52e27f343edb93bc6d0bcd8887be443" dependencies = [ - "data-encoding", "futures", - "hickory-proto", + "hickory-proto 0.25.2", "if-watch", "libp2p-core", "libp2p-identity", @@ -6977,14 +6840,13 @@ dependencies = [ "socket2 0.5.10", "tokio", "tracing", - "void", ] [[package]] name = "libp2p-metrics" -version = "0.15.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ebafa94a717c8442d8db8d3ae5d1c6a15e30f2d347e0cd31d057ca72e42566" +checksum = "805a555148522cb3414493a5153451910cb1a146c53ffbf4385708349baf62b7" dependencies = [ "futures", "libp2p-core", @@ -7000,25 +6862,22 @@ dependencies = [ [[package]] name = "libp2p-noise" -version = "0.45.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b137cb1ae86ee39f8e5d6245a296518912014eaa87427d24e6ff58cfc1b28c" +checksum = "bc73eacbe6462a0eb92a6527cac6e63f02026e5407f8831bde8293f19217bfbf" dependencies = [ "asynchronous-codec", "bytes", - "curve25519-dalek", "futures", "libp2p-core", "libp2p-identity", "multiaddr", "multihash", - "once_cell", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.9", "snow", "static_assertions", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", "x25519-dalek", "zeroize", @@ -7026,9 +6885,9 @@ dependencies = [ [[package]] name = "libp2p-plaintext" -version = "0.42.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63d926c6be56a2489e0e7316b17fe95a70bc5c4f3e85740bb3e67c0f3c6a44" +checksum = "7e659439578fc6d305da8303834beb9d62f155f40e7f5b9d81c9f2b2c69d1926" dependencies = [ "asynchronous-codec", "bytes", @@ -7042,90 +6901,81 @@ dependencies = [ [[package]] name = "libp2p-quic" -version = "0.11.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46352ac5cd040c70e88e7ff8257a2ae2f891a4076abad2c439584a31c15fd24e" +checksum = "8dc448b2de9f4745784e3751fe8bc6c473d01b8317edd5ababcb0dec803d843f" dependencies = [ - "bytes", "futures", "futures-timer", "if-watch", "libp2p-core", "libp2p-identity", "libp2p-tls", - "parking_lot", "quinn", "rand 0.8.5", - "ring 0.17.14", + "ring", "rustls 0.23.37", "socket2 0.5.10", - "thiserror 1.0.69", + "thiserror 2.0.18", "tokio", "tracing", ] [[package]] name = "libp2p-request-response" -version = "0.27.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1356c9e376a94a75ae830c42cdaea3d4fe1290ba409a22c809033d1b7dcab0a6" +checksum = "a9f1cca83488b90102abac7b67d5c36fc65bc02ed47620228af7ed002e6a1478" dependencies = [ "async-trait", "futures", "futures-bounded", - "futures-timer", "libp2p-core", "libp2p-identity", "libp2p-swarm", "rand 0.8.5", "smallvec", "tracing", - "void", - "web-time", ] [[package]] name = "libp2p-swarm" -version = "0.45.1" +version = "0.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7dd6741793d2c1fb2088f67f82cf07261f25272ebe3c0b0c311e0c6b50e851a" +checksum = "ce88c6c4bf746c8482480345ea3edfd08301f49e026889d1cbccfa1808a9ed9e" dependencies = [ - "async-std", "either", "fnv", "futures", "futures-timer", + "hashlink 0.10.0", "libp2p-core", "libp2p-identity", "libp2p-swarm-derive", - "lru 0.12.5", "multistream-select", - "once_cell", "rand 0.8.5", "smallvec", "tokio", "tracing", - "void", "web-time", ] [[package]] name = "libp2p-swarm-derive" -version = "0.35.0" +version = "0.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206e0aa0ebe004d778d79fb0966aa0de996c19894e2c0605ba2f8524dd4443d8" +checksum = "dd297cf53f0cb3dee4d2620bb319ae47ef27c702684309f682bdb7e55a18ae9c" dependencies = [ "heck 0.5.0", - "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "libp2p-swarm-test" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4e1d1d92421dc4c90cad42e3cd24f50fd210191c9f126d41bd483a09567f67" +checksum = "7b149112570d507efe305838c7130835955a0b1147aa8051c1c3867a83175cf6" dependencies = [ "async-trait", "futures", @@ -7136,52 +6986,49 @@ dependencies = [ "libp2p-swarm", "libp2p-tcp", "libp2p-yamux", - "rand 0.8.5", "tracing", ] [[package]] name = "libp2p-tcp" -version = "0.42.0" +version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad964f312c59dcfcac840acd8c555de8403e295d39edf96f5240048b5fcaa314" +checksum = "fb6585b9309699f58704ec9ab0bb102eca7a3777170fa91a8678d73ca9cafa93" dependencies = [ - "async-io", "futures", "futures-timer", "if-watch", "libc", "libp2p-core", - "libp2p-identity", - "socket2 0.5.10", + "socket2 0.6.3", "tokio", "tracing", ] [[package]] name = "libp2p-tls" -version = "0.5.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b23dddc2b9c355f73c1e36eb0c3ae86f7dc964a3715f0731cfad352db4d847" +checksum = "96ff65a82e35375cbc31ebb99cacbbf28cb6c4fefe26bf13756ddcf708d40080" dependencies = [ "futures", "futures-rustls", "libp2p-core", "libp2p-identity", "rcgen", - "ring 0.17.14", + "ring", "rustls 0.23.37", - "rustls-webpki 0.101.7", - "thiserror 1.0.69", + "rustls-webpki 0.103.10", + "thiserror 2.0.18", "x509-parser", "yasna", ] [[package]] name = "libp2p-upnp" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01bf2d1b772bd3abca049214a3304615e6a36fa6ffc742bdd1ba774486200b8f" +checksum = "4757e65fe69399c1a243bbb90ec1ae5a2114b907467bf09f3575e899815bb8d3" dependencies = [ "futures", "futures-timer", @@ -7190,14 +7037,13 @@ dependencies = [ "libp2p-swarm", "tokio", "tracing", - "void", ] [[package]] name = "libp2p-websocket" -version = "0.44.0" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "888b2ff2e5d8dcef97283daab35ad1043d18952b65e05279eecbe02af4c6e347" +checksum = "520e29066a48674c007bc11defe5dce49908c24cafd8fad2f5e1a6a8726ced53" dependencies = [ "either", "futures", @@ -7208,22 +7054,22 @@ dependencies = [ "pin-project-lite", "rw-stream-sink", "soketto", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", "url", - "webpki-roots 0.25.4", + "webpki-roots 0.26.11", ] [[package]] name = "libp2p-yamux" -version = "0.46.0" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788b61c80789dba9760d8c669a5bedb642c8267555c803fabd8396e4ca5c5882" +checksum = "f15df094914eb4af272acf9adaa9e287baa269943f32ea348ba29cfb9bfc60d8" dependencies = [ "either", "futures", "libp2p-core", - "thiserror 1.0.69", + "thiserror 2.0.18", "tracing", "yamux 0.12.1", "yamux 0.13.5", @@ -7328,9 +7174,6 @@ name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "value-bag", -] [[package]] name = "logos" @@ -7596,6 +7439,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "moka" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "equivalent", + "parking_lot", + "portable-atomic", + "smallvec", + "tagptr", + "uuid", +] + [[package]] name = "multer" version = "3.1.0" @@ -7609,7 +7469,7 @@ dependencies = [ "httparse", "memchr", "mime", - "spin 0.9.8", + "spin", "version_check", ] @@ -7718,7 +7578,6 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" dependencies = [ - "async-io", "bytes", "futures-util", "libc", @@ -7783,7 +7642,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7937,9 +7796,9 @@ dependencies = [ [[package]] name = "oid-registry" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ "asn1-rs", ] @@ -7949,6 +7808,10 @@ name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "once_cell_polyfill" @@ -7996,7 +7859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] @@ -8280,17 +8143,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - [[package]] name = "pkcs8" version = "0.9.0" @@ -8515,7 +8367,7 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "uint", + "uint 0.9.5", ] [[package]] @@ -8573,9 +8425,9 @@ dependencies = [ [[package]] name = "prometheus-client" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" +checksum = "cf41c1a7c32ed72abe5082fb19505b969095c12da9f5732a4bc9878757fd087c" dependencies = [ "dtoa", "itoa", @@ -8686,7 +8538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -8699,7 +8551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -8869,7 +8721,7 @@ dependencies = [ "getrandom 0.3.4", "lru-slab", "rand 0.9.2", - "ring 0.17.14", + "ring", "rustc-hash 2.1.2", "rustls 0.23.37", "rustls-pki-types", @@ -9032,12 +8884,13 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.11.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" dependencies = [ "pem", - "ring 0.16.20", + "ring", + "rustls-pki-types", "time", "yasna", ] @@ -9351,21 +9204,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.14" @@ -9376,7 +9214,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.17", "libc", - "untrusted 0.9.0", + "untrusted", "windows-sys 0.52.0", ] @@ -9464,7 +9302,6 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b960d5d873a75b5be9761b1e73b146f52dddcd27bac75263f40fba686d4d7b5" dependencies = [ - "async-global-executor", "futures-channel", "futures-util", "log", @@ -9572,7 +9409,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -9582,7 +9419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.14", + "ring", "rustls-webpki 0.101.7", "sct", ] @@ -9595,7 +9432,7 @@ checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "aws-lc-rs", "once_cell", - "ring 0.17.14", + "ring", "rustls-pki-types", "rustls-webpki 0.103.10", "subtle", @@ -9663,7 +9500,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -9678,8 +9515,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -9689,9 +9526,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ "aws-lc-rs", - "ring 0.17.14", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -9809,8 +9646,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", + "ring", + "untrusted", ] [[package]] @@ -10265,23 +10102,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smol" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", - "async-process", - "blocking", - "futures-lite", -] - [[package]] name = "snow" version = "0.9.6" @@ -10293,7 +10113,7 @@ dependencies = [ "chacha20poly1305", "curve25519-dalek", "rand_core 0.6.4", - "ring 0.17.14", + "ring", "rustc_version 0.4.1", "sha2 0.10.9", "subtle", @@ -10316,7 +10136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10334,12 +10154,6 @@ dependencies = [ "sha1", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -10645,6 +10459,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tai64" version = "4.0.0" @@ -10687,7 +10507,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -11645,6 +11465,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unarray" version = "0.1.4" @@ -11709,12 +11541,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -11769,12 +11595,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" - [[package]] name = "vcpkg" version = "0.2.15" @@ -11787,12 +11607,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "vsimd" version = "0.8.0" @@ -12209,6 +12023,15 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.6", +] + [[package]] name = "webpki-roots" version = "1.0.6" @@ -12246,7 +12069,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -12809,9 +12632,9 @@ dependencies = [ [[package]] name = "x509-parser" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" dependencies = [ "asn1-rs", "data-encoding", @@ -12820,7 +12643,7 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror 1.0.69", + "thiserror 2.0.18", "time", ] diff --git a/Cargo.toml b/Cargo.toml index 985424f5772..f14521ddde6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,7 +143,7 @@ pin-project-lite = "0.2" postcard = "1.0" pretty_assertions = "1.4.0" primitive-types = { version = "0.12", default-features = false } -prometheus-client = "0.22.0" +prometheus-client = "0.23.0" proptest = "1.1" prost = "0.14.1" rand = "0.8" diff --git a/crates/services/p2p/Cargo.toml b/crates/services/p2p/Cargo.toml index 782ffd9c0ff..249bea01fec 100644 --- a/crates/services/p2p/Cargo.toml +++ b/crates/services/p2p/Cargo.toml @@ -30,7 +30,8 @@ fuel-core-types = { workspace = true, features = ["std", "serde"] } futures = { workspace = true } hex = { workspace = true } hickory-resolver = "0.24.1" -libp2p = { version = "0.54.1", default-features = false, features = ["dns", "gossipsub", "identify", "kad", "macros", "mdns", "noise", "request-response", "secp256k1", "tcp", "tokio", "yamux", "websocket", "metrics"] } +libp2p = { version = "0.56.0", default-features = false, features = ["dns", "gossipsub", "identify", "kad", "macros", "mdns", "noise", "request-response", "secp256k1", "tcp", "tokio", "yamux", "websocket", "metrics"] } +libp2p-gossipsub = { version = "0.49", features = ["metrics"] } parking_lot = { workspace = true } postcard = { workspace = true, features = ["use-std"] } quick_cache = "0.6.9" @@ -42,7 +43,6 @@ strum_macros = { workspace = true } thiserror = "1.0.47" tokio = { workspace = true, features = ["sync"] } tracing = { workspace = true } -void = "1" # Fix publishing of the crate yamux = "=0.13.5" @@ -55,4 +55,4 @@ tokio = { workspace = true, features = ["full", "test-util"] } tracing-attributes = { workspace = true } [dev-dependencies.libp2p-swarm-test] -version = "0.4.0" +version = "0.6.0" diff --git a/crates/services/p2p/src/behavior.rs b/crates/services/p2p/src/behavior.rs index 011348e236d..d5b7814e4ec 100644 --- a/crates/services/p2p/src/behavior.rs +++ b/crates/services/p2p/src/behavior.rs @@ -228,18 +228,15 @@ impl FuelBehaviour { propagation_source, acceptance, ) { - Ok(true) => { + true => { tracing::debug!(target: "fuel-p2p", "Sent a report for MessageId: {} from PeerId: {}", msg_id, propagation_source); if should_check_score { return self.gossipsub.peer_score(propagation_source); } } - Ok(false) => { + false => { tracing::warn!(target: "fuel-p2p", "Message with MessageId: {} not found in the Gossipsub Message Cache", msg_id); } - Err(e) => { - tracing::error!(target: "fuel-p2p", "Failed to report Message with MessageId: {} with Error: {:?}", msg_id, e); - } } None @@ -255,6 +252,6 @@ impl FuelBehaviour { } pub fn block_peer(&mut self, peer_id: PeerId) { - self.blocked_peer.block_peer(peer_id) + self.blocked_peer.block_peer(peer_id); } } diff --git a/crates/services/p2p/src/connection_limits.rs b/crates/services/p2p/src/connection_limits.rs index 4fc5ada21ae..bb4cb241459 100644 --- a/crates/services/p2p/src/connection_limits.rs +++ b/crates/services/p2p/src/connection_limits.rs @@ -33,6 +33,7 @@ use std::{ HashSet, hash_map::Entry, }, + convert::Infallible, fmt, sync::Arc, task::{ @@ -40,7 +41,6 @@ use std::{ Poll, }, }; -use void::Void; #[derive(Default)] pub struct ConnectionsStatistic { @@ -196,7 +196,7 @@ impl ConnectionLimits { impl NetworkBehaviour for Behaviour { type ConnectionHandler = dummy::ConnectionHandler; - type ToSwarm = Void; + type ToSwarm = Infallible; fn handle_pending_inbound_connection( &mut self, @@ -409,7 +409,7 @@ impl NetworkBehaviour for Behaviour { _: ConnectionId, event: THandlerOutEvent, ) { - void::unreachable(event) + match event {} } fn poll( diff --git a/crates/services/p2p/src/discovery.rs b/crates/services/p2p/src/discovery.rs index d1de4cf2c87..c9422d6fa78 100644 --- a/crates/services/p2p/src/discovery.rs +++ b/crates/services/p2p/src/discovery.rs @@ -283,7 +283,7 @@ mod tests { let listen_addr: Multiaddr = Protocol::Memory(rand::random::()).into(); - let mut swarm = Swarm::new_ephemeral(behaviour_fn); + let mut swarm = Swarm::new_ephemeral_tokio(behaviour_fn); swarm .listen_on(listen_addr.clone()) diff --git a/crates/services/p2p/src/gossipsub/config.rs b/crates/services/p2p/src/gossipsub/config.rs index 2dd7d41e03c..106bab6c922 100644 --- a/crates/services/p2p/src/gossipsub/config.rs +++ b/crates/services/p2p/src/gossipsub/config.rs @@ -184,13 +184,12 @@ pub(crate) fn build_gossipsub_behaviour(p2p_config: &Config) -> gossipsub::Behav let metrics_config = MetricsConfig::default(); - let mut gossipsub = gossipsub::Behaviour::new_with_metrics( + let mut gossipsub = gossipsub::Behaviour::new( MessageAuthenticity::Signed(p2p_config.keypair.clone()), p2p_config.gossipsub_config.clone(), - registry.deref_mut(), - metrics_config, ) - .expect("gossipsub initialized"); + .expect("gossipsub initialized") + .with_metrics(registry.deref_mut(), metrics_config); initialize_gossipsub(&mut gossipsub, p2p_config); diff --git a/crates/services/p2p/src/lib.rs b/crates/services/p2p/src/lib.rs index 5f8d3335627..8cd398fdacc 100644 --- a/crates/services/p2p/src/lib.rs +++ b/crates/services/p2p/src/lib.rs @@ -3,6 +3,7 @@ #![deny(unused_crate_dependencies)] #![deny(warnings)] +use libp2p_gossipsub as _; use yamux as _; pub mod behavior; diff --git a/crates/services/p2p/src/limited_behaviour.rs b/crates/services/p2p/src/limited_behaviour.rs index 2e2c4b4c673..565cd26f892 100644 --- a/crates/services/p2p/src/limited_behaviour.rs +++ b/crates/services/p2p/src/limited_behaviour.rs @@ -214,7 +214,7 @@ where event: THandlerOutEvent, ) { match event { - Either::Left(event) => void::unreachable(event), + Either::Left(event) => match event {}, Either::Right(event) => { self.behaviour .on_connection_handler_event(id, connection_id, event) diff --git a/crates/services/p2p/src/p2p_service.rs b/crates/services/p2p/src/p2p_service.rs index 3ff9856cd0e..489ccbbef33 100644 --- a/crates/services/p2p/src/p2p_service.rs +++ b/crates/services/p2p/src/p2p_service.rs @@ -359,7 +359,7 @@ impl FuelP2PService { } } - #[cfg(feature = "test-helpers")] + #[cfg(any(test, feature = "test-helpers"))] pub fn multiaddrs(&self) -> Vec { let local_peer = self.local_peer_id; self.swarm @@ -654,7 +654,7 @@ impl FuelP2PService { event: request_response::Event, ) -> Option { match event { - request_response::Event::Message { peer, message } => match message { + request_response::Event::Message { peer, message, .. } => match message { request_response::Message::Request { request, channel, @@ -751,6 +751,7 @@ impl FuelP2PService { peer, error, request_id, + .. } => { tracing::error!( "RequestResponse inbound error for peer: {:?} with id: {:?} and error: {:?}", @@ -766,6 +767,7 @@ impl FuelP2PService { peer, error, request_id, + .. } => { // If the remote peer doesn't support the protocol, it is better to disconnect // to find another peer that supports it. diff --git a/crates/services/p2p/src/peer_report.rs b/crates/services/p2p/src/peer_report.rs index 365b4e18630..0e63e2d3312 100644 --- a/crates/services/p2p/src/peer_report.rs +++ b/crates/services/p2p/src/peer_report.rs @@ -36,6 +36,7 @@ use std::{ HashSet, VecDeque, }, + convert::Infallible, task::{ Context, Poll, @@ -49,7 +50,6 @@ use tokio::time::{ self, Interval, }; -use void::Void; const HEALTH_CHECK_INTERVAL_IN_SECONDS: u64 = 10; const REPUTATION_DECAY_INTERVAL_IN_SECONDS: u64 = 1; @@ -73,7 +73,7 @@ pub struct Behaviour { reserved_nodes_to_connect: VecDeque<(Instant, PeerId)>, connected_reserved_nodes: HashSet, pending_connections: HashSet, - pending_events: VecDeque>, + pending_events: VecDeque>, decay_interval: Interval, } diff --git a/crates/services/p2p/src/service.rs b/crates/services/p2p/src/service.rs index 834c6dff404..96e78870307 100644 --- a/crates/services/p2p/src/service.rs +++ b/crates/services/p2p/src/service.rs @@ -316,7 +316,7 @@ impl TaskP2PService for FuelP2PService { match result { Ok(_) => Ok(()), Err(e) => { - if matches!(&e, PublishError::InsufficientPeers) { + if matches!(&e, PublishError::NoPeersSubscribedToTopic) { Ok(()) } else { Err(anyhow!(e)) diff --git a/version-compatibility/Cargo.toml b/version-compatibility/Cargo.toml index 54413b5ef16..c03120e2090 100644 --- a/version-compatibility/Cargo.toml +++ b/version-compatibility/Cargo.toml @@ -1,3 +1,10 @@ [workspace] resolver = "2" members = ["forkless-upgrade"] + +# libp2p 0.54.1 (used by version-44-fuel-core-bin) pins libp2p-swarm-derive =0.35.0, +# while libp2p 0.56.0 (used by the latest fuel-core) pins =0.35.1. Since both are in +# the same semver bucket, Cargo can't select two versions. Patch libp2p-swarm 0.45.1 +# to use =0.35.1 instead — the diff between the two proc-macro versions is cosmetic. +[patch.crates-io] +libp2p-swarm = { path = "vendor/libp2p-swarm-0.45.1" } diff --git a/version-compatibility/forkless-upgrade/Cargo.toml b/version-compatibility/forkless-upgrade/Cargo.toml index 9bcface9893..1913a4870fd 100644 --- a/version-compatibility/forkless-upgrade/Cargo.toml +++ b/version-compatibility/forkless-upgrade/Cargo.toml @@ -9,7 +9,8 @@ build = "build.rs" [dev-dependencies] anyhow = "1.0" clap = "4.4" -libp2p = "0.54.1" +futures = "0.3" +libp2p-identity = { version = "0.2", features = ["secp256k1"] } hex = "0.4.3" rand = "0.8" tempfile = "3.4" diff --git a/version-compatibility/forkless-upgrade/src/backward_compatibility.rs b/version-compatibility/forkless-upgrade/src/backward_compatibility.rs index 3170a6c108d..f03dac364fc 100644 --- a/version-compatibility/forkless-upgrade/src/backward_compatibility.rs +++ b/version-compatibility/forkless-upgrade/src/backward_compatibility.rs @@ -20,10 +20,8 @@ use latest_fuel_core_type::{ }, }, }; -use libp2p::{ - futures::StreamExt, - identity::secp256k1::Keypair as SecpKeypair, -}; +use futures::StreamExt; +use libp2p_identity::secp256k1::Keypair as SecpKeypair; use std::time::Duration; use version_44_fuel_core_client::client::FuelClient as Version44Client; diff --git a/version-compatibility/forkless-upgrade/src/forward_compatibility.rs b/version-compatibility/forkless-upgrade/src/forward_compatibility.rs index b2e1c98e0de..08f5e5477b9 100644 --- a/version-compatibility/forkless-upgrade/src/forward_compatibility.rs +++ b/version-compatibility/forkless-upgrade/src/forward_compatibility.rs @@ -13,10 +13,8 @@ use crate::{ upgrade_transaction, }, }; -use libp2p::{ - futures::StreamExt, - identity::secp256k1::Keypair as SecpKeypair, -}; +use futures::StreamExt; +use libp2p_identity::secp256k1::Keypair as SecpKeypair; use rand::{ SeedableRng, rngs::StdRng, diff --git a/version-compatibility/forkless-upgrade/src/tests_helper.rs b/version-compatibility/forkless-upgrade/src/tests_helper.rs index 7a509ed36ec..d07f7c970b6 100644 --- a/version-compatibility/forkless-upgrade/src/tests_helper.rs +++ b/version-compatibility/forkless-upgrade/src/tests_helper.rs @@ -4,7 +4,7 @@ use genesis_fuel_core_client::client::FuelClient as GenesisClient; use genesis_fuel_core_services::Service as _; use latest_fuel_core_bin::FuelService as LatestFuelService; use latest_fuel_core_client::client::FuelClient as LatestClient; -use libp2p::PeerId; +use libp2p_identity::PeerId; use rand::{ Rng, prelude::StdRng, diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/.cargo-ok b/version-compatibility/vendor/libp2p-swarm-0.45.1/.cargo-ok new file mode 100644 index 00000000000..5f8b795830a --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/.cargo_vcs_info.json b/version-compatibility/vendor/libp2p-swarm-0.45.1/.cargo_vcs_info.json new file mode 100644 index 00000000000..4929f430a48 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "87101870709aaf64e141e07624855155ed4faaac" + }, + "path_in_vcs": "swarm" +} \ No newline at end of file diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/Cargo.toml b/version-compatibility/vendor/libp2p-swarm-0.45.1/Cargo.toml new file mode 100644 index 00000000000..9fa1b4cb0cb --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/Cargo.toml @@ -0,0 +1,172 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.75.0" +name = "libp2p-swarm" +version = "0.45.1" +authors = ["Parity Technologies "] +build = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "The libp2p swarm" +readme = false +keywords = [ + "peer-to-peer", + "libp2p", + "networking", +] +categories = [ + "network-programming", + "asynchronous", +] +license = "MIT" +repository = "https://github.com/libp2p/rust-libp2p" + +[package.metadata.docs.rs] +all-features = true + +[lib] +name = "libp2p_swarm" +path = "src/lib.rs" + + +[dependencies.either] +version = "1.11.0" + +[dependencies.fnv] +version = "1.0" + +[dependencies.futures] +version = "0.3.30" + +[dependencies.futures-timer] +version = "3.0.3" + +[dependencies.getrandom] +version = "0.2.15" +features = ["js"] +optional = true + +[dependencies.libp2p-core] +version = "0.42.0" + +[dependencies.libp2p-identity] +version = "0.2.9" + +[dependencies.libp2p-swarm-derive] +version = "=0.35.1" +optional = true + +[dependencies.lru] +version = "0.12.3" + +[dependencies.multistream-select] +version = "0.13.0" + +[dependencies.once_cell] +version = "1.19.0" + +[dependencies.rand] +version = "0.8" + +[dependencies.smallvec] +version = "1.13.2" + +[dependencies.tracing] +version = "0.1.37" + +[dependencies.void] +version = "1" + +[dependencies.wasm-bindgen-futures] +version = "0.4.42" +optional = true + +[dependencies.web-time] +version = "1.1.0" + +[dev-dependencies.async-std] +version = "1.6.2" +features = ["attributes"] + +[dev-dependencies.criterion] +version = "0.5" +features = ["async_tokio"] + +[dev-dependencies.either] +version = "1.11.0" + +[dev-dependencies.futures] +version = "0.3.30" + +[dev-dependencies.libp2p-identity] +version = "0.2.9" +features = ["ed25519"] + +[dev-dependencies.once_cell] +version = "1.19.0" + +[dev-dependencies.tokio] +version = "1.38" +features = [ + "time", + "rt", + "macros", + "rt-multi-thread", +] +default-features = false + +[dev-dependencies.tracing-subscriber] +version = "0.3" +features = ["env-filter"] + +[dev-dependencies.trybuild] +version = "1.0.95" + +[dev-dependencies.void] +version = "1" + +[features] +async-std = ["dep:async-std"] +macros = ["dep:libp2p-swarm-derive"] +tokio = ["dep:tokio"] +wasm-bindgen = [ + "dep:wasm-bindgen-futures", + "dep:getrandom", +] + +[target.'cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))'.dependencies.async-std] +version = "1.6.2" +optional = true + +[target.'cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))'.dependencies.tokio] +version = "1.38" +features = ["rt"] +optional = true +default-features = false + +[lints.clippy] +dbg_macro = "warn" +manual_let_else = "warn" +type_complexity = "allow" +unnecessary_wraps = "warn" +used_underscore_binding = "warn" + +[lints.clippy.pedantic] +level = "allow" +priority = -1 + +[lints.rust] +unreachable_pub = "warn" diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/LICENSE b/version-compatibility/vendor/libp2p-swarm-0.45.1/LICENSE new file mode 100644 index 00000000000..abc24985a5c --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/LICENSE @@ -0,0 +1,19 @@ +Copyright 2017-2020 Parity Technologies (UK) Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour.rs new file mode 100644 index 00000000000..35aed12fba5 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour.rs @@ -0,0 +1,585 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +mod either; +mod external_addresses; +mod listen_addresses; +mod peer_addresses; +pub mod toggle; + +pub use external_addresses::ExternalAddresses; +pub use listen_addresses::ListenAddresses; +pub use peer_addresses::PeerAddresses; + +use crate::connection::ConnectionId; +use crate::dial_opts::DialOpts; +use crate::listen_opts::ListenOpts; +use crate::{ + ConnectionDenied, ConnectionError, ConnectionHandler, DialError, ListenError, THandler, + THandlerInEvent, THandlerOutEvent, +}; +use libp2p_core::{ + transport::{ListenerId, PortUse}, + ConnectedPoint, Endpoint, Multiaddr, +}; +use libp2p_identity::PeerId; +use std::{task::Context, task::Poll}; + +/// A [`NetworkBehaviour`] defines the behaviour of the local node on the network. +/// +/// In contrast to [`Transport`](libp2p_core::Transport) which defines **how** to send bytes on the +/// network, [`NetworkBehaviour`] defines **what** bytes to send and **to whom**. +/// +/// Each protocol (e.g. `libp2p-ping`, `libp2p-identify` or `libp2p-kad`) implements +/// [`NetworkBehaviour`]. Multiple implementations of [`NetworkBehaviour`] can be composed into a +/// hierarchy of [`NetworkBehaviour`]s where parent implementations delegate to child +/// implementations. Finally the root of the [`NetworkBehaviour`] hierarchy is passed to +/// [`Swarm`](crate::Swarm) where it can then control the behaviour of the local node on a libp2p +/// network. +/// +/// # Hierarchy of [`NetworkBehaviour`] +/// +/// To compose multiple [`NetworkBehaviour`] implementations into a single [`NetworkBehaviour`] +/// implementation, potentially building a multi-level hierarchy of [`NetworkBehaviour`]s, one can +/// use one of the [`NetworkBehaviour`] combinators, and/or use the [`NetworkBehaviour`] derive +/// macro. +/// +/// ## Combinators +/// +/// [`NetworkBehaviour`] combinators wrap one or more [`NetworkBehaviour`] implementations and +/// implement [`NetworkBehaviour`] themselves. Example is the +/// [`Toggle`](crate::behaviour::toggle::Toggle) [`NetworkBehaviour`]. +/// +/// ``` rust +/// # use libp2p_swarm::dummy; +/// # use libp2p_swarm::behaviour::toggle::Toggle; +/// let my_behaviour = dummy::Behaviour; +/// let my_toggled_behaviour = Toggle::from(Some(my_behaviour)); +/// ``` +/// +/// ## Custom [`NetworkBehaviour`] with the Derive Macro +/// +/// One can derive [`NetworkBehaviour`] for a custom `struct` via the `#[derive(NetworkBehaviour)]` +/// proc macro re-exported by the `libp2p` crate. The macro generates a delegating `trait` +/// implementation for the custom `struct`. Each [`NetworkBehaviour`] trait method is simply +/// delegated to each `struct` member in the order the `struct` is defined. For example for +/// [`NetworkBehaviour::poll`] it will first poll the first `struct` member until it returns +/// [`Poll::Pending`] before moving on to later members. +/// +/// Events ([`NetworkBehaviour::ToSwarm`]) returned by each `struct` member are wrapped in a new +/// `enum` event, with an `enum` variant for each `struct` member. Users can define this event +/// `enum` themselves and provide the name to the derive macro via `#[behaviour(to_swarm = +/// "MyCustomOutEvent")]`. If the user does not specify an `to_swarm`, the derive macro generates +/// the event definition itself, naming it `Event`. +/// +/// The aforementioned conversion of each of the event types generated by the struct members to the +/// custom `to_swarm` is handled by [`From`] implementations which the user needs to define in +/// addition to the event `enum` itself. +/// +/// ``` rust +/// # use libp2p_identify as identify; +/// # use libp2p_ping as ping; +/// # use libp2p_swarm_derive::NetworkBehaviour; +/// #[derive(NetworkBehaviour)] +/// #[behaviour(to_swarm = "Event")] +/// # #[behaviour(prelude = "libp2p_swarm::derive_prelude")] +/// struct MyBehaviour { +/// identify: identify::Behaviour, +/// ping: ping::Behaviour, +/// } +/// +/// enum Event { +/// Identify(identify::Event), +/// Ping(ping::Event), +/// } +/// +/// impl From for Event { +/// fn from(event: identify::Event) -> Self { +/// Self::Identify(event) +/// } +/// } +/// +/// impl From for Event { +/// fn from(event: ping::Event) -> Self { +/// Self::Ping(event) +/// } +/// } +/// ``` +pub trait NetworkBehaviour: 'static { + /// Handler for all the protocols the network behaviour supports. + type ConnectionHandler: ConnectionHandler; + + /// Event generated by the `NetworkBehaviour` and that the swarm will report back. + type ToSwarm: Send + 'static; + + /// Callback that is invoked for every new inbound connection. + /// + /// At this point in the connection lifecycle, only the remote's and our local address are known. + /// We have also already allocated a [`ConnectionId`]. + /// + /// Any error returned from this function will immediately abort the dial attempt. + fn handle_pending_inbound_connection( + &mut self, + _connection_id: ConnectionId, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + Ok(()) + } + + /// Callback that is invoked for every established inbound connection. + /// + /// This is invoked once another peer has successfully dialed us. + /// + /// At this point, we have verified their [`PeerId`] and we know, which particular [`Multiaddr`] succeeded in the dial. + /// In order to actually use this connection, this function must return a [`ConnectionHandler`]. + /// Returning an error will immediately close the connection. + /// + /// Note when any composed behaviour returns an error the connection will be closed and a + /// [`FromSwarm::ListenFailure`] event will be emitted. + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied>; + + /// Callback that is invoked for every outbound connection attempt. + /// + /// We have access to: + /// + /// - The [`PeerId`], if known. Remember that we can dial without a [`PeerId`]. + /// - All addresses passed to [`DialOpts`] are passed in here too. + /// - The effective [`Role`](Endpoint) of this peer in the dial attempt. Typically, this is set to [`Endpoint::Dialer`] except if we are attempting a hole-punch. + /// - The [`ConnectionId`] identifying the future connection resulting from this dial, if successful. + /// + /// Note that the addresses returned from this function are only used for dialing if [`WithPeerIdWithAddresses::extend_addresses_through_behaviour`](crate::dial_opts::WithPeerIdWithAddresses::extend_addresses_through_behaviour) is set. + /// + /// Any error returned from this function will immediately abort the dial attempt. + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + _maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + Ok(vec![]) + } + + /// Callback that is invoked for every established outbound connection. + /// + /// This is invoked once we have successfully dialed a peer. + /// At this point, we have verified their [`PeerId`] and we know, which particular [`Multiaddr`] succeeded in the dial. + /// In order to actually use this connection, this function must return a [`ConnectionHandler`]. + /// Returning an error will immediately close the connection. + /// + /// Note when any composed behaviour returns an error the connection will be closed and a + /// [`FromSwarm::DialFailure`] event will be emitted. + fn handle_established_outbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result, ConnectionDenied>; + + /// Informs the behaviour about an event from the [`Swarm`](crate::Swarm). + fn on_swarm_event(&mut self, event: FromSwarm); + + /// Informs the behaviour about an event generated by the [`ConnectionHandler`] + /// dedicated to the peer identified by `peer_id`. for the behaviour. + /// + /// The [`PeerId`] is guaranteed to be in a connected state. In other words, + /// [`FromSwarm::ConnectionEstablished`] has previously been received with this [`PeerId`]. + fn on_connection_handler_event( + &mut self, + _peer_id: PeerId, + _connection_id: ConnectionId, + _event: THandlerOutEvent, + ); + + /// Polls for things that swarm should do. + /// + /// This API mimics the API of the `Stream` trait. The method may register the current task in + /// order to wake it up at a later point in time. + fn poll(&mut self, cx: &mut Context<'_>) + -> Poll>>; +} + +/// A command issued from a [`NetworkBehaviour`] for the [`Swarm`]. +/// +/// [`Swarm`]: super::Swarm +#[derive(Debug)] +#[non_exhaustive] +pub enum ToSwarm { + /// Instructs the `Swarm` to return an event when it is being polled. + GenerateEvent(TOutEvent), + + /// Instructs the swarm to start a dial. + /// + /// On success, [`NetworkBehaviour::on_swarm_event`] with `ConnectionEstablished` is invoked. + /// On failure, [`NetworkBehaviour::on_swarm_event`] with `DialFailure` is invoked. + /// + /// [`DialOpts`] provides access to the [`ConnectionId`] via [`DialOpts::connection_id`]. + /// This [`ConnectionId`] will be used throughout the connection's lifecycle to associate events with it. + /// This allows a [`NetworkBehaviour`] to identify a connection that resulted out of its own dial request. + Dial { opts: DialOpts }, + + /// Instructs the [`Swarm`](crate::Swarm) to listen on the provided address. + ListenOn { opts: ListenOpts }, + + /// Instructs the [`Swarm`](crate::Swarm) to remove the listener. + RemoveListener { id: ListenerId }, + + /// Instructs the `Swarm` to send an event to the handler dedicated to a + /// connection with a peer. + /// + /// If the `Swarm` is connected to the peer, the message is delivered to the [`ConnectionHandler`] + /// instance identified by the peer ID and connection ID. + /// + /// If the specified connection no longer exists, the event is silently dropped. + /// + /// Typically the connection ID given is the same as the one passed to + /// [`NetworkBehaviour::on_connection_handler_event`], i.e. whenever the behaviour wishes to + /// respond to a request on the same connection (and possibly the same + /// substream, as per the implementation of [`ConnectionHandler`]). + /// + /// Note that even if the peer is currently connected, connections can get closed + /// at any time and thus the event may not reach a handler. + NotifyHandler { + /// The peer for whom a [`ConnectionHandler`] should be notified. + peer_id: PeerId, + /// The options w.r.t. which connection handler to notify of the event. + handler: NotifyHandler, + /// The event to send. + event: TInEvent, + }, + + /// Reports a **new** candidate for an external address to the [`Swarm`](crate::Swarm). + /// + /// The emphasis on a **new** candidate is important. + /// Protocols MUST take care to only emit a candidate once per "source". + /// For example, the observed address of a TCP connection does not change throughout its lifetime. + /// Thus, only one candidate should be emitted per connection. + /// + /// This makes the report frequency of an address a meaningful data-point for consumers of this event. + /// This address will be shared with all [`NetworkBehaviour`]s via [`FromSwarm::NewExternalAddrCandidate`]. + /// + /// This address could come from a variety of sources: + /// - A protocol such as identify obtained it from a remote. + /// - The user provided it based on configuration. + /// - We made an educated guess based on one of our listen addresses. + NewExternalAddrCandidate(Multiaddr), + + /// Indicates to the [`Swarm`](crate::Swarm) that the provided address is confirmed to be externally reachable. + /// + /// This is intended to be issued in response to a [`FromSwarm::NewExternalAddrCandidate`] if we are indeed externally reachable on this address. + /// This address will be shared with all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrConfirmed`]. + ExternalAddrConfirmed(Multiaddr), + + /// Indicates to the [`Swarm`](crate::Swarm) that we are no longer externally reachable under the provided address. + /// + /// This expires an address that was earlier confirmed via [`ToSwarm::ExternalAddrConfirmed`]. + /// This address will be shared with all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrExpired`]. + ExternalAddrExpired(Multiaddr), + + /// Instructs the `Swarm` to initiate a graceful close of one or all connections with the given peer. + /// + /// Closing a connection via [`ToSwarm::CloseConnection`] will poll [`ConnectionHandler::poll_close`] to completion. + /// In most cases, stopping to "use" a connection is enough to have it closed. + /// The keep-alive algorithm will close a connection automatically once all [`ConnectionHandler`]s are idle. + /// + /// Use this command if you want to close a connection _despite_ it still being in use by one or more handlers. + CloseConnection { + /// The peer to disconnect. + peer_id: PeerId, + /// Whether to close a specific or all connections to the given peer. + connection: CloseConnection, + }, + + /// Reports external address of a remote peer to the [`Swarm`](crate::Swarm) and through that to other [`NetworkBehaviour`]s. + NewExternalAddrOfPeer { peer_id: PeerId, address: Multiaddr }, +} + +impl ToSwarm { + /// Map the handler event. + pub fn map_in( + self, + f: impl FnOnce(TInEventOld) -> TInEventNew, + ) -> ToSwarm { + match self { + ToSwarm::GenerateEvent(e) => ToSwarm::GenerateEvent(e), + ToSwarm::Dial { opts } => ToSwarm::Dial { opts }, + ToSwarm::ListenOn { opts } => ToSwarm::ListenOn { opts }, + ToSwarm::RemoveListener { id } => ToSwarm::RemoveListener { id }, + ToSwarm::NotifyHandler { + peer_id, + handler, + event, + } => ToSwarm::NotifyHandler { + peer_id, + handler, + event: f(event), + }, + ToSwarm::CloseConnection { + peer_id, + connection, + } => ToSwarm::CloseConnection { + peer_id, + connection, + }, + ToSwarm::NewExternalAddrCandidate(addr) => ToSwarm::NewExternalAddrCandidate(addr), + ToSwarm::ExternalAddrConfirmed(addr) => ToSwarm::ExternalAddrConfirmed(addr), + ToSwarm::ExternalAddrExpired(addr) => ToSwarm::ExternalAddrExpired(addr), + ToSwarm::NewExternalAddrOfPeer { + address: addr, + peer_id, + } => ToSwarm::NewExternalAddrOfPeer { + address: addr, + peer_id, + }, + } + } +} + +impl ToSwarm { + /// Map the event the swarm will return. + pub fn map_out(self, f: impl FnOnce(TOutEvent) -> E) -> ToSwarm { + match self { + ToSwarm::GenerateEvent(e) => ToSwarm::GenerateEvent(f(e)), + ToSwarm::Dial { opts } => ToSwarm::Dial { opts }, + ToSwarm::ListenOn { opts } => ToSwarm::ListenOn { opts }, + ToSwarm::RemoveListener { id } => ToSwarm::RemoveListener { id }, + ToSwarm::NotifyHandler { + peer_id, + handler, + event, + } => ToSwarm::NotifyHandler { + peer_id, + handler, + event, + }, + ToSwarm::NewExternalAddrCandidate(addr) => ToSwarm::NewExternalAddrCandidate(addr), + ToSwarm::ExternalAddrConfirmed(addr) => ToSwarm::ExternalAddrConfirmed(addr), + ToSwarm::ExternalAddrExpired(addr) => ToSwarm::ExternalAddrExpired(addr), + ToSwarm::CloseConnection { + peer_id, + connection, + } => ToSwarm::CloseConnection { + peer_id, + connection, + }, + ToSwarm::NewExternalAddrOfPeer { + address: addr, + peer_id, + } => ToSwarm::NewExternalAddrOfPeer { + address: addr, + peer_id, + }, + } + } +} + +/// The options w.r.t. which connection handler to notify of an event. +#[derive(Debug, Clone)] +pub enum NotifyHandler { + /// Notify a particular connection handler. + One(ConnectionId), + /// Notify an arbitrary connection handler. + Any, +} + +/// The options which connections to close. +#[derive(Debug, Clone, Default)] +pub enum CloseConnection { + /// Disconnect a particular connection. + One(ConnectionId), + /// Disconnect all connections. + #[default] + All, +} + +/// Enumeration with the list of the possible events +/// to pass to [`on_swarm_event`](NetworkBehaviour::on_swarm_event). +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum FromSwarm<'a> { + /// Informs the behaviour about a newly established connection to a peer. + ConnectionEstablished(ConnectionEstablished<'a>), + /// Informs the behaviour about a closed connection to a peer. + /// + /// This event is always paired with an earlier + /// [`FromSwarm::ConnectionEstablished`] with the same peer ID, connection ID + /// and endpoint. + ConnectionClosed(ConnectionClosed<'a>), + /// Informs the behaviour that the [`ConnectedPoint`] of an existing + /// connection has changed. + AddressChange(AddressChange<'a>), + /// Informs the behaviour that the dial to a known + /// or unknown node failed. + DialFailure(DialFailure<'a>), + /// Informs the behaviour that an error + /// happened on an incoming connection during its initial handshake. + /// + /// This can include, for example, an error during the handshake of the encryption layer, or the + /// connection unexpectedly closed. + ListenFailure(ListenFailure<'a>), + /// Informs the behaviour that a new listener was created. + NewListener(NewListener), + /// Informs the behaviour that we have started listening on a new multiaddr. + NewListenAddr(NewListenAddr<'a>), + /// Informs the behaviour that a multiaddr + /// we were listening on has expired, + /// which means that we are no longer listening on it. + ExpiredListenAddr(ExpiredListenAddr<'a>), + /// Informs the behaviour that a listener experienced an error. + ListenerError(ListenerError<'a>), + /// Informs the behaviour that a listener closed. + ListenerClosed(ListenerClosed<'a>), + /// Informs the behaviour that we have discovered a new candidate for an external address for us. + NewExternalAddrCandidate(NewExternalAddrCandidate<'a>), + /// Informs the behaviour that an external address of the local node was confirmed. + ExternalAddrConfirmed(ExternalAddrConfirmed<'a>), + /// Informs the behaviour that an external address of the local node expired, i.e. is no-longer confirmed. + ExternalAddrExpired(ExternalAddrExpired<'a>), + /// Informs the behaviour that we have discovered a new external address for a remote peer. + NewExternalAddrOfPeer(NewExternalAddrOfPeer<'a>), +} + +/// [`FromSwarm`] variant that informs the behaviour about a newly established connection to a peer. +#[derive(Debug, Clone, Copy)] +pub struct ConnectionEstablished<'a> { + pub peer_id: PeerId, + pub connection_id: ConnectionId, + pub endpoint: &'a ConnectedPoint, + pub failed_addresses: &'a [Multiaddr], + pub other_established: usize, +} + +/// [`FromSwarm`] variant that informs the behaviour about a closed connection to a peer. +/// +/// This event is always paired with an earlier +/// [`FromSwarm::ConnectionEstablished`] with the same peer ID, connection ID +/// and endpoint. +#[derive(Debug, Clone, Copy)] +pub struct ConnectionClosed<'a> { + pub peer_id: PeerId, + pub connection_id: ConnectionId, + pub endpoint: &'a ConnectedPoint, + pub cause: Option<&'a ConnectionError>, + pub remaining_established: usize, +} + +/// [`FromSwarm`] variant that informs the behaviour that the [`ConnectedPoint`] of an existing +/// connection has changed. +#[derive(Debug, Clone, Copy)] +pub struct AddressChange<'a> { + pub peer_id: PeerId, + pub connection_id: ConnectionId, + pub old: &'a ConnectedPoint, + pub new: &'a ConnectedPoint, +} + +/// [`FromSwarm`] variant that informs the behaviour that the dial to a known +/// or unknown node failed. +#[derive(Debug, Clone, Copy)] +pub struct DialFailure<'a> { + pub peer_id: Option, + pub error: &'a DialError, + pub connection_id: ConnectionId, +} + +/// [`FromSwarm`] variant that informs the behaviour that an error +/// happened on an incoming connection during its initial handshake. +/// +/// This can include, for example, an error during the handshake of the encryption layer, or the +/// connection unexpectedly closed. +#[derive(Debug, Clone, Copy)] +pub struct ListenFailure<'a> { + pub local_addr: &'a Multiaddr, + pub send_back_addr: &'a Multiaddr, + pub error: &'a ListenError, + pub connection_id: ConnectionId, + pub peer_id: Option, +} + +/// [`FromSwarm`] variant that informs the behaviour that a new listener was created. +#[derive(Debug, Clone, Copy)] +pub struct NewListener { + pub listener_id: ListenerId, +} + +/// [`FromSwarm`] variant that informs the behaviour +/// that we have started listening on a new multiaddr. +#[derive(Debug, Clone, Copy)] +pub struct NewListenAddr<'a> { + pub listener_id: ListenerId, + pub addr: &'a Multiaddr, +} + +/// [`FromSwarm`] variant that informs the behaviour that a multiaddr +/// we were listening on has expired, +/// which means that we are no longer listening on it. +#[derive(Debug, Clone, Copy)] +pub struct ExpiredListenAddr<'a> { + pub listener_id: ListenerId, + pub addr: &'a Multiaddr, +} + +/// [`FromSwarm`] variant that informs the behaviour that a listener experienced an error. +#[derive(Debug, Clone, Copy)] +pub struct ListenerError<'a> { + pub listener_id: ListenerId, + pub err: &'a (dyn std::error::Error + 'static), +} + +/// [`FromSwarm`] variant that informs the behaviour that a listener closed. +#[derive(Debug, Clone, Copy)] +pub struct ListenerClosed<'a> { + pub listener_id: ListenerId, + pub reason: Result<(), &'a std::io::Error>, +} + +/// [`FromSwarm`] variant that informs the behaviour about a new candidate for an external address for us. +#[derive(Debug, Clone, Copy)] +pub struct NewExternalAddrCandidate<'a> { + pub addr: &'a Multiaddr, +} + +/// [`FromSwarm`] variant that informs the behaviour that an external address was confirmed. +#[derive(Debug, Clone, Copy)] +pub struct ExternalAddrConfirmed<'a> { + pub addr: &'a Multiaddr, +} + +/// [`FromSwarm`] variant that informs the behaviour that an external address was removed. +#[derive(Debug, Clone, Copy)] +pub struct ExternalAddrExpired<'a> { + pub addr: &'a Multiaddr, +} + +/// [`FromSwarm`] variant that informs the behaviour that a new external address for a remote peer was detected. +#[derive(Clone, Copy, Debug)] +pub struct NewExternalAddrOfPeer<'a> { + pub peer_id: PeerId, + pub addr: &'a Multiaddr, +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/either.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/either.rs new file mode 100644 index 00000000000..7a51303e74d --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/either.rs @@ -0,0 +1,168 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::behaviour::{self, NetworkBehaviour, ToSwarm}; +use crate::connection::ConnectionId; +use crate::{ConnectionDenied, THandler, THandlerInEvent, THandlerOutEvent}; +use either::Either; +use libp2p_core::transport::PortUse; +use libp2p_core::{Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use std::{task::Context, task::Poll}; + +/// Implementation of [`NetworkBehaviour`] that can be either of two implementations. +impl NetworkBehaviour for Either +where + L: NetworkBehaviour, + R: NetworkBehaviour, +{ + type ConnectionHandler = Either, THandler>; + type ToSwarm = Either; + + fn handle_pending_inbound_connection( + &mut self, + id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + match self { + Either::Left(a) => a.handle_pending_inbound_connection(id, local_addr, remote_addr), + Either::Right(b) => b.handle_pending_inbound_connection(id, local_addr, remote_addr), + } + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + let handler = match self { + Either::Left(inner) => Either::Left(inner.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + )?), + Either::Right(inner) => Either::Right(inner.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + )?), + }; + + Ok(handler) + } + + fn handle_pending_outbound_connection( + &mut self, + connection_id: ConnectionId, + maybe_peer: Option, + addresses: &[Multiaddr], + effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + let addresses = match self { + Either::Left(inner) => inner.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + )?, + Either::Right(inner) => inner.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + )?, + }; + + Ok(addresses) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result, ConnectionDenied> { + let handler = match self { + Either::Left(inner) => Either::Left(inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + )?), + Either::Right(inner) => Either::Right(inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + )?), + }; + + Ok(handler) + } + + fn on_swarm_event(&mut self, event: behaviour::FromSwarm) { + match self { + Either::Left(b) => b.on_swarm_event(event), + Either::Right(b) => b.on_swarm_event(event), + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + connection_id: ConnectionId, + event: THandlerOutEvent, + ) { + match (self, event) { + (Either::Left(left), Either::Left(event)) => { + left.on_connection_handler_event(peer_id, connection_id, event); + } + (Either::Right(right), Either::Right(event)) => { + right.on_connection_handler_event(peer_id, connection_id, event); + } + _ => unreachable!(), + } + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll>> { + let event = match self { + Either::Left(behaviour) => futures::ready!(behaviour.poll(cx)) + .map_out(Either::Left) + .map_in(Either::Left), + Either::Right(behaviour) => futures::ready!(behaviour.poll(cx)) + .map_out(Either::Right) + .map_in(Either::Right), + }; + + Poll::Ready(event) + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/external_addresses.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/external_addresses.rs new file mode 100644 index 00000000000..579f46fe486 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/external_addresses.rs @@ -0,0 +1,182 @@ +use crate::behaviour::{ExternalAddrConfirmed, ExternalAddrExpired, FromSwarm}; +use libp2p_core::Multiaddr; + +/// The maximum number of local external addresses. When reached any +/// further externally reported addresses are ignored. The behaviour always +/// tracks all its listen addresses. +const MAX_LOCAL_EXTERNAL_ADDRS: usize = 20; + +/// Utility struct for tracking the external addresses of a [`Swarm`](crate::Swarm). +#[derive(Debug, Clone, Default)] +pub struct ExternalAddresses { + addresses: Vec, +} + +impl ExternalAddresses { + /// Returns an [`Iterator`] over all external addresses. + pub fn iter(&self) -> impl ExactSizeIterator { + self.addresses.iter() + } + + pub fn as_slice(&self) -> &[Multiaddr] { + self.addresses.as_slice() + } + + /// Feed a [`FromSwarm`] event to this struct. + /// + /// Returns whether the event changed our set of external addresses. + pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool { + match event { + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { addr }) => { + if let Some(pos) = self + .addresses + .iter() + .position(|candidate| candidate == *addr) + { + // Refresh the existing confirmed address. + self.addresses.remove(pos); + self.push_front(addr); + + tracing::debug!(address=%addr, "Refreshed external address"); + + return false; // No changes to our external addresses. + } + + self.push_front(addr); + + if self.addresses.len() > MAX_LOCAL_EXTERNAL_ADDRS { + let expired = self.addresses.pop().expect("list to be not empty"); + + tracing::debug!( + external_address=%expired, + address_limit=%MAX_LOCAL_EXTERNAL_ADDRS, + "Removing previously confirmed external address because we reached the address limit" + ); + } + + return true; + } + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { + addr: expired_addr, .. + }) => { + let pos = match self + .addresses + .iter() + .position(|candidate| candidate == *expired_addr) + { + None => return false, + Some(p) => p, + }; + + self.addresses.remove(pos); + return true; + } + _ => {} + } + + false + } + + fn push_front(&mut self, addr: &Multiaddr) { + self.addresses.insert(0, addr.clone()); // We have at most `MAX_LOCAL_EXTERNAL_ADDRS` so this isn't very expensive. + } +} + +#[cfg(test)] +mod tests { + use super::*; + use libp2p_core::multiaddr::Protocol; + use once_cell::sync::Lazy; + use rand::Rng; + + #[test] + fn new_external_addr_returns_correct_changed_value() { + let mut addresses = ExternalAddresses::default(); + + let changed = addresses.on_swarm_event(&new_external_addr1()); + assert!(changed); + + let changed = addresses.on_swarm_event(&new_external_addr1()); + assert!(!changed) + } + + #[test] + fn expired_external_addr_returns_correct_changed_value() { + let mut addresses = ExternalAddresses::default(); + addresses.on_swarm_event(&new_external_addr1()); + + let changed = addresses.on_swarm_event(&expired_external_addr1()); + assert!(changed); + + let changed = addresses.on_swarm_event(&expired_external_addr1()); + assert!(!changed) + } + + #[test] + fn more_recent_external_addresses_are_prioritized() { + let mut addresses = ExternalAddresses::default(); + + addresses.on_swarm_event(&new_external_addr1()); + addresses.on_swarm_event(&new_external_addr2()); + + assert_eq!( + addresses.as_slice(), + &[(*MEMORY_ADDR_2000).clone(), (*MEMORY_ADDR_1000).clone()] + ); + } + + #[test] + fn when_pushing_more_than_max_addresses_oldest_is_evicted() { + let mut addresses = ExternalAddresses::default(); + + while addresses.as_slice().len() < MAX_LOCAL_EXTERNAL_ADDRS { + let random_address = + Multiaddr::empty().with(Protocol::Memory(rand::thread_rng().gen_range(0..1000))); + addresses.on_swarm_event(&FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &random_address, + })); + } + + addresses.on_swarm_event(&new_external_addr2()); + + assert_eq!(addresses.as_slice().len(), 20); + assert_eq!(addresses.as_slice()[0], (*MEMORY_ADDR_2000).clone()); + } + + #[test] + fn reporting_existing_external_address_moves_it_to_the_front() { + let mut addresses = ExternalAddresses::default(); + + addresses.on_swarm_event(&new_external_addr1()); + addresses.on_swarm_event(&new_external_addr2()); + addresses.on_swarm_event(&new_external_addr1()); + + assert_eq!( + addresses.as_slice(), + &[(*MEMORY_ADDR_1000).clone(), (*MEMORY_ADDR_2000).clone()] + ); + } + + fn new_external_addr1() -> FromSwarm<'static> { + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &MEMORY_ADDR_1000, + }) + } + + fn new_external_addr2() -> FromSwarm<'static> { + FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &MEMORY_ADDR_2000, + }) + } + + fn expired_external_addr1() -> FromSwarm<'static> { + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { + addr: &MEMORY_ADDR_1000, + }) + } + + static MEMORY_ADDR_1000: Lazy = + Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(1000))); + static MEMORY_ADDR_2000: Lazy = + Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(2000))); +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/listen_addresses.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/listen_addresses.rs new file mode 100644 index 00000000000..6076f5e7923 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/listen_addresses.rs @@ -0,0 +1,78 @@ +use crate::behaviour::{ExpiredListenAddr, FromSwarm, NewListenAddr}; +use libp2p_core::Multiaddr; +use std::collections::HashSet; + +/// Utility struct for tracking the addresses a [`Swarm`](crate::Swarm) is listening on. +#[derive(Debug, Default, Clone)] +pub struct ListenAddresses { + addresses: HashSet, +} + +impl ListenAddresses { + /// Returns an [`Iterator`] over all listen addresses. + pub fn iter(&self) -> impl ExactSizeIterator { + self.addresses.iter() + } + + /// Feed a [`FromSwarm`] event to this struct. + /// + /// Returns whether the event changed our set of listen addresses. + pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool { + match event { + FromSwarm::NewListenAddr(NewListenAddr { addr, .. }) => { + self.addresses.insert((*addr).clone()) + } + FromSwarm::ExpiredListenAddr(ExpiredListenAddr { addr, .. }) => { + self.addresses.remove(addr) + } + _ => false, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use libp2p_core::{multiaddr::Protocol, transport::ListenerId}; + use once_cell::sync::Lazy; + + #[test] + fn new_listen_addr_returns_correct_changed_value() { + let mut addresses = ListenAddresses::default(); + + let changed = addresses.on_swarm_event(&new_listen_addr()); + assert!(changed); + + let changed = addresses.on_swarm_event(&new_listen_addr()); + assert!(!changed) + } + + #[test] + fn expired_listen_addr_returns_correct_changed_value() { + let mut addresses = ListenAddresses::default(); + addresses.on_swarm_event(&new_listen_addr()); + + let changed = addresses.on_swarm_event(&expired_listen_addr()); + assert!(changed); + + let changed = addresses.on_swarm_event(&expired_listen_addr()); + assert!(!changed) + } + + fn new_listen_addr() -> FromSwarm<'static> { + FromSwarm::NewListenAddr(NewListenAddr { + listener_id: ListenerId::next(), + addr: &MEMORY_ADDR, + }) + } + + fn expired_listen_addr() -> FromSwarm<'static> { + FromSwarm::ExpiredListenAddr(ExpiredListenAddr { + listener_id: ListenerId::next(), + addr: &MEMORY_ADDR, + }) + } + + static MEMORY_ADDR: Lazy = + Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(1000))); +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/peer_addresses.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/peer_addresses.rs new file mode 100644 index 00000000000..1eeead56ca1 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/peer_addresses.rs @@ -0,0 +1,338 @@ +use crate::behaviour::FromSwarm; +use crate::{DialError, DialFailure, NewExternalAddrOfPeer}; + +use libp2p_core::Multiaddr; +use libp2p_identity::PeerId; + +use lru::LruCache; + +use std::num::NonZeroUsize; + +/// Struct for tracking peers' external addresses of the [`Swarm`](crate::Swarm). +#[derive(Debug)] +pub struct PeerAddresses(LruCache>); + +impl PeerAddresses { + /// Creates a [`PeerAddresses`] cache with capacity for the given number of peers. + /// + /// For each peer, we will at most store 10 addresses. + pub fn new(number_of_peers: NonZeroUsize) -> Self { + Self(LruCache::new(number_of_peers)) + } + + /// Feed a [`FromSwarm`] event to this struct. + /// + /// Returns whether the event changed peer's known external addresses. + pub fn on_swarm_event(&mut self, event: &FromSwarm) -> bool { + match event { + FromSwarm::NewExternalAddrOfPeer(NewExternalAddrOfPeer { peer_id, addr }) => { + self.add(*peer_id, (*addr).clone()) + } + FromSwarm::DialFailure(DialFailure { + peer_id: Some(peer_id), + error: DialError::Transport(errors), + .. + }) => { + for (addr, _error) in errors { + self.remove(peer_id, addr); + } + true + } + _ => false, + } + } + + /// Adds address to cache. + /// Appends address to the existing set if peer addresses already exist. + /// Creates a new cache entry for peer_id if no addresses are present. + /// Returns true if the newly added address was not previously in the cache. + /// + pub fn add(&mut self, peer: PeerId, address: Multiaddr) -> bool { + match prepare_addr(&peer, &address) { + Ok(address) => { + if let Some(cached) = self.0.get_mut(&peer) { + cached.put(address, ()).is_none() + } else { + let mut set = LruCache::new(NonZeroUsize::new(10).expect("10 > 0")); + set.put(address, ()); + self.0.put(peer, set); + + true + } + } + Err(_) => false, + } + } + + /// Returns peer's external addresses. + pub fn get(&mut self, peer: &PeerId) -> impl Iterator + '_ { + self.0 + .get(peer) + .into_iter() + .flat_map(|c| c.iter().map(|(m, ())| m)) + .cloned() + } + + /// Removes address from peer addresses cache. + /// Returns true if the address was removed. + pub fn remove(&mut self, peer: &PeerId, address: &Multiaddr) -> bool { + match self.0.get_mut(peer) { + Some(addrs) => match prepare_addr(peer, address) { + Ok(address) => addrs.pop(&address).is_some(), + Err(_) => false, + }, + None => false, + } + } +} + +fn prepare_addr(peer: &PeerId, addr: &Multiaddr) -> Result { + addr.clone().with_p2p(*peer) +} + +impl Default for PeerAddresses { + fn default() -> Self { + Self(LruCache::new(NonZeroUsize::new(100).unwrap())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io; + + use crate::ConnectionId; + use libp2p_core::{ + multiaddr::Protocol, + transport::{memory::MemoryTransportError, TransportError}, + }; + + use once_cell::sync::Lazy; + + #[test] + fn new_peer_addr_returns_correct_changed_value() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + + let event = new_external_addr_of_peer1(peer_id); + + let changed = cache.on_swarm_event(&event); + assert!(changed); + + let changed = cache.on_swarm_event(&event); + assert!(!changed); + } + + #[test] + fn new_peer_addr_saves_peer_addrs() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + let event = new_external_addr_of_peer1(peer_id); + + let changed = cache.on_swarm_event(&event); + assert!(changed); + + let addr1 = MEMORY_ADDR_1000.clone().with_p2p(peer_id).unwrap(); + let expected = cache.get(&peer_id).collect::>(); + assert_eq!(expected, vec![addr1]); + + let event = new_external_addr_of_peer2(peer_id); + let changed = cache.on_swarm_event(&event); + + let addr1 = MEMORY_ADDR_1000.clone().with_p2p(peer_id).unwrap(); + let addr2 = MEMORY_ADDR_2000.clone().with_p2p(peer_id).unwrap(); + + let expected_addrs = cache.get(&peer_id).collect::>(); + assert!(expected_addrs.contains(&addr1)); + assert!(expected_addrs.contains(&addr2)); + + let expected = cache.get(&peer_id).collect::>().len(); + assert_eq!(expected, 2); + + assert!(changed); + } + + #[test] + fn existing_addr_is_not_added_to_cache() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + + let event = new_external_addr_of_peer1(peer_id); + + let addr1 = MEMORY_ADDR_1000.clone().with_p2p(peer_id).unwrap(); + let changed = cache.on_swarm_event(&event); + let expected = cache.get(&peer_id).collect::>(); + assert!(changed); + assert_eq!(expected, vec![addr1]); + + let addr1 = MEMORY_ADDR_1000.clone().with_p2p(peer_id).unwrap(); + let changed = cache.on_swarm_event(&event); + let expected = cache.get(&peer_id).collect::>(); + assert!(!changed); + assert_eq!(expected, [addr1]); + } + + #[test] + fn addresses_of_peer_are_removed_when_received_dial_failure() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + + let addr: Multiaddr = "/ip4/127.0.0.1/tcp/8080".parse().unwrap(); + let addr2: Multiaddr = "/ip4/127.0.0.1/tcp/8081".parse().unwrap(); + let addr3: Multiaddr = "/ip4/127.0.0.1/tcp/8082".parse().unwrap(); + + cache.add(peer_id, addr.clone()); + cache.add(peer_id, addr2.clone()); + cache.add(peer_id, addr3.clone()); + + let error = DialError::Transport(prepare_errors(vec![addr, addr3])); + + let event = FromSwarm::DialFailure(DialFailure { + peer_id: Some(peer_id), + error: &error, + connection_id: ConnectionId::new_unchecked(8), + }); + + let changed = cache.on_swarm_event(&event); + + assert!(changed); + + let cached = cache.get(&peer_id).collect::>(); + let expected = prepare_expected_addrs(peer_id, [addr2].into_iter()); + + assert_eq!(cached, expected); + } + + #[test] + fn remove_removes_address_if_present() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + let addr: Multiaddr = "/ip4/127.0.0.1/tcp/8080".parse().unwrap(); + + cache.add(peer_id, addr.clone()); + + assert!(cache.remove(&peer_id, &addr)); + } + + #[test] + fn remove_returns_false_if_address_not_present() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + let addr: Multiaddr = "/ip4/127.0.0.1/tcp/8080".parse().unwrap(); + + assert!(!cache.remove(&peer_id, &addr)); + } + + #[test] + fn remove_returns_false_if_peer_not_present() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + let addr: Multiaddr = "/ip4/127.0.0.1/tcp/8080".parse().unwrap(); + + assert!(!cache.remove(&peer_id, &addr)); + } + + #[test] + fn remove_removes_address_provided_in_param() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + let addr: Multiaddr = "/ip4/127.0.0.1/tcp/8080".parse().unwrap(); + let addr2: Multiaddr = "/ip4/127.0.0.1/tcp/8081".parse().unwrap(); + let addr3: Multiaddr = "/ip4/127.0.0.1/tcp/8082".parse().unwrap(); + + cache.add(peer_id, addr.clone()); + cache.add(peer_id, addr2.clone()); + cache.add(peer_id, addr3.clone()); + + assert!(cache.remove(&peer_id, &addr2)); + + let mut cached = cache.get(&peer_id).collect::>(); + cached.sort(); + + let expected = prepare_expected_addrs(peer_id, [addr, addr3].into_iter()); + + assert_eq!(cached, expected); + } + + #[test] + fn add_adds_new_address_to_cache() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + let addr: Multiaddr = "/ip4/127.0.0.1/tcp/8080".parse().unwrap(); + + assert!(cache.add(peer_id, addr.clone())); + + let mut cached = cache.get(&peer_id).collect::>(); + cached.sort(); + let expected = prepare_expected_addrs(peer_id, [addr].into_iter()); + + assert_eq!(cached, expected); + } + + #[test] + fn add_adds_address_to_cache_to_existing_key() { + let mut cache = PeerAddresses::default(); + let peer_id = PeerId::random(); + let addr: Multiaddr = "/ip4/127.0.0.1/tcp/8080".parse().unwrap(); + let addr2: Multiaddr = "/ip4/127.0.0.1/tcp/8081".parse().unwrap(); + let addr3: Multiaddr = "/ip4/127.0.0.1/tcp/8082".parse().unwrap(); + + assert!(cache.add(peer_id, addr.clone())); + + cache.add(peer_id, addr2.clone()); + cache.add(peer_id, addr3.clone()); + + let expected = prepare_expected_addrs(peer_id, [addr, addr2, addr3].into_iter()); + + let mut cached = cache.get(&peer_id).collect::>(); + cached.sort(); + + assert_eq!(cached, expected); + } + + fn prepare_expected_addrs( + peer_id: PeerId, + addrs: impl Iterator, + ) -> Vec { + let mut addrs = addrs + .filter_map(|a| a.with_p2p(peer_id).ok()) + .collect::>(); + addrs.sort(); + addrs + } + + fn new_external_addr_of_peer1(peer_id: PeerId) -> FromSwarm<'static> { + FromSwarm::NewExternalAddrOfPeer(NewExternalAddrOfPeer { + peer_id, + addr: &MEMORY_ADDR_1000, + }) + } + + fn new_external_addr_of_peer2(peer_id: PeerId) -> FromSwarm<'static> { + FromSwarm::NewExternalAddrOfPeer(NewExternalAddrOfPeer { + peer_id, + addr: &MEMORY_ADDR_2000, + }) + } + + fn prepare_errors(addrs: Vec) -> Vec<(Multiaddr, TransportError)> { + let errors: Vec<(Multiaddr, TransportError)> = addrs + .iter() + .map(|addr| { + ( + addr.clone(), + TransportError::Other(io::Error::new( + io::ErrorKind::Other, + MemoryTransportError::Unreachable, + )), + ) + }) + .collect(); + errors + } + + static MEMORY_ADDR_1000: Lazy = + Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(1000))); + static MEMORY_ADDR_2000: Lazy = + Lazy::new(|| Multiaddr::empty().with(Protocol::Memory(2000))); +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/toggle.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/toggle.rs new file mode 100644 index 00000000000..398c919ae86 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/behaviour/toggle.rs @@ -0,0 +1,377 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::behaviour::FromSwarm; +use crate::connection::ConnectionId; +use crate::handler::{ + AddressChange, ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, DialUpgradeError, + FullyNegotiatedInbound, FullyNegotiatedOutbound, ListenUpgradeError, SubstreamProtocol, +}; +use crate::upgrade::SendWrapper; +use crate::{ + ConnectionDenied, NetworkBehaviour, THandler, THandlerInEvent, THandlerOutEvent, ToSwarm, +}; +use either::Either; +use futures::future; +use libp2p_core::transport::PortUse; +use libp2p_core::{upgrade::DeniedUpgrade, Endpoint, Multiaddr}; +use libp2p_identity::PeerId; +use std::{task::Context, task::Poll}; + +/// Implementation of `NetworkBehaviour` that can be either in the disabled or enabled state. +/// +/// The state can only be chosen at initialization. +pub struct Toggle { + inner: Option, +} + +impl Toggle { + /// Returns `true` if `Toggle` is enabled and `false` if it's disabled. + pub fn is_enabled(&self) -> bool { + self.inner.is_some() + } + + /// Returns a reference to the inner `NetworkBehaviour`. + pub fn as_ref(&self) -> Option<&TBehaviour> { + self.inner.as_ref() + } + + /// Returns a mutable reference to the inner `NetworkBehaviour`. + pub fn as_mut(&mut self) -> Option<&mut TBehaviour> { + self.inner.as_mut() + } +} + +impl From> for Toggle { + fn from(inner: Option) -> Self { + Toggle { inner } + } +} + +impl NetworkBehaviour for Toggle +where + TBehaviour: NetworkBehaviour, +{ + type ConnectionHandler = ToggleConnectionHandler>; + type ToSwarm = TBehaviour::ToSwarm; + + fn handle_pending_inbound_connection( + &mut self, + connection_id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + let inner = match self.inner.as_mut() { + None => return Ok(()), + Some(inner) => inner, + }; + + inner.handle_pending_inbound_connection(connection_id, local_addr, remote_addr)?; + + Ok(()) + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + let inner = match self.inner.as_mut() { + None => return Ok(ToggleConnectionHandler { inner: None }), + Some(inner) => inner, + }; + + let handler = inner.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + )?; + + Ok(ToggleConnectionHandler { + inner: Some(handler), + }) + } + + fn handle_pending_outbound_connection( + &mut self, + connection_id: ConnectionId, + maybe_peer: Option, + addresses: &[Multiaddr], + effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + let inner = match self.inner.as_mut() { + None => return Ok(vec![]), + Some(inner) => inner, + }; + + let addresses = inner.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + )?; + + Ok(addresses) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result, ConnectionDenied> { + let inner = match self.inner.as_mut() { + None => return Ok(ToggleConnectionHandler { inner: None }), + Some(inner) => inner, + }; + + let handler = inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + )?; + + Ok(ToggleConnectionHandler { + inner: Some(handler), + }) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + if let Some(behaviour) = &mut self.inner { + behaviour.on_swarm_event(event); + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + connection_id: ConnectionId, + event: THandlerOutEvent, + ) { + if let Some(behaviour) = &mut self.inner { + behaviour.on_connection_handler_event(peer_id, connection_id, event) + } + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll>> { + if let Some(inner) = self.inner.as_mut() { + inner.poll(cx) + } else { + Poll::Pending + } + } +} + +/// Implementation of [`ConnectionHandler`] that can be in the disabled state. +pub struct ToggleConnectionHandler { + inner: Option, +} + +impl ToggleConnectionHandler +where + TInner: ConnectionHandler, +{ + fn on_fully_negotiated_inbound( + &mut self, + FullyNegotiatedInbound { + protocol: out, + info, + }: FullyNegotiatedInbound< + ::InboundProtocol, + ::InboundOpenInfo, + >, + ) { + let out = match out { + future::Either::Left(out) => out, + future::Either::Right(v) => void::unreachable(v), + }; + + if let Either::Left(info) = info { + self.inner + .as_mut() + .expect("Can't receive an inbound substream if disabled; QED") + .on_connection_event(ConnectionEvent::FullyNegotiatedInbound( + FullyNegotiatedInbound { + protocol: out, + info, + }, + )); + } else { + panic!("Unexpected Either::Right in enabled `on_fully_negotiated_inbound`.") + } + } + + fn on_listen_upgrade_error( + &mut self, + ListenUpgradeError { info, error: err }: ListenUpgradeError< + ::InboundOpenInfo, + ::InboundProtocol, + >, + ) { + let (inner, info) = match (self.inner.as_mut(), info) { + (Some(inner), Either::Left(info)) => (inner, info), + // Ignore listen upgrade errors in disabled state. + (None, Either::Right(())) => return, + (Some(_), Either::Right(())) => panic!( + "Unexpected `Either::Right` inbound info through \ + `on_listen_upgrade_error` in enabled state.", + ), + (None, Either::Left(_)) => panic!( + "Unexpected `Either::Left` inbound info through \ + `on_listen_upgrade_error` in disabled state.", + ), + }; + + let err = match err { + Either::Left(e) => e, + Either::Right(v) => void::unreachable(v), + }; + + inner.on_connection_event(ConnectionEvent::ListenUpgradeError(ListenUpgradeError { + info, + error: err, + })); + } +} + +impl ConnectionHandler for ToggleConnectionHandler +where + TInner: ConnectionHandler, +{ + type FromBehaviour = TInner::FromBehaviour; + type ToBehaviour = TInner::ToBehaviour; + type InboundProtocol = Either, SendWrapper>; + type OutboundProtocol = TInner::OutboundProtocol; + type OutboundOpenInfo = TInner::OutboundOpenInfo; + type InboundOpenInfo = Either; + + fn listen_protocol(&self) -> SubstreamProtocol { + if let Some(inner) = self.inner.as_ref() { + inner + .listen_protocol() + .map_upgrade(|u| Either::Left(SendWrapper(u))) + .map_info(Either::Left) + } else { + SubstreamProtocol::new(Either::Right(SendWrapper(DeniedUpgrade)), Either::Right(())) + } + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + self.inner + .as_mut() + .expect("Can't receive events if disabled; QED") + .on_behaviour_event(event) + } + + fn connection_keep_alive(&self) -> bool { + self.inner + .as_ref() + .map(|h| h.connection_keep_alive()) + .unwrap_or(false) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if let Some(inner) = self.inner.as_mut() { + inner.poll(cx) + } else { + Poll::Pending + } + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(fully_negotiated_inbound) => { + self.on_fully_negotiated_inbound(fully_negotiated_inbound) + } + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol: out, + info, + }) => self + .inner + .as_mut() + .expect("Can't receive an outbound substream if disabled; QED") + .on_connection_event(ConnectionEvent::FullyNegotiatedOutbound( + FullyNegotiatedOutbound { + protocol: out, + info, + }, + )), + ConnectionEvent::AddressChange(address_change) => { + if let Some(inner) = self.inner.as_mut() { + inner.on_connection_event(ConnectionEvent::AddressChange(AddressChange { + new_address: address_change.new_address, + })); + } + } + ConnectionEvent::DialUpgradeError(DialUpgradeError { info, error: err }) => self + .inner + .as_mut() + .expect("Can't receive an outbound substream if disabled; QED") + .on_connection_event(ConnectionEvent::DialUpgradeError(DialUpgradeError { + info, + error: err, + })), + ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { + self.on_listen_upgrade_error(listen_upgrade_error) + } + ConnectionEvent::LocalProtocolsChange(change) => { + if let Some(inner) = self.inner.as_mut() { + inner.on_connection_event(ConnectionEvent::LocalProtocolsChange(change)); + } + } + ConnectionEvent::RemoteProtocolsChange(change) => { + if let Some(inner) = self.inner.as_mut() { + inner.on_connection_event(ConnectionEvent::RemoteProtocolsChange(change)); + } + } + } + } + + fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll> { + let Some(inner) = self.inner.as_mut() else { + return Poll::Ready(None); + }; + + inner.poll_close(cx) + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection.rs new file mode 100644 index 00000000000..603a5b0d7c4 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection.rs @@ -0,0 +1,1387 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +mod error; + +pub(crate) mod pool; +mod supported_protocols; + +pub use error::ConnectionError; +pub(crate) use error::{ + PendingConnectionError, PendingInboundConnectionError, PendingOutboundConnectionError, +}; +use libp2p_core::transport::PortUse; +pub use supported_protocols::SupportedProtocols; + +use crate::handler::{ + AddressChange, ConnectionEvent, ConnectionHandler, DialUpgradeError, FullyNegotiatedInbound, + FullyNegotiatedOutbound, ListenUpgradeError, ProtocolSupport, ProtocolsChange, UpgradeInfoSend, +}; +use crate::stream::ActiveStreamCounter; +use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend}; +use crate::{ + ConnectionHandlerEvent, Stream, StreamProtocol, StreamUpgradeError, SubstreamProtocol, +}; +use futures::future::BoxFuture; +use futures::stream::FuturesUnordered; +use futures::StreamExt; +use futures::{stream, FutureExt}; +use futures_timer::Delay; +use libp2p_core::connection::ConnectedPoint; +use libp2p_core::multiaddr::Multiaddr; +use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerEvent, StreamMuxerExt, SubstreamBox}; +use libp2p_core::upgrade; +use libp2p_core::upgrade::{NegotiationError, ProtocolError}; +use libp2p_core::Endpoint; +use libp2p_identity::PeerId; +use std::collections::{HashMap, HashSet}; +use std::fmt::{Display, Formatter}; +use std::future::Future; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::Waker; +use std::time::Duration; +use std::{fmt, io, mem, pin::Pin, task::Context, task::Poll}; +use web_time::Instant; + +static NEXT_CONNECTION_ID: AtomicUsize = AtomicUsize::new(1); + +/// Connection identifier. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct ConnectionId(usize); + +impl ConnectionId { + /// Creates an _unchecked_ [`ConnectionId`]. + /// + /// [`Swarm`](crate::Swarm) enforces that [`ConnectionId`]s are unique and not reused. + /// This constructor does not, hence the _unchecked_. + /// + /// It is primarily meant for allowing manual tests of [`NetworkBehaviour`](crate::NetworkBehaviour)s. + pub fn new_unchecked(id: usize) -> Self { + Self(id) + } + + /// Returns the next available [`ConnectionId`]. + pub(crate) fn next() -> Self { + Self(NEXT_CONNECTION_ID.fetch_add(1, Ordering::SeqCst)) + } +} + +impl Display for ConnectionId { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Information about a successfully established connection. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct Connected { + /// The connected endpoint, including network address information. + pub(crate) endpoint: ConnectedPoint, + /// Information obtained from the transport. + pub(crate) peer_id: PeerId, +} + +/// Event generated by a [`Connection`]. +#[derive(Debug, Clone)] +pub(crate) enum Event { + /// Event generated by the [`ConnectionHandler`]. + Handler(T), + /// Address of the remote has changed. + AddressChange(Multiaddr), +} + +/// A multiplexed connection to a peer with an associated [`ConnectionHandler`]. +pub(crate) struct Connection +where + THandler: ConnectionHandler, +{ + /// Node that handles the muxing. + muxing: StreamMuxerBox, + /// The underlying handler. + handler: THandler, + /// Futures that upgrade incoming substreams. + negotiating_in: FuturesUnordered< + StreamUpgrade< + THandler::InboundOpenInfo, + ::Output, + ::Error, + >, + >, + /// Futures that upgrade outgoing substreams. + negotiating_out: FuturesUnordered< + StreamUpgrade< + THandler::OutboundOpenInfo, + ::Output, + ::Error, + >, + >, + /// The currently planned connection & handler shutdown. + shutdown: Shutdown, + /// The substream upgrade protocol override, if any. + substream_upgrade_protocol_override: Option, + /// The maximum number of inbound streams concurrently negotiating on a + /// connection. New inbound streams exceeding the limit are dropped and thus + /// reset. + /// + /// Note: This only enforces a limit on the number of concurrently + /// negotiating inbound streams. The total number of inbound streams on a + /// connection is the sum of negotiating and negotiated streams. A limit on + /// the total number of streams can be enforced at the [`StreamMuxerBox`] level. + max_negotiating_inbound_streams: usize, + /// Contains all upgrades that are waiting for a new outbound substream. + /// + /// The upgrade timeout is already ticking here so this may fail in case the remote is not quick + /// enough in providing us with a new stream. + requested_substreams: FuturesUnordered< + SubstreamRequested, + >, + + local_supported_protocols: + HashMap::Info>, bool>, + remote_supported_protocols: HashSet, + protocol_buffer: Vec, + + idle_timeout: Duration, + stream_counter: ActiveStreamCounter, +} + +impl fmt::Debug for Connection +where + THandler: ConnectionHandler + fmt::Debug, + THandler::OutboundOpenInfo: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Connection") + .field("handler", &self.handler) + .finish() + } +} + +impl Unpin for Connection where THandler: ConnectionHandler {} + +impl Connection +where + THandler: ConnectionHandler, +{ + /// Builds a new `Connection` from the given substream multiplexer + /// and connection handler. + pub(crate) fn new( + muxer: StreamMuxerBox, + mut handler: THandler, + substream_upgrade_protocol_override: Option, + max_negotiating_inbound_streams: usize, + idle_timeout: Duration, + ) -> Self { + let initial_protocols = gather_supported_protocols(&handler); + let mut buffer = Vec::new(); + + if !initial_protocols.is_empty() { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange( + ProtocolsChange::from_initial_protocols( + initial_protocols.keys().map(|e| &e.0), + &mut buffer, + ), + )); + } + + Connection { + muxing: muxer, + handler, + negotiating_in: Default::default(), + negotiating_out: Default::default(), + shutdown: Shutdown::None, + substream_upgrade_protocol_override, + max_negotiating_inbound_streams, + requested_substreams: Default::default(), + local_supported_protocols: initial_protocols, + remote_supported_protocols: Default::default(), + protocol_buffer: buffer, + idle_timeout, + stream_counter: ActiveStreamCounter::default(), + } + } + + /// Notifies the connection handler of an event. + pub(crate) fn on_behaviour_event(&mut self, event: THandler::FromBehaviour) { + self.handler.on_behaviour_event(event); + } + + /// Begins an orderly shutdown of the connection, returning a stream of final events and a `Future` that resolves when connection shutdown is complete. + pub(crate) fn close( + self, + ) -> ( + impl futures::Stream, + impl Future>, + ) { + let Connection { + mut handler, + muxing, + .. + } = self; + + ( + stream::poll_fn(move |cx| handler.poll_close(cx)), + muxing.close(), + ) + } + + /// Polls the handler and the substream, forwarding events from the former to the latter and + /// vice versa. + #[tracing::instrument(level = "debug", name = "Connection::poll", skip(self, cx))] + pub(crate) fn poll( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, ConnectionError>> { + let Self { + requested_substreams, + muxing, + handler, + negotiating_out, + negotiating_in, + shutdown, + max_negotiating_inbound_streams, + substream_upgrade_protocol_override, + local_supported_protocols: supported_protocols, + remote_supported_protocols, + protocol_buffer, + idle_timeout, + stream_counter, + .. + } = self.get_mut(); + + loop { + match requested_substreams.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(()))) => continue, + Poll::Ready(Some(Err(info))) => { + handler.on_connection_event(ConnectionEvent::DialUpgradeError( + DialUpgradeError { + info, + error: StreamUpgradeError::Timeout, + }, + )); + continue; + } + Poll::Ready(None) | Poll::Pending => {} + } + + // Poll the [`ConnectionHandler`]. + match handler.poll(cx) { + Poll::Pending => {} + Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { protocol }) => { + let timeout = *protocol.timeout(); + let (upgrade, user_data) = protocol.into_upgrade(); + + requested_substreams.push(SubstreamRequested::new(user_data, timeout, upgrade)); + continue; // Poll handler until exhausted. + } + Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(event)) => { + return Poll::Ready(Ok(Event::Handler(event))); + } + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Added(protocols), + )) => { + if let Some(added) = + ProtocolsChange::add(remote_supported_protocols, protocols, protocol_buffer) + { + handler.on_connection_event(ConnectionEvent::RemoteProtocolsChange(added)); + remote_supported_protocols.extend(protocol_buffer.drain(..)); + } + continue; + } + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Removed(protocols), + )) => { + if let Some(removed) = ProtocolsChange::remove( + remote_supported_protocols, + protocols, + protocol_buffer, + ) { + handler + .on_connection_event(ConnectionEvent::RemoteProtocolsChange(removed)); + } + continue; + } + } + + // In case the [`ConnectionHandler`] can not make any more progress, poll the negotiating outbound streams. + match negotiating_out.poll_next_unpin(cx) { + Poll::Pending | Poll::Ready(None) => {} + Poll::Ready(Some((info, Ok(protocol)))) => { + handler.on_connection_event(ConnectionEvent::FullyNegotiatedOutbound( + FullyNegotiatedOutbound { protocol, info }, + )); + continue; + } + Poll::Ready(Some((info, Err(error)))) => { + handler.on_connection_event(ConnectionEvent::DialUpgradeError( + DialUpgradeError { info, error }, + )); + continue; + } + } + + // In case both the [`ConnectionHandler`] and the negotiating outbound streams can not + // make any more progress, poll the negotiating inbound streams. + match negotiating_in.poll_next_unpin(cx) { + Poll::Pending | Poll::Ready(None) => {} + Poll::Ready(Some((info, Ok(protocol)))) => { + handler.on_connection_event(ConnectionEvent::FullyNegotiatedInbound( + FullyNegotiatedInbound { protocol, info }, + )); + continue; + } + Poll::Ready(Some((info, Err(StreamUpgradeError::Apply(error))))) => { + handler.on_connection_event(ConnectionEvent::ListenUpgradeError( + ListenUpgradeError { info, error }, + )); + continue; + } + Poll::Ready(Some((_, Err(StreamUpgradeError::Io(e))))) => { + tracing::debug!("failed to upgrade inbound stream: {e}"); + continue; + } + Poll::Ready(Some((_, Err(StreamUpgradeError::NegotiationFailed)))) => { + tracing::debug!("no protocol could be agreed upon for inbound stream"); + continue; + } + Poll::Ready(Some((_, Err(StreamUpgradeError::Timeout)))) => { + tracing::debug!("inbound stream upgrade timed out"); + continue; + } + } + + // Check if the connection (and handler) should be shut down. + // As long as we're still negotiating substreams or have any active streams shutdown is always postponed. + if negotiating_in.is_empty() + && negotiating_out.is_empty() + && requested_substreams.is_empty() + && stream_counter.has_no_active_streams() + { + if let Some(new_timeout) = + compute_new_shutdown(handler.connection_keep_alive(), shutdown, *idle_timeout) + { + *shutdown = new_timeout; + } + + match shutdown { + Shutdown::None => {} + Shutdown::Asap => return Poll::Ready(Err(ConnectionError::KeepAliveTimeout)), + Shutdown::Later(delay) => match Future::poll(Pin::new(delay), cx) { + Poll::Ready(_) => { + return Poll::Ready(Err(ConnectionError::KeepAliveTimeout)) + } + Poll::Pending => {} + }, + } + } else { + *shutdown = Shutdown::None; + } + + match muxing.poll_unpin(cx)? { + Poll::Pending => {} + Poll::Ready(StreamMuxerEvent::AddressChange(address)) => { + handler.on_connection_event(ConnectionEvent::AddressChange(AddressChange { + new_address: &address, + })); + return Poll::Ready(Ok(Event::AddressChange(address))); + } + } + + if let Some(requested_substream) = requested_substreams.iter_mut().next() { + match muxing.poll_outbound_unpin(cx)? { + Poll::Pending => {} + Poll::Ready(substream) => { + let (user_data, timeout, upgrade) = requested_substream.extract(); + + negotiating_out.push(StreamUpgrade::new_outbound( + substream, + user_data, + timeout, + upgrade, + *substream_upgrade_protocol_override, + stream_counter.clone(), + )); + + continue; // Go back to the top, handler can potentially make progress again. + } + } + } + + if negotiating_in.len() < *max_negotiating_inbound_streams { + match muxing.poll_inbound_unpin(cx)? { + Poll::Pending => {} + Poll::Ready(substream) => { + let protocol = handler.listen_protocol(); + + negotiating_in.push(StreamUpgrade::new_inbound( + substream, + protocol, + stream_counter.clone(), + )); + + continue; // Go back to the top, handler can potentially make progress again. + } + } + } + + let changes = ProtocolsChange::from_full_sets( + supported_protocols, + handler.listen_protocol().upgrade().protocol_info(), + protocol_buffer, + ); + + if !changes.is_empty() { + for change in changes { + handler.on_connection_event(ConnectionEvent::LocalProtocolsChange(change)); + } + continue; // Go back to the top, handler can potentially make progress again. + } + + return Poll::Pending; // Nothing can make progress, return `Pending`. + } + } + + #[cfg(test)] + fn poll_noop_waker(&mut self) -> Poll, ConnectionError>> { + Pin::new(self).poll(&mut Context::from_waker(futures::task::noop_waker_ref())) + } +} + +fn gather_supported_protocols( + handler: &C, +) -> HashMap::Info>, bool> { + handler + .listen_protocol() + .upgrade() + .protocol_info() + .map(|info| (AsStrHashEq(info), true)) + .collect() +} + +fn compute_new_shutdown( + handler_keep_alive: bool, + current_shutdown: &Shutdown, + idle_timeout: Duration, +) -> Option { + match (current_shutdown, handler_keep_alive) { + (_, false) if idle_timeout == Duration::ZERO => Some(Shutdown::Asap), + (Shutdown::Later(_), false) => None, // Do nothing, i.e. let the shutdown timer continue to tick. + (_, false) => { + let now = Instant::now(); + let safe_keep_alive = checked_add_fraction(now, idle_timeout); + + Some(Shutdown::Later(Delay::new(safe_keep_alive))) + } + (_, true) => Some(Shutdown::None), + } +} + +/// Repeatedly halves and adds the [`Duration`] to the [`Instant`] until [`Instant::checked_add`] succeeds. +/// +/// [`Instant`] depends on the underlying platform and has a limit of which points in time it can represent. +/// The [`Duration`] computed by the this function may not be the longest possible that we can add to `now` but it will work. +fn checked_add_fraction(start: Instant, mut duration: Duration) -> Duration { + while start.checked_add(duration).is_none() { + tracing::debug!(start=?start, duration=?duration, "start + duration cannot be presented, halving duration"); + + duration /= 2; + } + + duration +} + +/// Borrowed information about an incoming connection currently being negotiated. +#[derive(Debug, Copy, Clone)] +pub(crate) struct IncomingInfo<'a> { + /// Local connection address. + pub(crate) local_addr: &'a Multiaddr, + /// Address used to send back data to the remote. + pub(crate) send_back_addr: &'a Multiaddr, +} + +impl<'a> IncomingInfo<'a> { + /// Builds the [`ConnectedPoint`] corresponding to the incoming connection. + pub(crate) fn create_connected_point(&self) -> ConnectedPoint { + ConnectedPoint::Listener { + local_addr: self.local_addr.clone(), + send_back_addr: self.send_back_addr.clone(), + } + } +} + +struct StreamUpgrade { + user_data: Option, + timeout: Delay, + upgrade: BoxFuture<'static, Result>>, +} + +impl StreamUpgrade { + fn new_outbound( + substream: SubstreamBox, + user_data: UserData, + timeout: Delay, + upgrade: Upgrade, + version_override: Option, + counter: ActiveStreamCounter, + ) -> Self + where + Upgrade: OutboundUpgradeSend, + { + let effective_version = match version_override { + Some(version_override) if version_override != upgrade::Version::default() => { + tracing::debug!( + "Substream upgrade protocol override: {:?} -> {:?}", + upgrade::Version::default(), + version_override + ); + + version_override + } + _ => upgrade::Version::default(), + }; + let protocols = upgrade.protocol_info(); + + Self { + user_data: Some(user_data), + timeout, + upgrade: Box::pin(async move { + let (info, stream) = multistream_select::dialer_select_proto( + substream, + protocols, + effective_version, + ) + .await + .map_err(to_stream_upgrade_error)?; + + let output = upgrade + .upgrade_outbound(Stream::new(stream, counter), info) + .await + .map_err(StreamUpgradeError::Apply)?; + + Ok(output) + }), + } + } +} + +impl StreamUpgrade { + fn new_inbound( + substream: SubstreamBox, + protocol: SubstreamProtocol, + counter: ActiveStreamCounter, + ) -> Self + where + Upgrade: InboundUpgradeSend, + { + let timeout = *protocol.timeout(); + let (upgrade, open_info) = protocol.into_upgrade(); + let protocols = upgrade.protocol_info(); + + Self { + user_data: Some(open_info), + timeout: Delay::new(timeout), + upgrade: Box::pin(async move { + let (info, stream) = + multistream_select::listener_select_proto(substream, protocols) + .await + .map_err(to_stream_upgrade_error)?; + + let output = upgrade + .upgrade_inbound(Stream::new(stream, counter), info) + .await + .map_err(StreamUpgradeError::Apply)?; + + Ok(output) + }), + } + } +} + +fn to_stream_upgrade_error(e: NegotiationError) -> StreamUpgradeError { + match e { + NegotiationError::Failed => StreamUpgradeError::NegotiationFailed, + NegotiationError::ProtocolError(ProtocolError::IoError(e)) => StreamUpgradeError::Io(e), + NegotiationError::ProtocolError(other) => { + StreamUpgradeError::Io(io::Error::new(io::ErrorKind::Other, other)) + } + } +} + +impl Unpin for StreamUpgrade {} + +impl Future for StreamUpgrade { + type Output = (UserData, Result>); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.timeout.poll_unpin(cx) { + Poll::Ready(()) => { + return Poll::Ready(( + self.user_data + .take() + .expect("Future not to be polled again once ready."), + Err(StreamUpgradeError::Timeout), + )) + } + + Poll::Pending => {} + } + + let result = futures::ready!(self.upgrade.poll_unpin(cx)); + let user_data = self + .user_data + .take() + .expect("Future not to be polled again once ready."); + + Poll::Ready((user_data, result)) + } +} + +enum SubstreamRequested { + Waiting { + user_data: UserData, + timeout: Delay, + upgrade: Upgrade, + /// A waker to notify our [`FuturesUnordered`] that we have extracted the data. + /// + /// This will ensure that we will get polled again in the next iteration which allows us to + /// resolve with `Ok(())` and be removed from the [`FuturesUnordered`]. + extracted_waker: Option, + }, + Done, +} + +impl SubstreamRequested { + fn new(user_data: UserData, timeout: Duration, upgrade: Upgrade) -> Self { + Self::Waiting { + user_data, + timeout: Delay::new(timeout), + upgrade, + extracted_waker: None, + } + } + + fn extract(&mut self) -> (UserData, Delay, Upgrade) { + match mem::replace(self, Self::Done) { + SubstreamRequested::Waiting { + user_data, + timeout, + upgrade, + extracted_waker: waker, + } => { + if let Some(waker) = waker { + waker.wake(); + } + + (user_data, timeout, upgrade) + } + SubstreamRequested::Done => panic!("cannot extract twice"), + } + } +} + +impl Unpin for SubstreamRequested {} + +impl Future for SubstreamRequested { + type Output = Result<(), UserData>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.get_mut(); + + match mem::replace(this, Self::Done) { + SubstreamRequested::Waiting { + user_data, + upgrade, + mut timeout, + .. + } => match timeout.poll_unpin(cx) { + Poll::Ready(()) => Poll::Ready(Err(user_data)), + Poll::Pending => { + *this = Self::Waiting { + user_data, + upgrade, + timeout, + extracted_waker: Some(cx.waker().clone()), + }; + Poll::Pending + } + }, + SubstreamRequested::Done => Poll::Ready(Ok(())), + } + } +} + +/// The options for a planned connection & handler shutdown. +/// +/// A shutdown is planned anew based on the return value of +/// [`ConnectionHandler::connection_keep_alive`] of the underlying handler +/// after every invocation of [`ConnectionHandler::poll`]. +/// +/// A planned shutdown is always postponed for as long as there are ingoing +/// or outgoing substreams being negotiated, i.e. it is a graceful, "idle" +/// shutdown. +#[derive(Debug)] +enum Shutdown { + /// No shutdown is planned. + None, + /// A shut down is planned as soon as possible. + Asap, + /// A shut down is planned for when a `Delay` has elapsed. + Later(Delay), +} + +// Structure used to avoid allocations when storing the protocols in the `HashMap. +// Instead of allocating a new `String` for the key, +// we use `T::as_ref()` in `Hash`, `Eq` and `PartialEq` requirements. +pub(crate) struct AsStrHashEq(pub(crate) T); + +impl> Eq for AsStrHashEq {} + +impl> PartialEq for AsStrHashEq { + fn eq(&self, other: &Self) -> bool { + self.0.as_ref() == other.0.as_ref() + } +} + +impl> std::hash::Hash for AsStrHashEq { + fn hash(&self, state: &mut H) { + self.0.as_ref().hash(state) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::dummy; + use futures::future; + use futures::AsyncRead; + use futures::AsyncWrite; + use libp2p_core::upgrade::{DeniedUpgrade, InboundUpgrade, OutboundUpgrade, UpgradeInfo}; + use libp2p_core::StreamMuxer; + use quickcheck::*; + use std::sync::{Arc, Weak}; + use std::time::Instant; + use tracing_subscriber::EnvFilter; + use void::Void; + + #[test] + fn max_negotiating_inbound_streams() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + fn prop(max_negotiating_inbound_streams: u8) { + let max_negotiating_inbound_streams: usize = max_negotiating_inbound_streams.into(); + + let alive_substream_counter = Arc::new(()); + let mut connection = Connection::new( + StreamMuxerBox::new(DummyStreamMuxer { + counter: alive_substream_counter.clone(), + }), + MockConnectionHandler::new(Duration::from_secs(10)), + None, + max_negotiating_inbound_streams, + Duration::ZERO, + ); + + let result = connection.poll_noop_waker(); + + assert!(result.is_pending()); + assert_eq!( + Arc::weak_count(&alive_substream_counter), + max_negotiating_inbound_streams, + "Expect no more than the maximum number of allowed streams" + ); + } + + QuickCheck::new().quickcheck(prop as fn(_)); + } + + #[test] + fn outbound_stream_timeout_starts_on_request() { + let upgrade_timeout = Duration::from_secs(1); + let mut connection = Connection::new( + StreamMuxerBox::new(PendingStreamMuxer), + MockConnectionHandler::new(upgrade_timeout), + None, + 2, + Duration::ZERO, + ); + + connection.handler.open_new_outbound(); + let _ = connection.poll_noop_waker(); + + std::thread::sleep(upgrade_timeout + Duration::from_secs(1)); + + let _ = connection.poll_noop_waker(); + + assert!(matches!( + connection.handler.error.unwrap(), + StreamUpgradeError::Timeout + )) + } + + #[test] + fn propagates_changes_to_supported_inbound_protocols() { + let mut connection = Connection::new( + StreamMuxerBox::new(PendingStreamMuxer), + ConfigurableProtocolConnectionHandler::default(), + None, + 0, + Duration::ZERO, + ); + + // First, start listening on a single protocol. + connection.handler.listen_on(&["/foo"]); + let _ = connection.poll_noop_waker(); + + assert_eq!(connection.handler.local_added, vec![vec!["/foo"]]); + assert!(connection.handler.local_removed.is_empty()); + + // Second, listen on two protocols. + connection.handler.listen_on(&["/foo", "/bar"]); + let _ = connection.poll_noop_waker(); + + assert_eq!( + connection.handler.local_added, + vec![vec!["/foo"], vec!["/bar"]], + "expect to only receive an event for the newly added protocols" + ); + assert!(connection.handler.local_removed.is_empty()); + + // Third, stop listening on the first protocol. + connection.handler.listen_on(&["/bar"]); + let _ = connection.poll_noop_waker(); + + assert_eq!( + connection.handler.local_added, + vec![vec!["/foo"], vec!["/bar"]] + ); + assert_eq!(connection.handler.local_removed, vec![vec!["/foo"]]); + } + + #[test] + fn only_propagtes_actual_changes_to_remote_protocols_to_handler() { + let mut connection = Connection::new( + StreamMuxerBox::new(PendingStreamMuxer), + ConfigurableProtocolConnectionHandler::default(), + None, + 0, + Duration::ZERO, + ); + + // First, remote supports a single protocol. + connection.handler.remote_adds_support_for(&["/foo"]); + let _ = connection.poll_noop_waker(); + + assert_eq!(connection.handler.remote_added, vec![vec!["/foo"]]); + assert!(connection.handler.remote_removed.is_empty()); + + // Second, it adds a protocol but also still includes the first one. + connection + .handler + .remote_adds_support_for(&["/foo", "/bar"]); + let _ = connection.poll_noop_waker(); + + assert_eq!( + connection.handler.remote_added, + vec![vec!["/foo"], vec!["/bar"]], + "expect to only receive an event for the newly added protocol" + ); + assert!(connection.handler.remote_removed.is_empty()); + + // Third, stop listening on a protocol it never advertised (we can't control what handlers do so this needs to be handled gracefully). + connection.handler.remote_removes_support_for(&["/baz"]); + let _ = connection.poll_noop_waker(); + + assert_eq!( + connection.handler.remote_added, + vec![vec!["/foo"], vec!["/bar"]] + ); + assert!(&connection.handler.remote_removed.is_empty()); + + // Fourth, stop listening on a protocol that was previously supported + connection.handler.remote_removes_support_for(&["/bar"]); + let _ = connection.poll_noop_waker(); + + assert_eq!( + connection.handler.remote_added, + vec![vec!["/foo"], vec!["/bar"]] + ); + assert_eq!(connection.handler.remote_removed, vec![vec!["/bar"]]); + } + + #[tokio::test] + async fn idle_timeout_with_keep_alive_no() { + let idle_timeout = Duration::from_millis(100); + + let mut connection = Connection::new( + StreamMuxerBox::new(PendingStreamMuxer), + dummy::ConnectionHandler, + None, + 0, + idle_timeout, + ); + + assert!(connection.poll_noop_waker().is_pending()); + + tokio::time::sleep(idle_timeout).await; + + assert!(matches!( + connection.poll_noop_waker(), + Poll::Ready(Err(ConnectionError::KeepAliveTimeout)) + )); + } + + #[test] + fn checked_add_fraction_can_add_u64_max() { + let _ = tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init(); + let start = Instant::now(); + + let duration = checked_add_fraction(start, Duration::from_secs(u64::MAX)); + + assert!(start.checked_add(duration).is_some()) + } + + #[test] + fn compute_new_shutdown_does_not_panic() { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + #[derive(Debug)] + struct ArbitraryShutdown(Shutdown); + + impl Clone for ArbitraryShutdown { + fn clone(&self) -> Self { + let shutdown = match self.0 { + Shutdown::None => Shutdown::None, + Shutdown::Asap => Shutdown::Asap, + Shutdown::Later(_) => Shutdown::Later( + // compute_new_shutdown does not touch the delay. Delay does not + // implement Clone. Thus use a placeholder delay. + Delay::new(Duration::from_secs(1)), + ), + }; + + ArbitraryShutdown(shutdown) + } + } + + impl Arbitrary for ArbitraryShutdown { + fn arbitrary(g: &mut Gen) -> Self { + let shutdown = match g.gen_range(1u8..4) { + 1 => Shutdown::None, + 2 => Shutdown::Asap, + 3 => Shutdown::Later(Delay::new(Duration::from_secs(u32::arbitrary(g) as u64))), + _ => unreachable!(), + }; + + Self(shutdown) + } + } + + fn prop( + handler_keep_alive: bool, + current_shutdown: ArbitraryShutdown, + idle_timeout: Duration, + ) { + compute_new_shutdown(handler_keep_alive, ¤t_shutdown.0, idle_timeout); + } + + QuickCheck::new().quickcheck(prop as fn(_, _, _)); + } + + struct DummyStreamMuxer { + counter: Arc<()>, + } + + impl StreamMuxer for DummyStreamMuxer { + type Substream = PendingSubstream; + type Error = Void; + + fn poll_inbound( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(PendingSubstream { + _weak: Arc::downgrade(&self.counter), + })) + } + + fn poll_outbound( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll> { + Poll::Pending + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll> { + Poll::Pending + } + } + + /// A [`StreamMuxer`] which never returns a stream. + struct PendingStreamMuxer; + + impl StreamMuxer for PendingStreamMuxer { + type Substream = PendingSubstream; + type Error = Void; + + fn poll_inbound( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll> { + Poll::Pending + } + + fn poll_outbound( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll> { + Poll::Pending + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Pending + } + + fn poll( + self: Pin<&mut Self>, + _: &mut Context<'_>, + ) -> Poll> { + Poll::Pending + } + } + + struct PendingSubstream { + _weak: Weak<()>, + } + + impl AsyncRead for PendingSubstream { + fn poll_read( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &mut [u8], + ) -> Poll> { + Poll::Pending + } + } + + impl AsyncWrite for PendingSubstream { + fn poll_write( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + _buf: &[u8], + ) -> Poll> { + Poll::Pending + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Pending + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Pending + } + } + + struct MockConnectionHandler { + outbound_requested: bool, + error: Option>, + upgrade_timeout: Duration, + } + + impl MockConnectionHandler { + fn new(upgrade_timeout: Duration) -> Self { + Self { + outbound_requested: false, + error: None, + upgrade_timeout, + } + } + + fn open_new_outbound(&mut self) { + self.outbound_requested = true; + } + } + + #[derive(Default)] + struct ConfigurableProtocolConnectionHandler { + events: Vec>, + active_protocols: HashSet, + local_added: Vec>, + local_removed: Vec>, + remote_added: Vec>, + remote_removed: Vec>, + } + + impl ConfigurableProtocolConnectionHandler { + fn listen_on(&mut self, protocols: &[&'static str]) { + self.active_protocols = protocols.iter().copied().map(StreamProtocol::new).collect(); + } + + fn remote_adds_support_for(&mut self, protocols: &[&'static str]) { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Added( + protocols.iter().copied().map(StreamProtocol::new).collect(), + ), + )); + } + + fn remote_removes_support_for(&mut self, protocols: &[&'static str]) { + self.events + .push(ConnectionHandlerEvent::ReportRemoteProtocols( + ProtocolSupport::Removed( + protocols.iter().copied().map(StreamProtocol::new).collect(), + ), + )); + } + } + + impl ConnectionHandler for MockConnectionHandler { + type FromBehaviour = Void; + type ToBehaviour = Void; + type InboundProtocol = DeniedUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol( + &self, + ) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()).with_timeout(self.upgrade_timeout) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, + .. + }) => void::unreachable(protocol), + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, + .. + }) => void::unreachable(protocol), + ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { + self.error = Some(error) + } + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} + } + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + void::unreachable(event) + } + + fn connection_keep_alive(&self) -> bool { + true + } + + fn poll( + &mut self, + _: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::ToBehaviour, + >, + > { + if self.outbound_requested { + self.outbound_requested = false; + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(DeniedUpgrade, ()) + .with_timeout(self.upgrade_timeout), + }); + } + + Poll::Pending + } + } + + impl ConnectionHandler for ConfigurableProtocolConnectionHandler { + type FromBehaviour = Void; + type ToBehaviour = Void; + type InboundProtocol = ManyProtocolsUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol( + &self, + ) -> SubstreamProtocol { + SubstreamProtocol::new( + ManyProtocolsUpgrade { + protocols: Vec::from_iter(self.active_protocols.clone()), + }, + (), + ) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Added(added)) => { + self.local_added.push(added.cloned().collect()) + } + ConnectionEvent::LocalProtocolsChange(ProtocolsChange::Removed(removed)) => { + self.local_removed.push(removed.cloned().collect()) + } + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Added(added)) => { + self.remote_added.push(added.cloned().collect()) + } + ConnectionEvent::RemoteProtocolsChange(ProtocolsChange::Removed(removed)) => { + self.remote_removed.push(removed.cloned().collect()) + } + _ => {} + } + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + void::unreachable(event) + } + + fn connection_keep_alive(&self) -> bool { + true + } + + fn poll( + &mut self, + _: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::ToBehaviour, + >, + > { + if let Some(event) = self.events.pop() { + return Poll::Ready(event); + } + + Poll::Pending + } + } + + struct ManyProtocolsUpgrade { + protocols: Vec, + } + + impl UpgradeInfo for ManyProtocolsUpgrade { + type Info = StreamProtocol; + type InfoIter = std::vec::IntoIter; + + fn protocol_info(&self) -> Self::InfoIter { + self.protocols.clone().into_iter() + } + } + + impl InboundUpgrade for ManyProtocolsUpgrade { + type Output = C; + type Error = Void; + type Future = future::Ready>; + + fn upgrade_inbound(self, stream: C, _: Self::Info) -> Self::Future { + future::ready(Ok(stream)) + } + } + + impl OutboundUpgrade for ManyProtocolsUpgrade { + type Output = C; + type Error = Void; + type Future = future::Ready>; + + fn upgrade_outbound(self, stream: C, _: Self::Info) -> Self::Future { + future::ready(Ok(stream)) + } + } +} + +/// The endpoint roles associated with a pending peer-to-peer connection. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum PendingPoint { + /// The socket comes from a dialer. + /// + /// There is no single address associated with the Dialer of a pending + /// connection. Addresses are dialed in parallel. Only once the first dial + /// is successful is the address of the connection known. + Dialer { + /// Same as [`ConnectedPoint::Dialer`] `role_override`. + role_override: Endpoint, + port_use: PortUse, + }, + /// The socket comes from a listener. + Listener { + /// Local connection address. + local_addr: Multiaddr, + /// Address used to send back data to the remote. + send_back_addr: Multiaddr, + }, +} + +impl From for PendingPoint { + fn from(endpoint: ConnectedPoint) -> Self { + match endpoint { + ConnectedPoint::Dialer { + role_override, + port_use, + .. + } => PendingPoint::Dialer { + role_override, + port_use, + }, + ConnectedPoint::Listener { + local_addr, + send_back_addr, + } => PendingPoint::Listener { + local_addr, + send_back_addr, + }, + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/error.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/error.rs new file mode 100644 index 00000000000..33aa81c19a9 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/error.rs @@ -0,0 +1,147 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::transport::TransportError; +use crate::Multiaddr; +use crate::{ConnectedPoint, PeerId}; +use std::{fmt, io}; + +/// Errors that can occur in the context of an established `Connection`. +#[derive(Debug)] +pub enum ConnectionError { + /// An I/O error occurred on the connection. + // TODO: Eventually this should also be a custom error? + IO(io::Error), + + /// The connection keep-alive timeout expired. + KeepAliveTimeout, +} + +impl fmt::Display for ConnectionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ConnectionError::IO(err) => write!(f, "Connection error: I/O error: {err}"), + ConnectionError::KeepAliveTimeout => { + write!(f, "Connection closed due to expired keep-alive timeout.") + } + } + } +} + +impl std::error::Error for ConnectionError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + ConnectionError::IO(err) => Some(err), + ConnectionError::KeepAliveTimeout => None, + } + } +} + +impl From for ConnectionError { + fn from(error: io::Error) -> Self { + ConnectionError::IO(error) + } +} + +/// Errors that can occur in the context of a pending outgoing `Connection`. +/// +/// Note: Addresses for an outbound connection are dialed in parallel. Thus, compared to +/// [`PendingInboundConnectionError`], one or more [`TransportError`]s can occur for a single +/// connection. +pub(crate) type PendingOutboundConnectionError = + PendingConnectionError)>>; + +/// Errors that can occur in the context of a pending incoming `Connection`. +pub(crate) type PendingInboundConnectionError = PendingConnectionError>; + +/// Errors that can occur in the context of a pending `Connection`. +#[derive(Debug)] +pub enum PendingConnectionError { + /// An error occurred while negotiating the transport protocol(s) on a connection. + Transport(TTransErr), + + /// Pending connection attempt has been aborted. + Aborted, + + /// The peer identity obtained on the connection did not + /// match the one that was expected. + WrongPeerId { + obtained: PeerId, + endpoint: ConnectedPoint, + }, + + /// The connection was dropped because it resolved to our own [`PeerId`]. + LocalPeerId { endpoint: ConnectedPoint }, +} + +impl PendingConnectionError { + pub fn map(self, f: impl FnOnce(T) -> U) -> PendingConnectionError { + match self { + PendingConnectionError::Transport(t) => PendingConnectionError::Transport(f(t)), + PendingConnectionError::Aborted => PendingConnectionError::Aborted, + PendingConnectionError::WrongPeerId { obtained, endpoint } => { + PendingConnectionError::WrongPeerId { obtained, endpoint } + } + PendingConnectionError::LocalPeerId { endpoint } => { + PendingConnectionError::LocalPeerId { endpoint } + } + } + } +} + +impl fmt::Display for PendingConnectionError +where + TTransErr: fmt::Display + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PendingConnectionError::Aborted => write!(f, "Pending connection: Aborted."), + PendingConnectionError::Transport(err) => { + write!( + f, + "Pending connection: Transport error on connection: {err}" + ) + } + PendingConnectionError::WrongPeerId { obtained, endpoint } => { + write!( + f, + "Pending connection: Unexpected peer ID {obtained} at {endpoint:?}." + ) + } + PendingConnectionError::LocalPeerId { endpoint } => { + write!(f, "Pending connection: Local peer ID at {endpoint:?}.") + } + } + } +} + +impl std::error::Error for PendingConnectionError +where + TTransErr: std::error::Error + 'static, +{ + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + PendingConnectionError::Transport(_) => None, + PendingConnectionError::WrongPeerId { .. } => None, + PendingConnectionError::LocalPeerId { .. } => None, + PendingConnectionError::Aborted => None, + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool.rs new file mode 100644 index 00000000000..07f6968dec9 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool.rs @@ -0,0 +1,1044 @@ +// Copyright 2021 Protocol Labs. +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +use crate::connection::{Connection, ConnectionId, PendingPoint}; +use crate::{ + connection::{ + Connected, ConnectionError, IncomingInfo, PendingConnectionError, + PendingInboundConnectionError, PendingOutboundConnectionError, + }, + transport::TransportError, + ConnectedPoint, ConnectionHandler, Executor, Multiaddr, PeerId, +}; +use concurrent_dial::ConcurrentDial; +use fnv::FnvHashMap; +use futures::prelude::*; +use futures::stream::SelectAll; +use futures::{ + channel::{mpsc, oneshot}, + future::{poll_fn, BoxFuture, Either}, + ready, + stream::FuturesUnordered, +}; +use libp2p_core::connection::Endpoint; +use libp2p_core::muxing::{StreamMuxerBox, StreamMuxerExt}; +use libp2p_core::transport::PortUse; +use std::task::Waker; +use std::{ + collections::HashMap, + fmt, + num::{NonZeroU8, NonZeroUsize}, + pin::Pin, + task::Context, + task::Poll, +}; +use tracing::Instrument; +use void::Void; +use web_time::{Duration, Instant}; + +mod concurrent_dial; +mod task; + +enum ExecSwitch { + Executor(Box), + LocalSpawn(FuturesUnordered + Send>>>), +} + +impl ExecSwitch { + fn advance_local(&mut self, cx: &mut Context) { + match self { + ExecSwitch::Executor(_) => {} + ExecSwitch::LocalSpawn(local) => { + while let Poll::Ready(Some(())) = local.poll_next_unpin(cx) {} + } + } + } + + #[track_caller] + fn spawn(&mut self, task: impl Future + Send + 'static) { + let task = task.boxed(); + + match self { + Self::Executor(executor) => executor.exec(task), + Self::LocalSpawn(local) => local.push(task), + } + } +} + +/// A connection `Pool` manages a set of connections for each peer. +pub(crate) struct Pool +where + THandler: ConnectionHandler, +{ + local_id: PeerId, + + /// The connection counter(s). + counters: ConnectionCounters, + + /// The managed connections of each peer that are currently considered established. + established: FnvHashMap< + PeerId, + FnvHashMap>, + >, + + /// The pending connections that are currently being negotiated. + pending: HashMap, + + /// Size of the task command buffer (per task). + task_command_buffer_size: usize, + + /// Number of addresses concurrently dialed for a single outbound connection attempt. + dial_concurrency_factor: NonZeroU8, + + /// The configured override for substream protocol upgrades, if any. + substream_upgrade_protocol_override: Option, + + /// The maximum number of inbound streams concurrently negotiating on a connection. + /// + /// See [`Connection::max_negotiating_inbound_streams`]. + max_negotiating_inbound_streams: usize, + + /// How many [`task::EstablishedConnectionEvent`]s can be buffered before the connection is back-pressured. + per_connection_event_buffer_size: usize, + + /// The executor to use for running connection tasks. Can either be a global executor + /// or a local queue. + executor: ExecSwitch, + + /// Sender distributed to pending tasks for reporting events back + /// to the pool. + pending_connection_events_tx: mpsc::Sender, + + /// Receiver for events reported from pending tasks. + pending_connection_events_rx: mpsc::Receiver, + + /// Waker in case we haven't established any connections yet. + no_established_connections_waker: Option, + + /// Receivers for events reported from established connections. + established_connection_events: + SelectAll>>, + + /// Receivers for [`NewConnection`] objects that are dropped. + new_connection_dropped_listeners: FuturesUnordered>, + + /// How long a connection should be kept alive once it starts idling. + idle_connection_timeout: Duration, +} + +#[derive(Debug)] +pub(crate) struct EstablishedConnection { + endpoint: ConnectedPoint, + /// Channel endpoint to send commands to the task. + sender: mpsc::Sender>, +} + +impl EstablishedConnection { + /// (Asynchronously) sends an event to the connection handler. + /// + /// If the handler is not ready to receive the event, either because + /// it is busy or the connection is about to close, the given event + /// is returned with an `Err`. + /// + /// If execution of this method is preceded by successful execution of + /// `poll_ready_notify_handler` without another intervening execution + /// of `notify_handler`, it only fails if the connection is now about + /// to close. + pub(crate) fn notify_handler(&mut self, event: TInEvent) -> Result<(), TInEvent> { + let cmd = task::Command::NotifyHandler(event); + self.sender.try_send(cmd).map_err(|e| match e.into_inner() { + task::Command::NotifyHandler(event) => event, + _ => unreachable!("Expect failed send to return initial event."), + }) + } + + /// Checks if `notify_handler` is ready to accept an event. + /// + /// Returns `Ok(())` if the handler is ready to receive an event via `notify_handler`. + /// + /// Returns `Err(())` if the background task associated with the connection + /// is terminating and the connection is about to close. + pub(crate) fn poll_ready_notify_handler( + &mut self, + cx: &mut Context<'_>, + ) -> Poll> { + self.sender.poll_ready(cx).map_err(|_| ()) + } + + /// Initiates a graceful close of the connection. + /// + /// Has no effect if the connection is already closing. + pub(crate) fn start_close(&mut self) { + // Clone the sender so that we are guaranteed to have + // capacity for the close command (every sender gets a slot). + match self.sender.clone().try_send(task::Command::Close) { + Ok(()) => {} + Err(e) => assert!(e.is_disconnected(), "No capacity for close command."), + }; + } +} + +struct PendingConnection { + /// [`PeerId`] of the remote peer. + peer_id: Option, + endpoint: PendingPoint, + /// When dropped, notifies the task which then knows to terminate. + abort_notifier: Option>, + /// The moment we became aware of this possible connection, useful for timing metrics. + accepted_at: Instant, +} + +impl PendingConnection { + fn is_for_same_remote_as(&self, other: PeerId) -> bool { + self.peer_id.map_or(false, |peer| peer == other) + } + + /// Aborts the connection attempt, closing the connection. + fn abort(&mut self) { + if let Some(notifier) = self.abort_notifier.take() { + drop(notifier); + } + } +} + +impl fmt::Debug for Pool { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_struct("Pool") + .field("counters", &self.counters) + .finish() + } +} + +/// Event that can happen on the `Pool`. +#[derive(Debug)] +pub(crate) enum PoolEvent { + /// A new connection has been established. + ConnectionEstablished { + id: ConnectionId, + peer_id: PeerId, + endpoint: ConnectedPoint, + connection: NewConnection, + /// [`Some`] when the new connection is an outgoing connection. + /// Addresses are dialed in parallel. Contains the addresses and errors + /// of dial attempts that failed before the one successful dial. + concurrent_dial_errors: Option)>>, + /// How long it took to establish this connection. + established_in: std::time::Duration, + }, + + /// An established connection was closed. + /// + /// A connection may close if + /// + /// * it encounters an error, which includes the connection being + /// closed by the remote. In this case `error` is `Some`. + /// * it was actively closed by [`EstablishedConnection::start_close`], + /// i.e. a successful, orderly close. + /// * it was actively closed by [`Pool::disconnect`], i.e. + /// dropped without an orderly close. + /// + ConnectionClosed { + id: ConnectionId, + /// Information about the connection that errored. + connected: Connected, + /// The error that occurred, if any. If `None`, the connection + /// was closed by the local peer. + error: Option, + /// The remaining established connections to the same peer. + remaining_established_connection_ids: Vec, + }, + + /// An outbound connection attempt failed. + PendingOutboundConnectionError { + /// The ID of the failed connection. + id: ConnectionId, + /// The error that occurred. + error: PendingOutboundConnectionError, + /// The (expected) peer of the failed connection. + peer: Option, + }, + + /// An inbound connection attempt failed. + PendingInboundConnectionError { + /// The ID of the failed connection. + id: ConnectionId, + /// Address used to send back data to the remote. + send_back_addr: Multiaddr, + /// Local connection address. + local_addr: Multiaddr, + /// The error that occurred. + error: PendingInboundConnectionError, + }, + + /// A node has produced an event. + ConnectionEvent { + id: ConnectionId, + peer_id: PeerId, + /// The produced event. + event: ToBehaviour, + }, + + /// The connection to a node has changed its address. + AddressChange { + id: ConnectionId, + peer_id: PeerId, + /// The new endpoint. + new_endpoint: ConnectedPoint, + /// The old endpoint. + old_endpoint: ConnectedPoint, + }, +} + +impl Pool +where + THandler: ConnectionHandler, +{ + /// Creates a new empty `Pool`. + pub(crate) fn new(local_id: PeerId, config: PoolConfig) -> Self { + let (pending_connection_events_tx, pending_connection_events_rx) = mpsc::channel(0); + let executor = match config.executor { + Some(exec) => ExecSwitch::Executor(exec), + None => ExecSwitch::LocalSpawn(Default::default()), + }; + Pool { + local_id, + counters: ConnectionCounters::new(), + established: Default::default(), + pending: Default::default(), + task_command_buffer_size: config.task_command_buffer_size, + dial_concurrency_factor: config.dial_concurrency_factor, + substream_upgrade_protocol_override: config.substream_upgrade_protocol_override, + max_negotiating_inbound_streams: config.max_negotiating_inbound_streams, + per_connection_event_buffer_size: config.per_connection_event_buffer_size, + idle_connection_timeout: config.idle_connection_timeout, + executor, + pending_connection_events_tx, + pending_connection_events_rx, + no_established_connections_waker: None, + established_connection_events: Default::default(), + new_connection_dropped_listeners: Default::default(), + } + } + + /// Gets the dedicated connection counters. + pub(crate) fn counters(&self) -> &ConnectionCounters { + &self.counters + } + + /// Gets an established connection from the pool by ID. + pub(crate) fn get_established( + &mut self, + id: ConnectionId, + ) -> Option<&mut EstablishedConnection> { + self.established + .values_mut() + .find_map(|connections| connections.get_mut(&id)) + } + + /// Returns true if we are connected to the given peer. + /// + /// This will return true only after a `NodeReached` event has been produced by `poll()`. + pub(crate) fn is_connected(&self, id: PeerId) -> bool { + self.established.contains_key(&id) + } + + /// Returns the number of connected peers, i.e. those with at least one + /// established connection in the pool. + pub(crate) fn num_peers(&self) -> usize { + self.established.len() + } + + /// (Forcefully) close all connections to the given peer. + /// + /// All connections to the peer, whether pending or established are + /// closed asap and no more events from these connections are emitted + /// by the pool effective immediately. + pub(crate) fn disconnect(&mut self, peer: PeerId) { + if let Some(conns) = self.established.get_mut(&peer) { + for (_, conn) in conns.iter_mut() { + conn.start_close(); + } + } + + for connection in self + .pending + .iter_mut() + .filter_map(|(_, info)| info.is_for_same_remote_as(peer).then_some(info)) + { + connection.abort() + } + } + + /// Returns an iterator over all established connections of `peer`. + pub(crate) fn iter_established_connections_of_peer( + &mut self, + peer: &PeerId, + ) -> impl Iterator + '_ { + match self.established.get(peer) { + Some(conns) => either::Either::Left(conns.iter().map(|(id, _)| *id)), + None => either::Either::Right(std::iter::empty()), + } + } + + /// Checks whether we are currently dialing the given peer. + pub(crate) fn is_dialing(&self, peer: PeerId) -> bool { + self.pending.iter().any(|(_, info)| { + matches!(info.endpoint, PendingPoint::Dialer { .. }) && info.is_for_same_remote_as(peer) + }) + } + + /// Returns an iterator over all connected peers, i.e. those that have + /// at least one established connection in the pool. + pub(crate) fn iter_connected(&self) -> impl Iterator { + self.established.keys() + } + + /// Adds a pending outgoing connection to the pool in the form of a `Future` + /// that establishes and negotiates the connection. + pub(crate) fn add_outgoing( + &mut self, + dials: Vec< + BoxFuture< + 'static, + ( + Multiaddr, + Result<(PeerId, StreamMuxerBox), TransportError>, + ), + >, + >, + peer: Option, + role_override: Endpoint, + port_use: PortUse, + dial_concurrency_factor_override: Option, + connection_id: ConnectionId, + ) { + let concurrency_factor = + dial_concurrency_factor_override.unwrap_or(self.dial_concurrency_factor); + let span = tracing::debug_span!(parent: tracing::Span::none(), "new_outgoing_connection", %concurrency_factor, num_dials=%dials.len(), id = %connection_id); + span.follows_from(tracing::Span::current()); + + let (abort_notifier, abort_receiver) = oneshot::channel(); + + self.executor.spawn( + task::new_for_pending_outgoing_connection( + connection_id, + ConcurrentDial::new(dials, concurrency_factor), + abort_receiver, + self.pending_connection_events_tx.clone(), + ) + .instrument(span), + ); + + let endpoint = PendingPoint::Dialer { + role_override, + port_use, + }; + + self.counters.inc_pending(&endpoint); + self.pending.insert( + connection_id, + PendingConnection { + peer_id: peer, + endpoint, + abort_notifier: Some(abort_notifier), + accepted_at: Instant::now(), + }, + ); + } + + /// Adds a pending incoming connection to the pool in the form of a + /// `Future` that establishes and negotiates the connection. + pub(crate) fn add_incoming( + &mut self, + future: TFut, + info: IncomingInfo<'_>, + connection_id: ConnectionId, + ) where + TFut: Future> + Send + 'static, + { + let endpoint = info.create_connected_point(); + + let (abort_notifier, abort_receiver) = oneshot::channel(); + + let span = tracing::debug_span!(parent: tracing::Span::none(), "new_incoming_connection", remote_addr = %info.send_back_addr, id = %connection_id); + span.follows_from(tracing::Span::current()); + + self.executor.spawn( + task::new_for_pending_incoming_connection( + connection_id, + future, + abort_receiver, + self.pending_connection_events_tx.clone(), + ) + .instrument(span), + ); + + self.counters.inc_pending_incoming(); + self.pending.insert( + connection_id, + PendingConnection { + peer_id: None, + endpoint: endpoint.into(), + abort_notifier: Some(abort_notifier), + accepted_at: Instant::now(), + }, + ); + } + + pub(crate) fn spawn_connection( + &mut self, + id: ConnectionId, + obtained_peer_id: PeerId, + endpoint: &ConnectedPoint, + connection: NewConnection, + handler: THandler, + ) { + let connection = connection.extract(); + let conns = self.established.entry(obtained_peer_id).or_default(); + self.counters.inc_established(endpoint); + + let (command_sender, command_receiver) = mpsc::channel(self.task_command_buffer_size); + let (event_sender, event_receiver) = mpsc::channel(self.per_connection_event_buffer_size); + + conns.insert( + id, + EstablishedConnection { + endpoint: endpoint.clone(), + sender: command_sender, + }, + ); + self.established_connection_events.push(event_receiver); + if let Some(waker) = self.no_established_connections_waker.take() { + waker.wake(); + } + + let connection = Connection::new( + connection, + handler, + self.substream_upgrade_protocol_override, + self.max_negotiating_inbound_streams, + self.idle_connection_timeout, + ); + + let span = tracing::debug_span!(parent: tracing::Span::none(), "new_established_connection", remote_addr = %endpoint.get_remote_address(), %id, peer = %obtained_peer_id); + span.follows_from(tracing::Span::current()); + + self.executor.spawn( + task::new_for_established_connection( + id, + obtained_peer_id, + connection, + command_receiver, + event_sender, + ) + .instrument(span), + ) + } + + /// Polls the connection pool for events. + #[tracing::instrument(level = "debug", name = "Pool::poll", skip(self, cx))] + pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll> + where + THandler: ConnectionHandler + 'static, + ::OutboundOpenInfo: Send, + { + // Poll for events of established connections. + // + // Note that established connections are polled before pending connections, thus + // prioritizing established connections over pending connections. + match self.established_connection_events.poll_next_unpin(cx) { + Poll::Pending => {} + Poll::Ready(None) => { + self.no_established_connections_waker = Some(cx.waker().clone()); + } + + Poll::Ready(Some(task::EstablishedConnectionEvent::Notify { id, peer_id, event })) => { + return Poll::Ready(PoolEvent::ConnectionEvent { peer_id, id, event }); + } + Poll::Ready(Some(task::EstablishedConnectionEvent::AddressChange { + id, + peer_id, + new_address, + })) => { + let connection = self + .established + .get_mut(&peer_id) + .expect("Receive `AddressChange` event for established peer.") + .get_mut(&id) + .expect("Receive `AddressChange` event from established connection"); + let mut new_endpoint = connection.endpoint.clone(); + new_endpoint.set_remote_address(new_address); + let old_endpoint = + std::mem::replace(&mut connection.endpoint, new_endpoint.clone()); + + return Poll::Ready(PoolEvent::AddressChange { + peer_id, + id, + new_endpoint, + old_endpoint, + }); + } + Poll::Ready(Some(task::EstablishedConnectionEvent::Closed { id, peer_id, error })) => { + let connections = self + .established + .get_mut(&peer_id) + .expect("`Closed` event for established connection"); + let EstablishedConnection { endpoint, .. } = + connections.remove(&id).expect("Connection to be present"); + self.counters.dec_established(&endpoint); + let remaining_established_connection_ids: Vec = + connections.keys().cloned().collect(); + if remaining_established_connection_ids.is_empty() { + self.established.remove(&peer_id); + } + return Poll::Ready(PoolEvent::ConnectionClosed { + id, + connected: Connected { endpoint, peer_id }, + error, + remaining_established_connection_ids, + }); + } + } + + // Poll for events of pending connections. + loop { + if let Poll::Ready(Some(result)) = + self.new_connection_dropped_listeners.poll_next_unpin(cx) + { + if let Ok(dropped_connection) = result { + self.executor.spawn(async move { + let _ = dropped_connection.close().await; + }); + } + continue; + } + + let event = match self.pending_connection_events_rx.poll_next_unpin(cx) { + Poll::Ready(Some(event)) => event, + Poll::Pending => break, + Poll::Ready(None) => unreachable!("Pool holds both sender and receiver."), + }; + + match event { + task::PendingConnectionEvent::ConnectionEstablished { + id, + output: (obtained_peer_id, mut muxer), + outgoing, + } => { + let PendingConnection { + peer_id: expected_peer_id, + endpoint, + abort_notifier: _, + accepted_at, + } = self + .pending + .remove(&id) + .expect("Entry in `self.pending` for previously pending connection."); + + self.counters.dec_pending(&endpoint); + + let (endpoint, concurrent_dial_errors) = match (endpoint, outgoing) { + ( + PendingPoint::Dialer { + role_override, + port_use, + }, + Some((address, errors)), + ) => ( + ConnectedPoint::Dialer { + address, + role_override, + port_use, + }, + Some(errors), + ), + ( + PendingPoint::Listener { + local_addr, + send_back_addr, + }, + None, + ) => ( + ConnectedPoint::Listener { + local_addr, + send_back_addr, + }, + None, + ), + (PendingPoint::Dialer { .. }, None) => unreachable!( + "Established incoming connection via pending outgoing connection." + ), + (PendingPoint::Listener { .. }, Some(_)) => unreachable!( + "Established outgoing connection via pending incoming connection." + ), + }; + + let check_peer_id = || { + if let Some(peer) = expected_peer_id { + if peer != obtained_peer_id { + return Err(PendingConnectionError::WrongPeerId { + obtained: obtained_peer_id, + endpoint: endpoint.clone(), + }); + } + } + + if self.local_id == obtained_peer_id { + return Err(PendingConnectionError::LocalPeerId { + endpoint: endpoint.clone(), + }); + } + + Ok(()) + }; + + if let Err(error) = check_peer_id() { + self.executor.spawn(poll_fn(move |cx| { + if let Err(e) = ready!(muxer.poll_close_unpin(cx)) { + tracing::debug!( + peer=%obtained_peer_id, + connection=%id, + "Failed to close connection to peer: {:?}", + e + ); + } + Poll::Ready(()) + })); + + match endpoint { + ConnectedPoint::Dialer { .. } => { + return Poll::Ready(PoolEvent::PendingOutboundConnectionError { + id, + error: error + .map(|t| vec![(endpoint.get_remote_address().clone(), t)]), + peer: expected_peer_id.or(Some(obtained_peer_id)), + }) + } + ConnectedPoint::Listener { + send_back_addr, + local_addr, + } => { + return Poll::Ready(PoolEvent::PendingInboundConnectionError { + id, + error, + send_back_addr, + local_addr, + }) + } + }; + } + + let established_in = accepted_at.elapsed(); + + let (connection, drop_listener) = NewConnection::new(muxer); + self.new_connection_dropped_listeners.push(drop_listener); + + return Poll::Ready(PoolEvent::ConnectionEstablished { + peer_id: obtained_peer_id, + endpoint, + id, + connection, + concurrent_dial_errors, + established_in, + }); + } + task::PendingConnectionEvent::PendingFailed { id, error } => { + if let Some(PendingConnection { + peer_id, + endpoint, + abort_notifier: _, + accepted_at: _, // Ignoring the time it took for the connection to fail. + }) = self.pending.remove(&id) + { + self.counters.dec_pending(&endpoint); + + match (endpoint, error) { + (PendingPoint::Dialer { .. }, Either::Left(error)) => { + return Poll::Ready(PoolEvent::PendingOutboundConnectionError { + id, + error, + peer: peer_id, + }); + } + ( + PendingPoint::Listener { + send_back_addr, + local_addr, + }, + Either::Right(error), + ) => { + return Poll::Ready(PoolEvent::PendingInboundConnectionError { + id, + error, + send_back_addr, + local_addr, + }); + } + (PendingPoint::Dialer { .. }, Either::Right(_)) => { + unreachable!("Inbound error for outbound connection.") + } + (PendingPoint::Listener { .. }, Either::Left(_)) => { + unreachable!("Outbound error for inbound connection.") + } + } + } + } + } + } + + self.executor.advance_local(cx); + + Poll::Pending + } +} + +/// Opaque type for a new connection. +/// +/// This connection has just been established but isn't part of the [`Pool`] yet. +/// It either needs to be spawned via [`Pool::spawn_connection`] or dropped if undesired. +/// +/// On drop, this type send the connection back to the [`Pool`] where it will be gracefully closed. +#[derive(Debug)] +pub(crate) struct NewConnection { + connection: Option, + drop_sender: Option>, +} + +impl NewConnection { + fn new(conn: StreamMuxerBox) -> (Self, oneshot::Receiver) { + let (sender, receiver) = oneshot::channel(); + + ( + Self { + connection: Some(conn), + drop_sender: Some(sender), + }, + receiver, + ) + } + + fn extract(mut self) -> StreamMuxerBox { + self.connection.take().unwrap() + } +} + +impl Drop for NewConnection { + fn drop(&mut self) { + if let Some(connection) = self.connection.take() { + let _ = self + .drop_sender + .take() + .expect("`drop_sender` to always be `Some`") + .send(connection); + } + } +} + +/// Network connection information. +#[derive(Debug, Clone)] +pub struct ConnectionCounters { + /// The current number of incoming connections. + pending_incoming: u32, + /// The current number of outgoing connections. + pending_outgoing: u32, + /// The current number of established inbound connections. + established_incoming: u32, + /// The current number of established outbound connections. + established_outgoing: u32, +} + +impl ConnectionCounters { + fn new() -> Self { + Self { + pending_incoming: 0, + pending_outgoing: 0, + established_incoming: 0, + established_outgoing: 0, + } + } + + /// The total number of connections, both pending and established. + pub fn num_connections(&self) -> u32 { + self.num_pending() + self.num_established() + } + + /// The total number of pending connections, both incoming and outgoing. + pub fn num_pending(&self) -> u32 { + self.pending_incoming + self.pending_outgoing + } + + /// The number of incoming connections being established. + pub fn num_pending_incoming(&self) -> u32 { + self.pending_incoming + } + + /// The number of outgoing connections being established. + pub fn num_pending_outgoing(&self) -> u32 { + self.pending_outgoing + } + + /// The number of established incoming connections. + pub fn num_established_incoming(&self) -> u32 { + self.established_incoming + } + + /// The number of established outgoing connections. + pub fn num_established_outgoing(&self) -> u32 { + self.established_outgoing + } + + /// The total number of established connections. + pub fn num_established(&self) -> u32 { + self.established_outgoing + self.established_incoming + } + + fn inc_pending(&mut self, endpoint: &PendingPoint) { + match endpoint { + PendingPoint::Dialer { .. } => { + self.pending_outgoing += 1; + } + PendingPoint::Listener { .. } => { + self.pending_incoming += 1; + } + } + } + + fn inc_pending_incoming(&mut self) { + self.pending_incoming += 1; + } + + fn dec_pending(&mut self, endpoint: &PendingPoint) { + match endpoint { + PendingPoint::Dialer { .. } => { + self.pending_outgoing -= 1; + } + PendingPoint::Listener { .. } => { + self.pending_incoming -= 1; + } + } + } + + fn inc_established(&mut self, endpoint: &ConnectedPoint) { + match endpoint { + ConnectedPoint::Dialer { .. } => { + self.established_outgoing += 1; + } + ConnectedPoint::Listener { .. } => { + self.established_incoming += 1; + } + } + } + + fn dec_established(&mut self, endpoint: &ConnectedPoint) { + match endpoint { + ConnectedPoint::Dialer { .. } => { + self.established_outgoing -= 1; + } + ConnectedPoint::Listener { .. } => { + self.established_incoming -= 1; + } + } + } +} + +/// Configuration options when creating a [`Pool`]. +/// +/// The default configuration specifies no dedicated task executor, a +/// task event buffer size of 32, and a task command buffer size of 7. +pub(crate) struct PoolConfig { + /// Executor to use to spawn tasks. + pub(crate) executor: Option>, + /// Size of the task command buffer (per task). + pub(crate) task_command_buffer_size: usize, + /// Size of the pending connection task event buffer and the established connection task event + /// buffer. + pub(crate) per_connection_event_buffer_size: usize, + /// Number of addresses concurrently dialed for a single outbound connection attempt. + pub(crate) dial_concurrency_factor: NonZeroU8, + /// How long a connection should be kept alive once it is idling. + pub(crate) idle_connection_timeout: Duration, + /// The configured override for substream protocol upgrades, if any. + substream_upgrade_protocol_override: Option, + + /// The maximum number of inbound streams concurrently negotiating on a connection. + /// + /// See [`Connection::max_negotiating_inbound_streams`]. + max_negotiating_inbound_streams: usize, +} + +impl PoolConfig { + pub(crate) fn new(executor: Option>) -> Self { + Self { + executor, + task_command_buffer_size: 32, + per_connection_event_buffer_size: 7, + dial_concurrency_factor: NonZeroU8::new(8).expect("8 > 0"), + idle_connection_timeout: Duration::ZERO, + substream_upgrade_protocol_override: None, + max_negotiating_inbound_streams: 128, + } + } + + /// Sets the maximum number of events sent to a connection's background task + /// that may be buffered, if the task cannot keep up with their consumption and + /// delivery to the connection handler. + /// + /// When the buffer for a particular connection is full, `notify_handler` will no + /// longer be able to deliver events to the associated [`Connection`], + /// thus exerting back-pressure on the connection and peer API. + pub(crate) fn with_notify_handler_buffer_size(mut self, n: NonZeroUsize) -> Self { + self.task_command_buffer_size = n.get() - 1; + self + } + + /// Sets the maximum number of buffered connection events (beyond a guaranteed + /// buffer of 1 event per connection). + /// + /// When the buffer is full, the background tasks of all connections will stall. + /// In this way, the consumers of network events exert back-pressure on + /// the network connection I/O. + pub(crate) fn with_per_connection_event_buffer_size(mut self, n: usize) -> Self { + self.per_connection_event_buffer_size = n; + self + } + + /// Number of addresses concurrently dialed for a single outbound connection attempt. + pub(crate) fn with_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self { + self.dial_concurrency_factor = factor; + self + } + + /// Configures an override for the substream upgrade protocol to use. + pub(crate) fn with_substream_upgrade_protocol_override( + mut self, + v: libp2p_core::upgrade::Version, + ) -> Self { + self.substream_upgrade_protocol_override = Some(v); + self + } + + /// The maximum number of inbound streams concurrently negotiating on a connection. + /// + /// See [`Connection::max_negotiating_inbound_streams`]. + pub(crate) fn with_max_negotiating_inbound_streams(mut self, v: usize) -> Self { + self.max_negotiating_inbound_streams = v; + self + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool/concurrent_dial.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool/concurrent_dial.rs new file mode 100644 index 00000000000..57e4b078098 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool/concurrent_dial.rs @@ -0,0 +1,103 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::{transport::TransportError, Multiaddr}; +use futures::{ + future::{BoxFuture, Future}, + ready, + stream::{FuturesUnordered, StreamExt}, +}; +use libp2p_core::muxing::StreamMuxerBox; +use libp2p_identity::PeerId; +use std::{ + num::NonZeroU8, + pin::Pin, + task::{Context, Poll}, +}; + +type Dial = BoxFuture< + 'static, + ( + Multiaddr, + Result<(PeerId, StreamMuxerBox), TransportError>, + ), +>; + +pub(crate) struct ConcurrentDial { + dials: FuturesUnordered, + pending_dials: Box + Send>, + errors: Vec<(Multiaddr, TransportError)>, +} + +impl Unpin for ConcurrentDial {} + +impl ConcurrentDial { + pub(crate) fn new(pending_dials: Vec, concurrency_factor: NonZeroU8) -> Self { + let mut pending_dials = pending_dials.into_iter(); + + let dials = FuturesUnordered::new(); + for dial in pending_dials.by_ref() { + dials.push(dial); + if dials.len() == concurrency_factor.get() as usize { + break; + } + } + + Self { + dials, + errors: Default::default(), + pending_dials: Box::new(pending_dials), + } + } +} + +impl Future for ConcurrentDial { + type Output = Result< + // Either one dial succeeded, returning the negotiated [`PeerId`], the address, the + // muxer and the addresses and errors of the dials that failed before. + ( + Multiaddr, + (PeerId, StreamMuxerBox), + Vec<(Multiaddr, TransportError)>, + ), + // Or all dials failed, thus returning the address and error for each dial. + Vec<(Multiaddr, TransportError)>, + >; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + loop { + match ready!(self.dials.poll_next_unpin(cx)) { + Some((addr, Ok(output))) => { + let errors = std::mem::take(&mut self.errors); + return Poll::Ready(Ok((addr, output, errors))); + } + Some((addr, Err(e))) => { + self.errors.push((addr, e)); + if let Some(dial) = self.pending_dials.next() { + self.dials.push(dial) + } + } + None => { + return Poll::Ready(Err(std::mem::take(&mut self.errors))); + } + } + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool/task.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool/task.rs new file mode 100644 index 00000000000..08674fd2ee5 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/pool/task.rs @@ -0,0 +1,265 @@ +// Copyright 2021 Protocol Labs. +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Async functions driving pending and established connections in the form of a task. + +use super::concurrent_dial::ConcurrentDial; +use crate::{ + connection::{ + self, ConnectionError, ConnectionId, PendingInboundConnectionError, + PendingOutboundConnectionError, + }, + transport::TransportError, + ConnectionHandler, Multiaddr, PeerId, +}; +use futures::{ + channel::{mpsc, oneshot}, + future::{poll_fn, Either, Future}, + SinkExt, StreamExt, +}; +use libp2p_core::muxing::StreamMuxerBox; +use std::pin::Pin; +use void::Void; + +/// Commands that can be sent to a task driving an established connection. +#[derive(Debug)] +pub(crate) enum Command { + /// Notify the connection handler of an event. + NotifyHandler(T), + /// Gracefully close the connection (active close) before + /// terminating the task. + Close, +} + +pub(crate) enum PendingConnectionEvent { + ConnectionEstablished { + id: ConnectionId, + output: (PeerId, StreamMuxerBox), + /// [`Some`] when the new connection is an outgoing connection. + /// Addresses are dialed in parallel. Contains the addresses and errors + /// of dial attempts that failed before the one successful dial. + outgoing: Option<(Multiaddr, Vec<(Multiaddr, TransportError)>)>, + }, + /// A pending connection failed. + PendingFailed { + id: ConnectionId, + error: Either, + }, +} + +#[derive(Debug)] +pub(crate) enum EstablishedConnectionEvent { + /// A node we are connected to has changed its address. + AddressChange { + id: ConnectionId, + peer_id: PeerId, + new_address: Multiaddr, + }, + /// Notify the manager of an event from the connection. + Notify { + id: ConnectionId, + peer_id: PeerId, + event: ToBehaviour, + }, + /// A connection closed, possibly due to an error. + /// + /// If `error` is `None`, the connection has completed + /// an active orderly close. + Closed { + id: ConnectionId, + peer_id: PeerId, + error: Option, + }, +} + +pub(crate) async fn new_for_pending_outgoing_connection( + connection_id: ConnectionId, + dial: ConcurrentDial, + abort_receiver: oneshot::Receiver, + mut events: mpsc::Sender, +) { + match futures::future::select(abort_receiver, Box::pin(dial)).await { + Either::Left((Err(oneshot::Canceled), _)) => { + let _ = events + .send(PendingConnectionEvent::PendingFailed { + id: connection_id, + error: Either::Left(PendingOutboundConnectionError::Aborted), + }) + .await; + } + Either::Left((Ok(v), _)) => void::unreachable(v), + Either::Right((Ok((address, output, errors)), _)) => { + let _ = events + .send(PendingConnectionEvent::ConnectionEstablished { + id: connection_id, + output, + outgoing: Some((address, errors)), + }) + .await; + } + Either::Right((Err(e), _)) => { + let _ = events + .send(PendingConnectionEvent::PendingFailed { + id: connection_id, + error: Either::Left(PendingOutboundConnectionError::Transport(e)), + }) + .await; + } + } +} + +pub(crate) async fn new_for_pending_incoming_connection( + connection_id: ConnectionId, + future: TFut, + abort_receiver: oneshot::Receiver, + mut events: mpsc::Sender, +) where + TFut: Future> + Send + 'static, +{ + match futures::future::select(abort_receiver, Box::pin(future)).await { + Either::Left((Err(oneshot::Canceled), _)) => { + let _ = events + .send(PendingConnectionEvent::PendingFailed { + id: connection_id, + error: Either::Right(PendingInboundConnectionError::Aborted), + }) + .await; + } + Either::Left((Ok(v), _)) => void::unreachable(v), + Either::Right((Ok(output), _)) => { + let _ = events + .send(PendingConnectionEvent::ConnectionEstablished { + id: connection_id, + output, + outgoing: None, + }) + .await; + } + Either::Right((Err(e), _)) => { + let _ = events + .send(PendingConnectionEvent::PendingFailed { + id: connection_id, + error: Either::Right(PendingInboundConnectionError::Transport( + TransportError::Other(e), + )), + }) + .await; + } + } +} + +pub(crate) async fn new_for_established_connection( + connection_id: ConnectionId, + peer_id: PeerId, + mut connection: crate::connection::Connection, + mut command_receiver: mpsc::Receiver>, + mut events: mpsc::Sender>, +) where + THandler: ConnectionHandler, +{ + loop { + match futures::future::select( + command_receiver.next(), + poll_fn(|cx| Pin::new(&mut connection).poll(cx)), + ) + .await + { + Either::Left((Some(command), _)) => match command { + Command::NotifyHandler(event) => connection.on_behaviour_event(event), + Command::Close => { + command_receiver.close(); + let (remaining_events, closing_muxer) = connection.close(); + + let _ = events + .send_all(&mut remaining_events.map(|event| { + Ok(EstablishedConnectionEvent::Notify { + id: connection_id, + event, + peer_id, + }) + })) + .await; + + let error = closing_muxer.await.err().map(ConnectionError::IO); + + let _ = events + .send(EstablishedConnectionEvent::Closed { + id: connection_id, + peer_id, + error, + }) + .await; + return; + } + }, + + // The manager has disappeared; abort. + Either::Left((None, _)) => return, + + Either::Right((event, _)) => { + match event { + Ok(connection::Event::Handler(event)) => { + let _ = events + .send(EstablishedConnectionEvent::Notify { + id: connection_id, + peer_id, + event, + }) + .await; + } + Ok(connection::Event::AddressChange(new_address)) => { + let _ = events + .send(EstablishedConnectionEvent::AddressChange { + id: connection_id, + peer_id, + new_address, + }) + .await; + } + Err(error) => { + command_receiver.close(); + let (remaining_events, _closing_muxer) = connection.close(); + + let _ = events + .send_all(&mut remaining_events.map(|event| { + Ok(EstablishedConnectionEvent::Notify { + id: connection_id, + event, + peer_id, + }) + })) + .await; + + // Terminate the task with the error, dropping the connection. + let _ = events + .send(EstablishedConnectionEvent::Closed { + id: connection_id, + peer_id, + error: Some(error), + }) + .await; + return; + } + } + } + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/supported_protocols.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/supported_protocols.rs new file mode 100644 index 00000000000..124ec93d669 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/connection/supported_protocols.rs @@ -0,0 +1,92 @@ +use crate::handler::ProtocolsChange; +use crate::StreamProtocol; +use std::collections::HashSet; + +#[derive(Default, Clone, Debug)] +pub struct SupportedProtocols { + protocols: HashSet, +} + +impl SupportedProtocols { + pub fn on_protocols_change(&mut self, change: ProtocolsChange) -> bool { + match change { + ProtocolsChange::Added(added) => { + let mut changed = false; + + for p in added { + changed |= self.protocols.insert(p.clone()); + } + + changed + } + ProtocolsChange::Removed(removed) => { + let mut changed = false; + + for p in removed { + changed |= self.protocols.remove(p); + } + + changed + } + } + } + + pub fn iter(&self) -> impl Iterator { + self.protocols.iter() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::handler::{ProtocolsAdded, ProtocolsRemoved}; + + #[test] + fn protocols_change_added_returns_correct_changed_value() { + let mut protocols = SupportedProtocols::default(); + + let changed = protocols.on_protocols_change(add_foo()); + assert!(changed); + + let changed = protocols.on_protocols_change(add_foo()); + assert!(!changed); + + let changed = protocols.on_protocols_change(add_foo_bar()); + assert!(changed); + } + + #[test] + fn protocols_change_removed_returns_correct_changed_value() { + let mut protocols = SupportedProtocols::default(); + + let changed = protocols.on_protocols_change(remove_foo()); + assert!(!changed); + + protocols.on_protocols_change(add_foo()); + + let changed = protocols.on_protocols_change(remove_foo()); + assert!(changed); + } + + fn add_foo() -> ProtocolsChange<'static> { + ProtocolsChange::Added(ProtocolsAdded { + protocols: FOO_PROTOCOLS.iter(), + }) + } + + fn add_foo_bar() -> ProtocolsChange<'static> { + ProtocolsChange::Added(ProtocolsAdded { + protocols: FOO_BAR_PROTOCOLS.iter(), + }) + } + + fn remove_foo() -> ProtocolsChange<'static> { + ProtocolsChange::Removed(ProtocolsRemoved { + protocols: FOO_PROTOCOLS.iter(), + }) + } + + static FOO_PROTOCOLS: &[StreamProtocol] = &[StreamProtocol::new("/foo")]; + static FOO_BAR_PROTOCOLS: &[StreamProtocol] = + &[StreamProtocol::new("/foo"), StreamProtocol::new("/bar")]; +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/dial_opts.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/dial_opts.rs new file mode 100644 index 00000000000..4f5b621327c --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/dial_opts.rs @@ -0,0 +1,348 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::ConnectionId; +use libp2p_core::connection::Endpoint; +use libp2p_core::multiaddr::Protocol; +use libp2p_core::transport::PortUse; +use libp2p_core::Multiaddr; +use libp2p_identity::PeerId; +use std::num::NonZeroU8; + +macro_rules! fn_override_role { + () => { + /// Override role of local node on connection. I.e. execute the dial _as a + /// listener_. + /// + /// See + /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer) + /// for details. + pub fn override_role(mut self) -> Self { + self.role_override = Endpoint::Listener; + self + } + }; +} + +macro_rules! fn_allocate_new_port { + () => { + /// Enforce the allocation of a new port. + /// Default behaviour is best effort reuse of existing ports. If there is no existing + /// fitting listener, a new port is allocated. + pub fn allocate_new_port(mut self) -> Self { + self.port_use = PortUse::New; + self + } + }; +} + +/// Options to configure a dial to a known or unknown peer. +/// +/// Used in [`Swarm::dial`](crate::Swarm::dial) and +/// [`ToSwarm::Dial`](crate::behaviour::ToSwarm::Dial). +/// +/// To construct use either of: +/// +/// - [`DialOpts::peer_id`] dialing a known peer +/// +/// - [`DialOpts::unknown_peer_id`] dialing an unknown peer +#[derive(Debug)] +pub struct DialOpts { + peer_id: Option, + condition: PeerCondition, + addresses: Vec, + extend_addresses_through_behaviour: bool, + role_override: Endpoint, + dial_concurrency_factor_override: Option, + connection_id: ConnectionId, + port_use: PortUse, +} + +impl DialOpts { + /// Dial a known peer. + /// + /// ``` + /// # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition}; + /// # use libp2p_identity::PeerId; + /// DialOpts::peer_id(PeerId::random()) + /// .condition(PeerCondition::Disconnected) + /// .addresses(vec!["/ip6/::1/tcp/12345".parse().unwrap()]) + /// .extend_addresses_through_behaviour() + /// .build(); + /// ``` + pub fn peer_id(peer_id: PeerId) -> WithPeerId { + WithPeerId { + peer_id, + condition: Default::default(), + role_override: Endpoint::Dialer, + dial_concurrency_factor_override: Default::default(), + port_use: PortUse::Reuse, + } + } + + /// Dial an unknown peer. + /// + /// ``` + /// # use libp2p_swarm::dial_opts::DialOpts; + /// DialOpts::unknown_peer_id() + /// .address("/ip6/::1/tcp/12345".parse().unwrap()) + /// .build(); + /// ``` + pub fn unknown_peer_id() -> WithoutPeerId { + WithoutPeerId {} + } + + /// Retrieves the [`PeerId`] from the [`DialOpts`] if specified or otherwise tries to extract it + /// from the multihash in the `/p2p` part of the address, if present. + pub fn get_peer_id(&self) -> Option { + if let Some(peer_id) = self.peer_id { + return Some(peer_id); + } + + let first_address = self.addresses.first()?; + let last_protocol = first_address.iter().last()?; + + if let Protocol::P2p(p) = last_protocol { + return Some(p); + } + + None + } + + /// Get the [`ConnectionId`] of this dial attempt. + /// + /// All future events of this dial will be associated with this ID. + /// See [`DialFailure`](crate::DialFailure) and [`ConnectionEstablished`](crate::behaviour::ConnectionEstablished). + pub fn connection_id(&self) -> ConnectionId { + self.connection_id + } + + pub(crate) fn get_addresses(&self) -> Vec { + self.addresses.clone() + } + + pub(crate) fn extend_addresses_through_behaviour(&self) -> bool { + self.extend_addresses_through_behaviour + } + + pub(crate) fn peer_condition(&self) -> PeerCondition { + self.condition + } + + pub(crate) fn dial_concurrency_override(&self) -> Option { + self.dial_concurrency_factor_override + } + + pub(crate) fn role_override(&self) -> Endpoint { + self.role_override + } + + pub(crate) fn port_use(&self) -> PortUse { + self.port_use + } +} + +impl From for DialOpts { + fn from(address: Multiaddr) -> Self { + DialOpts::unknown_peer_id().address(address).build() + } +} + +impl From for DialOpts { + fn from(peer_id: PeerId) -> Self { + DialOpts::peer_id(peer_id).build() + } +} + +#[derive(Debug)] +pub struct WithPeerId { + peer_id: PeerId, + condition: PeerCondition, + role_override: Endpoint, + dial_concurrency_factor_override: Option, + port_use: PortUse, +} + +impl WithPeerId { + /// Specify a [`PeerCondition`] for the dial. + pub fn condition(mut self, condition: PeerCondition) -> Self { + self.condition = condition; + self + } + + /// Override + /// Number of addresses concurrently dialed for a single outbound connection attempt. + pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self { + self.dial_concurrency_factor_override = Some(factor); + self + } + + /// Specify a set of addresses to be used to dial the known peer. + pub fn addresses(self, addresses: Vec) -> WithPeerIdWithAddresses { + WithPeerIdWithAddresses { + peer_id: self.peer_id, + condition: self.condition, + addresses, + extend_addresses_through_behaviour: false, + role_override: self.role_override, + dial_concurrency_factor_override: self.dial_concurrency_factor_override, + port_use: self.port_use, + } + } + + fn_override_role!(); + fn_allocate_new_port!(); + + /// Build the final [`DialOpts`]. + pub fn build(self) -> DialOpts { + DialOpts { + peer_id: Some(self.peer_id), + condition: self.condition, + addresses: vec![], + extend_addresses_through_behaviour: true, + role_override: self.role_override, + dial_concurrency_factor_override: self.dial_concurrency_factor_override, + connection_id: ConnectionId::next(), + port_use: self.port_use, + } + } +} + +#[derive(Debug)] +pub struct WithPeerIdWithAddresses { + peer_id: PeerId, + condition: PeerCondition, + addresses: Vec, + extend_addresses_through_behaviour: bool, + role_override: Endpoint, + dial_concurrency_factor_override: Option, + port_use: PortUse, +} + +impl WithPeerIdWithAddresses { + /// Specify a [`PeerCondition`] for the dial. + pub fn condition(mut self, condition: PeerCondition) -> Self { + self.condition = condition; + self + } + + /// In addition to the provided addresses, extend the set via + /// [`NetworkBehaviour::handle_pending_outbound_connection`](crate::behaviour::NetworkBehaviour::handle_pending_outbound_connection). + pub fn extend_addresses_through_behaviour(mut self) -> Self { + self.extend_addresses_through_behaviour = true; + self + } + + fn_override_role!(); + fn_allocate_new_port!(); + + /// Override + /// Number of addresses concurrently dialed for a single outbound connection attempt. + pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self { + self.dial_concurrency_factor_override = Some(factor); + self + } + + /// Build the final [`DialOpts`]. + pub fn build(self) -> DialOpts { + DialOpts { + peer_id: Some(self.peer_id), + condition: self.condition, + addresses: self.addresses, + extend_addresses_through_behaviour: self.extend_addresses_through_behaviour, + role_override: self.role_override, + dial_concurrency_factor_override: self.dial_concurrency_factor_override, + connection_id: ConnectionId::next(), + port_use: self.port_use, + } + } +} + +#[derive(Debug)] +pub struct WithoutPeerId {} + +impl WithoutPeerId { + /// Specify a single address to dial the unknown peer. + pub fn address(self, address: Multiaddr) -> WithoutPeerIdWithAddress { + WithoutPeerIdWithAddress { + address, + role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, + } + } +} + +#[derive(Debug)] +pub struct WithoutPeerIdWithAddress { + address: Multiaddr, + role_override: Endpoint, + port_use: PortUse, +} + +impl WithoutPeerIdWithAddress { + fn_override_role!(); + fn_allocate_new_port!(); + + /// Build the final [`DialOpts`]. + pub fn build(self) -> DialOpts { + DialOpts { + peer_id: None, + condition: PeerCondition::Always, + addresses: vec![self.address], + extend_addresses_through_behaviour: false, + role_override: self.role_override, + dial_concurrency_factor_override: None, + connection_id: ConnectionId::next(), + port_use: self.port_use, + } + } +} + +/// The available conditions under which a new dialing attempt to +/// a known peer is initiated. +/// +/// ``` +/// # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition}; +/// # use libp2p_identity::PeerId; +/// # +/// DialOpts::peer_id(PeerId::random()) +/// .condition(PeerCondition::Disconnected) +/// .build(); +/// ``` +#[derive(Debug, Copy, Clone, Default)] +pub enum PeerCondition { + /// A new dialing attempt is initiated _only if_ the peer is currently + /// considered disconnected, i.e. there is no established connection. + Disconnected, + /// A new dialing attempt is initiated _only if_ there is currently + /// no ongoing dialing attempt, i.e. the peer is either considered + /// disconnected or connected but without an ongoing dialing attempt. + NotDialing, + /// A combination of [`Disconnected`](PeerCondition::Disconnected) and + /// [`NotDialing`](PeerCondition::NotDialing). A new dialing attempt is + /// iniated _only if_ the peer is both considered disconnected and there + /// is currently no ongoing dialing attempt. + #[default] + DisconnectedAndNotDialing, + /// A new dialing attempt is always initiated, only subject to the + /// configured connection limits. + Always, +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/dummy.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/dummy.rs new file mode 100644 index 00000000000..6e1b4d56eb9 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/dummy.rs @@ -0,0 +1,120 @@ +use crate::behaviour::{FromSwarm, NetworkBehaviour, ToSwarm}; +use crate::connection::ConnectionId; +use crate::handler::{ + ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, +}; +use crate::{ + ConnectionDenied, ConnectionHandlerEvent, StreamUpgradeError, SubstreamProtocol, THandler, + THandlerInEvent, THandlerOutEvent, +}; +use libp2p_core::transport::PortUse; +use libp2p_core::upgrade::DeniedUpgrade; +use libp2p_core::Endpoint; +use libp2p_core::Multiaddr; +use libp2p_identity::PeerId; +use std::task::{Context, Poll}; +use void::Void; + +/// Implementation of [`NetworkBehaviour`] that doesn't do anything. +pub struct Behaviour; + +impl NetworkBehaviour for Behaviour { + type ConnectionHandler = ConnectionHandler; + type ToSwarm = Void; + + fn handle_established_inbound_connection( + &mut self, + _: ConnectionId, + _: PeerId, + _: &Multiaddr, + _: &Multiaddr, + ) -> Result, ConnectionDenied> { + Ok(ConnectionHandler) + } + + fn handle_established_outbound_connection( + &mut self, + _: ConnectionId, + _: PeerId, + _: &Multiaddr, + _: Endpoint, + _: PortUse, + ) -> Result, ConnectionDenied> { + Ok(ConnectionHandler) + } + + fn on_connection_handler_event( + &mut self, + _: PeerId, + _: ConnectionId, + event: THandlerOutEvent, + ) { + void::unreachable(event) + } + + fn poll(&mut self, _: &mut Context<'_>) -> Poll>> { + Poll::Pending + } + + fn on_swarm_event(&mut self, _event: FromSwarm) {} +} + +/// An implementation of [`ConnectionHandler`] that neither handles any protocols nor does it keep the connection alive. +#[derive(Clone)] +pub struct ConnectionHandler; + +impl crate::handler::ConnectionHandler for ConnectionHandler { + type FromBehaviour = Void; + type ToBehaviour = Void; + type InboundProtocol = DeniedUpgrade; + type OutboundProtocol = DeniedUpgrade; + type InboundOpenInfo = (); + type OutboundOpenInfo = Void; + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + void::unreachable(event) + } + + fn poll( + &mut self, + _: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + Poll::Pending + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => void::unreachable(protocol), + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, .. + }) => void::unreachable(protocol), + ConnectionEvent::DialUpgradeError(DialUpgradeError { info: _, error }) => match error { + StreamUpgradeError::Timeout => unreachable!(), + StreamUpgradeError::Apply(e) => void::unreachable(e), + StreamUpgradeError::NegotiationFailed | StreamUpgradeError::Io(_) => { + unreachable!("Denied upgrade does not support any protocols") + } + }, + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/executor.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/executor.rs new file mode 100644 index 00000000000..a2abbbde6ef --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/executor.rs @@ -0,0 +1,70 @@ +//! Provides executors for spawning background tasks. +use futures::executor::ThreadPool; +use std::{future::Future, pin::Pin}; + +/// Implemented on objects that can run a `Future` in the background. +/// +/// > **Note**: While it may be tempting to implement this trait on types such as +/// > [`futures::stream::FuturesUnordered`], please note that passing an `Executor` is +/// > optional, and that `FuturesUnordered` (or a similar struct) will automatically +/// > be used as fallback by libp2p. The `Executor` trait should therefore only be +/// > about running `Future`s on a separate task. +pub trait Executor { + /// Run the given future in the background until it ends. + #[track_caller] + fn exec(&self, future: Pin + Send>>); +} + +impl + Send>>)> Executor for F { + fn exec(&self, f: Pin + Send>>) { + self(f) + } +} + +impl Executor for ThreadPool { + fn exec(&self, future: Pin + Send>>) { + self.spawn_ok(future) + } +} + +#[cfg(all( + feature = "tokio", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) +))] +#[derive(Default, Debug, Clone, Copy)] +pub(crate) struct TokioExecutor; +#[cfg(all( + feature = "tokio", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) +))] +impl Executor for TokioExecutor { + fn exec(&self, future: Pin + Send>>) { + tokio::spawn(future); + } +} + +#[cfg(all( + feature = "async-std", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) +))] +#[derive(Default, Debug, Clone, Copy)] +pub(crate) struct AsyncStdExecutor; +#[cfg(all( + feature = "async-std", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) +))] +impl Executor for AsyncStdExecutor { + fn exec(&self, future: Pin + Send>>) { + async_std::task::spawn(future); + } +} + +#[cfg(feature = "wasm-bindgen")] +#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct WasmBindgenExecutor; +#[cfg(feature = "wasm-bindgen")] +impl Executor for WasmBindgenExecutor { + fn exec(&self, future: Pin + Send>>) { + wasm_bindgen_futures::spawn_local(future) + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler.rs new file mode 100644 index 00000000000..610b95b8cf1 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler.rs @@ -0,0 +1,896 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Once a connection to a remote peer is established, a [`ConnectionHandler`] negotiates +//! and handles one or more specific protocols on the connection. +//! +//! Protocols are negotiated and used on individual substreams of the connection. Thus a +//! [`ConnectionHandler`] defines the inbound and outbound upgrades to apply when creating a new +//! inbound or outbound substream, respectively, and is notified by a [`Swarm`](crate::Swarm) when +//! these upgrades have been successfully applied, including the final output of the upgrade. A +//! [`ConnectionHandler`] can then continue communicating with the peer over the substream using the +//! negotiated protocol(s). +//! +//! Two [`ConnectionHandler`]s can be composed with [`ConnectionHandler::select()`] +//! in order to build a new handler supporting the combined set of protocols, +//! with methods being dispatched to the appropriate handler according to the +//! used protocol(s) determined by the associated types of the handlers. +//! +//! > **Note**: A [`ConnectionHandler`] handles one or more protocols in the context of a single +//! > connection with a remote. In order to handle a protocol that requires knowledge of +//! > the network as a whole, see the +//! > [`NetworkBehaviour`](crate::behaviour::NetworkBehaviour) trait. + +pub mod either; +mod map_in; +mod map_out; +pub mod multi; +mod one_shot; +mod pending; +mod select; + +use crate::connection::AsStrHashEq; +pub use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, SendWrapper, UpgradeInfoSend}; +pub use map_in::MapInEvent; +pub use map_out::MapOutEvent; +pub use one_shot::{OneShotHandler, OneShotHandlerConfig}; +pub use pending::PendingConnectionHandler; +pub use select::ConnectionHandlerSelect; +use smallvec::SmallVec; + +use crate::StreamProtocol; +use core::slice; +use libp2p_core::Multiaddr; +use std::collections::{HashMap, HashSet}; +use std::{error, fmt, io, task::Context, task::Poll, time::Duration}; + +/// A handler for a set of protocols used on a connection with a remote. +/// +/// This trait should be implemented for a type that maintains the state for +/// the execution of a specific protocol with a remote. +/// +/// # Handling a protocol +/// +/// Communication with a remote over a set of protocols is initiated in one of two ways: +/// +/// 1. Dialing by initiating a new outbound substream. In order to do so, +/// [`ConnectionHandler::poll()`] must return an [`ConnectionHandlerEvent::OutboundSubstreamRequest`], +/// providing an instance of [`libp2p_core::upgrade::OutboundUpgrade`] that is used to negotiate the +/// protocol(s). Upon success, [`ConnectionHandler::on_connection_event`] is called with +/// [`ConnectionEvent::FullyNegotiatedOutbound`] translating the final output of the upgrade. +/// +/// 2. Listening by accepting a new inbound substream. When a new inbound substream +/// is created on a connection, [`ConnectionHandler::listen_protocol`] is called +/// to obtain an instance of [`libp2p_core::upgrade::InboundUpgrade`] that is used to +/// negotiate the protocol(s). Upon success, +/// [`ConnectionHandler::on_connection_event`] is called with [`ConnectionEvent::FullyNegotiatedInbound`] +/// translating the final output of the upgrade. +/// +/// +/// # Connection Keep-Alive +/// +/// A [`ConnectionHandler`] can influence the lifetime of the underlying connection +/// through [`ConnectionHandler::connection_keep_alive`]. That is, the protocol +/// implemented by the handler can include conditions for terminating the connection. +/// The lifetime of successfully negotiated substreams is fully controlled by the handler. +/// +/// Implementors of this trait should keep in mind that the connection can be closed at any time. +/// When a connection is closed gracefully, the substreams used by the handler may still +/// continue reading data until the remote closes its side of the connection. +pub trait ConnectionHandler: Send + 'static { + /// A type representing the message(s) a [`NetworkBehaviour`](crate::behaviour::NetworkBehaviour) can send to a [`ConnectionHandler`] via [`ToSwarm::NotifyHandler`](crate::behaviour::ToSwarm::NotifyHandler) + type FromBehaviour: fmt::Debug + Send + 'static; + /// A type representing message(s) a [`ConnectionHandler`] can send to a [`NetworkBehaviour`](crate::behaviour::NetworkBehaviour) via [`ConnectionHandlerEvent::NotifyBehaviour`]. + type ToBehaviour: fmt::Debug + Send + 'static; + /// The inbound upgrade for the protocol(s) used by the handler. + type InboundProtocol: InboundUpgradeSend; + /// The outbound upgrade for the protocol(s) used by the handler. + type OutboundProtocol: OutboundUpgradeSend; + /// The type of additional information returned from `listen_protocol`. + type InboundOpenInfo: Send + 'static; + /// The type of additional information passed to an `OutboundSubstreamRequest`. + type OutboundOpenInfo: Send + 'static; + + /// The [`InboundUpgrade`](libp2p_core::upgrade::InboundUpgrade) to apply on inbound + /// substreams to negotiate the desired protocols. + /// + /// > **Note**: The returned `InboundUpgrade` should always accept all the generally + /// > supported protocols, even if in a specific context a particular one is + /// > not supported, (eg. when only allowing one substream at a time for a protocol). + /// > This allows a remote to put the list of supported protocols in a cache. + fn listen_protocol(&self) -> SubstreamProtocol; + + /// Returns whether the connection should be kept alive. + /// + /// ## Keep alive algorithm + /// + /// A connection is always kept alive: + /// + /// - Whilst a [`ConnectionHandler`] returns [`Poll::Ready`]. + /// - We are negotiating inbound or outbound streams. + /// - There are active [`Stream`](crate::Stream)s on the connection. + /// + /// The combination of the above means that _most_ protocols will not need to override this method. + /// This method is only invoked when all of the above are `false`, i.e. when the connection is entirely idle. + /// + /// ## Exceptions + /// + /// - Protocols like [circuit-relay v2](https://github.com/libp2p/specs/blob/master/relay/circuit-v2.md) need to keep a connection alive beyond these circumstances and can thus override this method. + /// - Protocols like [ping](https://github.com/libp2p/specs/blob/master/ping/ping.md) **don't** want to keep a connection alive despite an active streams. + /// + /// In that case, protocol authors can use [`Stream::ignore_for_keep_alive`](crate::Stream::ignore_for_keep_alive) to opt-out a particular stream from the keep-alive algorithm. + fn connection_keep_alive(&self) -> bool { + false + } + + /// Should behave like `Stream::poll()`. + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + >; + + /// Gracefully close the [`ConnectionHandler`]. + /// + /// The contract for this function is equivalent to a [`Stream`](futures::Stream). + /// When a connection is being shut down, we will first poll this function to completion. + /// Following that, the physical connection will be shut down. + /// + /// This is also called when the shutdown was initiated due to an error on the connection. + /// We therefore cannot guarantee that performing IO within here will succeed. + /// + /// To signal completion, [`Poll::Ready(None)`] should be returned. + /// + /// Implementations MUST have a [`fuse`](futures::StreamExt::fuse)-like behaviour. + /// That is, [`Poll::Ready(None)`] MUST be returned on repeated calls to [`ConnectionHandler::poll_close`]. + fn poll_close(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(None) + } + + /// Adds a closure that turns the input event into something else. + fn map_in_event(self, map: TMap) -> MapInEvent + where + Self: Sized, + TMap: Fn(&TNewIn) -> Option<&Self::FromBehaviour>, + { + MapInEvent::new(self, map) + } + + /// Adds a closure that turns the output event into something else. + fn map_out_event(self, map: TMap) -> MapOutEvent + where + Self: Sized, + TMap: FnMut(Self::ToBehaviour) -> TNewOut, + { + MapOutEvent::new(self, map) + } + + /// Creates a new [`ConnectionHandler`] that selects either this handler or + /// `other` by delegating methods calls appropriately. + fn select(self, other: TProto2) -> ConnectionHandlerSelect + where + Self: Sized, + { + ConnectionHandlerSelect::new(self, other) + } + + /// Informs the handler about an event from the [`NetworkBehaviour`](super::NetworkBehaviour). + fn on_behaviour_event(&mut self, _event: Self::FromBehaviour); + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ); +} + +/// Enumeration with the list of the possible stream events +/// to pass to [`on_connection_event`](ConnectionHandler::on_connection_event). +#[non_exhaustive] +pub enum ConnectionEvent<'a, IP: InboundUpgradeSend, OP: OutboundUpgradeSend, IOI, OOI> { + /// Informs the handler about the output of a successful upgrade on a new inbound substream. + FullyNegotiatedInbound(FullyNegotiatedInbound), + /// Informs the handler about the output of a successful upgrade on a new outbound stream. + FullyNegotiatedOutbound(FullyNegotiatedOutbound), + /// Informs the handler about a change in the address of the remote. + AddressChange(AddressChange<'a>), + /// Informs the handler that upgrading an outbound substream to the given protocol has failed. + DialUpgradeError(DialUpgradeError), + /// Informs the handler that upgrading an inbound substream to the given protocol has failed. + ListenUpgradeError(ListenUpgradeError), + /// The local [`ConnectionHandler`] added or removed support for one or more protocols. + LocalProtocolsChange(ProtocolsChange<'a>), + /// The remote [`ConnectionHandler`] now supports a different set of protocols. + RemoteProtocolsChange(ProtocolsChange<'a>), +} + +impl<'a, IP, OP, IOI, OOI> fmt::Debug for ConnectionEvent<'a, IP, OP, IOI, OOI> +where + IP: InboundUpgradeSend + fmt::Debug, + IP::Output: fmt::Debug, + IP::Error: fmt::Debug, + OP: OutboundUpgradeSend + fmt::Debug, + OP::Output: fmt::Debug, + OP::Error: fmt::Debug, + IOI: fmt::Debug, + OOI: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ConnectionEvent::FullyNegotiatedInbound(v) => { + f.debug_tuple("FullyNegotiatedInbound").field(v).finish() + } + ConnectionEvent::FullyNegotiatedOutbound(v) => { + f.debug_tuple("FullyNegotiatedOutbound").field(v).finish() + } + ConnectionEvent::AddressChange(v) => f.debug_tuple("AddressChange").field(v).finish(), + ConnectionEvent::DialUpgradeError(v) => { + f.debug_tuple("DialUpgradeError").field(v).finish() + } + ConnectionEvent::ListenUpgradeError(v) => { + f.debug_tuple("ListenUpgradeError").field(v).finish() + } + ConnectionEvent::LocalProtocolsChange(v) => { + f.debug_tuple("LocalProtocolsChange").field(v).finish() + } + ConnectionEvent::RemoteProtocolsChange(v) => { + f.debug_tuple("RemoteProtocolsChange").field(v).finish() + } + } + } +} + +impl<'a, IP: InboundUpgradeSend, OP: OutboundUpgradeSend, IOI, OOI> + ConnectionEvent<'a, IP, OP, IOI, OOI> +{ + /// Whether the event concerns an outbound stream. + pub fn is_outbound(&self) -> bool { + match self { + ConnectionEvent::DialUpgradeError(_) | ConnectionEvent::FullyNegotiatedOutbound(_) => { + true + } + ConnectionEvent::FullyNegotiatedInbound(_) + | ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) + | ConnectionEvent::ListenUpgradeError(_) => false, + } + } + + /// Whether the event concerns an inbound stream. + pub fn is_inbound(&self) -> bool { + match self { + ConnectionEvent::FullyNegotiatedInbound(_) | ConnectionEvent::ListenUpgradeError(_) => { + true + } + ConnectionEvent::FullyNegotiatedOutbound(_) + | ConnectionEvent::AddressChange(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) + | ConnectionEvent::DialUpgradeError(_) => false, + } + } +} + +/// [`ConnectionEvent`] variant that informs the handler about +/// the output of a successful upgrade on a new inbound substream. +/// +/// Note that it is up to the [`ConnectionHandler`] implementation to manage the lifetime of the +/// negotiated inbound substreams. E.g. the implementation has to enforce a limit on the number +/// of simultaneously open negotiated inbound substreams. In other words it is up to the +/// [`ConnectionHandler`] implementation to stop a malicious remote node to open and keep alive +/// an excessive amount of inbound substreams. +#[derive(Debug)] +pub struct FullyNegotiatedInbound { + pub protocol: IP::Output, + pub info: IOI, +} + +/// [`ConnectionEvent`] variant that informs the handler about successful upgrade on a new outbound stream. +/// +/// The `protocol` field is the information that was previously passed to +/// [`ConnectionHandlerEvent::OutboundSubstreamRequest`]. +#[derive(Debug)] +pub struct FullyNegotiatedOutbound { + pub protocol: OP::Output, + pub info: OOI, +} + +/// [`ConnectionEvent`] variant that informs the handler about a change in the address of the remote. +#[derive(Debug)] +pub struct AddressChange<'a> { + pub new_address: &'a Multiaddr, +} + +/// [`ConnectionEvent`] variant that informs the handler about a change in the protocols supported on the connection. +#[derive(Debug, Clone)] +pub enum ProtocolsChange<'a> { + Added(ProtocolsAdded<'a>), + Removed(ProtocolsRemoved<'a>), +} + +impl<'a> ProtocolsChange<'a> { + /// Compute the protocol change for the initial set of protocols. + pub(crate) fn from_initial_protocols<'b, T: AsRef + 'b>( + new_protocols: impl IntoIterator, + buffer: &'a mut Vec, + ) -> Self { + buffer.clear(); + buffer.extend( + new_protocols + .into_iter() + .filter_map(|i| StreamProtocol::try_from_owned(i.as_ref().to_owned()).ok()), + ); + + ProtocolsChange::Added(ProtocolsAdded { + protocols: buffer.iter(), + }) + } + + /// Compute the [`ProtocolsChange`] that results from adding `to_add` to `existing_protocols`. + /// + /// Returns `None` if the change is a no-op, i.e. `to_add` is a subset of `existing_protocols`. + pub(crate) fn add( + existing_protocols: &HashSet, + to_add: HashSet, + buffer: &'a mut Vec, + ) -> Option { + buffer.clear(); + buffer.extend( + to_add + .into_iter() + .filter(|i| !existing_protocols.contains(i)), + ); + + if buffer.is_empty() { + return None; + } + + Some(Self::Added(ProtocolsAdded { + protocols: buffer.iter(), + })) + } + + /// Compute the [`ProtocolsChange`] that results from removing `to_remove` from `existing_protocols`. Removes the protocols from `existing_protocols`. + /// + /// Returns `None` if the change is a no-op, i.e. none of the protocols in `to_remove` are in `existing_protocols`. + pub(crate) fn remove( + existing_protocols: &mut HashSet, + to_remove: HashSet, + buffer: &'a mut Vec, + ) -> Option { + buffer.clear(); + buffer.extend( + to_remove + .into_iter() + .filter_map(|i| existing_protocols.take(&i)), + ); + + if buffer.is_empty() { + return None; + } + + Some(Self::Removed(ProtocolsRemoved { + protocols: buffer.iter(), + })) + } + + /// Compute the [`ProtocolsChange`]s required to go from `existing_protocols` to `new_protocols`. + pub(crate) fn from_full_sets>( + existing_protocols: &mut HashMap, bool>, + new_protocols: impl IntoIterator, + buffer: &'a mut Vec, + ) -> SmallVec<[Self; 2]> { + buffer.clear(); + + // Initially, set the boolean for all protocols to `false`, meaning "not visited". + for v in existing_protocols.values_mut() { + *v = false; + } + + let mut new_protocol_count = 0; // We can only iterate `new_protocols` once, so keep track of its length separately. + for new_protocol in new_protocols { + existing_protocols + .entry(AsStrHashEq(new_protocol)) + .and_modify(|v| *v = true) // Mark protocol as visited (i.e. we still support it) + .or_insert_with_key(|k| { + // Encountered a previously unsupported protocol, remember it in `buffer`. + buffer.extend(StreamProtocol::try_from_owned(k.0.as_ref().to_owned()).ok()); + true + }); + new_protocol_count += 1; + } + + if new_protocol_count == existing_protocols.len() && buffer.is_empty() { + return SmallVec::new(); + } + + let num_new_protocols = buffer.len(); + // Drain all protocols that we haven't visited. + // For existing protocols that are not in `new_protocols`, the boolean will be false, meaning we need to remove it. + existing_protocols.retain(|p, &mut is_supported| { + if !is_supported { + buffer.extend(StreamProtocol::try_from_owned(p.0.as_ref().to_owned()).ok()); + } + + is_supported + }); + + let (added, removed) = buffer.split_at(num_new_protocols); + let mut changes = SmallVec::new(); + if !added.is_empty() { + changes.push(ProtocolsChange::Added(ProtocolsAdded { + protocols: added.iter(), + })); + } + if !removed.is_empty() { + changes.push(ProtocolsChange::Removed(ProtocolsRemoved { + protocols: removed.iter(), + })); + } + changes + } +} + +/// An [`Iterator`] over all protocols that have been added. +#[derive(Debug, Clone)] +pub struct ProtocolsAdded<'a> { + pub(crate) protocols: slice::Iter<'a, StreamProtocol>, +} + +/// An [`Iterator`] over all protocols that have been removed. +#[derive(Debug, Clone)] +pub struct ProtocolsRemoved<'a> { + pub(crate) protocols: slice::Iter<'a, StreamProtocol>, +} + +impl<'a> Iterator for ProtocolsAdded<'a> { + type Item = &'a StreamProtocol; + fn next(&mut self) -> Option { + self.protocols.next() + } +} + +impl<'a> Iterator for ProtocolsRemoved<'a> { + type Item = &'a StreamProtocol; + fn next(&mut self) -> Option { + self.protocols.next() + } +} + +/// [`ConnectionEvent`] variant that informs the handler +/// that upgrading an outbound substream to the given protocol has failed. +#[derive(Debug)] +pub struct DialUpgradeError { + pub info: OOI, + pub error: StreamUpgradeError, +} + +/// [`ConnectionEvent`] variant that informs the handler +/// that upgrading an inbound substream to the given protocol has failed. +#[derive(Debug)] +pub struct ListenUpgradeError { + pub info: IOI, + pub error: IP::Error, +} + +/// Configuration of inbound or outbound substream protocol(s) +/// for a [`ConnectionHandler`]. +/// +/// The inbound substream protocol(s) are defined by [`ConnectionHandler::listen_protocol`] +/// and the outbound substream protocol(s) by [`ConnectionHandlerEvent::OutboundSubstreamRequest`]. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SubstreamProtocol { + upgrade: TUpgrade, + info: TInfo, + timeout: Duration, +} + +impl SubstreamProtocol { + /// Create a new `SubstreamProtocol` from the given upgrade. + /// + /// The default timeout for applying the given upgrade on a substream is + /// 10 seconds. + pub fn new(upgrade: TUpgrade, info: TInfo) -> Self { + SubstreamProtocol { + upgrade, + info, + timeout: Duration::from_secs(10), + } + } + + /// Maps a function over the protocol upgrade. + pub fn map_upgrade(self, f: F) -> SubstreamProtocol + where + F: FnOnce(TUpgrade) -> U, + { + SubstreamProtocol { + upgrade: f(self.upgrade), + info: self.info, + timeout: self.timeout, + } + } + + /// Maps a function over the protocol info. + pub fn map_info(self, f: F) -> SubstreamProtocol + where + F: FnOnce(TInfo) -> U, + { + SubstreamProtocol { + upgrade: self.upgrade, + info: f(self.info), + timeout: self.timeout, + } + } + + /// Sets a new timeout for the protocol upgrade. + pub fn with_timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + /// Borrows the contained protocol upgrade. + pub fn upgrade(&self) -> &TUpgrade { + &self.upgrade + } + + /// Borrows the contained protocol info. + pub fn info(&self) -> &TInfo { + &self.info + } + + /// Borrows the timeout for the protocol upgrade. + pub fn timeout(&self) -> &Duration { + &self.timeout + } + + /// Converts the substream protocol configuration into the contained upgrade. + pub fn into_upgrade(self) -> (TUpgrade, TInfo) { + (self.upgrade, self.info) + } +} + +/// Event produced by a handler. +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum ConnectionHandlerEvent { + /// Request a new outbound substream to be opened with the remote. + OutboundSubstreamRequest { + /// The protocol(s) to apply on the substream. + protocol: SubstreamProtocol, + }, + /// We learned something about the protocols supported by the remote. + ReportRemoteProtocols(ProtocolSupport), + + /// Event that is sent to a [`NetworkBehaviour`](crate::behaviour::NetworkBehaviour). + NotifyBehaviour(TCustom), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ProtocolSupport { + /// The remote now supports these additional protocols. + Added(HashSet), + /// The remote no longer supports these protocols. + Removed(HashSet), +} + +/// Event produced by a handler. +impl + ConnectionHandlerEvent +{ + /// If this is an `OutboundSubstreamRequest`, maps the `info` member from a + /// `TOutboundOpenInfo` to something else. + pub fn map_outbound_open_info( + self, + map: F, + ) -> ConnectionHandlerEvent + where + F: FnOnce(TOutboundOpenInfo) -> I, + { + match self { + ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } => { + ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: protocol.map_info(map), + } + } + ConnectionHandlerEvent::NotifyBehaviour(val) => { + ConnectionHandlerEvent::NotifyBehaviour(val) + } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) + } + } + } + + /// If this is an `OutboundSubstreamRequest`, maps the protocol (`TConnectionUpgrade`) + /// to something else. + pub fn map_protocol(self, map: F) -> ConnectionHandlerEvent + where + F: FnOnce(TConnectionUpgrade) -> I, + { + match self { + ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } => { + ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: protocol.map_upgrade(map), + } + } + ConnectionHandlerEvent::NotifyBehaviour(val) => { + ConnectionHandlerEvent::NotifyBehaviour(val) + } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) + } + } + } + + /// If this is a `Custom` event, maps the content to something else. + pub fn map_custom( + self, + map: F, + ) -> ConnectionHandlerEvent + where + F: FnOnce(TCustom) -> I, + { + match self { + ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } => { + ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } + } + ConnectionHandlerEvent::NotifyBehaviour(val) => { + ConnectionHandlerEvent::NotifyBehaviour(map(val)) + } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) + } + } + } +} + +/// Error that can happen on an outbound substream opening attempt. +#[derive(Debug)] +pub enum StreamUpgradeError { + /// The opening attempt timed out before the negotiation was fully completed. + Timeout, + /// The upgrade produced an error. + Apply(TUpgrErr), + /// No protocol could be agreed upon. + NegotiationFailed, + /// An IO or otherwise unrecoverable error happened. + Io(io::Error), +} + +impl StreamUpgradeError { + /// Map the inner [`StreamUpgradeError`] type. + pub fn map_upgrade_err(self, f: F) -> StreamUpgradeError + where + F: FnOnce(TUpgrErr) -> E, + { + match self { + StreamUpgradeError::Timeout => StreamUpgradeError::Timeout, + StreamUpgradeError::Apply(e) => StreamUpgradeError::Apply(f(e)), + StreamUpgradeError::NegotiationFailed => StreamUpgradeError::NegotiationFailed, + StreamUpgradeError::Io(e) => StreamUpgradeError::Io(e), + } + } +} + +impl fmt::Display for StreamUpgradeError +where + TUpgrErr: error::Error + 'static, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + StreamUpgradeError::Timeout => { + write!(f, "Timeout error while opening a substream") + } + StreamUpgradeError::Apply(err) => { + write!(f, "Apply: ")?; + crate::print_error_chain(f, err) + } + StreamUpgradeError::NegotiationFailed => { + write!(f, "no protocols could be agreed upon") + } + StreamUpgradeError::Io(e) => { + write!(f, "IO error: ")?; + crate::print_error_chain(f, e) + } + } + } +} + +impl error::Error for StreamUpgradeError +where + TUpgrErr: error::Error + 'static, +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + None + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn protocol_set_of(s: &'static str) -> HashSet { + s.split_whitespace() + .map(|p| StreamProtocol::try_from_owned(format!("/{p}")).unwrap()) + .collect() + } + + fn test_remove( + existing: &mut HashSet, + to_remove: HashSet, + ) -> HashSet { + ProtocolsChange::remove(existing, to_remove, &mut Vec::new()) + .into_iter() + .flat_map(|c| match c { + ProtocolsChange::Added(_) => panic!("unexpected added"), + ProtocolsChange::Removed(r) => r.cloned(), + }) + .collect::>() + } + + #[test] + fn test_protocol_remove_subset() { + let mut existing = protocol_set_of("a b c"); + let to_remove = protocol_set_of("a b"); + + let change = test_remove(&mut existing, to_remove); + + assert_eq!(existing, protocol_set_of("c")); + assert_eq!(change, protocol_set_of("a b")); + } + + #[test] + fn test_protocol_remove_all() { + let mut existing = protocol_set_of("a b c"); + let to_remove = protocol_set_of("a b c"); + + let change = test_remove(&mut existing, to_remove); + + assert_eq!(existing, protocol_set_of("")); + assert_eq!(change, protocol_set_of("a b c")); + } + + #[test] + fn test_protocol_remove_superset() { + let mut existing = protocol_set_of("a b c"); + let to_remove = protocol_set_of("a b c d"); + + let change = test_remove(&mut existing, to_remove); + + assert_eq!(existing, protocol_set_of("")); + assert_eq!(change, protocol_set_of("a b c")); + } + + #[test] + fn test_protocol_remove_none() { + let mut existing = protocol_set_of("a b c"); + let to_remove = protocol_set_of("d"); + + let change = test_remove(&mut existing, to_remove); + + assert_eq!(existing, protocol_set_of("a b c")); + assert_eq!(change, protocol_set_of("")); + } + + #[test] + fn test_protocol_remove_none_from_empty() { + let mut existing = protocol_set_of(""); + let to_remove = protocol_set_of("d"); + + let change = test_remove(&mut existing, to_remove); + + assert_eq!(existing, protocol_set_of("")); + assert_eq!(change, protocol_set_of("")); + } + + fn test_from_full_sets( + existing: HashSet, + new: HashSet, + ) -> [HashSet; 2] { + let mut buffer = Vec::new(); + let mut existing = existing + .iter() + .map(|p| (AsStrHashEq(p.as_ref()), true)) + .collect::>(); + + let changes = ProtocolsChange::from_full_sets( + &mut existing, + new.iter().map(AsRef::as_ref), + &mut buffer, + ); + + let mut added_changes = HashSet::new(); + let mut removed_changes = HashSet::new(); + + for change in changes { + match change { + ProtocolsChange::Added(a) => { + added_changes.extend(a.cloned()); + } + ProtocolsChange::Removed(r) => { + removed_changes.extend(r.cloned()); + } + } + } + + [removed_changes, added_changes] + } + + #[test] + fn test_from_full_stes_subset() { + let existing = protocol_set_of("a b c"); + let new = protocol_set_of("a b"); + + let [removed_changes, added_changes] = test_from_full_sets(existing, new); + + assert_eq!(added_changes, protocol_set_of("")); + assert_eq!(removed_changes, protocol_set_of("c")); + } + + #[test] + fn test_from_full_sets_superset() { + let existing = protocol_set_of("a b"); + let new = protocol_set_of("a b c"); + + let [removed_changes, added_changes] = test_from_full_sets(existing, new); + + assert_eq!(added_changes, protocol_set_of("c")); + assert_eq!(removed_changes, protocol_set_of("")); + } + + #[test] + fn test_from_full_sets_intersection() { + let existing = protocol_set_of("a b c"); + let new = protocol_set_of("b c d"); + + let [removed_changes, added_changes] = test_from_full_sets(existing, new); + + assert_eq!(added_changes, protocol_set_of("d")); + assert_eq!(removed_changes, protocol_set_of("a")); + } + + #[test] + fn test_from_full_sets_disjoint() { + let existing = protocol_set_of("a b c"); + let new = protocol_set_of("d e f"); + + let [removed_changes, added_changes] = test_from_full_sets(existing, new); + + assert_eq!(added_changes, protocol_set_of("d e f")); + assert_eq!(removed_changes, protocol_set_of("a b c")); + } + + #[test] + fn test_from_full_sets_empty() { + let existing = protocol_set_of(""); + let new = protocol_set_of(""); + + let [removed_changes, added_changes] = test_from_full_sets(existing, new); + + assert_eq!(added_changes, protocol_set_of("")); + assert_eq!(removed_changes, protocol_set_of("")); + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/either.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/either.rs new file mode 100644 index 00000000000..a5aab9b5fee --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/either.rs @@ -0,0 +1,230 @@ +// Copyright 2021 Protocol Labs. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::handler::{ + ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, FullyNegotiatedInbound, + InboundUpgradeSend, ListenUpgradeError, SubstreamProtocol, +}; +use crate::upgrade::SendWrapper; +use either::Either; +use futures::future; +use std::task::{Context, Poll}; + +impl + FullyNegotiatedInbound, SendWrapper>, Either> +where + RIP: InboundUpgradeSend, + LIP: InboundUpgradeSend, +{ + pub(crate) fn transpose( + self, + ) -> Either, FullyNegotiatedInbound> { + match self { + FullyNegotiatedInbound { + protocol: future::Either::Left(protocol), + info: Either::Left(info), + } => Either::Left(FullyNegotiatedInbound { protocol, info }), + FullyNegotiatedInbound { + protocol: future::Either::Right(protocol), + info: Either::Right(info), + } => Either::Right(FullyNegotiatedInbound { protocol, info }), + _ => unreachable!(), + } + } +} + +impl + ListenUpgradeError, Either, SendWrapper>> +where + RIP: InboundUpgradeSend, + LIP: InboundUpgradeSend, +{ + fn transpose(self) -> Either, ListenUpgradeError> { + match self { + ListenUpgradeError { + error: Either::Left(error), + info: Either::Left(info), + } => Either::Left(ListenUpgradeError { error, info }), + ListenUpgradeError { + error: Either::Right(error), + info: Either::Right(info), + } => Either::Right(ListenUpgradeError { error, info }), + _ => unreachable!(), + } + } +} + +/// Implementation of a [`ConnectionHandler`] that represents either of two [`ConnectionHandler`] +/// implementations. +impl ConnectionHandler for Either +where + L: ConnectionHandler, + R: ConnectionHandler, +{ + type FromBehaviour = Either; + type ToBehaviour = Either; + type InboundProtocol = Either, SendWrapper>; + type OutboundProtocol = + Either, SendWrapper>; + type InboundOpenInfo = Either; + type OutboundOpenInfo = Either; + + fn listen_protocol(&self) -> SubstreamProtocol { + match self { + Either::Left(a) => a + .listen_protocol() + .map_upgrade(|u| Either::Left(SendWrapper(u))) + .map_info(Either::Left), + Either::Right(b) => b + .listen_protocol() + .map_upgrade(|u| Either::Right(SendWrapper(u))) + .map_info(Either::Right), + } + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + match (self, event) { + (Either::Left(handler), Either::Left(event)) => handler.on_behaviour_event(event), + (Either::Right(handler), Either::Right(event)) => handler.on_behaviour_event(event), + _ => unreachable!(), + } + } + + fn connection_keep_alive(&self) -> bool { + match self { + Either::Left(handler) => handler.connection_keep_alive(), + Either::Right(handler) => handler.connection_keep_alive(), + } + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + let event = match self { + Either::Left(handler) => futures::ready!(handler.poll(cx)) + .map_custom(Either::Left) + .map_protocol(|p| Either::Left(SendWrapper(p))) + .map_outbound_open_info(Either::Left), + Either::Right(handler) => futures::ready!(handler.poll(cx)) + .map_custom(Either::Right) + .map_protocol(|p| Either::Right(SendWrapper(p))) + .map_outbound_open_info(Either::Right), + }; + + Poll::Ready(event) + } + + fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll> { + let event = match self { + Either::Left(handler) => futures::ready!(handler.poll_close(cx)).map(Either::Left), + Either::Right(handler) => futures::ready!(handler.poll_close(cx)).map(Either::Right), + }; + + Poll::Ready(event) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(fully_negotiated_inbound) => { + match (fully_negotiated_inbound.transpose(), self) { + (Either::Left(fully_negotiated_inbound), Either::Left(handler)) => handler + .on_connection_event(ConnectionEvent::FullyNegotiatedInbound( + fully_negotiated_inbound, + )), + (Either::Right(fully_negotiated_inbound), Either::Right(handler)) => handler + .on_connection_event(ConnectionEvent::FullyNegotiatedInbound( + fully_negotiated_inbound, + )), + _ => unreachable!(), + } + } + ConnectionEvent::FullyNegotiatedOutbound(fully_negotiated_outbound) => { + match (fully_negotiated_outbound.transpose(), self) { + (Either::Left(fully_negotiated_outbound), Either::Left(handler)) => handler + .on_connection_event(ConnectionEvent::FullyNegotiatedOutbound( + fully_negotiated_outbound, + )), + (Either::Right(fully_negotiated_outbound), Either::Right(handler)) => handler + .on_connection_event(ConnectionEvent::FullyNegotiatedOutbound( + fully_negotiated_outbound, + )), + _ => unreachable!(), + } + } + ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { + match (dial_upgrade_error.transpose(), self) { + (Either::Left(dial_upgrade_error), Either::Left(handler)) => handler + .on_connection_event(ConnectionEvent::DialUpgradeError(dial_upgrade_error)), + (Either::Right(dial_upgrade_error), Either::Right(handler)) => handler + .on_connection_event(ConnectionEvent::DialUpgradeError(dial_upgrade_error)), + _ => unreachable!(), + } + } + ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { + match (listen_upgrade_error.transpose(), self) { + (Either::Left(listen_upgrade_error), Either::Left(handler)) => handler + .on_connection_event(ConnectionEvent::ListenUpgradeError( + listen_upgrade_error, + )), + (Either::Right(listen_upgrade_error), Either::Right(handler)) => handler + .on_connection_event(ConnectionEvent::ListenUpgradeError( + listen_upgrade_error, + )), + _ => unreachable!(), + } + } + ConnectionEvent::AddressChange(address_change) => match self { + Either::Left(handler) => { + handler.on_connection_event(ConnectionEvent::AddressChange(address_change)) + } + Either::Right(handler) => { + handler.on_connection_event(ConnectionEvent::AddressChange(address_change)) + } + }, + ConnectionEvent::LocalProtocolsChange(supported_protocols) => match self { + Either::Left(handler) => handler.on_connection_event( + ConnectionEvent::LocalProtocolsChange(supported_protocols), + ), + Either::Right(handler) => handler.on_connection_event( + ConnectionEvent::LocalProtocolsChange(supported_protocols), + ), + }, + ConnectionEvent::RemoteProtocolsChange(supported_protocols) => match self { + Either::Left(handler) => handler.on_connection_event( + ConnectionEvent::RemoteProtocolsChange(supported_protocols), + ), + Either::Right(handler) => handler.on_connection_event( + ConnectionEvent::RemoteProtocolsChange(supported_protocols), + ), + }, + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/map_in.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/map_in.rs new file mode 100644 index 00000000000..9316ef4d2ce --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/map_in.rs @@ -0,0 +1,98 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::handler::{ + ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, SubstreamProtocol, +}; +use std::{fmt::Debug, marker::PhantomData, task::Context, task::Poll}; + +/// Wrapper around a protocol handler that turns the input event into something else. +#[derive(Debug)] +pub struct MapInEvent { + inner: TConnectionHandler, + map: TMap, + marker: PhantomData, +} + +impl MapInEvent { + /// Creates a `MapInEvent`. + pub(crate) fn new(inner: TConnectionHandler, map: TMap) -> Self { + MapInEvent { + inner, + map, + marker: PhantomData, + } + } +} + +impl ConnectionHandler + for MapInEvent +where + TConnectionHandler: ConnectionHandler, + TMap: Fn(TNewIn) -> Option, + TNewIn: Debug + Send + 'static, + TMap: Send + 'static, +{ + type FromBehaviour = TNewIn; + type ToBehaviour = TConnectionHandler::ToBehaviour; + type InboundProtocol = TConnectionHandler::InboundProtocol; + type OutboundProtocol = TConnectionHandler::OutboundProtocol; + type InboundOpenInfo = TConnectionHandler::InboundOpenInfo; + type OutboundOpenInfo = TConnectionHandler::OutboundOpenInfo; + + fn listen_protocol(&self) -> SubstreamProtocol { + self.inner.listen_protocol() + } + + fn on_behaviour_event(&mut self, event: TNewIn) { + if let Some(event) = (self.map)(event) { + self.inner.on_behaviour_event(event); + } + } + + fn connection_keep_alive(&self) -> bool { + self.inner.connection_keep_alive() + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + self.inner.poll(cx) + } + + fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_close(cx) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + self.inner.on_connection_event(event); + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/map_out.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/map_out.rs new file mode 100644 index 00000000000..f877bfa6f64 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/map_out.rs @@ -0,0 +1,106 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::handler::{ + ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, SubstreamProtocol, +}; +use futures::ready; +use std::fmt::Debug; +use std::task::{Context, Poll}; + +/// Wrapper around a protocol handler that turns the output event into something else. +#[derive(Debug)] +pub struct MapOutEvent { + inner: TConnectionHandler, + map: TMap, +} + +impl MapOutEvent { + /// Creates a `MapOutEvent`. + pub(crate) fn new(inner: TConnectionHandler, map: TMap) -> Self { + MapOutEvent { inner, map } + } +} + +impl ConnectionHandler for MapOutEvent +where + TConnectionHandler: ConnectionHandler, + TMap: FnMut(TConnectionHandler::ToBehaviour) -> TNewOut, + TNewOut: Debug + Send + 'static, + TMap: Send + 'static, +{ + type FromBehaviour = TConnectionHandler::FromBehaviour; + type ToBehaviour = TNewOut; + type InboundProtocol = TConnectionHandler::InboundProtocol; + type OutboundProtocol = TConnectionHandler::OutboundProtocol; + type InboundOpenInfo = TConnectionHandler::InboundOpenInfo; + type OutboundOpenInfo = TConnectionHandler::OutboundOpenInfo; + + fn listen_protocol(&self) -> SubstreamProtocol { + self.inner.listen_protocol() + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + self.inner.on_behaviour_event(event) + } + + fn connection_keep_alive(&self) -> bool { + self.inner.connection_keep_alive() + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + self.inner.poll(cx).map(|ev| match ev { + ConnectionHandlerEvent::NotifyBehaviour(ev) => { + ConnectionHandlerEvent::NotifyBehaviour((self.map)(ev)) + } + ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } => { + ConnectionHandlerEvent::OutboundSubstreamRequest { protocol } + } + ConnectionHandlerEvent::ReportRemoteProtocols(support) => { + ConnectionHandlerEvent::ReportRemoteProtocols(support) + } + }) + } + + fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll> { + let Some(e) = ready!(self.inner.poll_close(cx)) else { + return Poll::Ready(None); + }; + + Poll::Ready(Some((self.map)(e))) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + self.inner.on_connection_event(event); + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/multi.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/multi.rs new file mode 100644 index 00000000000..5efcde5c2bb --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/multi.rs @@ -0,0 +1,456 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! A [`ConnectionHandler`] implementation that combines multiple other [`ConnectionHandler`]s +//! indexed by some key. + +use crate::handler::{ + AddressChange, ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, DialUpgradeError, + FullyNegotiatedInbound, FullyNegotiatedOutbound, ListenUpgradeError, SubstreamProtocol, +}; +use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend, UpgradeInfoSend}; +use crate::Stream; +use futures::{future::BoxFuture, prelude::*, ready}; +use rand::Rng; +use std::{ + cmp, + collections::{HashMap, HashSet}, + error, + fmt::{self, Debug}, + hash::Hash, + iter, + task::{Context, Poll}, + time::Duration, +}; + +/// A [`ConnectionHandler`] for multiple [`ConnectionHandler`]s of the same type. +#[derive(Clone)] +pub struct MultiHandler { + handlers: HashMap, +} + +impl fmt::Debug for MultiHandler +where + K: fmt::Debug + Eq + Hash, + H: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("MultiHandler") + .field("handlers", &self.handlers) + .finish() + } +} + +impl MultiHandler +where + K: Clone + Debug + Hash + Eq + Send + 'static, + H: ConnectionHandler, +{ + /// Create and populate a `MultiHandler` from the given handler iterator. + /// + /// It is an error for any two protocols handlers to share the same protocol name. + pub fn try_from_iter(iter: I) -> Result + where + I: IntoIterator, + { + let m = MultiHandler { + handlers: HashMap::from_iter(iter), + }; + uniq_proto_names( + m.handlers + .values() + .map(|h| h.listen_protocol().into_upgrade().0), + )?; + Ok(m) + } + + fn on_listen_upgrade_error( + &mut self, + ListenUpgradeError { + error: (key, error), + mut info, + }: ListenUpgradeError< + ::InboundOpenInfo, + ::InboundProtocol, + >, + ) { + if let Some(h) = self.handlers.get_mut(&key) { + if let Some(i) = info.take(&key) { + h.on_connection_event(ConnectionEvent::ListenUpgradeError(ListenUpgradeError { + info: i, + error, + })); + } + } + } +} + +impl ConnectionHandler for MultiHandler +where + K: Clone + Debug + Hash + Eq + Send + 'static, + H: ConnectionHandler, + H::InboundProtocol: InboundUpgradeSend, + H::OutboundProtocol: OutboundUpgradeSend, +{ + type FromBehaviour = (K, ::FromBehaviour); + type ToBehaviour = (K, ::ToBehaviour); + type InboundProtocol = Upgrade::InboundProtocol>; + type OutboundProtocol = ::OutboundProtocol; + type InboundOpenInfo = Info::InboundOpenInfo>; + type OutboundOpenInfo = (K, ::OutboundOpenInfo); + + fn listen_protocol(&self) -> SubstreamProtocol { + let (upgrade, info, timeout) = self + .handlers + .iter() + .map(|(key, handler)| { + let proto = handler.listen_protocol(); + let timeout = *proto.timeout(); + let (upgrade, info) = proto.into_upgrade(); + (key.clone(), (upgrade, info, timeout)) + }) + .fold( + (Upgrade::new(), Info::new(), Duration::from_secs(0)), + |(mut upg, mut inf, mut timeout), (k, (u, i, t))| { + upg.upgrades.push((k.clone(), u)); + inf.infos.push((k, i)); + timeout = cmp::max(timeout, t); + (upg, inf, timeout) + }, + ); + SubstreamProtocol::new(upgrade, info).with_timeout(timeout) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, + info: (key, arg), + }) => { + if let Some(h) = self.handlers.get_mut(&key) { + h.on_connection_event(ConnectionEvent::FullyNegotiatedOutbound( + FullyNegotiatedOutbound { + protocol, + info: arg, + }, + )); + } else { + tracing::error!("FullyNegotiatedOutbound: no handler for key") + } + } + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol: (key, arg), + mut info, + }) => { + if let Some(h) = self.handlers.get_mut(&key) { + if let Some(i) = info.take(&key) { + h.on_connection_event(ConnectionEvent::FullyNegotiatedInbound( + FullyNegotiatedInbound { + protocol: arg, + info: i, + }, + )); + } + } else { + tracing::error!("FullyNegotiatedInbound: no handler for key") + } + } + ConnectionEvent::AddressChange(AddressChange { new_address }) => { + for h in self.handlers.values_mut() { + h.on_connection_event(ConnectionEvent::AddressChange(AddressChange { + new_address, + })); + } + } + ConnectionEvent::DialUpgradeError(DialUpgradeError { + info: (key, arg), + error, + }) => { + if let Some(h) = self.handlers.get_mut(&key) { + h.on_connection_event(ConnectionEvent::DialUpgradeError(DialUpgradeError { + info: arg, + error, + })); + } else { + tracing::error!("DialUpgradeError: no handler for protocol") + } + } + ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { + self.on_listen_upgrade_error(listen_upgrade_error) + } + ConnectionEvent::LocalProtocolsChange(supported_protocols) => { + for h in self.handlers.values_mut() { + h.on_connection_event(ConnectionEvent::LocalProtocolsChange( + supported_protocols.clone(), + )); + } + } + ConnectionEvent::RemoteProtocolsChange(supported_protocols) => { + for h in self.handlers.values_mut() { + h.on_connection_event(ConnectionEvent::RemoteProtocolsChange( + supported_protocols.clone(), + )); + } + } + } + } + + fn on_behaviour_event(&mut self, (key, event): Self::FromBehaviour) { + if let Some(h) = self.handlers.get_mut(&key) { + h.on_behaviour_event(event) + } else { + tracing::error!("on_behaviour_event: no handler for key") + } + } + + fn connection_keep_alive(&self) -> bool { + self.handlers + .values() + .map(|h| h.connection_keep_alive()) + .max() + .unwrap_or(false) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + // Calling `gen_range(0, 0)` (see below) would panic, so we have return early to avoid + // that situation. + if self.handlers.is_empty() { + return Poll::Pending; + } + + // Not always polling handlers in the same order should give anyone the chance to make progress. + let pos = rand::thread_rng().gen_range(0..self.handlers.len()); + + for (k, h) in self.handlers.iter_mut().skip(pos) { + if let Poll::Ready(e) = h.poll(cx) { + let e = e + .map_outbound_open_info(|i| (k.clone(), i)) + .map_custom(|p| (k.clone(), p)); + return Poll::Ready(e); + } + } + + for (k, h) in self.handlers.iter_mut().take(pos) { + if let Poll::Ready(e) = h.poll(cx) { + let e = e + .map_outbound_open_info(|i| (k.clone(), i)) + .map_custom(|p| (k.clone(), p)); + return Poll::Ready(e); + } + } + + Poll::Pending + } + + fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll> { + for (k, h) in self.handlers.iter_mut() { + let Some(e) = ready!(h.poll_close(cx)) else { + continue; + }; + return Poll::Ready(Some((k.clone(), e))); + } + + Poll::Ready(None) + } +} + +/// Split [`MultiHandler`] into parts. +impl IntoIterator for MultiHandler { + type Item = ::Item; + type IntoIter = std::collections::hash_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.handlers.into_iter() + } +} + +/// Index and protocol name pair used as `UpgradeInfo::Info`. +#[derive(Debug, Clone)] +pub struct IndexedProtoName(usize, H); + +impl> AsRef for IndexedProtoName { + fn as_ref(&self) -> &str { + self.1.as_ref() + } +} + +/// The aggregated `InboundOpenInfo`s of supported inbound substream protocols. +#[derive(Clone)] +pub struct Info { + infos: Vec<(K, I)>, +} + +impl Info { + fn new() -> Self { + Info { infos: Vec::new() } + } + + pub fn take(&mut self, k: &K) -> Option { + if let Some(p) = self.infos.iter().position(|(key, _)| key == k) { + return Some(self.infos.remove(p).1); + } + None + } +} + +/// Inbound and outbound upgrade for all [`ConnectionHandler`]s. +#[derive(Clone)] +pub struct Upgrade { + upgrades: Vec<(K, H)>, +} + +impl Upgrade { + fn new() -> Self { + Upgrade { + upgrades: Vec::new(), + } + } +} + +impl fmt::Debug for Upgrade +where + K: fmt::Debug + Eq + Hash, + H: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Upgrade") + .field("upgrades", &self.upgrades) + .finish() + } +} + +impl UpgradeInfoSend for Upgrade +where + H: UpgradeInfoSend, + K: Send + 'static, +{ + type Info = IndexedProtoName; + type InfoIter = std::vec::IntoIter; + + fn protocol_info(&self) -> Self::InfoIter { + self.upgrades + .iter() + .enumerate() + .flat_map(|(i, (_, h))| iter::repeat(i).zip(h.protocol_info())) + .map(|(i, h)| IndexedProtoName(i, h)) + .collect::>() + .into_iter() + } +} + +impl InboundUpgradeSend for Upgrade +where + H: InboundUpgradeSend, + K: Send + 'static, +{ + type Output = (K, ::Output); + type Error = (K, ::Error); + type Future = BoxFuture<'static, Result>; + + fn upgrade_inbound(mut self, resource: Stream, info: Self::Info) -> Self::Future { + let IndexedProtoName(index, info) = info; + let (key, upgrade) = self.upgrades.remove(index); + upgrade + .upgrade_inbound(resource, info) + .map(move |out| match out { + Ok(o) => Ok((key, o)), + Err(e) => Err((key, e)), + }) + .boxed() + } +} + +impl OutboundUpgradeSend for Upgrade +where + H: OutboundUpgradeSend, + K: Send + 'static, +{ + type Output = (K, ::Output); + type Error = (K, ::Error); + type Future = BoxFuture<'static, Result>; + + fn upgrade_outbound(mut self, resource: Stream, info: Self::Info) -> Self::Future { + let IndexedProtoName(index, info) = info; + let (key, upgrade) = self.upgrades.remove(index); + upgrade + .upgrade_outbound(resource, info) + .map(move |out| match out { + Ok(o) => Ok((key, o)), + Err(e) => Err((key, e)), + }) + .boxed() + } +} + +/// Check that no two protocol names are equal. +fn uniq_proto_names(iter: I) -> Result<(), DuplicateProtonameError> +where + I: Iterator, + T: UpgradeInfoSend, +{ + let mut set = HashSet::new(); + for infos in iter { + for i in infos.protocol_info() { + let v = Vec::from(i.as_ref()); + if set.contains(&v) { + return Err(DuplicateProtonameError(v)); + } else { + set.insert(v); + } + } + } + Ok(()) +} + +/// It is an error if two handlers share the same protocol name. +#[derive(Debug, Clone)] +pub struct DuplicateProtonameError(Vec); + +impl DuplicateProtonameError { + /// The protocol name bytes that occurred in more than one handler. + pub fn protocol_name(&self) -> &[u8] { + &self.0 + } +} + +impl fmt::Display for DuplicateProtonameError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Ok(s) = std::str::from_utf8(&self.0) { + write!(f, "duplicate protocol name: {s}") + } else { + write!(f, "duplicate protocol name: {:?}", self.0) + } + } +} + +impl error::Error for DuplicateProtonameError {} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/one_shot.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/one_shot.rs new file mode 100644 index 00000000000..fc1074b31e4 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/one_shot.rs @@ -0,0 +1,237 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::handler::{ + ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, DialUpgradeError, + FullyNegotiatedInbound, FullyNegotiatedOutbound, SubstreamProtocol, +}; +use crate::upgrade::{InboundUpgradeSend, OutboundUpgradeSend}; +use crate::StreamUpgradeError; +use smallvec::SmallVec; +use std::{error, fmt::Debug, task::Context, task::Poll, time::Duration}; + +/// A [`ConnectionHandler`] that opens a new substream for each request. +// TODO: Debug +pub struct OneShotHandler +where + TOutbound: OutboundUpgradeSend, +{ + /// The upgrade for inbound substreams. + listen_protocol: SubstreamProtocol, + /// Queue of events to produce in `poll()`. + events_out: SmallVec<[Result>; 4]>, + /// Queue of outbound substreams to open. + dial_queue: SmallVec<[TOutbound; 4]>, + /// Current number of concurrent outbound substreams being opened. + dial_negotiated: u32, + /// The configuration container for the handler + config: OneShotHandlerConfig, +} + +impl OneShotHandler +where + TOutbound: OutboundUpgradeSend, +{ + /// Creates a `OneShotHandler`. + pub fn new( + listen_protocol: SubstreamProtocol, + config: OneShotHandlerConfig, + ) -> Self { + OneShotHandler { + listen_protocol, + events_out: SmallVec::new(), + dial_queue: SmallVec::new(), + dial_negotiated: 0, + config, + } + } + + /// Returns the number of pending requests. + pub fn pending_requests(&self) -> u32 { + self.dial_negotiated + self.dial_queue.len() as u32 + } + + /// Returns a reference to the listen protocol configuration. + /// + /// > **Note**: If you modify the protocol, modifications will only applies to future inbound + /// > substreams, not the ones already being negotiated. + pub fn listen_protocol_ref(&self) -> &SubstreamProtocol { + &self.listen_protocol + } + + /// Returns a mutable reference to the listen protocol configuration. + /// + /// > **Note**: If you modify the protocol, modifications will only applies to future inbound + /// > substreams, not the ones already being negotiated. + pub fn listen_protocol_mut(&mut self) -> &mut SubstreamProtocol { + &mut self.listen_protocol + } + + /// Opens an outbound substream with `upgrade`. + pub fn send_request(&mut self, upgrade: TOutbound) { + self.dial_queue.push(upgrade); + } +} + +impl Default for OneShotHandler +where + TOutbound: OutboundUpgradeSend, + TInbound: InboundUpgradeSend + Default, +{ + fn default() -> Self { + OneShotHandler::new( + SubstreamProtocol::new(Default::default(), ()), + OneShotHandlerConfig::default(), + ) + } +} + +impl ConnectionHandler for OneShotHandler +where + TInbound: InboundUpgradeSend + Send + 'static, + TOutbound: Debug + OutboundUpgradeSend, + TInbound::Output: Into, + TOutbound::Output: Into, + TOutbound::Error: error::Error + Send + 'static, + SubstreamProtocol: Clone, + TEvent: Debug + Send + 'static, +{ + type FromBehaviour = TOutbound; + type ToBehaviour = Result>; + type InboundProtocol = TInbound; + type OutboundProtocol = TOutbound; + type OutboundOpenInfo = (); + type InboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + self.listen_protocol.clone() + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + self.send_request(event); + } + + fn poll( + &mut self, + _: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + if !self.events_out.is_empty() { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( + self.events_out.remove(0), + )); + } else { + self.events_out.shrink_to_fit(); + } + + if !self.dial_queue.is_empty() { + if self.dial_negotiated < self.config.max_dial_negotiated { + self.dial_negotiated += 1; + let upgrade = self.dial_queue.remove(0); + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(upgrade, ()) + .with_timeout(self.config.outbound_substream_timeout), + }); + } + } else { + self.dial_queue.shrink_to_fit(); + } + + Poll::Pending + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol: out, + .. + }) => { + self.events_out.push(Ok(out.into())); + } + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol: out, + .. + }) => { + self.dial_negotiated -= 1; + self.events_out.push(Ok(out.into())); + } + ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { + self.events_out.push(Err(error)); + } + ConnectionEvent::AddressChange(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} + } + } +} + +/// Configuration parameters for the `OneShotHandler` +#[derive(Debug)] +pub struct OneShotHandlerConfig { + /// Timeout for outbound substream upgrades. + pub outbound_substream_timeout: Duration, + /// Maximum number of concurrent outbound substreams being opened. + pub max_dial_negotiated: u32, +} + +impl Default for OneShotHandlerConfig { + fn default() -> Self { + OneShotHandlerConfig { + outbound_substream_timeout: Duration::from_secs(10), + max_dial_negotiated: 8, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use futures::executor::block_on; + use futures::future::poll_fn; + use libp2p_core::upgrade::DeniedUpgrade; + use void::Void; + + #[test] + fn do_not_keep_idle_connection_alive() { + let mut handler: OneShotHandler<_, DeniedUpgrade, Void> = OneShotHandler::new( + SubstreamProtocol::new(DeniedUpgrade {}, ()), + Default::default(), + ); + + block_on(poll_fn(|cx| loop { + if handler.poll(cx).is_pending() { + return Poll::Ready(()); + } + })); + + assert!(!handler.connection_keep_alive()); + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/pending.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/pending.rs new file mode 100644 index 00000000000..23b9adcfd90 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/pending.rs @@ -0,0 +1,97 @@ +// Copyright 2022 Protocol Labs. +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::handler::{ + ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, FullyNegotiatedInbound, + FullyNegotiatedOutbound, SubstreamProtocol, +}; +use libp2p_core::upgrade::PendingUpgrade; +use std::task::{Context, Poll}; +use void::Void; + +/// Implementation of [`ConnectionHandler`] that returns a pending upgrade. +#[derive(Clone, Debug)] +pub struct PendingConnectionHandler { + protocol_name: String, +} + +impl PendingConnectionHandler { + pub fn new(protocol_name: String) -> Self { + PendingConnectionHandler { protocol_name } + } +} + +impl ConnectionHandler for PendingConnectionHandler { + type FromBehaviour = Void; + type ToBehaviour = Void; + type InboundProtocol = PendingUpgrade; + type OutboundProtocol = PendingUpgrade; + type OutboundOpenInfo = Void; + type InboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(PendingUpgrade::new(self.protocol_name.clone()), ()) + } + + fn on_behaviour_event(&mut self, v: Self::FromBehaviour) { + void::unreachable(v) + } + + fn poll( + &mut self, + _: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + Poll::Pending + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol, .. + }) => void::unreachable(protocol), + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, + info: _info, + }) => { + void::unreachable(protocol); + #[allow(unreachable_code, clippy::used_underscore_binding)] + { + void::unreachable(_info); + } + } + ConnectionEvent::AddressChange(_) + | ConnectionEvent::DialUpgradeError(_) + | ConnectionEvent::ListenUpgradeError(_) + | ConnectionEvent::LocalProtocolsChange(_) + | ConnectionEvent::RemoteProtocolsChange(_) => {} + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/select.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/select.rs new file mode 100644 index 00000000000..e049252d448 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/handler/select.rs @@ -0,0 +1,350 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::handler::{ + AddressChange, ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, DialUpgradeError, + FullyNegotiatedInbound, FullyNegotiatedOutbound, InboundUpgradeSend, ListenUpgradeError, + OutboundUpgradeSend, StreamUpgradeError, SubstreamProtocol, +}; +use crate::upgrade::SendWrapper; +use either::Either; +use futures::{future, ready}; +use libp2p_core::upgrade::SelectUpgrade; +use std::{cmp, task::Context, task::Poll}; + +/// Implementation of [`ConnectionHandler`] that combines two protocols into one. +#[derive(Debug, Clone)] +pub struct ConnectionHandlerSelect { + /// The first protocol. + proto1: TProto1, + /// The second protocol. + proto2: TProto2, +} + +impl ConnectionHandlerSelect { + /// Builds a [`ConnectionHandlerSelect`]. + pub(crate) fn new(proto1: TProto1, proto2: TProto2) -> Self { + ConnectionHandlerSelect { proto1, proto2 } + } + + pub fn into_inner(self) -> (TProto1, TProto2) { + (self.proto1, self.proto2) + } +} + +impl + FullyNegotiatedOutbound, SendWrapper>, Either> +where + S1OP: OutboundUpgradeSend, + S2OP: OutboundUpgradeSend, + S1OOI: Send + 'static, + S2OOI: Send + 'static, +{ + pub(crate) fn transpose( + self, + ) -> Either, FullyNegotiatedOutbound> { + match self { + FullyNegotiatedOutbound { + protocol: future::Either::Left(protocol), + info: Either::Left(info), + } => Either::Left(FullyNegotiatedOutbound { protocol, info }), + FullyNegotiatedOutbound { + protocol: future::Either::Right(protocol), + info: Either::Right(info), + } => Either::Right(FullyNegotiatedOutbound { protocol, info }), + _ => panic!("wrong API usage: the protocol doesn't match the upgrade info"), + } + } +} + +impl + FullyNegotiatedInbound, SendWrapper>, (S1IOI, S2IOI)> +where + S1IP: InboundUpgradeSend, + S2IP: InboundUpgradeSend, +{ + pub(crate) fn transpose( + self, + ) -> Either, FullyNegotiatedInbound> { + match self { + FullyNegotiatedInbound { + protocol: future::Either::Left(protocol), + info: (i1, _i2), + } => Either::Left(FullyNegotiatedInbound { protocol, info: i1 }), + FullyNegotiatedInbound { + protocol: future::Either::Right(protocol), + info: (_i1, i2), + } => Either::Right(FullyNegotiatedInbound { protocol, info: i2 }), + } + } +} + +impl + DialUpgradeError, Either, SendWrapper>> +where + S1OP: OutboundUpgradeSend, + S2OP: OutboundUpgradeSend, + S1OOI: Send + 'static, + S2OOI: Send + 'static, +{ + pub(crate) fn transpose( + self, + ) -> Either, DialUpgradeError> { + match self { + DialUpgradeError { + info: Either::Left(info), + error: StreamUpgradeError::Apply(Either::Left(err)), + } => Either::Left(DialUpgradeError { + info, + error: StreamUpgradeError::Apply(err), + }), + DialUpgradeError { + info: Either::Right(info), + error: StreamUpgradeError::Apply(Either::Right(err)), + } => Either::Right(DialUpgradeError { + info, + error: StreamUpgradeError::Apply(err), + }), + DialUpgradeError { + info: Either::Left(info), + error: e, + } => Either::Left(DialUpgradeError { + info, + error: e.map_upgrade_err(|_| panic!("already handled above")), + }), + DialUpgradeError { + info: Either::Right(info), + error: e, + } => Either::Right(DialUpgradeError { + info, + error: e.map_upgrade_err(|_| panic!("already handled above")), + }), + } + } +} + +impl ConnectionHandlerSelect +where + TProto1: ConnectionHandler, + TProto2: ConnectionHandler, +{ + fn on_listen_upgrade_error( + &mut self, + ListenUpgradeError { + info: (i1, i2), + error, + }: ListenUpgradeError< + ::InboundOpenInfo, + ::InboundProtocol, + >, + ) { + match error { + Either::Left(error) => { + self.proto1 + .on_connection_event(ConnectionEvent::ListenUpgradeError(ListenUpgradeError { + info: i1, + error, + })); + } + Either::Right(error) => { + self.proto2 + .on_connection_event(ConnectionEvent::ListenUpgradeError(ListenUpgradeError { + info: i2, + error, + })); + } + } + } +} + +impl ConnectionHandler for ConnectionHandlerSelect +where + TProto1: ConnectionHandler, + TProto2: ConnectionHandler, +{ + type FromBehaviour = Either; + type ToBehaviour = Either; + type InboundProtocol = SelectUpgrade< + SendWrapper<::InboundProtocol>, + SendWrapper<::InboundProtocol>, + >; + type OutboundProtocol = + Either, SendWrapper>; + type OutboundOpenInfo = Either; + type InboundOpenInfo = (TProto1::InboundOpenInfo, TProto2::InboundOpenInfo); + + fn listen_protocol(&self) -> SubstreamProtocol { + let proto1 = self.proto1.listen_protocol(); + let proto2 = self.proto2.listen_protocol(); + let timeout = *std::cmp::max(proto1.timeout(), proto2.timeout()); + let (u1, i1) = proto1.into_upgrade(); + let (u2, i2) = proto2.into_upgrade(); + let choice = SelectUpgrade::new(SendWrapper(u1), SendWrapper(u2)); + SubstreamProtocol::new(choice, (i1, i2)).with_timeout(timeout) + } + + fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { + match event { + Either::Left(event) => self.proto1.on_behaviour_event(event), + Either::Right(event) => self.proto2.on_behaviour_event(event), + } + } + + fn connection_keep_alive(&self) -> bool { + cmp::max( + self.proto1.connection_keep_alive(), + self.proto2.connection_keep_alive(), + ) + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent, + > { + match self.proto1.poll(cx) { + Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(event)) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Left(event))); + } + Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { protocol }) => { + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: protocol + .map_upgrade(|u| Either::Left(SendWrapper(u))) + .map_info(Either::Left), + }); + } + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols(support)) => { + return Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols(support)); + } + Poll::Pending => (), + }; + + match self.proto2.poll(cx) { + Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(event)) => { + return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Either::Right( + event, + ))); + } + Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { protocol }) => { + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: protocol + .map_upgrade(|u| Either::Right(SendWrapper(u))) + .map_info(Either::Right), + }); + } + Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols(support)) => { + return Poll::Ready(ConnectionHandlerEvent::ReportRemoteProtocols(support)); + } + Poll::Pending => (), + }; + + Poll::Pending + } + + fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll> { + if let Some(e) = ready!(self.proto1.poll_close(cx)) { + return Poll::Ready(Some(Either::Left(e))); + } + + if let Some(e) = ready!(self.proto2.poll_close(cx)) { + return Poll::Ready(Some(Either::Right(e))); + } + + Poll::Ready(None) + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedOutbound(fully_negotiated_outbound) => { + match fully_negotiated_outbound.transpose() { + Either::Left(f) => self + .proto1 + .on_connection_event(ConnectionEvent::FullyNegotiatedOutbound(f)), + Either::Right(f) => self + .proto2 + .on_connection_event(ConnectionEvent::FullyNegotiatedOutbound(f)), + } + } + ConnectionEvent::FullyNegotiatedInbound(fully_negotiated_inbound) => { + match fully_negotiated_inbound.transpose() { + Either::Left(f) => self + .proto1 + .on_connection_event(ConnectionEvent::FullyNegotiatedInbound(f)), + Either::Right(f) => self + .proto2 + .on_connection_event(ConnectionEvent::FullyNegotiatedInbound(f)), + } + } + ConnectionEvent::AddressChange(address) => { + self.proto1 + .on_connection_event(ConnectionEvent::AddressChange(AddressChange { + new_address: address.new_address, + })); + + self.proto2 + .on_connection_event(ConnectionEvent::AddressChange(AddressChange { + new_address: address.new_address, + })); + } + ConnectionEvent::DialUpgradeError(dial_upgrade_error) => { + match dial_upgrade_error.transpose() { + Either::Left(err) => self + .proto1 + .on_connection_event(ConnectionEvent::DialUpgradeError(err)), + Either::Right(err) => self + .proto2 + .on_connection_event(ConnectionEvent::DialUpgradeError(err)), + } + } + ConnectionEvent::ListenUpgradeError(listen_upgrade_error) => { + self.on_listen_upgrade_error(listen_upgrade_error) + } + ConnectionEvent::LocalProtocolsChange(supported_protocols) => { + self.proto1 + .on_connection_event(ConnectionEvent::LocalProtocolsChange( + supported_protocols.clone(), + )); + self.proto2 + .on_connection_event(ConnectionEvent::LocalProtocolsChange( + supported_protocols, + )); + } + ConnectionEvent::RemoteProtocolsChange(supported_protocols) => { + self.proto1 + .on_connection_event(ConnectionEvent::RemoteProtocolsChange( + supported_protocols.clone(), + )); + self.proto2 + .on_connection_event(ConnectionEvent::RemoteProtocolsChange( + supported_protocols, + )); + } + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/lib.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/lib.rs new file mode 100644 index 00000000000..81b1ca1a68d --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/lib.rs @@ -0,0 +1,2346 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! High-level network manager. +//! +//! A [`Swarm`] contains the state of the network as a whole. The entire +//! behaviour of a libp2p network can be controlled through the `Swarm`. +//! The `Swarm` struct contains all active and pending connections to +//! remotes and manages the state of all the substreams that have been +//! opened, and all the upgrades that were built upon these substreams. +//! +//! # Initializing a Swarm +//! +//! Creating a `Swarm` requires three things: +//! +//! 1. A network identity of the local node in form of a [`PeerId`]. +//! 2. An implementation of the [`Transport`] trait. This is the type that +//! will be used in order to reach nodes on the network based on their +//! address. See the `transport` module for more information. +//! 3. An implementation of the [`NetworkBehaviour`] trait. This is a state +//! machine that defines how the swarm should behave once it is connected +//! to a node. +//! +//! # Network Behaviour +//! +//! The [`NetworkBehaviour`] trait is implemented on types that indicate to +//! the swarm how it should behave. This includes which protocols are supported +//! and which nodes to try to connect to. It is the `NetworkBehaviour` that +//! controls what happens on the network. Multiple types that implement +//! `NetworkBehaviour` can be composed into a single behaviour. +//! +//! # Protocols Handler +//! +//! The [`ConnectionHandler`] trait defines how each active connection to a +//! remote should behave: how to handle incoming substreams, which protocols +//! are supported, when to open a new outbound substream, etc. +//! + +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod connection; +mod executor; +mod stream; +mod stream_protocol; +#[cfg(test)] +mod test; +mod upgrade; + +pub mod behaviour; +pub mod dial_opts; +pub mod dummy; +pub mod handler; +mod listen_opts; +mod translation; + +/// Bundles all symbols required for the [`libp2p_swarm_derive::NetworkBehaviour`] macro. +#[doc(hidden)] +pub mod derive_prelude { + pub use crate::behaviour::AddressChange; + pub use crate::behaviour::ConnectionClosed; + pub use crate::behaviour::ConnectionEstablished; + pub use crate::behaviour::DialFailure; + pub use crate::behaviour::ExpiredListenAddr; + pub use crate::behaviour::ExternalAddrConfirmed; + pub use crate::behaviour::ExternalAddrExpired; + pub use crate::behaviour::FromSwarm; + pub use crate::behaviour::ListenFailure; + pub use crate::behaviour::ListenerClosed; + pub use crate::behaviour::ListenerError; + pub use crate::behaviour::NewExternalAddrCandidate; + pub use crate::behaviour::NewExternalAddrOfPeer; + pub use crate::behaviour::NewListenAddr; + pub use crate::behaviour::NewListener; + pub use crate::connection::ConnectionId; + pub use crate::ConnectionDenied; + pub use crate::ConnectionHandler; + pub use crate::ConnectionHandlerSelect; + pub use crate::DialError; + pub use crate::NetworkBehaviour; + pub use crate::THandler; + pub use crate::THandlerInEvent; + pub use crate::THandlerOutEvent; + pub use crate::ToSwarm; + pub use either::Either; + pub use futures::prelude as futures; + pub use libp2p_core::transport::{ListenerId, PortUse}; + pub use libp2p_core::ConnectedPoint; + pub use libp2p_core::Endpoint; + pub use libp2p_core::Multiaddr; + pub use libp2p_identity::PeerId; +} + +pub use behaviour::{ + AddressChange, CloseConnection, ConnectionClosed, DialFailure, ExpiredListenAddr, + ExternalAddrExpired, ExternalAddresses, FromSwarm, ListenAddresses, ListenFailure, + ListenerClosed, ListenerError, NetworkBehaviour, NewExternalAddrCandidate, + NewExternalAddrOfPeer, NewListenAddr, NotifyHandler, PeerAddresses, ToSwarm, +}; +pub use connection::pool::ConnectionCounters; +pub use connection::{ConnectionError, ConnectionId, SupportedProtocols}; +pub use executor::Executor; +pub use handler::{ + ConnectionHandler, ConnectionHandlerEvent, ConnectionHandlerSelect, OneShotHandler, + OneShotHandlerConfig, StreamUpgradeError, SubstreamProtocol, +}; +#[cfg(feature = "macros")] +pub use libp2p_swarm_derive::NetworkBehaviour; +pub use listen_opts::ListenOpts; +pub use stream::Stream; +pub use stream_protocol::{InvalidProtocol, StreamProtocol}; + +use crate::behaviour::ExternalAddrConfirmed; +use crate::handler::UpgradeInfoSend; +use connection::pool::{EstablishedConnection, Pool, PoolConfig, PoolEvent}; +use connection::IncomingInfo; +use connection::{ + PendingConnectionError, PendingInboundConnectionError, PendingOutboundConnectionError, +}; +use dial_opts::{DialOpts, PeerCondition}; +use futures::{prelude::*, stream::FusedStream}; + +use libp2p_core::{ + connection::ConnectedPoint, + muxing::StreamMuxerBox, + transport::{self, ListenerId, TransportError, TransportEvent}, + Multiaddr, Transport, +}; +use libp2p_identity::PeerId; + +use smallvec::SmallVec; +use std::collections::{HashMap, HashSet, VecDeque}; +use std::num::{NonZeroU32, NonZeroU8, NonZeroUsize}; +use std::time::Duration; +use std::{ + error, fmt, io, + pin::Pin, + task::{Context, Poll}, +}; +use tracing::Instrument; +#[doc(hidden)] +pub use translation::_address_translation; + +/// Event generated by the [`NetworkBehaviour`] that the swarm will report back. +type TBehaviourOutEvent = ::ToSwarm; + +/// [`ConnectionHandler`] of the [`NetworkBehaviour`] for all the protocols the [`NetworkBehaviour`] +/// supports. +pub type THandler = ::ConnectionHandler; + +/// Custom event that can be received by the [`ConnectionHandler`] of the +/// [`NetworkBehaviour`]. +pub type THandlerInEvent = as ConnectionHandler>::FromBehaviour; + +/// Custom event that can be produced by the [`ConnectionHandler`] of the [`NetworkBehaviour`]. +pub type THandlerOutEvent = as ConnectionHandler>::ToBehaviour; + +/// Event generated by the `Swarm`. +#[derive(Debug)] +#[non_exhaustive] +pub enum SwarmEvent { + /// Event generated by the `NetworkBehaviour`. + Behaviour(TBehaviourOutEvent), + /// A connection to the given peer has been opened. + ConnectionEstablished { + /// Identity of the peer that we have connected to. + peer_id: PeerId, + /// Identifier of the connection. + connection_id: ConnectionId, + /// Endpoint of the connection that has been opened. + endpoint: ConnectedPoint, + /// Number of established connections to this peer, including the one that has just been + /// opened. + num_established: NonZeroU32, + /// [`Some`] when the new connection is an outgoing connection. + /// Addresses are dialed concurrently. Contains the addresses and errors + /// of dial attempts that failed before the one successful dial. + concurrent_dial_errors: Option)>>, + /// How long it took to establish this connection + established_in: std::time::Duration, + }, + /// A connection with the given peer has been closed, + /// possibly as a result of an error. + ConnectionClosed { + /// Identity of the peer that we have connected to. + peer_id: PeerId, + /// Identifier of the connection. + connection_id: ConnectionId, + /// Endpoint of the connection that has been closed. + endpoint: ConnectedPoint, + /// Number of other remaining connections to this same peer. + num_established: u32, + /// Reason for the disconnection, if it was not a successful + /// active close. + cause: Option, + }, + /// A new connection arrived on a listener and is in the process of protocol negotiation. + /// + /// A corresponding [`ConnectionEstablished`](SwarmEvent::ConnectionEstablished) or + /// [`IncomingConnectionError`](SwarmEvent::IncomingConnectionError) event will later be + /// generated for this connection. + IncomingConnection { + /// Identifier of the connection. + connection_id: ConnectionId, + /// Local connection address. + /// This address has been earlier reported with a [`NewListenAddr`](SwarmEvent::NewListenAddr) + /// event. + local_addr: Multiaddr, + /// Address used to send back data to the remote. + send_back_addr: Multiaddr, + }, + /// An error happened on an inbound connection during its initial handshake. + /// + /// This can include, for example, an error during the handshake of the encryption layer, or + /// the connection unexpectedly closed. + IncomingConnectionError { + /// Identifier of the connection. + connection_id: ConnectionId, + /// Local connection address. + /// This address has been earlier reported with a [`NewListenAddr`](SwarmEvent::NewListenAddr) + /// event. + local_addr: Multiaddr, + /// Address used to send back data to the remote. + send_back_addr: Multiaddr, + /// The error that happened. + error: ListenError, + }, + /// An error happened on an outbound connection. + OutgoingConnectionError { + /// Identifier of the connection. + connection_id: ConnectionId, + /// If known, [`PeerId`] of the peer we tried to reach. + peer_id: Option, + /// Error that has been encountered. + error: DialError, + }, + /// One of our listeners has reported a new local listening address. + NewListenAddr { + /// The listener that is listening on the new address. + listener_id: ListenerId, + /// The new address that is being listened on. + address: Multiaddr, + }, + /// One of our listeners has reported the expiration of a listening address. + ExpiredListenAddr { + /// The listener that is no longer listening on the address. + listener_id: ListenerId, + /// The expired address. + address: Multiaddr, + }, + /// One of the listeners gracefully closed. + ListenerClosed { + /// The listener that closed. + listener_id: ListenerId, + /// The addresses that the listener was listening on. These addresses are now considered + /// expired, similar to if a [`ExpiredListenAddr`](SwarmEvent::ExpiredListenAddr) event + /// has been generated for each of them. + addresses: Vec, + /// Reason for the closure. Contains `Ok(())` if the stream produced `None`, or `Err` + /// if the stream produced an error. + reason: Result<(), io::Error>, + }, + /// One of the listeners reported a non-fatal error. + ListenerError { + /// The listener that errored. + listener_id: ListenerId, + /// The listener error. + error: io::Error, + }, + /// A new dialing attempt has been initiated by the [`NetworkBehaviour`] + /// implementation. + /// + /// A [`ConnectionEstablished`](SwarmEvent::ConnectionEstablished) event is + /// reported if the dialing attempt succeeds, otherwise a + /// [`OutgoingConnectionError`](SwarmEvent::OutgoingConnectionError) event + /// is reported. + Dialing { + /// Identity of the peer that we are connecting to. + peer_id: Option, + + /// Identifier of the connection. + connection_id: ConnectionId, + }, + /// We have discovered a new candidate for an external address for us. + NewExternalAddrCandidate { address: Multiaddr }, + /// An external address of the local node was confirmed. + ExternalAddrConfirmed { address: Multiaddr }, + /// An external address of the local node expired, i.e. is no-longer confirmed. + ExternalAddrExpired { address: Multiaddr }, + /// We have discovered a new address of a peer. + NewExternalAddrOfPeer { peer_id: PeerId, address: Multiaddr }, +} + +impl SwarmEvent { + /// Extract the `TBehaviourOutEvent` from this [`SwarmEvent`] in case it is the `Behaviour` variant, otherwise fail. + #[allow(clippy::result_large_err)] + pub fn try_into_behaviour_event(self) -> Result { + match self { + SwarmEvent::Behaviour(inner) => Ok(inner), + other => Err(other), + } + } +} + +/// Contains the state of the network, plus the way it should behave. +/// +/// Note: Needs to be polled via `` in order to make +/// progress. +pub struct Swarm +where + TBehaviour: NetworkBehaviour, +{ + /// [`Transport`] for dialing remote peers and listening for incoming connection. + transport: transport::Boxed<(PeerId, StreamMuxerBox)>, + + /// The nodes currently active. + pool: Pool>, + + /// The local peer ID. + local_peer_id: PeerId, + + /// Handles which nodes to connect to and how to handle the events sent back by the protocol + /// handlers. + behaviour: TBehaviour, + + /// List of protocols that the behaviour says it supports. + supported_protocols: SmallVec<[Vec; 16]>, + + confirmed_external_addr: HashSet, + + /// Multiaddresses that our listeners are listening on, + listened_addrs: HashMap>, + + /// Pending event to be delivered to connection handlers + /// (or dropped if the peer disconnected) before the `behaviour` + /// can be polled again. + pending_handler_event: Option<(PeerId, PendingNotifyHandler, THandlerInEvent)>, + + pending_swarm_events: VecDeque>, +} + +impl Unpin for Swarm where TBehaviour: NetworkBehaviour {} + +impl Swarm +where + TBehaviour: NetworkBehaviour, +{ + /// Creates a new [`Swarm`] from the given [`Transport`], [`NetworkBehaviour`], [`PeerId`] and + /// [`Config`]. + pub fn new( + transport: transport::Boxed<(PeerId, StreamMuxerBox)>, + behaviour: TBehaviour, + local_peer_id: PeerId, + config: Config, + ) -> Self { + tracing::info!(%local_peer_id); + + Swarm { + local_peer_id, + transport, + pool: Pool::new(local_peer_id, config.pool_config), + behaviour, + supported_protocols: Default::default(), + confirmed_external_addr: Default::default(), + listened_addrs: HashMap::new(), + pending_handler_event: None, + pending_swarm_events: VecDeque::default(), + } + } + + /// Returns information about the connections underlying the [`Swarm`]. + pub fn network_info(&self) -> NetworkInfo { + let num_peers = self.pool.num_peers(); + let connection_counters = self.pool.counters().clone(); + NetworkInfo { + num_peers, + connection_counters, + } + } + + /// Starts listening on the given address. + /// Returns an error if the address is not supported. + /// + /// Listeners report their new listening addresses as [`SwarmEvent::NewListenAddr`]. + /// Depending on the underlying transport, one listener may have multiple listening addresses. + pub fn listen_on(&mut self, addr: Multiaddr) -> Result> { + let opts = ListenOpts::new(addr); + let id = opts.listener_id(); + self.add_listener(opts)?; + Ok(id) + } + + /// Remove some listener. + /// + /// Returns `true` if there was a listener with this ID, `false` + /// otherwise. + pub fn remove_listener(&mut self, listener_id: ListenerId) -> bool { + self.transport.remove_listener(listener_id) + } + + /// Dial a known or unknown peer. + /// + /// See also [`DialOpts`]. + /// + /// ``` + /// # use libp2p_swarm::Swarm; + /// # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition}; + /// # use libp2p_core::{Multiaddr, Transport}; + /// # use libp2p_core::transport::dummy::DummyTransport; + /// # use libp2p_swarm::dummy; + /// # use libp2p_identity::PeerId; + /// # + /// # #[tokio::main] + /// # async fn main() { + /// let mut swarm = build_swarm(); + /// + /// // Dial a known peer. + /// swarm.dial(PeerId::random()); + /// + /// // Dial an unknown peer. + /// swarm.dial("/ip6/::1/tcp/12345".parse::().unwrap()); + /// # } + /// + /// # fn build_swarm() -> Swarm { + /// # Swarm::new(DummyTransport::new().boxed(), dummy::Behaviour, PeerId::random(), libp2p_swarm::Config::with_tokio_executor()) + /// # } + /// ``` + pub fn dial(&mut self, opts: impl Into) -> Result<(), DialError> { + let dial_opts = opts.into(); + + let peer_id = dial_opts.get_peer_id(); + let condition = dial_opts.peer_condition(); + let connection_id = dial_opts.connection_id(); + + let should_dial = match (condition, peer_id) { + (_, None) => true, + (PeerCondition::Always, _) => true, + (PeerCondition::Disconnected, Some(peer_id)) => !self.pool.is_connected(peer_id), + (PeerCondition::NotDialing, Some(peer_id)) => !self.pool.is_dialing(peer_id), + (PeerCondition::DisconnectedAndNotDialing, Some(peer_id)) => { + !self.pool.is_dialing(peer_id) && !self.pool.is_connected(peer_id) + } + }; + + if !should_dial { + let e = DialError::DialPeerConditionFalse(condition); + + self.behaviour + .on_swarm_event(FromSwarm::DialFailure(DialFailure { + peer_id, + error: &e, + connection_id, + })); + + return Err(e); + } + + let addresses = { + let mut addresses_from_opts = dial_opts.get_addresses(); + + match self.behaviour.handle_pending_outbound_connection( + connection_id, + peer_id, + addresses_from_opts.as_slice(), + dial_opts.role_override(), + ) { + Ok(addresses) => { + if dial_opts.extend_addresses_through_behaviour() { + addresses_from_opts.extend(addresses) + } else { + let num_addresses = addresses.len(); + + if num_addresses > 0 { + tracing::debug!( + connection=%connection_id, + discarded_addresses_count=%num_addresses, + "discarding addresses from `NetworkBehaviour` because `DialOpts::extend_addresses_through_behaviour is `false` for connection" + ) + } + } + } + Err(cause) => { + let error = DialError::Denied { cause }; + + self.behaviour + .on_swarm_event(FromSwarm::DialFailure(DialFailure { + peer_id, + error: &error, + connection_id, + })); + + return Err(error); + } + } + + let mut unique_addresses = HashSet::new(); + addresses_from_opts.retain(|addr| { + !self.listened_addrs.values().flatten().any(|a| a == addr) + && unique_addresses.insert(addr.clone()) + }); + + if addresses_from_opts.is_empty() { + let error = DialError::NoAddresses; + self.behaviour + .on_swarm_event(FromSwarm::DialFailure(DialFailure { + peer_id, + error: &error, + connection_id, + })); + return Err(error); + }; + + addresses_from_opts + }; + + let dials = addresses + .into_iter() + .map(|a| match peer_id.map_or(Ok(a.clone()), |p| a.with_p2p(p)) { + Ok(address) => { + let dial = self.transport.dial( + address.clone(), + transport::DialOpts { + role: dial_opts.role_override(), + port_use: dial_opts.port_use(), + }, + ); + let span = tracing::debug_span!(parent: tracing::Span::none(), "Transport::dial", %address); + span.follows_from(tracing::Span::current()); + match dial { + Ok(fut) => fut + .map(|r| (address, r.map_err(TransportError::Other))) + .instrument(span) + .boxed(), + Err(err) => futures::future::ready((address, Err(err))).boxed(), + } + } + Err(address) => futures::future::ready(( + address.clone(), + Err(TransportError::MultiaddrNotSupported(address)), + )) + .boxed(), + }) + .collect(); + + self.pool.add_outgoing( + dials, + peer_id, + dial_opts.role_override(), + dial_opts.port_use(), + dial_opts.dial_concurrency_override(), + connection_id, + ); + + Ok(()) + } + + /// Returns an iterator that produces the list of addresses we're listening on. + pub fn listeners(&self) -> impl Iterator { + self.listened_addrs.values().flatten() + } + + /// Returns the peer ID of the swarm passed as parameter. + pub fn local_peer_id(&self) -> &PeerId { + &self.local_peer_id + } + + /// List all **confirmed** external address for the local node. + pub fn external_addresses(&self) -> impl Iterator { + self.confirmed_external_addr.iter() + } + + fn add_listener(&mut self, opts: ListenOpts) -> Result<(), TransportError> { + let addr = opts.address(); + let listener_id = opts.listener_id(); + + if let Err(e) = self.transport.listen_on(listener_id, addr.clone()) { + self.behaviour + .on_swarm_event(FromSwarm::ListenerError(behaviour::ListenerError { + listener_id, + err: &e, + })); + + return Err(e); + } + + self.behaviour + .on_swarm_event(FromSwarm::NewListener(behaviour::NewListener { + listener_id, + })); + + Ok(()) + } + + /// Add a **confirmed** external address for the local node. + /// + /// This function should only be called with addresses that are guaranteed to be reachable. + /// The address is broadcast to all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrConfirmed`]. + pub fn add_external_address(&mut self, a: Multiaddr) { + self.behaviour + .on_swarm_event(FromSwarm::ExternalAddrConfirmed(ExternalAddrConfirmed { + addr: &a, + })); + self.confirmed_external_addr.insert(a); + } + + /// Remove an external address for the local node. + /// + /// The address is broadcast to all [`NetworkBehaviour`]s via [`FromSwarm::ExternalAddrExpired`]. + pub fn remove_external_address(&mut self, addr: &Multiaddr) { + self.behaviour + .on_swarm_event(FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr })); + self.confirmed_external_addr.remove(addr); + } + + /// Add a new external address of a remote peer. + /// + /// The address is broadcast to all [`NetworkBehaviour`]s via [`FromSwarm::NewExternalAddrOfPeer`]. + pub fn add_peer_address(&mut self, peer_id: PeerId, addr: Multiaddr) { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrOfPeer(NewExternalAddrOfPeer { + peer_id, + addr: &addr, + })) + } + + /// Disconnects a peer by its peer ID, closing all connections to said peer. + /// + /// Returns `Ok(())` if there was one or more established connections to the peer. + /// + /// Closing a connection via [`Swarm::disconnect_peer_id`] will poll [`ConnectionHandler::poll_close`] to completion. + /// Use this function if you want to close a connection _despite_ it still being in use by one or more handlers. + #[allow(clippy::result_unit_err)] + pub fn disconnect_peer_id(&mut self, peer_id: PeerId) -> Result<(), ()> { + let was_connected = self.pool.is_connected(peer_id); + self.pool.disconnect(peer_id); + + if was_connected { + Ok(()) + } else { + Err(()) + } + } + + /// Attempt to gracefully close a connection. + /// + /// Closing a connection is asynchronous but this function will return immediately. + /// A [`SwarmEvent::ConnectionClosed`] event will be emitted once the connection is actually closed. + /// + /// # Returns + /// + /// - `true` if the connection was established and is now being closed. + /// - `false` if the connection was not found or is no longer established. + pub fn close_connection(&mut self, connection_id: ConnectionId) -> bool { + if let Some(established) = self.pool.get_established(connection_id) { + established.start_close(); + return true; + } + + false + } + + /// Checks whether there is an established connection to a peer. + pub fn is_connected(&self, peer_id: &PeerId) -> bool { + self.pool.is_connected(*peer_id) + } + + /// Returns the currently connected peers. + pub fn connected_peers(&self) -> impl Iterator { + self.pool.iter_connected() + } + + /// Returns a reference to the provided [`NetworkBehaviour`]. + pub fn behaviour(&self) -> &TBehaviour { + &self.behaviour + } + + /// Returns a mutable reference to the provided [`NetworkBehaviour`]. + pub fn behaviour_mut(&mut self) -> &mut TBehaviour { + &mut self.behaviour + } + + fn handle_pool_event(&mut self, event: PoolEvent>) { + match event { + PoolEvent::ConnectionEstablished { + peer_id, + id, + endpoint, + connection, + concurrent_dial_errors, + established_in, + } => { + let handler = match endpoint.clone() { + ConnectedPoint::Dialer { + address, + role_override, + port_use, + } => { + match self.behaviour.handle_established_outbound_connection( + id, + peer_id, + &address, + role_override, + port_use, + ) { + Ok(handler) => handler, + Err(cause) => { + let dial_error = DialError::Denied { cause }; + self.behaviour.on_swarm_event(FromSwarm::DialFailure( + DialFailure { + connection_id: id, + error: &dial_error, + peer_id: Some(peer_id), + }, + )); + + self.pending_swarm_events.push_back( + SwarmEvent::OutgoingConnectionError { + peer_id: Some(peer_id), + connection_id: id, + error: dial_error, + }, + ); + return; + } + } + } + ConnectedPoint::Listener { + local_addr, + send_back_addr, + } => { + match self.behaviour.handle_established_inbound_connection( + id, + peer_id, + &local_addr, + &send_back_addr, + ) { + Ok(handler) => handler, + Err(cause) => { + let listen_error = ListenError::Denied { cause }; + self.behaviour.on_swarm_event(FromSwarm::ListenFailure( + ListenFailure { + local_addr: &local_addr, + send_back_addr: &send_back_addr, + error: &listen_error, + connection_id: id, + peer_id: Some(peer_id), + }, + )); + + self.pending_swarm_events.push_back( + SwarmEvent::IncomingConnectionError { + connection_id: id, + send_back_addr, + local_addr, + error: listen_error, + }, + ); + return; + } + } + } + }; + + let supported_protocols = handler + .listen_protocol() + .upgrade() + .protocol_info() + .map(|p| p.as_ref().as_bytes().to_vec()) + .collect(); + let other_established_connection_ids = self + .pool + .iter_established_connections_of_peer(&peer_id) + .collect::>(); + let num_established = NonZeroU32::new( + u32::try_from(other_established_connection_ids.len() + 1).unwrap(), + ) + .expect("n + 1 is always non-zero; qed"); + + self.pool + .spawn_connection(id, peer_id, &endpoint, connection, handler); + + tracing::debug!( + peer=%peer_id, + ?endpoint, + total_peers=%num_established, + "Connection established" + ); + let failed_addresses = concurrent_dial_errors + .as_ref() + .map(|es| { + es.iter() + .map(|(a, _)| a) + .cloned() + .collect::>() + }) + .unwrap_or_default(); + self.behaviour + .on_swarm_event(FromSwarm::ConnectionEstablished( + behaviour::ConnectionEstablished { + peer_id, + connection_id: id, + endpoint: &endpoint, + failed_addresses: &failed_addresses, + other_established: other_established_connection_ids.len(), + }, + )); + self.supported_protocols = supported_protocols; + self.pending_swarm_events + .push_back(SwarmEvent::ConnectionEstablished { + peer_id, + connection_id: id, + num_established, + endpoint, + concurrent_dial_errors, + established_in, + }); + } + PoolEvent::PendingOutboundConnectionError { + id: connection_id, + error, + peer, + } => { + let error = error.into(); + + self.behaviour + .on_swarm_event(FromSwarm::DialFailure(DialFailure { + peer_id: peer, + error: &error, + connection_id, + })); + + if let Some(peer) = peer { + tracing::debug!(%peer, "Connection attempt to peer failed with {:?}.", error,); + } else { + tracing::debug!("Connection attempt to unknown peer failed with {:?}", error); + } + + self.pending_swarm_events + .push_back(SwarmEvent::OutgoingConnectionError { + peer_id: peer, + connection_id, + error, + }); + } + PoolEvent::PendingInboundConnectionError { + id, + send_back_addr, + local_addr, + error, + } => { + let error = error.into(); + + tracing::debug!("Incoming connection failed: {:?}", error); + self.behaviour + .on_swarm_event(FromSwarm::ListenFailure(ListenFailure { + local_addr: &local_addr, + send_back_addr: &send_back_addr, + error: &error, + connection_id: id, + peer_id: None, + })); + self.pending_swarm_events + .push_back(SwarmEvent::IncomingConnectionError { + connection_id: id, + local_addr, + send_back_addr, + error, + }); + } + PoolEvent::ConnectionClosed { + id, + connected, + error, + remaining_established_connection_ids, + .. + } => { + if let Some(error) = error.as_ref() { + tracing::debug!( + total_peers=%remaining_established_connection_ids.len(), + "Connection closed with error {:?}: {:?}", + error, + connected, + ); + } else { + tracing::debug!( + total_peers=%remaining_established_connection_ids.len(), + "Connection closed: {:?}", + connected + ); + } + let peer_id = connected.peer_id; + let endpoint = connected.endpoint; + let num_established = + u32::try_from(remaining_established_connection_ids.len()).unwrap(); + + self.behaviour + .on_swarm_event(FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id: id, + endpoint: &endpoint, + cause: error.as_ref(), + remaining_established: num_established as usize, + })); + self.pending_swarm_events + .push_back(SwarmEvent::ConnectionClosed { + peer_id, + connection_id: id, + endpoint, + cause: error, + num_established, + }); + } + PoolEvent::ConnectionEvent { peer_id, id, event } => { + self.behaviour + .on_connection_handler_event(peer_id, id, event); + } + PoolEvent::AddressChange { + peer_id, + id, + new_endpoint, + old_endpoint, + } => { + self.behaviour + .on_swarm_event(FromSwarm::AddressChange(AddressChange { + peer_id, + connection_id: id, + old: &old_endpoint, + new: &new_endpoint, + })); + } + } + } + + fn handle_transport_event( + &mut self, + event: TransportEvent< + as Transport>::ListenerUpgrade, + io::Error, + >, + ) { + match event { + TransportEvent::Incoming { + listener_id: _, + upgrade, + local_addr, + send_back_addr, + } => { + let connection_id = ConnectionId::next(); + + match self.behaviour.handle_pending_inbound_connection( + connection_id, + &local_addr, + &send_back_addr, + ) { + Ok(()) => {} + Err(cause) => { + let listen_error = ListenError::Denied { cause }; + + self.behaviour + .on_swarm_event(FromSwarm::ListenFailure(ListenFailure { + local_addr: &local_addr, + send_back_addr: &send_back_addr, + error: &listen_error, + connection_id, + peer_id: None, + })); + + self.pending_swarm_events + .push_back(SwarmEvent::IncomingConnectionError { + connection_id, + local_addr, + send_back_addr, + error: listen_error, + }); + return; + } + } + + self.pool.add_incoming( + upgrade, + IncomingInfo { + local_addr: &local_addr, + send_back_addr: &send_back_addr, + }, + connection_id, + ); + + self.pending_swarm_events + .push_back(SwarmEvent::IncomingConnection { + connection_id, + local_addr, + send_back_addr, + }) + } + TransportEvent::NewAddress { + listener_id, + listen_addr, + } => { + tracing::debug!( + listener=?listener_id, + address=%listen_addr, + "New listener address" + ); + let addrs = self.listened_addrs.entry(listener_id).or_default(); + if !addrs.contains(&listen_addr) { + addrs.push(listen_addr.clone()) + } + self.behaviour + .on_swarm_event(FromSwarm::NewListenAddr(NewListenAddr { + listener_id, + addr: &listen_addr, + })); + self.pending_swarm_events + .push_back(SwarmEvent::NewListenAddr { + listener_id, + address: listen_addr, + }) + } + TransportEvent::AddressExpired { + listener_id, + listen_addr, + } => { + tracing::debug!( + listener=?listener_id, + address=%listen_addr, + "Expired listener address" + ); + if let Some(addrs) = self.listened_addrs.get_mut(&listener_id) { + addrs.retain(|a| a != &listen_addr); + } + self.behaviour + .on_swarm_event(FromSwarm::ExpiredListenAddr(ExpiredListenAddr { + listener_id, + addr: &listen_addr, + })); + self.pending_swarm_events + .push_back(SwarmEvent::ExpiredListenAddr { + listener_id, + address: listen_addr, + }) + } + TransportEvent::ListenerClosed { + listener_id, + reason, + } => { + tracing::debug!( + listener=?listener_id, + ?reason, + "Listener closed" + ); + let addrs = self.listened_addrs.remove(&listener_id).unwrap_or_default(); + for addr in addrs.iter() { + self.behaviour.on_swarm_event(FromSwarm::ExpiredListenAddr( + ExpiredListenAddr { listener_id, addr }, + )); + } + self.behaviour + .on_swarm_event(FromSwarm::ListenerClosed(ListenerClosed { + listener_id, + reason: reason.as_ref().copied(), + })); + self.pending_swarm_events + .push_back(SwarmEvent::ListenerClosed { + listener_id, + addresses: addrs.to_vec(), + reason, + }) + } + TransportEvent::ListenerError { listener_id, error } => { + self.behaviour + .on_swarm_event(FromSwarm::ListenerError(ListenerError { + listener_id, + err: &error, + })); + self.pending_swarm_events + .push_back(SwarmEvent::ListenerError { listener_id, error }) + } + } + } + + fn handle_behaviour_event( + &mut self, + event: ToSwarm>, + ) { + match event { + ToSwarm::GenerateEvent(event) => { + self.pending_swarm_events + .push_back(SwarmEvent::Behaviour(event)); + } + ToSwarm::Dial { opts } => { + let peer_id = opts.get_peer_id(); + let connection_id = opts.connection_id(); + if let Ok(()) = self.dial(opts) { + self.pending_swarm_events.push_back(SwarmEvent::Dialing { + peer_id, + connection_id, + }); + } + } + ToSwarm::ListenOn { opts } => { + // Error is dispatched internally, safe to ignore. + let _ = self.add_listener(opts); + } + ToSwarm::RemoveListener { id } => { + self.remove_listener(id); + } + ToSwarm::NotifyHandler { + peer_id, + handler, + event, + } => { + assert!(self.pending_handler_event.is_none()); + let handler = match handler { + NotifyHandler::One(connection) => PendingNotifyHandler::One(connection), + NotifyHandler::Any => { + let ids = self + .pool + .iter_established_connections_of_peer(&peer_id) + .collect(); + PendingNotifyHandler::Any(ids) + } + }; + + self.pending_handler_event = Some((peer_id, handler, event)); + } + ToSwarm::NewExternalAddrCandidate(addr) => { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrCandidate( + NewExternalAddrCandidate { addr: &addr }, + )); + self.pending_swarm_events + .push_back(SwarmEvent::NewExternalAddrCandidate { address: addr }); + } + ToSwarm::ExternalAddrConfirmed(addr) => { + self.add_external_address(addr.clone()); + self.pending_swarm_events + .push_back(SwarmEvent::ExternalAddrConfirmed { address: addr }); + } + ToSwarm::ExternalAddrExpired(addr) => { + self.remove_external_address(&addr); + self.pending_swarm_events + .push_back(SwarmEvent::ExternalAddrExpired { address: addr }); + } + ToSwarm::CloseConnection { + peer_id, + connection, + } => match connection { + CloseConnection::One(connection_id) => { + if let Some(conn) = self.pool.get_established(connection_id) { + conn.start_close(); + } + } + CloseConnection::All => { + self.pool.disconnect(peer_id); + } + }, + ToSwarm::NewExternalAddrOfPeer { peer_id, address } => { + self.behaviour + .on_swarm_event(FromSwarm::NewExternalAddrOfPeer(NewExternalAddrOfPeer { + peer_id, + addr: &address, + })); + self.pending_swarm_events + .push_back(SwarmEvent::NewExternalAddrOfPeer { peer_id, address }); + } + } + } + + /// Internal function used by everything event-related. + /// + /// Polls the `Swarm` for the next event. + #[tracing::instrument(level = "debug", name = "Swarm::poll", skip(self, cx))] + fn poll_next_event( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + // We use a `this` variable because the compiler can't mutably borrow multiple times + // across a `Deref`. + let this = &mut *self; + + // This loop polls the components below in a prioritized order. + // + // 1. [`NetworkBehaviour`] + // 2. Connection [`Pool`] + // 3. [`ListenersStream`] + // + // (1) is polled before (2) to prioritize local work over work coming from a remote. + // + // (2) is polled before (3) to prioritize existing connections over upgrading new incoming connections. + loop { + if let Some(swarm_event) = this.pending_swarm_events.pop_front() { + return Poll::Ready(swarm_event); + } + + match this.pending_handler_event.take() { + // Try to deliver the pending event emitted by the [`NetworkBehaviour`] in the previous + // iteration to the connection handler(s). + Some((peer_id, handler, event)) => match handler { + PendingNotifyHandler::One(conn_id) => { + match this.pool.get_established(conn_id) { + Some(conn) => match notify_one(conn, event, cx) { + None => continue, + Some(event) => { + this.pending_handler_event = Some((peer_id, handler, event)); + } + }, + None => continue, + } + } + PendingNotifyHandler::Any(ids) => { + match notify_any::<_, TBehaviour>(ids, &mut this.pool, event, cx) { + None => continue, + Some((event, ids)) => { + let handler = PendingNotifyHandler::Any(ids); + this.pending_handler_event = Some((peer_id, handler, event)); + } + } + } + }, + // No pending event. Allow the [`NetworkBehaviour`] to make progress. + None => match this.behaviour.poll(cx) { + Poll::Pending => {} + Poll::Ready(behaviour_event) => { + this.handle_behaviour_event(behaviour_event); + + continue; + } + }, + } + + // Poll the known peers. + match this.pool.poll(cx) { + Poll::Pending => {} + Poll::Ready(pool_event) => { + this.handle_pool_event(pool_event); + continue; + } + } + + // Poll the listener(s) for new connections. + match Pin::new(&mut this.transport).poll(cx) { + Poll::Pending => {} + Poll::Ready(transport_event) => { + this.handle_transport_event(transport_event); + continue; + } + } + + return Poll::Pending; + } + } +} + +/// Connection to notify of a pending event. +/// +/// The connection IDs out of which to notify one of an event are captured at +/// the time the behaviour emits the event, in order not to forward the event to +/// a new connection which the behaviour may not have been aware of at the time +/// it issued the request for sending it. +enum PendingNotifyHandler { + One(ConnectionId), + Any(SmallVec<[ConnectionId; 10]>), +} + +/// Notify a single connection of an event. +/// +/// Returns `Some` with the given event if the connection is not currently +/// ready to receive another event, in which case the current task is +/// scheduled to be woken up. +/// +/// Returns `None` if the connection is closing or the event has been +/// successfully sent, in either case the event is consumed. +fn notify_one( + conn: &mut EstablishedConnection, + event: THandlerInEvent, + cx: &mut Context<'_>, +) -> Option { + match conn.poll_ready_notify_handler(cx) { + Poll::Pending => Some(event), + Poll::Ready(Err(())) => None, // connection is closing + Poll::Ready(Ok(())) => { + // Can now only fail if connection is closing. + let _ = conn.notify_handler(event); + None + } + } +} + +/// Notify any one of a given list of connections of a peer of an event. +/// +/// Returns `Some` with the given event and a new list of connections if +/// none of the given connections was able to receive the event but at +/// least one of them is not closing, in which case the current task +/// is scheduled to be woken up. The returned connections are those which +/// may still become ready to receive another event. +/// +/// Returns `None` if either all connections are closing or the event +/// was successfully sent to a handler, in either case the event is consumed. +fn notify_any( + ids: SmallVec<[ConnectionId; 10]>, + pool: &mut Pool, + event: THandlerInEvent, + cx: &mut Context<'_>, +) -> Option<(THandlerInEvent, SmallVec<[ConnectionId; 10]>)> +where + TBehaviour: NetworkBehaviour, + THandler: ConnectionHandler< + FromBehaviour = THandlerInEvent, + ToBehaviour = THandlerOutEvent, + >, +{ + let mut pending = SmallVec::new(); + let mut event = Some(event); // (1) + for id in ids.into_iter() { + if let Some(conn) = pool.get_established(id) { + match conn.poll_ready_notify_handler(cx) { + Poll::Pending => pending.push(id), + Poll::Ready(Err(())) => {} // connection is closing + Poll::Ready(Ok(())) => { + let e = event.take().expect("by (1),(2)"); + if let Err(e) = conn.notify_handler(e) { + event = Some(e) // (2) + } else { + break; + } + } + } + } + } + + event.and_then(|e| { + if !pending.is_empty() { + Some((e, pending)) + } else { + None + } + }) +} + +/// Stream of events returned by [`Swarm`]. +/// +/// Includes events from the [`NetworkBehaviour`] as well as events about +/// connection and listener status. See [`SwarmEvent`] for details. +/// +/// Note: This stream is infinite and it is guaranteed that +/// [`futures::Stream::poll_next`] will never return `Poll::Ready(None)`. +impl futures::Stream for Swarm +where + TBehaviour: NetworkBehaviour, +{ + type Item = SwarmEvent>; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.as_mut().poll_next_event(cx).map(Some) + } +} + +/// The stream of swarm events never terminates, so we can implement fused for it. +impl FusedStream for Swarm +where + TBehaviour: NetworkBehaviour, +{ + fn is_terminated(&self) -> bool { + false + } +} + +pub struct Config { + pool_config: PoolConfig, +} + +impl Config { + /// Creates a new [`Config`] from the given executor. The [`Swarm`] is obtained via + /// [`Swarm::new`]. + pub fn with_executor(executor: impl Executor + Send + 'static) -> Self { + Self { + pool_config: PoolConfig::new(Some(Box::new(executor))), + } + } + + #[doc(hidden)] + /// Used on connection benchmarks. + pub fn without_executor() -> Self { + Self { + pool_config: PoolConfig::new(None), + } + } + + /// Sets executor to the `wasm` executor. + /// Background tasks will be executed by the browser on the next micro-tick. + /// + /// Spawning a task is similar too: + /// ```typescript + /// function spawn(task: () => Promise) { + /// task() + /// } + /// ``` + #[cfg(feature = "wasm-bindgen")] + pub fn with_wasm_executor() -> Self { + Self::with_executor(crate::executor::WasmBindgenExecutor) + } + + /// Builds a new [`Config`] from the given `tokio` executor. + #[cfg(all( + feature = "tokio", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) + ))] + pub fn with_tokio_executor() -> Self { + Self::with_executor(crate::executor::TokioExecutor) + } + + /// Builds a new [`Config`] from the given `async-std` executor. + #[cfg(all( + feature = "async-std", + not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")) + ))] + pub fn with_async_std_executor() -> Self { + Self::with_executor(crate::executor::AsyncStdExecutor) + } + + /// Configures the number of events from the [`NetworkBehaviour`] in + /// destination to the [`ConnectionHandler`] that can be buffered before + /// the [`Swarm`] has to wait. An individual buffer with this number of + /// events exists for each individual connection. + /// + /// The ideal value depends on the executor used, the CPU speed, and the + /// volume of events. If this value is too low, then the [`Swarm`] will + /// be sleeping more often than necessary. Increasing this value increases + /// the overall memory usage. + pub fn with_notify_handler_buffer_size(mut self, n: NonZeroUsize) -> Self { + self.pool_config = self.pool_config.with_notify_handler_buffer_size(n); + self + } + + /// Configures the size of the buffer for events sent by a [`ConnectionHandler`] to the + /// [`NetworkBehaviour`]. + /// + /// Each connection has its own buffer. + /// + /// The ideal value depends on the executor used, the CPU speed and the volume of events. + /// If this value is too low, then the [`ConnectionHandler`]s will be sleeping more often + /// than necessary. Increasing this value increases the overall memory + /// usage, and more importantly the latency between the moment when an + /// event is emitted and the moment when it is received by the + /// [`NetworkBehaviour`]. + pub fn with_per_connection_event_buffer_size(mut self, n: usize) -> Self { + self.pool_config = self.pool_config.with_per_connection_event_buffer_size(n); + self + } + + /// Number of addresses concurrently dialed for a single outbound connection attempt. + pub fn with_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self { + self.pool_config = self.pool_config.with_dial_concurrency_factor(factor); + self + } + + /// Configures an override for the substream upgrade protocol to use. + /// + /// The subtream upgrade protocol is the multistream-select protocol + /// used for protocol negotiation on substreams. Since a listener + /// supports all existing versions, the choice of upgrade protocol + /// only effects the "dialer", i.e. the peer opening a substream. + /// + /// > **Note**: If configured, specific upgrade protocols for + /// > individual [`SubstreamProtocol`]s emitted by the `NetworkBehaviour` + /// > are ignored. + pub fn with_substream_upgrade_protocol_override( + mut self, + v: libp2p_core::upgrade::Version, + ) -> Self { + self.pool_config = self.pool_config.with_substream_upgrade_protocol_override(v); + self + } + + /// The maximum number of inbound streams concurrently negotiating on a + /// connection. New inbound streams exceeding the limit are dropped and thus + /// reset. + /// + /// Note: This only enforces a limit on the number of concurrently + /// negotiating inbound streams. The total number of inbound streams on a + /// connection is the sum of negotiating and negotiated streams. A limit on + /// the total number of streams can be enforced at the + /// [`StreamMuxerBox`] level. + pub fn with_max_negotiating_inbound_streams(mut self, v: usize) -> Self { + self.pool_config = self.pool_config.with_max_negotiating_inbound_streams(v); + self + } + + /// How long to keep a connection alive once it is idling. + /// + /// Defaults to 0. + pub fn with_idle_connection_timeout(mut self, timeout: Duration) -> Self { + self.pool_config.idle_connection_timeout = timeout; + self + } +} + +/// Possible errors when trying to establish or upgrade an outbound connection. +#[derive(Debug)] +pub enum DialError { + /// The peer identity obtained on the connection matches the local peer. + LocalPeerId { endpoint: ConnectedPoint }, + /// No addresses have been provided by [`NetworkBehaviour::handle_pending_outbound_connection`] and [`DialOpts`]. + NoAddresses, + /// The provided [`dial_opts::PeerCondition`] evaluated to false and thus + /// the dial was aborted. + DialPeerConditionFalse(dial_opts::PeerCondition), + /// Pending connection attempt has been aborted. + Aborted, + /// The peer identity obtained on the connection did not match the one that was expected. + WrongPeerId { + obtained: PeerId, + endpoint: ConnectedPoint, + }, + /// One of the [`NetworkBehaviour`]s rejected the outbound connection + /// via [`NetworkBehaviour::handle_pending_outbound_connection`] or + /// [`NetworkBehaviour::handle_established_outbound_connection`]. + Denied { cause: ConnectionDenied }, + /// An error occurred while negotiating the transport protocol(s) on a connection. + Transport(Vec<(Multiaddr, TransportError)>), +} + +impl From for DialError { + fn from(error: PendingOutboundConnectionError) -> Self { + match error { + PendingConnectionError::Aborted => DialError::Aborted, + PendingConnectionError::WrongPeerId { obtained, endpoint } => { + DialError::WrongPeerId { obtained, endpoint } + } + PendingConnectionError::LocalPeerId { endpoint } => DialError::LocalPeerId { endpoint }, + PendingConnectionError::Transport(e) => DialError::Transport(e), + } + } +} + +impl fmt::Display for DialError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DialError::NoAddresses => write!(f, "Dial error: no addresses for peer."), + DialError::LocalPeerId { endpoint } => write!( + f, + "Dial error: tried to dial local peer id at {endpoint:?}." + ), + DialError::DialPeerConditionFalse(PeerCondition::Disconnected) => write!(f, "Dial error: dial condition was configured to only happen when disconnected (`PeerCondition::Disconnected`), but node is already connected, thus cancelling new dial."), + DialError::DialPeerConditionFalse(PeerCondition::NotDialing) => write!(f, "Dial error: dial condition was configured to only happen if there is currently no ongoing dialing attempt (`PeerCondition::NotDialing`), but a dial is in progress, thus cancelling new dial."), + DialError::DialPeerConditionFalse(PeerCondition::DisconnectedAndNotDialing) => write!(f, "Dial error: dial condition was configured to only happen when both disconnected (`PeerCondition::Disconnected`) and there is currently no ongoing dialing attempt (`PeerCondition::NotDialing`), but node is already connected or dial is in progress, thus cancelling new dial."), + DialError::DialPeerConditionFalse(PeerCondition::Always) => unreachable!("Dial peer condition is by definition true."), + DialError::Aborted => write!( + f, + "Dial error: Pending connection attempt has been aborted." + ), + DialError::WrongPeerId { obtained, endpoint } => write!( + f, + "Dial error: Unexpected peer ID {obtained} at {endpoint:?}." + ), + DialError::Transport(errors) => { + write!(f, "Failed to negotiate transport protocol(s): [")?; + + for (addr, error) in errors { + write!(f, "({addr}")?; + print_error_chain(f, error)?; + write!(f, ")")?; + } + write!(f, "]")?; + + Ok(()) + } + DialError::Denied { .. } => { + write!(f, "Dial error") + } + } + } +} + +fn print_error_chain(f: &mut fmt::Formatter<'_>, e: &dyn error::Error) -> fmt::Result { + write!(f, ": {e}")?; + + if let Some(source) = e.source() { + print_error_chain(f, source)?; + } + + Ok(()) +} + +impl error::Error for DialError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + DialError::LocalPeerId { .. } => None, + DialError::NoAddresses => None, + DialError::DialPeerConditionFalse(_) => None, + DialError::Aborted => None, + DialError::WrongPeerId { .. } => None, + DialError::Transport(_) => None, + DialError::Denied { cause } => Some(cause), + } + } +} + +/// Possible errors when upgrading an inbound connection. +#[derive(Debug)] +pub enum ListenError { + /// Pending connection attempt has been aborted. + Aborted, + /// The peer identity obtained on the connection did not match the one that was expected. + WrongPeerId { + obtained: PeerId, + endpoint: ConnectedPoint, + }, + /// The connection was dropped because it resolved to our own [`PeerId`]. + LocalPeerId { + endpoint: ConnectedPoint, + }, + Denied { + cause: ConnectionDenied, + }, + /// An error occurred while negotiating the transport protocol(s) on a connection. + Transport(TransportError), +} + +impl From for ListenError { + fn from(error: PendingInboundConnectionError) -> Self { + match error { + PendingInboundConnectionError::Transport(inner) => ListenError::Transport(inner), + PendingInboundConnectionError::Aborted => ListenError::Aborted, + PendingInboundConnectionError::WrongPeerId { obtained, endpoint } => { + ListenError::WrongPeerId { obtained, endpoint } + } + PendingInboundConnectionError::LocalPeerId { endpoint } => { + ListenError::LocalPeerId { endpoint } + } + } + } +} + +impl fmt::Display for ListenError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ListenError::Aborted => write!( + f, + "Listen error: Pending connection attempt has been aborted." + ), + ListenError::WrongPeerId { obtained, endpoint } => write!( + f, + "Listen error: Unexpected peer ID {obtained} at {endpoint:?}." + ), + ListenError::Transport(_) => { + write!(f, "Listen error: Failed to negotiate transport protocol(s)") + } + ListenError::Denied { cause } => { + write!(f, "Listen error: Denied: {cause}") + } + ListenError::LocalPeerId { endpoint } => { + write!(f, "Listen error: Local peer ID at {endpoint:?}.") + } + } + } +} + +impl error::Error for ListenError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + ListenError::WrongPeerId { .. } => None, + ListenError::Transport(err) => Some(err), + ListenError::Aborted => None, + ListenError::Denied { cause } => Some(cause), + ListenError::LocalPeerId { .. } => None, + } + } +} + +/// A connection was denied. +/// +/// To figure out which [`NetworkBehaviour`] denied the connection, use [`ConnectionDenied::downcast`]. +#[derive(Debug)] +pub struct ConnectionDenied { + inner: Box, +} + +impl ConnectionDenied { + pub fn new(cause: impl Into>) -> Self { + Self { + inner: cause.into(), + } + } + + /// Attempt to downcast to a particular reason for why the connection was denied. + pub fn downcast(self) -> Result + where + E: error::Error + Send + Sync + 'static, + { + let inner = self + .inner + .downcast::() + .map_err(|inner| ConnectionDenied { inner })?; + + Ok(*inner) + } + + /// Attempt to downcast to a particular reason for why the connection was denied. + pub fn downcast_ref(&self) -> Option<&E> + where + E: error::Error + Send + Sync + 'static, + { + self.inner.downcast_ref::() + } +} + +impl fmt::Display for ConnectionDenied { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "connection denied") + } +} + +impl error::Error for ConnectionDenied { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(self.inner.as_ref()) + } +} + +/// Information about the connections obtained by [`Swarm::network_info()`]. +#[derive(Clone, Debug)] +pub struct NetworkInfo { + /// The total number of connected peers. + num_peers: usize, + /// Counters of ongoing network connections. + connection_counters: ConnectionCounters, +} + +impl NetworkInfo { + /// The number of connected peers, i.e. peers with whom at least + /// one established connection exists. + pub fn num_peers(&self) -> usize { + self.num_peers + } + + /// Gets counters for ongoing network connections. + pub fn connection_counters(&self) -> &ConnectionCounters { + &self.connection_counters + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test::{CallTraceBehaviour, MockBehaviour}; + use libp2p_core::multiaddr::multiaddr; + use libp2p_core::transport::memory::MemoryTransportError; + use libp2p_core::transport::{PortUse, TransportEvent}; + use libp2p_core::Endpoint; + use libp2p_core::{multiaddr, transport, upgrade}; + use libp2p_identity as identity; + use libp2p_plaintext as plaintext; + use libp2p_yamux as yamux; + use quickcheck::*; + + // Test execution state. + // Connection => Disconnecting => Connecting. + enum State { + Connecting, + Disconnecting, + } + + fn new_test_swarm( + config: Config, + ) -> Swarm>> { + let id_keys = identity::Keypair::generate_ed25519(); + let local_public_key = id_keys.public(); + let transport = transport::MemoryTransport::default() + .upgrade(upgrade::Version::V1) + .authenticate(plaintext::Config::new(&id_keys)) + .multiplex(yamux::Config::default()) + .boxed(); + let behaviour = CallTraceBehaviour::new(MockBehaviour::new(dummy::ConnectionHandler)); + + Swarm::new( + transport, + behaviour, + local_public_key.into(), + config.with_idle_connection_timeout(Duration::from_secs(5)), + ) + } + + fn swarms_connected( + swarm1: &Swarm>, + swarm2: &Swarm>, + num_connections: usize, + ) -> bool + where + TBehaviour: NetworkBehaviour, + THandlerOutEvent: Clone, + { + swarm1 + .behaviour() + .num_connections_to_peer(*swarm2.local_peer_id()) + == num_connections + && swarm2 + .behaviour() + .num_connections_to_peer(*swarm1.local_peer_id()) + == num_connections + && swarm1.is_connected(swarm2.local_peer_id()) + && swarm2.is_connected(swarm1.local_peer_id()) + } + + fn swarms_disconnected( + swarm1: &Swarm>, + swarm2: &Swarm>, + ) -> bool + where + TBehaviour: NetworkBehaviour, + THandlerOutEvent: Clone, + { + swarm1 + .behaviour() + .num_connections_to_peer(*swarm2.local_peer_id()) + == 0 + && swarm2 + .behaviour() + .num_connections_to_peer(*swarm1.local_peer_id()) + == 0 + && !swarm1.is_connected(swarm2.local_peer_id()) + && !swarm2.is_connected(swarm1.local_peer_id()) + } + + /// Establishes multiple connections between two peers, + /// after which one peer disconnects the other using [`Swarm::disconnect_peer_id`]. + /// + /// The test expects both behaviours to be notified via calls to [`NetworkBehaviour::on_swarm_event`] + /// with pairs of [`FromSwarm::ConnectionEstablished`] / [`FromSwarm::ConnectionClosed`] + #[tokio::test] + async fn test_swarm_disconnect() { + let mut swarm1 = new_test_swarm(Config::with_tokio_executor()); + let mut swarm2 = new_test_swarm(Config::with_tokio_executor()); + + let addr1: Multiaddr = multiaddr::Protocol::Memory(rand::random::()).into(); + let addr2: Multiaddr = multiaddr::Protocol::Memory(rand::random::()).into(); + + swarm1.listen_on(addr1.clone()).unwrap(); + swarm2.listen_on(addr2.clone()).unwrap(); + + let swarm1_id = *swarm1.local_peer_id(); + + let mut reconnected = false; + let num_connections = 10; + + for _ in 0..num_connections { + swarm1.dial(addr2.clone()).unwrap(); + } + let mut state = State::Connecting; + + future::poll_fn(move |cx| loop { + let poll1 = Swarm::poll_next_event(Pin::new(&mut swarm1), cx); + let poll2 = Swarm::poll_next_event(Pin::new(&mut swarm2), cx); + match state { + State::Connecting => { + if swarms_connected(&swarm1, &swarm2, num_connections) { + if reconnected { + return Poll::Ready(()); + } + swarm2 + .disconnect_peer_id(swarm1_id) + .expect("Error disconnecting"); + state = State::Disconnecting; + } + } + State::Disconnecting => { + if swarms_disconnected(&swarm1, &swarm2) { + if reconnected { + return Poll::Ready(()); + } + reconnected = true; + for _ in 0..num_connections { + swarm2.dial(addr1.clone()).unwrap(); + } + state = State::Connecting; + } + } + } + + if poll1.is_pending() && poll2.is_pending() { + return Poll::Pending; + } + }) + .await + } + + /// Establishes multiple connections between two peers, + /// after which one peer disconnects the other + /// using [`ToSwarm::CloseConnection`] returned by a [`NetworkBehaviour`]. + /// + /// The test expects both behaviours to be notified via calls to [`NetworkBehaviour::on_swarm_event`] + /// with pairs of [`FromSwarm::ConnectionEstablished`] / [`FromSwarm::ConnectionClosed`] + #[tokio::test] + async fn test_behaviour_disconnect_all() { + let mut swarm1 = new_test_swarm(Config::with_tokio_executor()); + let mut swarm2 = new_test_swarm(Config::with_tokio_executor()); + + let addr1: Multiaddr = multiaddr::Protocol::Memory(rand::random::()).into(); + let addr2: Multiaddr = multiaddr::Protocol::Memory(rand::random::()).into(); + + swarm1.listen_on(addr1.clone()).unwrap(); + swarm2.listen_on(addr2.clone()).unwrap(); + + let swarm1_id = *swarm1.local_peer_id(); + + let mut reconnected = false; + let num_connections = 10; + + for _ in 0..num_connections { + swarm1.dial(addr2.clone()).unwrap(); + } + let mut state = State::Connecting; + + future::poll_fn(move |cx| loop { + let poll1 = Swarm::poll_next_event(Pin::new(&mut swarm1), cx); + let poll2 = Swarm::poll_next_event(Pin::new(&mut swarm2), cx); + match state { + State::Connecting => { + if swarms_connected(&swarm1, &swarm2, num_connections) { + if reconnected { + return Poll::Ready(()); + } + swarm2 + .behaviour + .inner() + .next_action + .replace(ToSwarm::CloseConnection { + peer_id: swarm1_id, + connection: CloseConnection::All, + }); + state = State::Disconnecting; + continue; + } + } + State::Disconnecting => { + if swarms_disconnected(&swarm1, &swarm2) { + reconnected = true; + for _ in 0..num_connections { + swarm2.dial(addr1.clone()).unwrap(); + } + state = State::Connecting; + continue; + } + } + } + + if poll1.is_pending() && poll2.is_pending() { + return Poll::Pending; + } + }) + .await + } + + /// Establishes multiple connections between two peers, + /// after which one peer closes a single connection + /// using [`ToSwarm::CloseConnection`] returned by a [`NetworkBehaviour`]. + /// + /// The test expects both behaviours to be notified via calls to [`NetworkBehaviour::on_swarm_event`] + /// with pairs of [`FromSwarm::ConnectionEstablished`] / [`FromSwarm::ConnectionClosed`] + #[tokio::test] + async fn test_behaviour_disconnect_one() { + let mut swarm1 = new_test_swarm(Config::with_tokio_executor()); + let mut swarm2 = new_test_swarm(Config::with_tokio_executor()); + + let addr1: Multiaddr = multiaddr::Protocol::Memory(rand::random::()).into(); + let addr2: Multiaddr = multiaddr::Protocol::Memory(rand::random::()).into(); + + swarm1.listen_on(addr1).unwrap(); + swarm2.listen_on(addr2.clone()).unwrap(); + + let swarm1_id = *swarm1.local_peer_id(); + + let num_connections = 10; + + for _ in 0..num_connections { + swarm1.dial(addr2.clone()).unwrap(); + } + let mut state = State::Connecting; + let mut disconnected_conn_id = None; + + future::poll_fn(move |cx| loop { + let poll1 = Swarm::poll_next_event(Pin::new(&mut swarm1), cx); + let poll2 = Swarm::poll_next_event(Pin::new(&mut swarm2), cx); + match state { + State::Connecting => { + if swarms_connected(&swarm1, &swarm2, num_connections) { + disconnected_conn_id = { + let conn_id = + swarm2.behaviour.on_connection_established[num_connections / 2].1; + swarm2.behaviour.inner().next_action.replace( + ToSwarm::CloseConnection { + peer_id: swarm1_id, + connection: CloseConnection::One(conn_id), + }, + ); + Some(conn_id) + }; + state = State::Disconnecting; + } + } + State::Disconnecting => { + for s in &[&swarm1, &swarm2] { + assert!(s + .behaviour + .on_connection_closed + .iter() + .all(|(.., remaining_conns)| *remaining_conns > 0)); + assert_eq!(s.behaviour.on_connection_established.len(), num_connections); + s.behaviour.assert_connected(num_connections, 1); + } + if [&swarm1, &swarm2] + .iter() + .all(|s| s.behaviour.on_connection_closed.len() == 1) + { + let conn_id = swarm2.behaviour.on_connection_closed[0].1; + assert_eq!(Some(conn_id), disconnected_conn_id); + return Poll::Ready(()); + } + } + } + + if poll1.is_pending() && poll2.is_pending() { + return Poll::Pending; + } + }) + .await + } + + #[test] + fn concurrent_dialing() { + #[derive(Clone, Debug)] + struct DialConcurrencyFactor(NonZeroU8); + + impl Arbitrary for DialConcurrencyFactor { + fn arbitrary(g: &mut Gen) -> Self { + Self(NonZeroU8::new(g.gen_range(1..11)).unwrap()) + } + } + + fn prop(concurrency_factor: DialConcurrencyFactor) { + tokio::runtime::Runtime::new().unwrap().block_on(async { + let mut swarm = new_test_swarm( + Config::with_tokio_executor() + .with_dial_concurrency_factor(concurrency_factor.0), + ); + + // Listen on `concurrency_factor + 1` addresses. + // + // `+ 2` to ensure a subset of addresses is dialed by network_2. + let num_listen_addrs = concurrency_factor.0.get() + 2; + let mut listen_addresses = Vec::new(); + let mut transports = Vec::new(); + for _ in 0..num_listen_addrs { + let mut transport = transport::MemoryTransport::default().boxed(); + transport + .listen_on(ListenerId::next(), "/memory/0".parse().unwrap()) + .unwrap(); + + match transport.select_next_some().await { + TransportEvent::NewAddress { listen_addr, .. } => { + listen_addresses.push(listen_addr); + } + _ => panic!("Expected `NewListenAddr` event."), + } + + transports.push(transport); + } + + // Have swarm dial each listener and wait for each listener to receive the incoming + // connections. + swarm + .dial( + DialOpts::peer_id(PeerId::random()) + .addresses(listen_addresses) + .build(), + ) + .unwrap(); + for mut transport in transports.into_iter() { + match futures::future::select(transport.select_next_some(), swarm.next()).await + { + future::Either::Left((TransportEvent::Incoming { .. }, _)) => {} + future::Either::Left(_) => { + panic!("Unexpected transport event.") + } + future::Either::Right((e, _)) => { + panic!("Expect swarm to not emit any event {e:?}") + } + } + } + + match swarm.next().await.unwrap() { + SwarmEvent::OutgoingConnectionError { .. } => {} + e => panic!("Unexpected swarm event {e:?}"), + } + }) + } + + QuickCheck::new().tests(10).quickcheck(prop as fn(_) -> _); + } + + #[tokio::test] + async fn invalid_peer_id() { + // Checks whether dialing an address containing the wrong peer id raises an error + // for the expected peer id instead of the obtained peer id. + + let mut swarm1 = new_test_swarm(Config::with_tokio_executor()); + let mut swarm2 = new_test_swarm(Config::with_tokio_executor()); + + swarm1.listen_on("/memory/0".parse().unwrap()).unwrap(); + + let address = future::poll_fn(|cx| match swarm1.poll_next_unpin(cx) { + Poll::Ready(Some(SwarmEvent::NewListenAddr { address, .. })) => Poll::Ready(address), + Poll::Pending => Poll::Pending, + _ => panic!("Was expecting the listen address to be reported"), + }) + .await; + + let other_id = PeerId::random(); + let other_addr = address.with(multiaddr::Protocol::P2p(other_id)); + + swarm2.dial(other_addr.clone()).unwrap(); + + let (peer_id, error) = future::poll_fn(|cx| { + if let Poll::Ready(Some(SwarmEvent::IncomingConnection { .. })) = + swarm1.poll_next_unpin(cx) + {} + + match swarm2.poll_next_unpin(cx) { + Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { + peer_id, error, .. + })) => Poll::Ready((peer_id, error)), + Poll::Ready(x) => panic!("unexpected {x:?}"), + Poll::Pending => Poll::Pending, + } + }) + .await; + assert_eq!(peer_id.unwrap(), other_id); + match error { + DialError::WrongPeerId { obtained, endpoint } => { + assert_eq!(obtained, *swarm1.local_peer_id()); + assert_eq!( + endpoint, + ConnectedPoint::Dialer { + address: other_addr, + role_override: Endpoint::Dialer, + port_use: PortUse::Reuse, + } + ); + } + x => panic!("wrong error {x:?}"), + } + } + + #[tokio::test] + async fn dial_self() { + // Check whether dialing ourselves correctly fails. + // + // Dialing the same address we're listening should result in three events: + // + // - The incoming connection notification (before we know the incoming peer ID). + // - The connection error for the dialing endpoint (once we've determined that it's our own ID). + // - The connection error for the listening endpoint (once we've determined that it's our own ID). + // + // The last two can happen in any order. + + let mut swarm = new_test_swarm(Config::with_tokio_executor()); + swarm.listen_on("/memory/0".parse().unwrap()).unwrap(); + + let local_address = future::poll_fn(|cx| match swarm.poll_next_unpin(cx) { + Poll::Ready(Some(SwarmEvent::NewListenAddr { address, .. })) => Poll::Ready(address), + Poll::Pending => Poll::Pending, + _ => panic!("Was expecting the listen address to be reported"), + }) + .await; + + swarm.listened_addrs.clear(); // This is a hack to actually execute the dial to ourselves which would otherwise be filtered. + + swarm.dial(local_address.clone()).unwrap(); + + let mut got_dial_err = false; + let mut got_inc_err = false; + future::poll_fn(|cx| -> Poll> { + loop { + match swarm.poll_next_unpin(cx) { + Poll::Ready(Some(SwarmEvent::OutgoingConnectionError { + peer_id, + error: DialError::LocalPeerId { .. }, + .. + })) => { + assert_eq!(&peer_id.unwrap(), swarm.local_peer_id()); + assert!(!got_dial_err); + got_dial_err = true; + if got_inc_err { + return Poll::Ready(Ok(())); + } + } + Poll::Ready(Some(SwarmEvent::IncomingConnectionError { + local_addr, .. + })) => { + assert!(!got_inc_err); + assert_eq!(local_addr, local_address); + got_inc_err = true; + if got_dial_err { + return Poll::Ready(Ok(())); + } + } + Poll::Ready(Some(SwarmEvent::IncomingConnection { local_addr, .. })) => { + assert_eq!(local_addr, local_address); + } + Poll::Ready(ev) => { + panic!("Unexpected event: {ev:?}") + } + Poll::Pending => break Poll::Pending, + } + } + }) + .await + .unwrap(); + } + + #[tokio::test] + async fn dial_self_by_id() { + // Trying to dial self by passing the same `PeerId` shouldn't even be possible in the first + // place. + let swarm = new_test_swarm(Config::with_tokio_executor()); + let peer_id = *swarm.local_peer_id(); + assert!(!swarm.is_connected(&peer_id)); + } + + #[tokio::test] + async fn multiple_addresses_err() { + // Tries dialing multiple addresses, and makes sure there's one dialing error per address. + + let target = PeerId::random(); + + let mut swarm = new_test_swarm(Config::with_tokio_executor()); + + let addresses = HashSet::from([ + multiaddr![Ip4([0, 0, 0, 0]), Tcp(rand::random::())], + multiaddr![Ip4([0, 0, 0, 0]), Tcp(rand::random::())], + multiaddr![Ip4([0, 0, 0, 0]), Tcp(rand::random::())], + multiaddr![Udp(rand::random::())], + multiaddr![Udp(rand::random::())], + multiaddr![Udp(rand::random::())], + multiaddr![Udp(rand::random::())], + multiaddr![Udp(rand::random::())], + ]); + + swarm + .dial( + DialOpts::peer_id(target) + .addresses(addresses.iter().cloned().collect()) + .build(), + ) + .unwrap(); + + match swarm.next().await.unwrap() { + SwarmEvent::OutgoingConnectionError { + peer_id, + // multiaddr, + error: DialError::Transport(errors), + .. + } => { + assert_eq!(target, peer_id.unwrap()); + + let failed_addresses = errors.into_iter().map(|(addr, _)| addr).collect::>(); + let expected_addresses = addresses + .into_iter() + .map(|addr| addr.with(multiaddr::Protocol::P2p(target))) + .collect::>(); + + assert_eq!(expected_addresses, failed_addresses); + } + e => panic!("Unexpected event: {e:?}"), + } + } + + #[tokio::test] + async fn aborting_pending_connection_surfaces_error() { + let _ = tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .try_init(); + + let mut dialer = new_test_swarm(Config::with_tokio_executor()); + let mut listener = new_test_swarm(Config::with_tokio_executor()); + + let listener_peer_id = *listener.local_peer_id(); + listener.listen_on(multiaddr![Memory(0u64)]).unwrap(); + let listener_address = match listener.next().await.unwrap() { + SwarmEvent::NewListenAddr { address, .. } => address, + e => panic!("Unexpected network event: {e:?}"), + }; + + dialer + .dial( + DialOpts::peer_id(listener_peer_id) + .addresses(vec![listener_address]) + .build(), + ) + .unwrap(); + + dialer + .disconnect_peer_id(listener_peer_id) + .expect_err("Expect peer to not yet be connected."); + + match dialer.next().await.unwrap() { + SwarmEvent::OutgoingConnectionError { + error: DialError::Aborted, + .. + } => {} + e => panic!("Unexpected swarm event {e:?}."), + } + } + + #[test] + fn dial_error_prints_sources() { + // This constitutes a fairly typical error for chained transports. + let error = DialError::Transport(vec![( + "/ip4/127.0.0.1/tcp/80".parse().unwrap(), + TransportError::Other(io::Error::new( + io::ErrorKind::Other, + MemoryTransportError::Unreachable, + )), + )]); + + let string = format!("{error}"); + + // Unfortunately, we have some "empty" errors that lead to multiple colons without text but that is the best we can do. + assert_eq!("Failed to negotiate transport protocol(s): [(/ip4/127.0.0.1/tcp/80: : No listener on the given port.)]", string) + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/listen_opts.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/listen_opts.rs new file mode 100644 index 00000000000..9c4d69a6fa0 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/listen_opts.rs @@ -0,0 +1,33 @@ +use crate::ListenerId; +use libp2p_core::Multiaddr; + +#[derive(Debug)] +pub struct ListenOpts { + id: ListenerId, + address: Multiaddr, +} + +impl ListenOpts { + pub fn new(address: Multiaddr) -> ListenOpts { + ListenOpts { + id: ListenerId::next(), + address, + } + } + + /// Get the [`ListenerId`] of this listen attempt + pub fn listener_id(&self) -> ListenerId { + self.id + } + + /// Get the [`Multiaddr`] that is being listened on + pub fn address(&self) -> &Multiaddr { + &self.address + } +} + +impl From for ListenOpts { + fn from(addr: Multiaddr) -> Self { + ListenOpts::new(addr) + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/stream.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/stream.rs new file mode 100644 index 00000000000..871352f3c6a --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/stream.rs @@ -0,0 +1,98 @@ +use futures::{AsyncRead, AsyncWrite}; +use libp2p_core::muxing::SubstreamBox; +use libp2p_core::Negotiated; +use std::{ + io::{IoSlice, IoSliceMut}, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; + +/// Counter for the number of active streams on a connection. +#[derive(Debug, Clone)] +pub(crate) struct ActiveStreamCounter(Arc<()>); + +impl ActiveStreamCounter { + pub(crate) fn default() -> Self { + Self(Arc::new(())) + } + + pub(crate) fn has_no_active_streams(&self) -> bool { + self.num_alive_streams() == 1 + } + + fn num_alive_streams(&self) -> usize { + Arc::strong_count(&self.0) + } +} + +#[derive(Debug)] +pub struct Stream { + stream: Negotiated, + counter: Option, +} + +impl Stream { + pub(crate) fn new(stream: Negotiated, counter: ActiveStreamCounter) -> Self { + Self { + stream, + counter: Some(counter), + } + } + + /// Ignore this stream in the [Swarm](crate::Swarm)'s connection-keep-alive algorithm. + /// + /// By default, any active stream keeps a connection alive. For most protocols, + /// this is a good default as it ensures that the protocol is completed before + /// a connection is shut down. + /// Some protocols like libp2p's [ping](https://github.com/libp2p/specs/blob/master/ping/ping.md) + /// for example never complete and are of an auxiliary nature. + /// These protocols should opt-out of the keep alive algorithm using this method. + pub fn ignore_for_keep_alive(&mut self) { + self.counter.take(); + } +} + +impl AsyncRead for Stream { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.get_mut().stream).poll_read(cx, buf) + } + + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + Pin::new(&mut self.get_mut().stream).poll_read_vectored(cx, bufs) + } +} + +impl AsyncWrite for Stream { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.get_mut().stream).poll_write(cx, buf) + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Pin::new(&mut self.get_mut().stream).poll_write_vectored(cx, bufs) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.get_mut().stream).poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.get_mut().stream).poll_close(cx) + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/stream_protocol.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/stream_protocol.rs new file mode 100644 index 00000000000..f746429a3d7 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/stream_protocol.rs @@ -0,0 +1,132 @@ +use either::Either; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::sync::Arc; + +/// Identifies a protocol for a stream. +/// +/// libp2p nodes use stream protocols to negotiate what to do with a newly opened stream. +/// Stream protocols are string-based and must start with a forward slash: `/`. +#[derive(Clone, Eq)] +pub struct StreamProtocol { + inner: Either<&'static str, Arc>, +} + +impl StreamProtocol { + /// Construct a new protocol from a static string slice. + /// + /// # Panics + /// + /// This function panics if the protocol does not start with a forward slash: `/`. + pub const fn new(s: &'static str) -> Self { + match s.as_bytes() { + [b'/', ..] => {} + _ => panic!("Protocols should start with a /"), + } + + StreamProtocol { + inner: Either::Left(s), + } + } + + /// Attempt to construct a protocol from an owned string. + /// + /// This function will fail if the protocol does not start with a forward slash: `/`. + /// Where possible, you should use [`StreamProtocol::new`] instead to avoid allocations. + pub fn try_from_owned(protocol: String) -> Result { + if !protocol.starts_with('/') { + return Err(InvalidProtocol::missing_forward_slash()); + } + + Ok(StreamProtocol { + inner: Either::Right(Arc::from(protocol)), // FIXME: Can we somehow reuse the allocation from the owned string? + }) + } +} + +impl AsRef for StreamProtocol { + fn as_ref(&self) -> &str { + either::for_both!(&self.inner, s => s) + } +} + +impl fmt::Debug for StreamProtocol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + either::for_both!(&self.inner, s => s.fmt(f)) + } +} + +impl fmt::Display for StreamProtocol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl PartialEq<&str> for StreamProtocol { + fn eq(&self, other: &&str) -> bool { + self.as_ref() == *other + } +} + +impl PartialEq for &str { + fn eq(&self, other: &StreamProtocol) -> bool { + *self == other.as_ref() + } +} + +impl PartialEq for StreamProtocol { + fn eq(&self, other: &Self) -> bool { + self.as_ref() == other.as_ref() + } +} + +impl Hash for StreamProtocol { + fn hash(&self, state: &mut H) { + self.as_ref().hash(state) + } +} + +#[derive(Debug)] +pub struct InvalidProtocol { + // private field to prevent construction outside of this module + _private: (), +} + +impl InvalidProtocol { + pub(crate) fn missing_forward_slash() -> Self { + InvalidProtocol { _private: () } + } +} + +impl fmt::Display for InvalidProtocol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "invalid protocol: string does not start with a forward slash" + ) + } +} + +impl std::error::Error for InvalidProtocol {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn stream_protocol_print() { + let protocol = StreamProtocol::new("/foo/bar/1.0.0"); + + let debug = format!("{protocol:?}"); + let display = format!("{protocol}"); + + assert_eq!( + debug, r#""/foo/bar/1.0.0""#, + "protocol to debug print as string with quotes" + ); + assert_eq!( + display, "/foo/bar/1.0.0", + "protocol to display print as string without quotes" + ); + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/test.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/test.rs new file mode 100644 index 00000000000..a6cb7c4d4eb --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/test.rs @@ -0,0 +1,522 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::behaviour::{ + ConnectionClosed, ConnectionEstablished, DialFailure, ExpiredListenAddr, ExternalAddrExpired, + FromSwarm, ListenerClosed, ListenerError, NewExternalAddrCandidate, NewListenAddr, NewListener, +}; +use crate::{ + ConnectionDenied, ConnectionHandler, ConnectionId, NetworkBehaviour, THandler, THandlerInEvent, + THandlerOutEvent, ToSwarm, +}; +use libp2p_core::transport::PortUse; +use libp2p_core::{multiaddr::Multiaddr, transport::ListenerId, ConnectedPoint, Endpoint}; +use libp2p_identity::PeerId; +use std::collections::HashMap; +use std::task::{Context, Poll}; + +/// A `MockBehaviour` is a `NetworkBehaviour` that allows for +/// the instrumentation of return values, without keeping +/// any further state. +pub(crate) struct MockBehaviour +where + THandler: ConnectionHandler + Clone, + THandler::ToBehaviour: Clone, + TOutEvent: Send + 'static, +{ + /// The prototype protocols handler that is cloned for every + /// invocation of [`NetworkBehaviour::handle_established_inbound_connection`] and [`NetworkBehaviour::handle_established_outbound_connection`] + pub(crate) handler_proto: THandler, + /// The addresses to return from [`NetworkBehaviour::handle_established_outbound_connection`]. + pub(crate) addresses: HashMap>, + /// The next action to return from `poll`. + /// + /// An action is only returned once. + pub(crate) next_action: Option>, +} + +impl MockBehaviour +where + THandler: ConnectionHandler + Clone, + THandler::ToBehaviour: Clone, + TOutEvent: Send + 'static, +{ + pub(crate) fn new(handler_proto: THandler) -> Self { + MockBehaviour { + handler_proto, + addresses: HashMap::new(), + next_action: None, + } + } +} + +impl NetworkBehaviour for MockBehaviour +where + THandler: ConnectionHandler + Clone, + THandler::ToBehaviour: Clone, + TOutEvent: Send + 'static, +{ + type ConnectionHandler = THandler; + type ToSwarm = TOutEvent; + + fn handle_established_inbound_connection( + &mut self, + _: ConnectionId, + _: PeerId, + _: &Multiaddr, + _: &Multiaddr, + ) -> Result { + Ok(self.handler_proto.clone()) + } + + fn handle_established_outbound_connection( + &mut self, + _: ConnectionId, + _: PeerId, + _: &Multiaddr, + _: Endpoint, + _: PortUse, + ) -> Result { + Ok(self.handler_proto.clone()) + } + + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + let p = match maybe_peer { + None => return Ok(vec![]), + Some(peer) => peer, + }; + + Ok(self.addresses.get(&p).map_or(Vec::new(), |v| v.clone())) + } + + fn poll(&mut self, _: &mut Context<'_>) -> Poll>> { + self.next_action.take().map_or(Poll::Pending, Poll::Ready) + } + + fn on_swarm_event(&mut self, _event: FromSwarm) {} + + fn on_connection_handler_event( + &mut self, + _peer_id: PeerId, + _connection_id: ConnectionId, + _event: THandlerOutEvent, + ) { + } +} + +/// A `CallTraceBehaviour` is a `NetworkBehaviour` that tracks +/// invocations of callback methods and their arguments, wrapping +/// around an inner behaviour. It ensures certain invariants are met. +pub(crate) struct CallTraceBehaviour +where + TInner: NetworkBehaviour, +{ + inner: TInner, + + pub(crate) handle_pending_inbound_connection: Vec<(ConnectionId, Multiaddr, Multiaddr)>, + pub(crate) handle_pending_outbound_connection: + Vec<(Option, Vec, Endpoint, ConnectionId)>, + pub(crate) handle_established_inbound_connection: + Vec<(PeerId, ConnectionId, Multiaddr, Multiaddr)>, + pub(crate) handle_established_outbound_connection: + Vec<(PeerId, Multiaddr, Endpoint, ConnectionId)>, + pub(crate) on_connection_established: Vec<(PeerId, ConnectionId, ConnectedPoint, usize)>, + pub(crate) on_connection_closed: Vec<(PeerId, ConnectionId, ConnectedPoint, usize)>, + pub(crate) on_connection_handler_event: Vec<(PeerId, ConnectionId, THandlerOutEvent)>, + pub(crate) on_dial_failure: Vec>, + pub(crate) on_new_listener: Vec, + pub(crate) on_new_listen_addr: Vec<(ListenerId, Multiaddr)>, + pub(crate) on_new_external_addr: Vec, + pub(crate) on_expired_listen_addr: Vec<(ListenerId, Multiaddr)>, + pub(crate) on_expired_external_addr: Vec, + pub(crate) on_listener_error: Vec, + pub(crate) on_listener_closed: Vec<(ListenerId, bool)>, + pub(crate) poll: usize, +} + +impl CallTraceBehaviour +where + TInner: NetworkBehaviour, + THandlerOutEvent: Clone, +{ + pub(crate) fn new(inner: TInner) -> Self { + Self { + inner, + handle_pending_inbound_connection: Vec::new(), + handle_pending_outbound_connection: Vec::new(), + handle_established_inbound_connection: Vec::new(), + handle_established_outbound_connection: Vec::new(), + on_connection_established: Vec::new(), + on_connection_closed: Vec::new(), + on_connection_handler_event: Vec::new(), + on_dial_failure: Vec::new(), + on_new_listener: Vec::new(), + on_new_listen_addr: Vec::new(), + on_new_external_addr: Vec::new(), + on_expired_listen_addr: Vec::new(), + on_expired_external_addr: Vec::new(), + on_listener_error: Vec::new(), + on_listener_closed: Vec::new(), + poll: 0, + } + } + + #[allow(dead_code)] + pub(crate) fn reset(&mut self) { + self.handle_pending_inbound_connection = Vec::new(); + self.handle_pending_outbound_connection = Vec::new(); + self.handle_established_inbound_connection = Vec::new(); + self.handle_established_outbound_connection = Vec::new(); + self.on_connection_established = Vec::new(); + self.on_connection_closed = Vec::new(); + self.on_connection_handler_event = Vec::new(); + self.on_dial_failure = Vec::new(); + self.on_new_listen_addr = Vec::new(); + self.on_new_external_addr = Vec::new(); + self.on_expired_listen_addr = Vec::new(); + self.on_listener_error = Vec::new(); + self.on_listener_closed = Vec::new(); + self.poll = 0; + } + + pub(crate) fn inner(&mut self) -> &mut TInner { + &mut self.inner + } + + pub(crate) fn num_connections_to_peer(&self, peer: PeerId) -> usize { + self.on_connection_established + .iter() + .filter(|(peer_id, _, _, _)| *peer_id == peer) + .count() + - self + .on_connection_closed + .iter() + .filter(|(peer_id, _, _, _)| *peer_id == peer) + .count() + } + + /// Checks that when the expected number of established connection notifications are received, + /// a given number of expected connections have been received as well. + /// + /// Returns if the first condition is met. + pub(crate) fn assert_connected( + &self, + expected_established_connections: usize, + expected_connections: usize, + ) -> bool { + if self.on_connection_established.len() == expected_established_connections { + assert_eq!( + self.on_connection_established + .iter() + .filter(|(.., reported_aditional_connections)| { + *reported_aditional_connections == 0 + }) + .count(), + expected_connections + ); + return true; + } + + false + } + + fn on_connection_established( + &mut self, + ConnectionEstablished { + peer_id, + connection_id, + endpoint, + failed_addresses, + other_established, + }: ConnectionEstablished, + ) { + let mut other_peer_connections = self + .on_connection_established + .iter() + .rev() // take last to first + .filter_map(|(peer, .., other_established)| { + if &peer_id == peer { + Some(other_established) + } else { + None + } + }) + .take(other_established); + + // We are informed that there are `other_established` additional connections. Ensure that the + // number of previous connections is consistent with this + if let Some(&prev) = other_peer_connections.next() { + if prev < other_established { + assert_eq!( + prev, + other_established - 1, + "Inconsistent connection reporting" + ) + } + assert_eq!(other_peer_connections.count(), other_established - 1); + } else { + assert_eq!(other_established, 0) + } + self.on_connection_established.push(( + peer_id, + connection_id, + endpoint.clone(), + other_established, + )); + self.inner + .on_swarm_event(FromSwarm::ConnectionEstablished(ConnectionEstablished { + peer_id, + connection_id, + endpoint, + failed_addresses, + other_established, + })); + } + + fn on_connection_closed( + &mut self, + ConnectionClosed { + peer_id, + connection_id, + endpoint, + remaining_established, + cause, + }: ConnectionClosed, + ) { + let mut other_closed_connections = self + .on_connection_established + .iter() + .rev() // take last to first + .filter_map(|(peer, .., remaining_established)| { + if &peer_id == peer { + Some(remaining_established) + } else { + None + } + }) + .take(remaining_established); + + // We are informed that there are `other_established` additional connections. Ensure that the + // number of previous connections is consistent with this + if let Some(&prev) = other_closed_connections.next() { + if prev < remaining_established { + assert_eq!( + prev, + remaining_established - 1, + "Inconsistent closed connection reporting" + ) + } + assert_eq!(other_closed_connections.count(), remaining_established - 1); + } else { + assert_eq!(remaining_established, 0) + } + assert!( + self.on_connection_established + .iter() + .any(|(peer, conn_id, endpoint, _)| (peer, conn_id, endpoint) + == (&peer_id, &connection_id, endpoint)), + "`on_swarm_event` with `FromSwarm::ConnectionClosed is called only for connections for\ + which `on_swarm_event` with `FromSwarm::ConnectionEstablished` was called first." + ); + self.on_connection_closed.push(( + peer_id, + connection_id, + endpoint.clone(), + remaining_established, + )); + self.inner + .on_swarm_event(FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id, + endpoint, + remaining_established, + cause, + })); + } +} + +impl NetworkBehaviour for CallTraceBehaviour +where + TInner: NetworkBehaviour, + THandlerOutEvent: Clone, +{ + type ConnectionHandler = TInner::ConnectionHandler; + type ToSwarm = TInner::ToSwarm; + + fn handle_pending_inbound_connection( + &mut self, + connection_id: ConnectionId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + self.handle_pending_inbound_connection.push(( + connection_id, + local_addr.clone(), + remote_addr.clone(), + )); + self.inner + .handle_pending_inbound_connection(connection_id, local_addr, remote_addr) + } + + fn handle_established_inbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + local_addr: &Multiaddr, + remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + self.handle_established_inbound_connection.push(( + peer, + connection_id, + local_addr.clone(), + remote_addr.clone(), + )); + self.inner.handle_established_inbound_connection( + connection_id, + peer, + local_addr, + remote_addr, + ) + } + + fn handle_pending_outbound_connection( + &mut self, + connection_id: ConnectionId, + maybe_peer: Option, + addresses: &[Multiaddr], + effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + self.handle_pending_outbound_connection.push(( + maybe_peer, + addresses.to_vec(), + effective_role, + connection_id, + )); + self.inner.handle_pending_outbound_connection( + connection_id, + maybe_peer, + addresses, + effective_role, + ) + } + + fn handle_established_outbound_connection( + &mut self, + connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + role_override: Endpoint, + port_use: PortUse, + ) -> Result, ConnectionDenied> { + self.handle_established_outbound_connection.push(( + peer, + addr.clone(), + role_override, + connection_id, + )); + self.inner.handle_established_outbound_connection( + connection_id, + peer, + addr, + role_override, + port_use, + ) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + self.inner.on_swarm_event(event); + + match event { + FromSwarm::ConnectionEstablished(connection_established) => { + self.on_connection_established(connection_established) + } + FromSwarm::ConnectionClosed(connection_closed) => { + self.on_connection_closed(connection_closed) + } + FromSwarm::DialFailure(DialFailure { peer_id, .. }) => { + self.on_dial_failure.push(peer_id); + } + FromSwarm::NewListener(NewListener { listener_id }) => { + self.on_new_listener.push(listener_id); + } + FromSwarm::NewListenAddr(NewListenAddr { listener_id, addr }) => { + self.on_new_listen_addr.push((listener_id, addr.clone())); + } + FromSwarm::ExpiredListenAddr(ExpiredListenAddr { listener_id, addr }) => { + self.on_expired_listen_addr + .push((listener_id, addr.clone())); + } + FromSwarm::NewExternalAddrCandidate(NewExternalAddrCandidate { addr }) => { + self.on_new_external_addr.push(addr.clone()); + } + FromSwarm::ExternalAddrExpired(ExternalAddrExpired { addr }) => { + self.on_expired_external_addr.push(addr.clone()); + } + FromSwarm::ListenerError(ListenerError { listener_id, .. }) => { + self.on_listener_error.push(listener_id); + } + FromSwarm::ListenerClosed(ListenerClosed { + listener_id, + reason, + }) => { + self.on_listener_closed.push((listener_id, reason.is_ok())); + } + _ => {} + } + } + + fn on_connection_handler_event( + &mut self, + p: PeerId, + c: ConnectionId, + e: THandlerOutEvent, + ) { + assert!( + self.on_connection_established + .iter() + .any(|(peer_id, conn_id, ..)| *peer_id == p && c == *conn_id), + "`on_connection_handler_event` is called for reported connections." + ); + assert!( + !self + .on_connection_closed + .iter() + .any(|(peer_id, conn_id, ..)| *peer_id == p && c == *conn_id), + "`on_connection_handler_event` is never called for closed connections." + ); + + self.on_connection_handler_event.push((p, c, e.clone())); + self.inner.on_connection_handler_event(p, c, e); + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll>> { + self.poll += 1; + self.inner.poll(cx) + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/translation.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/translation.rs new file mode 100644 index 00000000000..baa80c907b5 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/translation.rs @@ -0,0 +1,115 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use libp2p_core::{multiaddr::Protocol, Multiaddr}; + +/// Perform IP address translation. +/// +/// Given an `original` [`Multiaddr`] and some `observed` [`Multiaddr`], replace the first protocol +/// of the `original` with the first protocol of the `observed` [`Multiaddr`] and return this +/// translated [`Multiaddr`]. +/// +/// This function can for example be useful when handling tcp connections. Tcp does not listen and +/// dial on the same port by default. Thus when receiving an observed address on a connection that +/// we initiated, it will contain our dialing port, not our listening port. We need to take the ip +/// address or dns address from the observed address and the port from the original address. +/// +/// This is a mixed-mode translation, i.e. an IPv4 / DNS4 address may be replaced by an IPv6 / DNS6 +/// address and vice versa. +/// +/// If the first [`Protocol`]s are not IP addresses, `None` is returned instead. +#[doc(hidden)] +pub fn _address_translation(original: &Multiaddr, observed: &Multiaddr) -> Option { + original.replace(0, move |proto| match proto { + Protocol::Ip4(_) + | Protocol::Ip6(_) + | Protocol::Dns(_) + | Protocol::Dns4(_) + | Protocol::Dns6(_) => match observed.iter().next() { + x @ Some(Protocol::Ip4(_)) => x, + x @ Some(Protocol::Ip6(_)) => x, + x @ Some(Protocol::Dns(_)) => x, + x @ Some(Protocol::Dns4(_)) => x, + x @ Some(Protocol::Dns6(_)) => x, + _ => None, + }, + _ => None, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_address_translation() { + struct Test { + original: Multiaddr, + observed: Multiaddr, + expected: Multiaddr, + } + + let tests = [ + // Basic ipv4. + Test { + original: "/ip4/192.0.2.1/tcp/1".parse().unwrap(), + observed: "/ip4/192.0.2.2/tcp/2".parse().unwrap(), + expected: "/ip4/192.0.2.2/tcp/1".parse().unwrap(), + }, + // Basic ipv6. + Test { + original: "/ip6/2001:db8:0:0:0:0:0:0/tcp/1".parse().unwrap(), + observed: "/ip6/2001:db8:0:0:0:0:0:1/tcp/2".parse().unwrap(), + expected: "/ip6/2001:db8:0:0:0:0:0:1/tcp/1".parse().unwrap(), + }, + // Ipv4 ipv6 mix. + Test { + original: "/ip4/192.0.2.1/tcp/1".parse().unwrap(), + observed: "/ip6/2001:db8:0:0:0:0:0:1/tcp/2".parse().unwrap(), + expected: "/ip6/2001:db8:0:0:0:0:0:1/tcp/1".parse().unwrap(), + }, + // Ipv6 ipv4 mix. + Test { + original: "/ip6/2001:db8:0:0:0:0:0:0/tcp/1".parse().unwrap(), + observed: "/ip4/192.0.2.2/tcp/2".parse().unwrap(), + expected: "/ip4/192.0.2.2/tcp/1".parse().unwrap(), + }, + // Dns. + Test { + original: "/dns4/foo/tcp/1".parse().unwrap(), + observed: "/dns4/bar/tcp/2".parse().unwrap(), + expected: "/dns4/bar/tcp/1".parse().unwrap(), + }, + // Ipv4 Dns mix. + Test { + original: "/ip4/192.0.2.1/tcp/1".parse().unwrap(), + observed: "/dns4/bar/tcp/2".parse().unwrap(), + expected: "/dns4/bar/tcp/1".parse().unwrap(), + }, + ]; + + for test in tests.iter() { + assert_eq!( + _address_translation(&test.original, &test.observed), + Some(test.expected.clone()) + ); + } + } +} diff --git a/version-compatibility/vendor/libp2p-swarm-0.45.1/src/upgrade.rs b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/upgrade.rs new file mode 100644 index 00000000000..53b627458c9 --- /dev/null +++ b/version-compatibility/vendor/libp2p-swarm-0.45.1/src/upgrade.rs @@ -0,0 +1,158 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use crate::Stream; + +use futures::prelude::*; +use libp2p_core::upgrade; + +/// Implemented automatically on all types that implement [`UpgradeInfo`](upgrade::UpgradeInfo) +/// and `Send + 'static`. +/// +/// Do not implement this trait yourself. Instead, please implement +/// [`UpgradeInfo`](upgrade::UpgradeInfo). +pub trait UpgradeInfoSend: Send + 'static { + /// Equivalent to [`UpgradeInfo::Info`](upgrade::UpgradeInfo::Info). + type Info: AsRef + Clone + Send + 'static; + /// Equivalent to [`UpgradeInfo::InfoIter`](upgrade::UpgradeInfo::InfoIter). + type InfoIter: Iterator + Send + 'static; + + /// Equivalent to [`UpgradeInfo::protocol_info`](upgrade::UpgradeInfo::protocol_info). + fn protocol_info(&self) -> Self::InfoIter; +} + +impl UpgradeInfoSend for T +where + T: upgrade::UpgradeInfo + Send + 'static, + T::Info: Send + 'static, + ::IntoIter: Send + 'static, +{ + type Info = T::Info; + type InfoIter = ::IntoIter; + + fn protocol_info(&self) -> Self::InfoIter { + upgrade::UpgradeInfo::protocol_info(self).into_iter() + } +} + +/// Implemented automatically on all types that implement +/// [`OutboundUpgrade`](upgrade::OutboundUpgrade) and `Send + 'static`. +/// +/// Do not implement this trait yourself. Instead, please implement +/// [`OutboundUpgrade`](upgrade::OutboundUpgrade). +pub trait OutboundUpgradeSend: UpgradeInfoSend { + /// Equivalent to [`OutboundUpgrade::Output`](upgrade::OutboundUpgrade::Output). + type Output: Send + 'static; + /// Equivalent to [`OutboundUpgrade::Error`](upgrade::OutboundUpgrade::Error). + type Error: Send + 'static; + /// Equivalent to [`OutboundUpgrade::Future`](upgrade::OutboundUpgrade::Future). + type Future: Future> + Send + 'static; + + /// Equivalent to [`OutboundUpgrade::upgrade_outbound`](upgrade::OutboundUpgrade::upgrade_outbound). + fn upgrade_outbound(self, socket: Stream, info: Self::Info) -> Self::Future; +} + +impl OutboundUpgradeSend for T +where + T: upgrade::OutboundUpgrade + UpgradeInfoSend, + TInfo: AsRef + Clone + Send + 'static, + T::Output: Send + 'static, + T::Error: Send + 'static, + T::Future: Send + 'static, +{ + type Output = T::Output; + type Error = T::Error; + type Future = T::Future; + + fn upgrade_outbound(self, socket: Stream, info: TInfo) -> Self::Future { + upgrade::OutboundUpgrade::upgrade_outbound(self, socket, info) + } +} + +/// Implemented automatically on all types that implement +/// [`InboundUpgrade`](upgrade::InboundUpgrade) and `Send + 'static`. +/// +/// Do not implement this trait yourself. Instead, please implement +/// [`InboundUpgrade`](upgrade::InboundUpgrade). +pub trait InboundUpgradeSend: UpgradeInfoSend { + /// Equivalent to [`InboundUpgrade::Output`](upgrade::InboundUpgrade::Output). + type Output: Send + 'static; + /// Equivalent to [`InboundUpgrade::Error`](upgrade::InboundUpgrade::Error). + type Error: Send + 'static; + /// Equivalent to [`InboundUpgrade::Future`](upgrade::InboundUpgrade::Future). + type Future: Future> + Send + 'static; + + /// Equivalent to [`InboundUpgrade::upgrade_inbound`](upgrade::InboundUpgrade::upgrade_inbound). + fn upgrade_inbound(self, socket: Stream, info: Self::Info) -> Self::Future; +} + +impl InboundUpgradeSend for T +where + T: upgrade::InboundUpgrade + UpgradeInfoSend, + TInfo: AsRef + Clone + Send + 'static, + T::Output: Send + 'static, + T::Error: Send + 'static, + T::Future: Send + 'static, +{ + type Output = T::Output; + type Error = T::Error; + type Future = T::Future; + + fn upgrade_inbound(self, socket: Stream, info: TInfo) -> Self::Future { + upgrade::InboundUpgrade::upgrade_inbound(self, socket, info) + } +} + +/// Wraps around a type that implements [`OutboundUpgradeSend`], [`InboundUpgradeSend`], or +/// both, and implements [`OutboundUpgrade`](upgrade::OutboundUpgrade) and/or +/// [`InboundUpgrade`](upgrade::InboundUpgrade). +/// +/// > **Note**: This struct is mostly an implementation detail of the library and normally +/// > doesn't need to be used directly. +pub struct SendWrapper(pub T); + +impl upgrade::UpgradeInfo for SendWrapper { + type Info = T::Info; + type InfoIter = T::InfoIter; + + fn protocol_info(&self) -> Self::InfoIter { + UpgradeInfoSend::protocol_info(&self.0) + } +} + +impl upgrade::OutboundUpgrade for SendWrapper { + type Output = T::Output; + type Error = T::Error; + type Future = T::Future; + + fn upgrade_outbound(self, socket: Stream, info: T::Info) -> Self::Future { + OutboundUpgradeSend::upgrade_outbound(self.0, socket, info) + } +} + +impl upgrade::InboundUpgrade for SendWrapper { + type Output = T::Output; + type Error = T::Error; + type Future = T::Future; + + fn upgrade_inbound(self, socket: Stream, info: T::Info) -> Self::Future { + InboundUpgradeSend::upgrade_inbound(self.0, socket, info) + } +}