diff --git a/Cargo.lock b/Cargo.lock index d0a68be3e5e..cae910fa6d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,7 +87,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e3e798aa0c8239776f54415bc06f3d74b1850f3f830b45c35cfc80556973f70" dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -100,7 +100,7 @@ dependencies = [ "cipher 0.3.0", "cpufeatures", "ctr", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -114,7 +114,7 @@ dependencies = [ "cipher 0.3.0", "ctr", "ghash", - "subtle 2.4.1", + "subtle", ] [[package]] @@ -638,20 +638,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4" dependencies = [ "crypto-mac 0.8.0", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding 0.1.5", - "byte-tools", - "byteorder", - "generic-array 0.12.4", + "digest", + "opaque-debug", ] [[package]] @@ -660,17 +648,8 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding 0.2.1", - "generic-array 0.14.4", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", + "block-padding", + "generic-array", ] [[package]] @@ -793,12 +772,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65c1bf4a04a88c54f589125563643d773f3254b5c38571395e2b591c693bbc81" -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - [[package]] name = "byteorder" version = "1.4.3" @@ -930,7 +903,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -939,7 +912,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -1205,24 +1178,14 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.4", - "subtle 1.0.0", -] - [[package]] name = "crypto-mac" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.4", - "subtle 2.4.1", + "generic-array", + "subtle", ] [[package]] @@ -1231,8 +1194,8 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4857fd85a0c34b3c3297875b747c1e02e06b6a0ea32dd892d8192b9ce0813ea6" dependencies = [ - "generic-array 0.14.4", - "subtle 2.4.1", + "generic-array", + "subtle", ] [[package]] @@ -1241,8 +1204,8 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" dependencies = [ - "generic-array 0.14.4", - "subtle 2.4.1", + "generic-array", + "subtle", ] [[package]] @@ -1334,9 +1297,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "639891fde0dbea823fc3d798a0fdf9d2f9440a42d64a78ab3488b0ca025117b3" dependencies = [ "byteorder", - "digest 0.9.0", + "digest", "rand_core 0.5.1", - "subtle 2.4.1", + "subtle", "zeroize", ] @@ -1416,7 +1379,7 @@ dependencies = [ "hex", "reqwest", "serde_json", - "sha2 0.9.5", + "sha2", "tree_hash", "types", ] @@ -1478,22 +1441,13 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.4", + "generic-array", ] [[package]] @@ -1562,20 +1516,20 @@ dependencies = [ "aes", "aes-gcm", "arrayvec 0.7.1", - "digest 0.9.0", + "digest", "enr", "fnv", "futures", "hex", "hkdf", "lazy_static", - "libp2p-core 0.29.0", + "libp2p-core 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", "lru", "lru_time_cache", "parking_lot", "rand 0.8.4", "rlp 0.5.0", - "sha2 0.9.5", + "sha2", "smallvec", "tokio 1.8.1", "tokio-stream", @@ -1623,7 +1577,7 @@ dependencies = [ "ed25519", "rand 0.7.3", "serde", - "sha2 0.9.5", + "sha2", "zeroize", ] @@ -1669,11 +1623,11 @@ checksum = "c13e9b0c3c4170dcc2a12783746c4205d98e18957f57854251eea3f9750fe005" dependencies = [ "bitvec 0.20.4", "ff", - "generic-array 0.14.4", + "generic-array", "group", "pkcs8", "rand_core 0.6.3", - "subtle 2.4.1", + "subtle", "zeroize", ] @@ -1841,7 +1795,7 @@ dependencies = [ "futures", "futures-util", "hex", - "libsecp256k1 0.5.0", + "libsecp256k1", "procinfo", "proto_array", "psutil", @@ -1872,7 +1826,7 @@ dependencies = [ "lazy_static", "ring", "rustc-hex", - "sha2 0.9.5", + "sha2", "wasm-bindgen-test", ] @@ -1899,7 +1853,7 @@ dependencies = [ "hex", "num-bigint-dig", "ring", - "sha2 0.9.5", + "sha2", "zeroize", ] @@ -1919,7 +1873,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", - "sha2 0.9.5", + "sha2", "tempfile", "unicode-normalization", "uuid", @@ -1954,7 +1908,7 @@ dependencies = [ "regex", "serde", "serde_derive", - "sha2 0.9.5", + "sha2", "slog", "slog-async", "slog-term", @@ -2143,12 +2097,6 @@ dependencies = [ "futures", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - [[package]] name = "fallback" version = "0.1.0" @@ -2185,7 +2133,7 @@ checksum = "72a4d941a5b7c2a75222e2d44fcdf634a67133d9db31e177ae5ff6ecda852bfe" dependencies = [ "bitvec 0.20.4", "rand_core 0.6.3", - "subtle 2.4.1", + "subtle", ] [[package]] @@ -2440,15 +2388,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - [[package]] name = "generic-array" version = "0.14.4" @@ -2513,7 +2452,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bbd60caa311237d508927dbba7594b483db3ef05faa55172fcf89b1bcda7853" dependencies = [ - "opaque-debug 0.3.0", + "opaque-debug", "polyval", ] @@ -2572,7 +2511,7 @@ checksum = "61b3c1e8b4f1ca07e6605ea1be903a5f6956aec5c8a67fd44d56076631675ed8" dependencies = [ "ff", "rand_core 0.6.3", - "subtle 2.4.1", + "subtle", ] [[package]] @@ -2694,20 +2633,10 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" dependencies = [ - "digest 0.9.0", + "digest", "hmac 0.11.0", ] -[[package]] -name = "hmac" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" -dependencies = [ - "crypto-mac 0.7.0", - "digest 0.8.1", -] - [[package]] name = "hmac" version = "0.8.1" @@ -2715,7 +2644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" dependencies = [ "crypto-mac 0.8.0", - "digest 0.9.0", + "digest", ] [[package]] @@ -2725,7 +2654,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ "crypto-mac 0.10.0", - "digest 0.9.0", + "digest", ] [[package]] @@ -2735,18 +2664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ "crypto-mac 0.11.0", - "digest 0.9.0", -] - -[[package]] -name = "hmac-drbg" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" -dependencies = [ - "digest 0.8.1", - "generic-array 0.12.4", - "hmac 0.7.1", + "digest", ] [[package]] @@ -2755,8 +2673,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ - "digest 0.9.0", - "generic-array 0.14.4", + "digest", + "generic-array", "hmac 0.8.1", ] @@ -3187,7 +3105,7 @@ dependencies = [ "cfg-if 1.0.0", "ecdsa", "elliptic-curve", - "sha2 0.9.5", + "sha2", ] [[package]] @@ -3355,15 +3273,14 @@ dependencies = [ [[package]] name = "libp2p" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbb17eece4aec5bb970880c73825c16ca59ca05a4e41803751e68c7e5f0c618" +version = "0.39.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "atomic", "bytes 1.0.1", "futures", "lazy_static", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "libp2p-dns", "libp2p-gossipsub", "libp2p-identify", @@ -3374,7 +3291,7 @@ dependencies = [ "libp2p-tcp", "libp2p-websocket", "libp2p-yamux", - "parity-multiaddr", + "multiaddr 0.12.0", "parking_lot", "pin-project 1.0.7", "smallvec", @@ -3383,9 +3300,9 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.28.3" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554d3e7e9e65f939d66b75fd6a4c67f258fe250da61b91f46c545fc4a89b51d9" +checksum = "af9b4abdeaa420593a297c8592f63fad4234f4b88dc9343b8fd8e736c35faa59" dependencies = [ "asn1_der", "bs58", @@ -3395,19 +3312,19 @@ dependencies = [ "futures", "futures-timer", "lazy_static", - "libsecp256k1 0.3.5", + "libsecp256k1", "log", - "multihash 0.13.2", - "multistream-select", - "parity-multiaddr", + "multiaddr 0.13.0", + "multihash 0.14.0", + "multistream-select 0.10.2", "parking_lot", "pin-project 1.0.7", - "prost 0.7.0", - "prost-build 0.7.0", + "prost 0.8.0", + "prost-build 0.8.0", "rand 0.7.3", "ring", "rw-stream-sink", - "sha2 0.9.5", + "sha2", "smallvec", "thiserror", "unsigned-varint 0.7.0", @@ -3418,8 +3335,7 @@ dependencies = [ [[package]] name = "libp2p-core" version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9b4abdeaa420593a297c8592f63fad4234f4b88dc9343b8fd8e736c35faa59" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "asn1_der", "bs58", @@ -3429,19 +3345,19 @@ dependencies = [ "futures", "futures-timer", "lazy_static", - "libsecp256k1 0.5.0", + "libsecp256k1", "log", - "multiaddr", - "multihash 0.14.0", - "multistream-select", + "multiaddr 0.12.0", + "multihash 0.13.2", + "multistream-select 0.10.3", "parking_lot", "pin-project 1.0.7", - "prost 0.8.0", - "prost-build 0.8.0", + "prost 0.7.0", + "prost-build 0.7.0", "rand 0.7.3", "ring", "rw-stream-sink", - "sha2 0.9.5", + "sha2", "smallvec", "thiserror", "unsigned-varint 0.7.0", @@ -3451,12 +3367,11 @@ dependencies = [ [[package]] name = "libp2p-dns" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e63dab8b5ff35e0c101a3e51e843ba782c07bbb1682f5fd827622e0d02b98b" +version = "0.29.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "futures", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "log", "smallvec", "trust-dns-resolver", @@ -3464,9 +3379,8 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e562308761818b0c52f2a81a0544b9c22d0cf56d7b2d928a0ff61382404498ce" +version = "0.32.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "asynchronous-codec", "base64 0.13.0", @@ -3475,14 +3389,14 @@ dependencies = [ "fnv", "futures", "hex_fmt", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "libp2p-swarm", "log", "prost 0.7.0", "prost-build 0.7.0", "rand 0.7.3", "regex", - "sha2 0.9.5", + "sha2", "smallvec", "unsigned-varint 0.7.0", "wasm-timer", @@ -3490,12 +3404,11 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f668f00efd9883e8b7bcc582eaf0164615792608f886f6577da18bcbeea0a46" +version = "0.30.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "futures", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "libp2p-swarm", "log", "prost 0.7.0", @@ -3506,14 +3419,13 @@ dependencies = [ [[package]] name = "libp2p-mplex" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e9b544335d1ed30af71daa96edbefadef6f19c7a55f078b9fc92c87163105d" +version = "0.29.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "asynchronous-codec", "bytes 1.0.1", "futures", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "log", "nohash-hasher", "parking_lot", @@ -3524,20 +3436,19 @@ dependencies = [ [[package]] name = "libp2p-noise" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a2aa6fc4e6855eaf9ea1941a14f7ec4df35636fb6b85951e17481df8dcecf6" +version = "0.32.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "bytes 1.0.1", "curve25519-dalek", "futures", "lazy_static", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "log", "prost 0.7.0", "prost-build 0.7.0", "rand 0.8.4", - "sha2 0.9.5", + "sha2", "snow", "static_assertions", "x25519-dalek", @@ -3546,13 +3457,12 @@ dependencies = [ [[package]] name = "libp2p-swarm" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e04d8e1eef675029ec728ba14e8d0da7975d84b6679b699b4ae91a1de9c3a92" +version = "0.30.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "either", "futures", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "log", "rand 0.7.3", "smallvec", @@ -3563,8 +3473,7 @@ dependencies = [ [[package]] name = "libp2p-swarm-derive" version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365b0a699fea5168676840567582a012ea297b1ca02eee467e58301b9c9c5eed" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "quote", "syn", @@ -3572,16 +3481,15 @@ dependencies = [ [[package]] name = "libp2p-tcp" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b1a27d21c477951799e99d5c105d78868258502ce092988040a808d5a19bbd9" +version = "0.29.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "futures", "futures-timer", "if-addrs", "ipnet", "libc", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "log", "socket2 0.4.0", "tokio 1.8.1", @@ -3589,14 +3497,13 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cace60995ef6f637e4752cccbb2590f6bc358e8741a0d066307636c69a4b3a74" +version = "0.30.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "either", "futures", "futures-rustls", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "log", "quicksink", "rw-stream-sink", @@ -3607,33 +3514,16 @@ dependencies = [ [[package]] name = "libp2p-yamux" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f35da42cfc6d5cb0dcf3ad6881bc68d146cdf38f98655e09e33fbba4d13eabc4" +version = "0.33.0" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" dependencies = [ "futures", - "libp2p-core 0.28.3", + "libp2p-core 0.29.0 (git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93)", "parking_lot", "thiserror", "yamux", ] -[[package]] -name = "libsecp256k1" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" -dependencies = [ - "arrayref", - "crunchy", - "digest 0.8.1", - "hmac-drbg 0.2.0", - "rand 0.7.3", - "sha2 0.8.2", - "subtle 2.4.1", - "typenum", -] - [[package]] name = "libsecp256k1" version = "0.5.0" @@ -3642,14 +3532,14 @@ checksum = "bd1137239ab33b41aa9637a88a28249e5e70c40a42ccc92db7f12cc356c1fcd7" dependencies = [ "arrayref", "base64 0.12.3", - "digest 0.9.0", - "hmac-drbg 0.3.0", + "digest", + "hmac-drbg", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", "rand 0.7.3", "serde", - "sha2 0.9.5", + "sha2", "typenum", ] @@ -3660,8 +3550,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee11012b293ea30093c129173cac4335513064094619f4639a25b310fd33c11" dependencies = [ "crunchy", - "digest 0.9.0", - "subtle 2.4.1", + "digest", + "subtle", ] [[package]] @@ -4011,6 +3901,24 @@ dependencies = [ "tokio 1.8.1", ] +[[package]] +name = "multiaddr" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7139982f583d7e53879d9f611fe48ced18e77d684309484f2252c76bcd39f549" +dependencies = [ + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash 0.13.2", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint 0.7.0", + "url", +] + [[package]] name = "multiaddr" version = "0.13.0" @@ -4035,10 +3943,10 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dac63698b887d2d929306ea48b63760431ff8a24fac40ddb22f9c7f49fb7cab" dependencies = [ - "digest 0.9.0", - "generic-array 0.14.4", + "digest", + "generic-array", "multihash-derive", - "sha2 0.9.5", + "sha2", "unsigned-varint 0.5.1", ] @@ -4048,10 +3956,10 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "752a61cd890ff691b4411423d23816d5866dd5621e4d1c5687a53b94b5a979d8" dependencies = [ - "digest 0.9.0", - "generic-array 0.14.4", + "digest", + "generic-array", "multihash-derive", - "sha2 0.9.5", + "sha2", "unsigned-varint 0.7.0", ] @@ -4107,6 +4015,19 @@ dependencies = [ "unsigned-varint 0.7.0", ] +[[package]] +name = "multistream-select" +version = "0.10.3" +source = "git+https://github.com/libp2p/rust-libp2p?rev=c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93#c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" +dependencies = [ + "bytes 1.0.1", + "futures", + "log", + "pin-project 1.0.7", + "smallvec", + "unsigned-varint 0.7.0", +] + [[package]] name = "native-tls" version = "0.2.7" @@ -4337,12 +4258,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - [[package]] name = "opaque-debug" version = "0.3.0" @@ -4415,24 +4330,6 @@ dependencies = [ "types", ] -[[package]] -name = "parity-multiaddr" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58341485071825827b7f03cf7efd1cb21e6a709bea778fb50227fd45d2f361b4" -dependencies = [ - "arrayref", - "bs58", - "byteorder", - "data-encoding", - "multihash 0.13.2", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint 0.7.0", - "url", -] - [[package]] name = "parity-scale-codec" version = "1.3.7" @@ -4688,7 +4585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fe800695325da85083cd23b56826fccb2e2dc29b218e7811a6f33bc93f414be" dependencies = [ "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", "universal-hash", ] @@ -4700,7 +4597,7 @@ checksum = "e597450cbf209787f0e6de80bf3795c6b2356a380ee87837b545aded8dbc1823" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug", "universal-hash", ] @@ -5572,7 +5469,7 @@ dependencies = [ "hmac 0.10.1", "pbkdf2 0.6.0", "salsa20", - "sha2 0.9.5", + "sha2", ] [[package]] @@ -5775,11 +5672,11 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16" dependencies = [ - "block-buffer 0.9.0", + "block-buffer", "cfg-if 1.0.0", "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest", + "opaque-debug", ] [[package]] @@ -5788,29 +5685,17 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha2" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" dependencies = [ - "block-buffer 0.9.0", + "block-buffer", "cfg-if 1.0.0", "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest", + "opaque-debug", ] [[package]] @@ -5819,10 +5704,10 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", + "block-buffer", + "digest", "keccak", - "opaque-debug 0.3.0", + "opaque-debug", ] [[package]] @@ -5859,7 +5744,7 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19772be3c4dd2ceaacf03cb41d5885f2a02c4d8804884918e3a258480803335" dependencies = [ - "digest 0.9.0", + "digest", "rand_core 0.6.3", ] @@ -6134,8 +6019,8 @@ dependencies = [ "rand_core 0.6.3", "ring", "rustc_version 0.3.3", - "sha2 0.9.5", - "subtle 2.4.1", + "sha2", + "subtle", "x25519-dalek", ] @@ -6364,12 +6249,6 @@ dependencies = [ "syn", ] -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" - [[package]] name = "subtle" version = "2.4.1" @@ -6621,7 +6500,7 @@ dependencies = [ "pbkdf2 0.4.0", "rand 0.7.3", "rustc-hash", - "sha2 0.9.5", + "sha2", "thiserror", "unicode-normalization", "zeroize", @@ -7148,8 +7027,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" dependencies = [ - "generic-array 0.14.4", - "subtle 2.4.1", + "generic-array", + "subtle", ] [[package]] @@ -7239,7 +7118,7 @@ dependencies = [ "hyper", "lazy_static", "libc", - "libsecp256k1 0.5.0", + "libsecp256k1", "lighthouse_metrics", "lighthouse_version", "lockfile", diff --git a/beacon_node/eth2_libp2p/Cargo.toml b/beacon_node/eth2_libp2p/Cargo.toml index 80e340c73c0..0d4fbf76f20 100644 --- a/beacon_node/eth2_libp2p/Cargo.toml +++ b/beacon_node/eth2_libp2p/Cargo.toml @@ -42,7 +42,10 @@ regex = "1.3.9" strum = { version = "0.20", features = ["derive"] } [dependencies.libp2p] -version = "0.38.0" +#version = "0.38.0" +# Bleeding edge, while we wait for 0.39.0 +git = "https://github.com/libp2p/rust-libp2p" +rev = "c1ef4bffd225a78cefe8fa43dc8ee18e03ff4f93" default-features = false features = ["websocket", "identify", "mplex", "yamux", "noise", "gossipsub", "dns-tokio", "tcp-tokio"] diff --git a/beacon_node/eth2_libp2p/src/behaviour/handler/delegate.rs b/beacon_node/eth2_libp2p/src/behaviour/handler/delegate.rs deleted file mode 100644 index 62a01d8cb86..00000000000 --- a/beacon_node/eth2_libp2p/src/behaviour/handler/delegate.rs +++ /dev/null @@ -1,368 +0,0 @@ -use crate::behaviour::Gossipsub; -use crate::rpc::*; -use libp2p::{ - core::either::{EitherError, EitherOutput}, - core::upgrade::{EitherUpgrade, InboundUpgrade, OutboundUpgrade, SelectUpgrade, UpgradeError}, - identify::Identify, - swarm::{ - protocols_handler::{ - KeepAlive, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, - }, - NegotiatedSubstream, NetworkBehaviour, ProtocolsHandler, - }, -}; -use std::task::{Context, Poll}; -use types::EthSpec; - -/* Auxiliary types for simplicity */ -type GossipHandler = ::ProtocolsHandler; -type RPCHandler = as NetworkBehaviour>::ProtocolsHandler; -type IdentifyHandler = ::ProtocolsHandler; - -/// Handler that combines Lighthouse's Behaviours' handlers in a delegating manner. -pub(super) struct DelegatingHandler { - /// Handler for the Gossipsub protocol. - gossip_handler: GossipHandler, - /// Handler for the RPC protocol. - rpc_handler: RPCHandler, - /// Handler for the Identify protocol. - identify_handler: IdentifyHandler, -} - -impl DelegatingHandler { - pub fn new(gossipsub: &mut Gossipsub, rpc: &mut RPC, identify: &mut Identify) -> Self { - DelegatingHandler { - gossip_handler: gossipsub.new_handler(), - rpc_handler: rpc.new_handler(), - identify_handler: identify.new_handler(), - } - } - - /// Gives mutable access to the rpc handler. - pub fn rpc_mut(&mut self) -> &mut RPCHandler { - &mut self.rpc_handler - } - - /// Gives access to the rpc handler. - pub fn rpc(&self) -> &RPCHandler { - &self.rpc_handler - } - - /// Gives access to identify's handler. - pub fn _identify(&self) -> &IdentifyHandler { - &self.identify_handler - } -} - -/// Wrapper around the `ProtocolsHandler::InEvent` types of the handlers. -/// Simply delegated to the corresponding behaviour's handler. -#[derive(Debug)] -pub enum DelegateIn { - Gossipsub(::InEvent), - RPC( as ProtocolsHandler>::InEvent), - Identify(::InEvent), -} - -/// Wrapper around the `ProtocolsHandler::OutEvent` types of the handlers. -/// Simply delegated to the corresponding behaviour's handler. -pub enum DelegateOut { - Gossipsub(::OutEvent), - RPC( as ProtocolsHandler>::OutEvent), - Identify(Box<::OutEvent>), -} - -/// Wrapper around the `ProtocolsHandler::Error` types of the handlers. -/// Simply delegated to the corresponding behaviour's handler. -#[derive(Debug)] -pub enum DelegateError { - Gossipsub(::Error), - RPC( as ProtocolsHandler>::Error), - Identify(::Error), - Disconnected, -} - -impl std::error::Error for DelegateError {} - -impl std::fmt::Display for DelegateError { - fn fmt( - &self, - formater: &mut std::fmt::Formatter<'_>, - ) -> std::result::Result<(), std::fmt::Error> { - match self { - DelegateError::Gossipsub(err) => err.fmt(formater), - DelegateError::RPC(err) => err.fmt(formater), - DelegateError::Identify(err) => err.fmt(formater), - DelegateError::Disconnected => write!(formater, "Disconnected"), - } - } -} - -pub type DelegateInProto = SelectUpgrade< - ::InboundProtocol, - SelectUpgrade< - as ProtocolsHandler>::InboundProtocol, - ::InboundProtocol, - >, ->; - -pub type DelegateOutProto = EitherUpgrade< - ::OutboundProtocol, - EitherUpgrade< - as ProtocolsHandler>::OutboundProtocol, - ::OutboundProtocol, - >, ->; - -pub type DelegateOutInfo = EitherOutput< - ::OutboundOpenInfo, - EitherOutput< - as ProtocolsHandler>::OutboundOpenInfo, - ::OutboundOpenInfo, - >, ->; - -impl ProtocolsHandler for DelegatingHandler { - type InEvent = DelegateIn; - type OutEvent = DelegateOut; - type Error = DelegateError; - type InboundProtocol = DelegateInProto; - type OutboundProtocol = DelegateOutProto; - type OutboundOpenInfo = DelegateOutInfo; - type InboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - let gossip_proto = self.gossip_handler.listen_protocol(); - let rpc_proto = self.rpc_handler.listen_protocol(); - let identify_proto = self.identify_handler.listen_protocol(); - - let timeout = *gossip_proto - .timeout() - .max(rpc_proto.timeout()) - .max(identify_proto.timeout()); - - let select = SelectUpgrade::new( - gossip_proto.into_upgrade().0, - SelectUpgrade::new(rpc_proto.into_upgrade().0, identify_proto.into_upgrade().0), - ); - - SubstreamProtocol::new(select, ()).with_timeout(timeout) - } - - fn inject_fully_negotiated_inbound( - &mut self, - out: >::Output, - _info: Self::InboundOpenInfo, - ) { - match out { - // Gossipsub - EitherOutput::First(out) => { - self.gossip_handler.inject_fully_negotiated_inbound(out, ()) - } - // RPC - EitherOutput::Second(EitherOutput::First(out)) => { - self.rpc_handler.inject_fully_negotiated_inbound(out, ()) - } - // Identify - EitherOutput::Second(EitherOutput::Second(out)) => self - .identify_handler - .inject_fully_negotiated_inbound(out, ()), - } - } - - fn inject_fully_negotiated_outbound( - &mut self, - protocol: >::Output, - info: Self::OutboundOpenInfo, - ) { - match (protocol, info) { - // Gossipsub - (EitherOutput::First(protocol), EitherOutput::First(info)) => self - .gossip_handler - .inject_fully_negotiated_outbound(protocol, info), - // RPC - ( - EitherOutput::Second(EitherOutput::First(protocol)), - EitherOutput::Second(EitherOutput::First(info)), - ) => self - .rpc_handler - .inject_fully_negotiated_outbound(protocol, info), - // Identify - ( - EitherOutput::Second(EitherOutput::Second(protocol)), - EitherOutput::Second(EitherOutput::Second(())), - ) => self - .identify_handler - .inject_fully_negotiated_outbound(protocol, ()), - // Reaching here means we got a protocol and info for different behaviours - _ => unreachable!("output and protocol don't match"), - } - } - - fn inject_event(&mut self, event: Self::InEvent) { - match event { - DelegateIn::Gossipsub(ev) => self.gossip_handler.inject_event(ev), - DelegateIn::RPC(ev) => self.rpc_handler.inject_event(ev), - DelegateIn::Identify(ev) => self.identify_handler.inject_event(ev), - } - } - - fn inject_dial_upgrade_error( - &mut self, - info: Self::OutboundOpenInfo, - error: ProtocolsHandlerUpgrErr< - >::Error, - >, - ) { - match info { - // Gossipsub - EitherOutput::First(info) => match error { - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)) => { - self.gossip_handler.inject_dial_upgrade_error( - info, - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)), - ) - } - ProtocolsHandlerUpgrErr::Timer => self - .gossip_handler - .inject_dial_upgrade_error(info, ProtocolsHandlerUpgrErr::Timer), - ProtocolsHandlerUpgrErr::Timeout => self - .gossip_handler - .inject_dial_upgrade_error(info, ProtocolsHandlerUpgrErr::Timeout), - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(EitherError::A(err))) => { - self.gossip_handler.inject_dial_upgrade_error( - info, - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(err)), - ) - } - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(_)) => { - unreachable!("info and error don't match") - } - }, - // RPC - EitherOutput::Second(EitherOutput::First(info)) => match error { - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)) => { - self.rpc_handler.inject_dial_upgrade_error( - info, - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)), - ) - } - ProtocolsHandlerUpgrErr::Timer => self - .rpc_handler - .inject_dial_upgrade_error(info, ProtocolsHandlerUpgrErr::Timer), - ProtocolsHandlerUpgrErr::Timeout => self - .rpc_handler - .inject_dial_upgrade_error(info, ProtocolsHandlerUpgrErr::Timeout), - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(EitherError::B( - EitherError::A(err), - ))) => self.rpc_handler.inject_dial_upgrade_error( - info, - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(err)), - ), - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(_)) => { - unreachable!("info and error don't match") - } - }, - // Identify - EitherOutput::Second(EitherOutput::Second(())) => match error { - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)) => { - self.identify_handler.inject_dial_upgrade_error( - (), - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)), - ) - } - ProtocolsHandlerUpgrErr::Timer => self - .identify_handler - .inject_dial_upgrade_error((), ProtocolsHandlerUpgrErr::Timer), - ProtocolsHandlerUpgrErr::Timeout => self - .identify_handler - .inject_dial_upgrade_error((), ProtocolsHandlerUpgrErr::Timeout), - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(EitherError::B( - EitherError::B(err), - ))) => self.identify_handler.inject_dial_upgrade_error( - (), - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(err)), - ), - ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(_)) => { - unreachable!("info and error don't match") - } - }, - } - } - - fn connection_keep_alive(&self) -> KeepAlive { - self.gossip_handler - .connection_keep_alive() - .max(self.rpc_handler.connection_keep_alive()) - .max(self.identify_handler.connection_keep_alive()) - } - - #[allow(clippy::type_complexity)] - fn poll( - &mut self, - cx: &mut Context, - ) -> Poll< - ProtocolsHandlerEvent< - Self::OutboundProtocol, - Self::OutboundOpenInfo, - Self::OutEvent, - Self::Error, - >, - > { - match self.gossip_handler.poll(cx) { - Poll::Ready(ProtocolsHandlerEvent::Custom(event)) => { - return Poll::Ready(ProtocolsHandlerEvent::Custom(DelegateOut::Gossipsub(event))); - } - Poll::Ready(ProtocolsHandlerEvent::Close(event)) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(DelegateError::Gossipsub( - event, - ))); - } - Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol }) => { - return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: protocol - .map_upgrade(EitherUpgrade::A) - .map_info(EitherOutput::First), - }); - } - Poll::Pending => (), - }; - - match self.rpc_handler.poll(cx) { - Poll::Ready(ProtocolsHandlerEvent::Custom(event)) => { - return Poll::Ready(ProtocolsHandlerEvent::Custom(DelegateOut::RPC(event))); - } - Poll::Ready(ProtocolsHandlerEvent::Close(event)) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(DelegateError::RPC(event))); - } - Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol }) => { - return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: protocol - .map_upgrade(|u| EitherUpgrade::B(EitherUpgrade::A(u))) - .map_info(|info| EitherOutput::Second(EitherOutput::First(info))), - }); - } - Poll::Pending => (), - }; - - match self.identify_handler.poll(cx) { - Poll::Ready(ProtocolsHandlerEvent::Custom(event)) => { - return Poll::Ready(ProtocolsHandlerEvent::Custom(DelegateOut::Identify( - Box::new(event), - ))); - } - Poll::Ready(ProtocolsHandlerEvent::Close(event)) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(DelegateError::Identify(event))); - } - Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol }) => { - return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: protocol - .map_upgrade(|u| EitherUpgrade::B(EitherUpgrade::B(u))) - .map_info(|_| EitherOutput::Second(EitherOutput::Second(()))), - }); - } - Poll::Pending => (), - }; - - Poll::Pending - } -} diff --git a/beacon_node/eth2_libp2p/src/behaviour/handler/mod.rs b/beacon_node/eth2_libp2p/src/behaviour/handler/mod.rs deleted file mode 100644 index 4cacea920d6..00000000000 --- a/beacon_node/eth2_libp2p/src/behaviour/handler/mod.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::behaviour::Gossipsub; -use crate::rpc::*; -use delegate::DelegatingHandler; -pub(super) use delegate::{ - DelegateError, DelegateIn, DelegateInProto, DelegateOut, DelegateOutInfo, DelegateOutProto, -}; -use libp2p::{ - core::upgrade::{InboundUpgrade, OutboundUpgrade}, - identify::Identify, - swarm::protocols_handler::{ - KeepAlive, ProtocolsHandlerEvent, ProtocolsHandlerUpgrErr, SubstreamProtocol, - }, - swarm::{NegotiatedSubstream, ProtocolsHandler}, -}; -use std::task::{Context, Poll}; -use types::EthSpec; - -mod delegate; - -/// Handler that combines Lighthouse's Behaviours' handlers in a delegating manner. -pub struct BehaviourHandler { - /// Handler combining all sub behaviour's handlers. - delegate: DelegatingHandler, - /// Flag indicating if the handler is shutting down. - shutting_down: bool, -} - -impl BehaviourHandler { - pub fn new(gossipsub: &mut Gossipsub, rpc: &mut RPC, identify: &mut Identify) -> Self { - BehaviourHandler { - delegate: DelegatingHandler::new(gossipsub, rpc, identify), - shutting_down: false, - } - } -} - -pub enum BehaviourHandlerIn { - Delegate(DelegateIn), - /// Start the shutdown process. - Shutdown(Option<(RequestId, OutboundRequest)>), -} - -impl ProtocolsHandler for BehaviourHandler { - type InEvent = BehaviourHandlerIn; - type OutEvent = DelegateOut; - type Error = DelegateError; - type InboundProtocol = DelegateInProto; - type OutboundProtocol = DelegateOutProto; - type OutboundOpenInfo = DelegateOutInfo; - type InboundOpenInfo = (); - - fn listen_protocol(&self) -> SubstreamProtocol { - self.delegate.listen_protocol() - } - - fn inject_fully_negotiated_inbound( - &mut self, - out: >::Output, - _info: Self::InboundOpenInfo, - ) { - self.delegate.inject_fully_negotiated_inbound(out, ()) - } - - fn inject_fully_negotiated_outbound( - &mut self, - out: >::Output, - info: Self::OutboundOpenInfo, - ) { - self.delegate.inject_fully_negotiated_outbound(out, info) - } - - fn inject_event(&mut self, event: Self::InEvent) { - match event { - BehaviourHandlerIn::Delegate(delegated_ev) => self.delegate.inject_event(delegated_ev), - /* Events coming from the behaviour */ - BehaviourHandlerIn::Shutdown(last_message) => { - self.shutting_down = true; - self.delegate.rpc_mut().shutdown(last_message); - } - } - } - - fn inject_dial_upgrade_error( - &mut self, - info: Self::OutboundOpenInfo, - err: ProtocolsHandlerUpgrErr< - >::Error, - >, - ) { - self.delegate.inject_dial_upgrade_error(info, err) - } - - // We don't use the keep alive to disconnect. This is handled in the poll - fn connection_keep_alive(&self) -> KeepAlive { - KeepAlive::Yes - } - - #[allow(clippy::type_complexity)] - fn poll( - &mut self, - cx: &mut Context, - ) -> Poll< - ProtocolsHandlerEvent< - Self::OutboundProtocol, - Self::OutboundOpenInfo, - Self::OutEvent, - Self::Error, - >, - > { - // Disconnect if the sub-handlers are ready. - // Currently we only respect the RPC handler. - if self.shutting_down && KeepAlive::No == self.delegate.rpc().connection_keep_alive() { - return Poll::Ready(ProtocolsHandlerEvent::Close(DelegateError::Disconnected)); - } - - match self.delegate.poll(cx) { - Poll::Ready(ProtocolsHandlerEvent::Custom(event)) => { - return Poll::Ready(ProtocolsHandlerEvent::Custom(event)) - } - Poll::Ready(ProtocolsHandlerEvent::Close(err)) => { - return Poll::Ready(ProtocolsHandlerEvent::Close(err)) - } - Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol }) => { - return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol }); - } - Poll::Pending => (), - } - - Poll::Pending - } -} diff --git a/beacon_node/eth2_libp2p/src/behaviour/mod.rs b/beacon_node/eth2_libp2p/src/behaviour/mod.rs index 058d6665a6a..50936e251e2 100644 --- a/beacon_node/eth2_libp2p/src/behaviour/mod.rs +++ b/beacon_node/eth2_libp2p/src/behaviour/mod.rs @@ -1,7 +1,7 @@ use crate::behaviour::gossipsub_scoring_parameters::PeerScoreSettings; +use crate::discovery::{subnet_predicate, Discovery, DiscoveryEvent, TARGET_SUBNET_PEERS}; use crate::peer_manager::{ - score::{PeerAction, ReportSource}, - ConnectionDirection, PeerManager, PeerManagerEvent, + score::ReportSource, ConnectionDirection, PeerManager, PeerManagerEvent, }; use crate::rpc::*; use crate::service::METADATA_FILENAME; @@ -12,12 +12,9 @@ use crate::types::{ use crate::Eth2Enr; use crate::{error, metrics, Enr, NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash}; use futures::prelude::*; -use handler::{BehaviourHandler, BehaviourHandlerIn, DelegateIn, DelegateOut}; use libp2p::{ core::{ - connection::{ConnectedPoint, ConnectionId, ListenerId}, - identity::Keypair, - Multiaddr, + connection::ConnectionId, identity::Keypair, multiaddr::Protocol as MProtocol, Multiaddr, }, gossipsub::{ subscription_filter::{MaxCountSubscriptionFilter, WhitelistSubscriptionFilter}, @@ -26,10 +23,10 @@ use libp2p::{ }, identify::{Identify, IdentifyConfig, IdentifyEvent}, swarm::{ - AddressScore, NetworkBehaviour, NetworkBehaviourAction as NBAction, NotifyHandler, - PollParameters, ProtocolsHandler, + AddressScore, DialPeerCondition, NetworkBehaviourAction as NBAction, + NetworkBehaviourEventProcess, PollParameters, }, - PeerId, + NetworkBehaviour, PeerId, }; use slog::{crit, debug, o, trace, warn}; use ssz::Encode; @@ -46,7 +43,6 @@ use std::{ use types::{ChainSpec, EnrForkId, EthSpec, SignedBeaconBlock, Slot, SubnetId}; mod gossipsub_scoring_parameters; -mod handler; const MAX_IDENTIFY_ADDRESSES: usize = 10; pub const GOSSIPSUB_GREYLIST_THRESHOLD: f64 = -16000.0; @@ -61,11 +57,15 @@ pub type Gossipsub = BaseGossipsub; #[derive(Debug)] pub enum BehaviourEvent { /// We have successfully dialed and connected to a peer. - PeerDialed(PeerId), + PeerConnectedOutgoing(PeerId), /// A peer has successfully dialed and connected to us. - PeerConnected(PeerId), + PeerConnectedIncoming(PeerId), /// A peer has disconnected. PeerDisconnected(PeerId), + /// The peer needs to be banned. + PeerBanned(PeerId), + /// The peer has been unbanned. + PeerUnbanned(PeerId), /// An RPC Request that was sent failed. RPCFailed { /// The id of the failed request. @@ -103,55 +103,83 @@ pub enum BehaviourEvent { StatusPeer(PeerId), } +/// Internal type to pass messages from sub-behaviours to the poll of the global behaviour to be +/// specified as an NBAction. +enum InternalBehaviourMessage { + /// Dial a Peer. + DialPeer(PeerId), + /// The socket has been updated. + SocketUpdated(Multiaddr), +} + /// Builds the network behaviour that manages the core protocols of eth2. /// This core behaviour is managed by `Behaviour` which adds peer management to all core /// behaviours. +#[derive(NetworkBehaviour)] +#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")] pub struct Behaviour { + /* Sub-Behaviours */ /// The routing pub-sub mechanism for eth2. gossipsub: Gossipsub, /// The Eth2 RPC specified in the wire-0 protocol. eth2_rpc: RPC, + /// Discv5 Discovery protocol. + discovery: Discovery, /// Keep regular connection to peers and disconnect if absent. // NOTE: The id protocol is used for initial interop. This will be removed by mainnet. /// Provides IP addresses and peer information. identify: Identify, + + /* Auxiliary Fields */ /// The peer manager that keeps track of peer's reputation and status. + #[behaviour(ignore)] peer_manager: PeerManager, /// The output events generated by this behaviour to be consumed in the swarm poll. + #[behaviour(ignore)] events: VecDeque>, - /// Queue of peers to disconnect and an optional reason for the disconnection. - peers_to_dc: VecDeque<(PeerId, Option)>, + /// Internal behaviour events, the NBAction type is composed of sub-behaviours, so we use a + /// custom type here to avoid having to specify the concrete type. + #[behaviour(ignore)] + internal_events: VecDeque, /// A collections of variables accessible outside the network service. + #[behaviour(ignore)] network_globals: Arc>, /// Keeps track of the current EnrForkId for upgrading gossipsub topics. // NOTE: This can be accessed via the network_globals ENR. However we keep it here for quick // lookups for every gossipsub message send. + #[behaviour(ignore)] enr_fork_id: EnrForkId, - /// The waker for the current thread. + /// The waker for the current task. This is used to wake the task when events are added to the + /// queue. + #[behaviour(ignore)] waker: Option, - /// Directory where metadata is stored + /// Directory where metadata is stored. + #[behaviour(ignore)] network_dir: PathBuf, - /// Logger for behaviour actions. - log: slog::Logger, - + /// Gossipsub score parameters. + #[behaviour(ignore)] score_settings: PeerScoreSettings, - /// The interval for updating gossipsub scores + #[behaviour(ignore)] update_gossipsub_scores: tokio::time::Interval, + /// Logger for behaviour actions. + #[behaviour(ignore)] + log: slog::Logger, } /// Implements the combined behaviour for the libp2p service. impl Behaviour { pub async fn new( local_key: &Keypair, - net_conf: &NetworkConfig, + config: &NetworkConfig, network_globals: Arc>, log: &slog::Logger, chain_spec: &ChainSpec, ) -> error::Result { let behaviour_log = log.new(o!()); - let identify_config = if net_conf.private { + // Set up the Identify Behaviour + let identify_config = if config.private { IdentifyConfig::new( "".into(), local_key.public(), // Still send legitimate public key @@ -161,6 +189,12 @@ impl Behaviour { .with_agent_version(lighthouse_version::version_with_platform()) }; + // Build and start the discovery sub-behaviour + let mut discovery = Discovery::new(local_key, config, network_globals.clone(), log).await?; + // start searching for peers + discovery.discover_peers(); + + // Grab our local ENR FORK ID let enr_fork_id = network_globals .local_enr() .eth2() @@ -173,18 +207,18 @@ impl Behaviour { max_subscriptions_per_request: 100, //this is according to the current go implementation }; - // Initialize the compression transform. - let snappy_transform = SnappyTransform::new(net_conf.gs_config.max_transmit_size()); - + // Build and configure the Gossipsub behaviour + let snappy_transform = SnappyTransform::new(config.gs_config.max_transmit_size()); let mut gossipsub = Gossipsub::new_with_subscription_filter_and_transform( MessageAuthenticity::Anonymous, - net_conf.gs_config.clone(), + config.gs_config.clone(), filter, snappy_transform, ) .map_err(|e| format!("Could not construct gossipsub: {:?}", e))?; - //we don't know the number of active validators and the current slot yet + // Construct a set of gossipsub peer scoring parameters + // We don't know the number of active validators and the current slot yet let active_validators = TSpec::minimum_validator_count(); let current_slot = Slot::new(0); @@ -196,9 +230,9 @@ impl Behaviour { opportunistic_graft_threshold: 5.0, }; - let score_settings = PeerScoreSettings::new(chain_spec, &net_conf.gs_config); + let score_settings = PeerScoreSettings::new(chain_spec, &config.gs_config); - //Prepare scoring parameters + // Prepare scoring parameters let params = score_settings.get_peer_score_params( active_validators, &thresholds, @@ -208,6 +242,7 @@ impl Behaviour { trace!(behaviour_log, "Using peer score params"; "params" => ?params); + // Set up a scoring update interval let update_gossipsub_scores = tokio::time::interval(params.decay_interval); gossipsub @@ -215,71 +250,35 @@ impl Behaviour { .expect("Valid score params and thresholds"); Ok(Behaviour { - eth2_rpc: RPC::new(log.clone()), + // Sub-behaviours gossipsub, + eth2_rpc: RPC::new(log.clone()), + discovery, identify: Identify::new(identify_config), - peer_manager: PeerManager::new(local_key, net_conf, network_globals.clone(), log) - .await?, + // Auxiliary fields + peer_manager: PeerManager::new(config, network_globals.clone(), log).await?, events: VecDeque::new(), - peers_to_dc: VecDeque::new(), + internal_events: VecDeque::new(), network_globals, enr_fork_id, waker: None, - network_dir: net_conf.network_dir.clone(), + network_dir: config.network_dir.clone(), log: behaviour_log, score_settings, update_gossipsub_scores, }) } - pub fn update_gossipsub_parameters( - &mut self, - active_validators: usize, - current_slot: Slot, - ) -> error::Result<()> { - let (beacon_block_params, beacon_aggregate_proof_params, beacon_attestation_subnet_params) = - self.score_settings - .get_dynamic_topic_params(active_validators, current_slot)?; - - let fork_digest = self.enr_fork_id.fork_digest; - let get_topic = |kind: GossipKind| -> Topic { - GossipTopic::new(kind, GossipEncoding::default(), fork_digest).into() - }; - - debug!(self.log, "Updating gossipsub score parameters"; - "active_validators" => active_validators); - trace!(self.log, "Updated gossipsub score parameters"; - "beacon_block_params" => ?beacon_block_params, - "beacon_aggregate_proof_params" => ?beacon_aggregate_proof_params, - "beacon_attestation_subnet_params" => ?beacon_attestation_subnet_params, - ); - - self.gossipsub - .set_topic_params(get_topic(GossipKind::BeaconBlock), beacon_block_params)?; - - self.gossipsub.set_topic_params( - get_topic(GossipKind::BeaconAggregateAndProof), - beacon_aggregate_proof_params, - )?; - - for i in 0..self.score_settings.attestation_subnet_count() { - self.gossipsub.set_topic_params( - get_topic(GossipKind::Attestation(SubnetId::new(i))), - beacon_attestation_subnet_params.clone(), - )?; - } + /* Public Accessible Functions to interact with the behaviour */ - Ok(()) + /// Get a mutable reference to the underlying discovery sub-behaviour. + pub fn discovery_mut(&mut self) -> &mut Discovery { + &mut self.discovery } - /// Attempts to connect to a libp2p peer. - /// - /// This MUST be used over Swarm::dial() as this keeps track of the peer in the peer manager. - /// - /// All external dials, dial a multiaddr. This is currently unused but kept here in case any - /// part of lighthouse needs to connect to a peer_id in the future. - pub fn dial(&mut self, peer_id: &PeerId) { - self.peer_manager.dial_peer(peer_id); + /// Get a mutable reference to the peer manager. + pub fn peer_manager_mut(&mut self) -> &mut PeerManager { + &mut self.peer_manager } /// Returns the local ENR of the node. @@ -451,6 +450,48 @@ impl Behaviour { } } + /// Updates the current gossipsub scoring parameters based on the validator count and current + /// slot. + pub fn update_gossipsub_parameters( + &mut self, + active_validators: usize, + current_slot: Slot, + ) -> error::Result<()> { + let (beacon_block_params, beacon_aggregate_proof_params, beacon_attestation_subnet_params) = + self.score_settings + .get_dynamic_topic_params(active_validators, current_slot)?; + + let fork_digest = self.enr_fork_id.fork_digest; + let get_topic = |kind: GossipKind| -> Topic { + GossipTopic::new(kind, GossipEncoding::default(), fork_digest).into() + }; + + debug!(self.log, "Updating gossipsub score parameters"; + "active_validators" => active_validators); + trace!(self.log, "Updated gossipsub score parameters"; + "beacon_block_params" => ?beacon_block_params, + "beacon_aggregate_proof_params" => ?beacon_aggregate_proof_params, + "beacon_attestation_subnet_params" => ?beacon_attestation_subnet_params, + ); + + self.gossipsub + .set_topic_params(get_topic(GossipKind::BeaconBlock), beacon_block_params)?; + + self.gossipsub.set_topic_params( + get_topic(GossipKind::BeaconAggregateAndProof), + beacon_aggregate_proof_params, + )?; + + for i in 0..self.score_settings.attestation_subnet_count() { + self.gossipsub.set_topic_params( + get_topic(GossipKind::Attestation(SubnetId::new(i))), + beacon_attestation_subnet_params.clone(), + )?; + } + + Ok(()) + } + /* Eth2 RPC behaviour functions */ /// Send a request to a peer over RPC. @@ -483,11 +524,6 @@ impl Behaviour { /* Peer management functions */ - /// Report a peer's action. - pub fn report_peer(&mut self, peer_id: &PeerId, action: PeerAction, source: ReportSource) { - self.peer_manager.report_peer(peer_id, action, source) - } - /// Disconnects from a peer providing a reason. /// /// This will send a goodbye, disconnect and then ban the peer. @@ -498,23 +534,19 @@ impl Behaviour { /// Returns an iterator over all enr entries in the DHT. pub fn enr_entries(&mut self) -> Vec { - self.peer_manager.discovery_mut().table_entries_enr() + self.discovery.table_entries_enr() } /// Add an ENR to the routing table of the discovery mechanism. pub fn add_enr(&mut self, enr: Enr) { - self.peer_manager.discovery_mut().add_enr(enr); + self.discovery.add_enr(enr); } /// Updates a subnet value to the ENR bitfield. /// /// The `value` is `true` if a subnet is being added and false otherwise. pub fn update_enr_subnet(&mut self, subnet_id: SubnetId, value: bool) { - if let Err(e) = self - .peer_manager - .discovery_mut() - .update_enr_bitfield(subnet_id, value) - { + if let Err(e) = self.discovery.update_enr_bitfield(subnet_id, value) { crit!(self.log, "Could not update ENR bitfield"; "error" => e); } // update the local meta data which informs our peers of the update during PINGS @@ -523,16 +555,58 @@ impl Behaviour { /// Attempts to discover new peers for a given subnet. The `min_ttl` gives the time at which we /// would like to retain the peers for. - pub fn discover_subnet_peers(&mut self, subnet_subscriptions: Vec) { - self.peer_manager - .discover_subnet_peers(subnet_subscriptions) + pub fn discover_subnet_peers(&mut self, subnets_to_discover: Vec) { + // If discovery is not started or disabled, ignore the request + if !self.discovery.started { + return; + } + + let filtered: Vec = subnets_to_discover + .into_iter() + .filter(|s| { + // Extend min_ttl of connected peers on required subnets + if let Some(min_ttl) = s.min_ttl { + self.network_globals + .peers + .write() + .extend_peers_on_subnet(s.subnet_id, min_ttl); + } + // Already have target number of peers, no need for subnet discovery + let peers_on_subnet = self + .network_globals + .peers + .read() + .good_peers_on_subnet(s.subnet_id) + .count(); + if peers_on_subnet >= TARGET_SUBNET_PEERS { + trace!( + self.log, + "Discovery query ignored"; + "subnet_id" => ?s.subnet_id, + "reason" => "Already connected to desired peers", + "connected_peers_on_subnet" => peers_on_subnet, + "target_subnet_peers" => TARGET_SUBNET_PEERS, + ); + false + // Queue an outgoing connection request to the cached peers that are on `s.subnet_id`. + // If we connect to the cached peers before the discovery query starts, then we potentially + // save a costly discovery query. + } else { + self.dial_cached_enrs_in_subnet(s.subnet_id); + true + } + }) + .collect(); + + // request the subnet query from discovery + if !filtered.is_empty() { + self.discovery.discover_subnet_peers(filtered); + } } /// Updates the local ENR's "eth2" field with the latest EnrForkId. pub fn update_fork_version(&mut self, enr_fork_id: EnrForkId) { - self.peer_manager - .discovery_mut() - .update_eth2_enr(enr_fork_id.clone()); + self.discovery.update_eth2_enr(enr_fork_id.clone()); // unsubscribe from all gossip topics and re-subscribe to their new fork counterparts let subscribed_topics = self @@ -563,8 +637,7 @@ impl Behaviour { /// Updates the current meta data of the node to match the local ENR. fn update_metadata(&mut self) { let local_attnets = self - .peer_manager - .discovery() + .discovery .local_enr() .bitfield::() .expect("Local discovery must have bitfield"); @@ -625,7 +698,97 @@ impl Behaviour { &mut self.peer_manager } - fn on_gossip_event(&mut self, event: GossipsubEvent) { + // RPC Propagation methods + /// Queues the response to be sent upwards as long at it was requested outside the Behaviour. + fn propagate_response(&mut self, id: RequestId, peer_id: PeerId, response: Response) { + if !matches!(id, RequestId::Behaviour) { + self.add_event(BehaviourEvent::ResponseReceived { + peer_id, + id, + response, + }); + } + } + + /// Convenience function to propagate a request. + fn propagate_request(&mut self, id: PeerRequestId, peer_id: PeerId, request: Request) { + self.add_event(BehaviourEvent::RequestReceived { + peer_id, + id, + request, + }); + } + + /// Adds an event to the queue waking the current task to process it. + fn add_event(&mut self, event: BehaviourEvent) { + self.events.push_back(event); + if let Some(waker) = &self.waker { + waker.wake_by_ref(); + } + } + + /// Dial cached enrs in discovery service that are in the given `subnet_id` and aren't + /// in Connected, Dialing or Banned state. + fn dial_cached_enrs_in_subnet(&mut self, subnet_id: SubnetId) { + let predicate = subnet_predicate::(vec![subnet_id], &self.log); + let peers_to_dial: Vec = self + .discovery + .cached_enrs() + .filter_map(|(peer_id, enr)| { + let peers = self.network_globals.peers.read(); + if predicate(enr) && peers.should_dial(peer_id) { + Some(*peer_id) + } else { + None + } + }) + .collect(); + for peer_id in peers_to_dial { + debug!(self.log, "Dialing cached ENR peer"; "peer_id" => %peer_id); + // Remove the ENR from the cache to prevent continual re-dialing on disconnects + self.discovery.remove_cached_enr(&peer_id); + self.internal_events + .push_back(InternalBehaviourMessage::DialPeer(peer_id)); + } + } + + /// Creates a whitelist topic filter that covers all possible topics using the given set of + /// possible fork digests. + fn create_whitelist_filter( + possible_fork_digests: Vec<[u8; 4]>, + attestation_subnet_count: u64, + ) -> WhitelistSubscriptionFilter { + let mut possible_hashes = HashSet::new(); + for fork_digest in possible_fork_digests { + let mut add = |kind| { + let topic: Topic = + GossipTopic::new(kind, GossipEncoding::SSZSnappy, fork_digest).into(); + possible_hashes.insert(topic.hash()); + }; + + use GossipKind::*; + add(BeaconBlock); + add(BeaconAggregateAndProof); + add(VoluntaryExit); + add(ProposerSlashing); + add(AttesterSlashing); + for id in 0..attestation_subnet_count { + add(Attestation(SubnetId::new(id))); + } + } + WhitelistSubscriptionFilter(possible_hashes) + } +} + +/* Behaviour Event Process Implementations + * + * These implementations dictate how to process each event that is emitted from each + * sub-behaviour. + */ + +// Gossipsub +impl NetworkBehaviourEventProcess for Behaviour { + fn inject_event(&mut self, event: GossipsubEvent) { match event { GossipsubEvent::Message { propagation_source, @@ -669,43 +832,26 @@ impl Behaviour { } } } +} - /// Queues the response to be sent upwards as long at it was requested outside the Behaviour. - fn propagate_response(&mut self, id: RequestId, peer_id: PeerId, response: Response) { - if !matches!(id, RequestId::Behaviour) { - self.add_event(BehaviourEvent::ResponseReceived { - peer_id, - id, - response, - }); - } - } - - /// Convenience function to propagate a request. - fn propagate_request(&mut self, id: PeerRequestId, peer_id: PeerId, request: Request) { - self.add_event(BehaviourEvent::RequestReceived { - peer_id, - id, - request, - }); - } - - fn on_rpc_event(&mut self, message: RPCMessage) { - let peer_id = message.peer_id; +// RPC +impl NetworkBehaviourEventProcess> for Behaviour { + fn inject_event(&mut self, event: RPCMessage) { + let peer_id = event.peer_id; if !self.peer_manager.is_connected(&peer_id) { - //ignore this event + // NOTE: Upgraded to log to test occurrences debug!( self.log, - "Ignoring rpc message of disconnected peer"; + "Ignoring rpc message of disconnecting peer"; "peer" => %peer_id ); return; } - let handler_id = message.conn_id; + let handler_id = event.conn_id; // The METADATA and PING RPC responses are handled within the behaviour and not propagated - match message.event { + match event.event { Err(handler_err) => { match handler_err { HandlerErr::Inbound { @@ -763,12 +909,10 @@ impl Behaviour { "reason" => %reason, "client" => %self.network_globals.client(&peer_id), ); - self.peers_to_dc.push_back((peer_id, None)); // NOTE: We currently do not inform the application that we are - // disconnecting here. - // The actual disconnection event will be relayed to the application. Ideally - // this time difference is short, but we may need to introduce a message to - // inform the application layer early. + // disconnecting here. The RPC handler will automatically + // disconnect for us. + // The actual disconnection event will be relayed to the application. } /* Protocols propagated to the Network */ InboundRequest::Status(msg) => { @@ -818,85 +962,37 @@ impl Behaviour { } } } +} - /// Consumes the events list when polled. - fn custom_poll( - &mut self, - cx: &mut Context, - ) -> Poll, BehaviourEvent>> { - // handle pending disconnections to perform - if let Some((peer_id, reason)) = self.peers_to_dc.pop_front() { - return Poll::Ready(NBAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: BehaviourHandlerIn::Shutdown( - reason.map(|reason| (RequestId::Behaviour, OutboundRequest::Goodbye(reason))), - ), - }); - } - - // check the peer manager for events - loop { - match self.peer_manager.poll_next_unpin(cx) { - Poll::Ready(Some(event)) => match event { - PeerManagerEvent::Dial(peer_id) => { - return Poll::Ready(NBAction::DialPeer { - peer_id, - condition: libp2p::swarm::DialPeerCondition::Disconnected, - }); - } - PeerManagerEvent::SocketUpdated(address) => { - return Poll::Ready(NBAction::ReportObservedAddr { - address, - score: AddressScore::Finite(1), - }); - } - PeerManagerEvent::Status(peer_id) => { - // it's time to status. We don't keep a beacon chain reference here, so we inform - // the network to send a status to this peer - return Poll::Ready(NBAction::GenerateEvent(BehaviourEvent::StatusPeer( - peer_id, - ))); - } - PeerManagerEvent::Ping(peer_id) => { - // send a ping request to this peer - self.ping(RequestId::Behaviour, peer_id); - } - PeerManagerEvent::MetaData(peer_id) => { - self.send_meta_data_request(peer_id); - } - PeerManagerEvent::DisconnectPeer(peer_id, reason) => { - debug!(self.log, "PeerManager disconnecting peer"; - "peer_id" => %peer_id, "reason" => %reason); - // send one goodbye - return Poll::Ready(NBAction::NotifyHandler { - peer_id, - handler: NotifyHandler::Any, - event: BehaviourHandlerIn::Shutdown(Some(( - RequestId::Behaviour, - OutboundRequest::Goodbye(reason), - ))), - }); - } - }, - Poll::Pending => break, - Poll::Ready(None) => break, // peer manager ended +// Discovery +impl NetworkBehaviourEventProcess for Behaviour { + fn inject_event(&mut self, event: DiscoveryEvent) { + match event { + DiscoveryEvent::SocketUpdated(socket_addr) => { + // A new UDP socket has been detected. + // Build a multiaddr to report to libp2p + let mut multiaddr = Multiaddr::from(socket_addr.ip()); + // NOTE: This doesn't actually track the external TCP port. More sophisticated NAT handling + // should handle this. + multiaddr.push(MProtocol::Tcp(self.network_globals.listen_port_tcp())); + self.internal_events + .push_back(InternalBehaviourMessage::SocketUpdated(multiaddr)); + } + DiscoveryEvent::QueryResult(results) => { + let to_dial_peers = self.peer_manager.peers_discovered(results); + for peer_id in to_dial_peers { + debug!(self.log, "Dialing discovered peer"; "peer_id" => %peer_id); + self.internal_events + .push_back(InternalBehaviourMessage::DialPeer(peer_id)); + } } } - - if let Some(event) = self.events.pop_front() { - return Poll::Ready(NBAction::GenerateEvent(event)); - } - - // perform gossipsub score updates when necessary - while self.update_gossipsub_scores.poll_tick(cx).is_ready() { - self.peer_manager.update_gossipsub_scores(&self.gossipsub); - } - - Poll::Pending } +} - fn on_identify_event(&mut self, event: IdentifyEvent) { +// Identify +impl NetworkBehaviourEventProcess for Behaviour { + fn inject_event(&mut self, event: IdentifyEvent) { match event { IdentifyEvent::Received { peer_id, mut info } => { if info.listen_addrs.len() > MAX_IDENTIFY_ADDRESSES { @@ -922,354 +1018,112 @@ impl Behaviour { IdentifyEvent::Pushed { .. } => {} } } - - /// Adds an event to the queue waking the current thread to process it. - fn add_event(&mut self, event: BehaviourEvent) { - self.events.push_back(event); - if let Some(waker) = &self.waker { - waker.wake_by_ref(); - } - } - - /// Creates a whitelist topic filter that covers all possible topics using the given set of - /// possible fork digests. - fn create_whitelist_filter( - possible_fork_digests: Vec<[u8; 4]>, - attestation_subnet_count: u64, - ) -> WhitelistSubscriptionFilter { - let mut possible_hashes = HashSet::new(); - for fork_digest in possible_fork_digests { - let mut add = |kind| { - let topic: Topic = - GossipTopic::new(kind, GossipEncoding::SSZSnappy, fork_digest).into(); - possible_hashes.insert(topic.hash()); - }; - - use GossipKind::*; - add(BeaconBlock); - add(BeaconAggregateAndProof); - add(VoluntaryExit); - add(ProposerSlashing); - add(AttesterSlashing); - for id in 0..attestation_subnet_count { - add(Attestation(SubnetId::new(id))); - } - } - WhitelistSubscriptionFilter(possible_hashes) - } } -/// Calls the given function with the given args on all sub behaviours. -macro_rules! delegate_to_behaviours { - ($self: ident, $fn: ident, $($arg: ident), *) => { - $self.gossipsub.$fn($($arg),*); - $self.eth2_rpc.$fn($($arg),*); - $self.identify.$fn($($arg),*); - }; -} - -impl NetworkBehaviour for Behaviour { - type ProtocolsHandler = BehaviourHandler; - type OutEvent = BehaviourEvent; - - fn new_handler(&mut self) -> Self::ProtocolsHandler { - BehaviourHandler::new(&mut self.gossipsub, &mut self.eth2_rpc, &mut self.identify) - } - - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - self.peer_manager.addresses_of_peer(peer_id) - } - - // This gets called every time a connection is established. - // NOTE: The current logic implies that we would reject extra connections for already connected - // peers if we have reached our peer limit. This is fine for the time being as we currently - // only allow a single connection per peer. - fn inject_connection_established( +impl Behaviour { + /// Consumes the events list and drives the Lighthouse global NetworkBehaviour. + fn poll( &mut self, - peer_id: &PeerId, - conn_id: &ConnectionId, - endpoint: &ConnectedPoint, - ) { - let goodbye_reason: Option = if self.peer_manager.is_banned(peer_id) { - // If the peer is banned, send goodbye with reason banned. - // A peer that has recently transitioned to the banned state should be in the - // disconnecting state, but the `is_banned()` function is dependent on score so should - // be true here in this case. - Some(GoodbyeReason::Banned) - } else if self.peer_manager.peer_limit_reached() - && self - .network_globals - .peers - .read() - .peer_info(peer_id) - .map_or(true, |i| !i.has_future_duty()) - { - // If we are at our peer limit and we don't need the peer for a future validator - // duty, send goodbye with reason TooManyPeers - Some(GoodbyeReason::TooManyPeers) + cx: &mut Context, + _: &mut impl PollParameters, + ) -> Poll>> { + if let Some(waker) = &self.waker { + if waker.will_wake(cx.waker()) { + self.waker = Some(cx.waker().clone()); + } } else { - None - }; + self.waker = Some(cx.waker().clone()); + } - if let Some(goodbye_reason) = goodbye_reason { - match goodbye_reason { - GoodbyeReason::Banned => { - debug!(self.log, "Disconnecting newly connected peer"; "peer_id" => %peer_id, "reason" => %goodbye_reason) + // Handle internal events first + if let Some(event) = self.internal_events.pop_front() { + match event { + InternalBehaviourMessage::DialPeer(peer_id) => { + return Poll::Ready(NBAction::DialPeer { + peer_id, + condition: DialPeerCondition::Disconnected, + }); } - _ => { - trace!(self.log, "Disconnecting newly connected peer"; "peer_id" => %peer_id, "reason" => %goodbye_reason) + InternalBehaviourMessage::SocketUpdated(address) => { + return Poll::Ready(NBAction::ReportObservedAddr { + address, + score: AddressScore::Finite(1), + }); } } - self.peers_to_dc.push_back((*peer_id, Some(goodbye_reason))); - // NOTE: We don't inform the peer manager that this peer is disconnecting. It is simply - // rejected with a goodbye. - return; - } - - // All peers at this point will be registered as being connected. - // Notify the peer manager of a successful connection - match endpoint { - ConnectedPoint::Listener { send_back_addr, .. } => { - self.peer_manager - .connect_ingoing(&peer_id, send_back_addr.clone()); - self.add_event(BehaviourEvent::PeerConnected(*peer_id)); - debug!(self.log, "Connection established"; "peer_id" => %peer_id, "connection" => "Incoming"); - } - ConnectedPoint::Dialer { address } => { - self.peer_manager - .connect_outgoing(&peer_id, address.clone()); - self.add_event(BehaviourEvent::PeerDialed(*peer_id)); - debug!(self.log, "Connection established"; "peer_id" => %peer_id, "connection" => "Dialed"); - } - } - // report the event to the behaviour - delegate_to_behaviours!( - self, - inject_connection_established, - peer_id, - conn_id, - endpoint - ); - } - - // This gets called on the initial connection establishment. - // NOTE: This gets called after inject_connection_established. Therefore the logic in that - // function dictates the logic here. - fn inject_connected(&mut self, peer_id: &PeerId) { - // If the PeerManager has connected this peer, inform the behaviours - if !self.network_globals.peers.read().is_connected(&peer_id) { - return; - } - - // increment prometheus metrics - metrics::inc_counter(&metrics::PEER_CONNECT_EVENT_COUNT); - metrics::set_gauge( - &metrics::PEERS_CONNECTED, - self.network_globals.connected_peers() as i64, - ); - - delegate_to_behaviours!(self, inject_connected, peer_id); - } - - // This gets called every time a connection is closed. - // NOTE: The peer manager state can be modified in the lifetime of the peer. Due to the scoring - // mechanism. Peers can become banned. In this case, we still want to inform the behaviours. - fn inject_connection_closed( - &mut self, - peer_id: &PeerId, - conn_id: &ConnectionId, - endpoint: &ConnectedPoint, - ) { - // If the peer manager (and therefore the behaviour's) believe this peer connected, inform - // about the disconnection. - // It could be the peer was in the process of being disconnected. In this case the - // sub-behaviours are expecting this peer to be connected and we inform them. - if self - .network_globals - .peers - .read() - .is_connected_or_disconnecting(peer_id) - { - // We are disconnecting the peer or the peer has already been connected. - // Both these cases, the peer has been previously registered in the sub protocols. - delegate_to_behaviours!(self, inject_connection_closed, peer_id, conn_id, endpoint); } - } - // This gets called once there are no more active connections. - fn inject_disconnected(&mut self, peer_id: &PeerId) { - // If the application/behaviour layers thinks this peer has connected inform it of the disconnect. - - // Remove all subnet subscriptions from peerdb for the disconnected peer. - self.peer_manager().remove_all_subscriptions(&peer_id); - - if self - .network_globals - .peers - .read() - .is_connected_or_disconnecting(peer_id) - { - // We are disconnecting the peer or the peer has already been connected. - // Both these cases, the peer has been previously registered in the sub protocols and - // potentially the application layer. - // Inform the application. - self.add_event(BehaviourEvent::PeerDisconnected(*peer_id)); - // Inform the behaviour. - delegate_to_behaviours!(self, inject_disconnected, peer_id); - - debug!(self.log, "Peer disconnected"; "peer_id" => %peer_id); - - // Decrement the PEERS_PER_CLIENT metric - if let Some(kind) = self - .network_globals - .peers - .read() - .peer_info(peer_id) - .map(|info| info.client.kind.clone()) - { - if let Some(v) = - metrics::get_int_gauge(&metrics::PEERS_PER_CLIENT, &[&kind.to_string()]) - { - v.dec() - }; + // check the peer manager for events + loop { + match self.peer_manager.poll_next_unpin(cx) { + Poll::Ready(Some(event)) => match event { + PeerManagerEvent::PeerConnectedIncoming(peer_id) => { + return Poll::Ready(NBAction::GenerateEvent( + BehaviourEvent::PeerConnectedIncoming(peer_id), + )); + } + PeerManagerEvent::PeerConnectedOutgoing(peer_id) => { + return Poll::Ready(NBAction::GenerateEvent( + BehaviourEvent::PeerConnectedOutgoing(peer_id), + )); + } + PeerManagerEvent::PeerDisconnected(peer_id) => { + return Poll::Ready(NBAction::GenerateEvent( + BehaviourEvent::PeerDisconnected(peer_id), + )); + } + PeerManagerEvent::Banned(peer_id, associated_ips) => { + self.discovery.ban_peer(&peer_id, associated_ips); + return Poll::Ready(NBAction::GenerateEvent(BehaviourEvent::PeerBanned( + peer_id, + ))); + } + PeerManagerEvent::UnBanned(peer_id, associated_ips) => { + self.discovery.unban_peer(&peer_id, associated_ips); + return Poll::Ready(NBAction::GenerateEvent(BehaviourEvent::PeerUnbanned( + peer_id, + ))); + } + PeerManagerEvent::Status(peer_id) => { + // it's time to status. We don't keep a beacon chain reference here, so we inform + // the network to send a status to this peer + return Poll::Ready(NBAction::GenerateEvent(BehaviourEvent::StatusPeer( + peer_id, + ))); + } + PeerManagerEvent::DiscoverPeers => { + // Peer manager has requested a discovery query for more peers. + self.discovery.discover_peers(); + } + PeerManagerEvent::Ping(peer_id) => { + // send a ping request to this peer + self.ping(RequestId::Behaviour, peer_id); + } + PeerManagerEvent::MetaData(peer_id) => { + self.send_meta_data_request(peer_id); + } + PeerManagerEvent::DisconnectPeer(peer_id, reason) => { + debug!(self.log, "Peer Manager disconnecting peer"; + "peer_id" => %peer_id, "reason" => %reason); + // send one goodbye + self.eth2_rpc.shutdown(peer_id, reason); + } + }, + Poll::Pending => break, + Poll::Ready(None) => break, // peer manager ended } } - // Inform the peer manager. - // NOTE: It may be the case that a rejected node, due to too many peers is disconnected - // here and the peer manager has no knowledge of its connection. We insert it here for - // reference so that peer manager can track this peer. - self.peer_manager.notify_disconnect(&peer_id); - - // Update the prometheus metrics - metrics::inc_counter(&metrics::PEER_DISCONNECT_EVENT_COUNT); - metrics::set_gauge( - &metrics::PEERS_CONNECTED, - self.network_globals.connected_peers() as i64, - ); - } - - fn inject_addr_reach_failure( - &mut self, - peer_id: Option<&PeerId>, - addr: &Multiaddr, - error: &dyn std::error::Error, - ) { - delegate_to_behaviours!(self, inject_addr_reach_failure, peer_id, addr, error); - } - - fn inject_dial_failure(&mut self, peer_id: &PeerId) { - // Could not dial the peer, inform the peer manager. - self.peer_manager.notify_dial_failure(&peer_id); - delegate_to_behaviours!(self, inject_dial_failure, peer_id); - } - - fn inject_new_listener(&mut self, id: ListenerId) { - delegate_to_behaviours!(self, inject_new_listener, id); - } - - fn inject_new_listen_addr(&mut self, id: ListenerId, addr: &Multiaddr) { - delegate_to_behaviours!(self, inject_new_listen_addr, id, addr); - } - - fn inject_expired_listen_addr(&mut self, id: ListenerId, addr: &Multiaddr) { - delegate_to_behaviours!(self, inject_expired_listen_addr, id, addr); - } - - fn inject_new_external_addr(&mut self, addr: &Multiaddr) { - delegate_to_behaviours!(self, inject_new_external_addr, addr); - } - - fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn std::error::Error + 'static)) { - delegate_to_behaviours!(self, inject_listener_error, id, err); - } - fn inject_listener_closed(&mut self, id: ListenerId, reason: Result<(), &std::io::Error>) { - delegate_to_behaviours!(self, inject_listener_closed, id, reason); - } - - fn inject_event( - &mut self, - peer_id: PeerId, - conn_id: ConnectionId, - event: ::OutEvent, - ) { - // If the peer is not supposed to be connected (undergoing active disconnection, - // don't process any of its messages. - if !self.network_globals.peers.read().is_connected(&peer_id) { - return; - } - - // Events comming from the handler, redirected to each behaviour - match event { - DelegateOut::Gossipsub(ev) => self.gossipsub.inject_event(peer_id, conn_id, ev), - DelegateOut::RPC(ev) => self.eth2_rpc.inject_event(peer_id, conn_id, ev), - DelegateOut::Identify(ev) => self.identify.inject_event(peer_id, conn_id, *ev), - } - } - - fn poll( - &mut self, - cx: &mut Context, - poll_params: &mut impl PollParameters, - ) -> Poll::InEvent, Self::OutEvent>> { - // update the waker if needed - if let Some(waker) = &self.waker { - if waker.will_wake(cx.waker()) { - self.waker = Some(cx.waker().clone()); - } - } else { - self.waker = Some(cx.waker().clone()); + if let Some(event) = self.events.pop_front() { + return Poll::Ready(NBAction::GenerateEvent(event)); } - macro_rules! poll_behaviour { - /* $behaviour: The sub-behaviour being polled. - * $on_event_fn: Function to call if we get an event from the sub-behaviour. - * $notify_handler_event_closure: Closure mapping the received event type to - * the one that the handler should get. - */ - ($behaviour: ident, $on_event_fn: ident, $notify_handler_event_closure: expr) => { - loop { - // poll the sub-behaviour - match self.$behaviour.poll(cx, poll_params) { - Poll::Ready(action) => match action { - // call the designated function to handle the event from sub-behaviour - NBAction::GenerateEvent(event) => self.$on_event_fn(event), - NBAction::DialAddress { address } => { - return Poll::Ready(NBAction::DialAddress { address }) - } - NBAction::DialPeer { peer_id, condition } => { - return Poll::Ready(NBAction::DialPeer { peer_id, condition }) - } - NBAction::NotifyHandler { - peer_id, - handler, - event, - } => { - return Poll::Ready(NBAction::NotifyHandler { - peer_id, - handler, - // call the closure mapping the received event to the needed one - // in order to notify the handler - event: BehaviourHandlerIn::Delegate( - $notify_handler_event_closure(event), - ), - }); - } - NBAction::ReportObservedAddr { address, score } => { - return Poll::Ready(NBAction::ReportObservedAddr { address, score }) - } - }, - Poll::Pending => break, - } - } - }; + // perform gossipsub score updates when necessary + while self.update_gossipsub_scores.poll_tick(cx).is_ready() { + self.peer_manager.update_gossipsub_scores(&self.gossipsub); } - poll_behaviour!(gossipsub, on_gossip_event, DelegateIn::Gossipsub); - poll_behaviour!(eth2_rpc, on_rpc_event, DelegateIn::RPC); - poll_behaviour!(identify, on_identify_event, DelegateIn::Identify); - - self.custom_poll(cx) + Poll::Pending } } diff --git a/beacon_node/eth2_libp2p/src/discovery/mod.rs b/beacon_node/eth2_libp2p/src/discovery/mod.rs index 5554ba1198f..29c060d8ab0 100644 --- a/beacon_node/eth2_libp2p/src/discovery/mod.rs +++ b/beacon_node/eth2_libp2p/src/discovery/mod.rs @@ -1,22 +1,34 @@ -///! This manages the discovery and management of peers. +//! The discovery sub-behaviour of Lighthouse. +//! +//! This module creates a libp2p dummy-behaviour built around the discv5 protocol. It handles +//! queries and manages access to the discovery routing table. + pub(crate) mod enr; pub mod enr_ext; // Allow external use of the lighthouse ENR builder +use crate::{config, metrics}; +use crate::{error, Enr, NetworkConfig, NetworkGlobals, SubnetDiscovery}; +use discv5::{enr::NodeId, Discv5, Discv5Event}; pub use enr::{ build_enr, create_enr_builder_from_config, load_enr_from_disk, use_or_load_enr, CombinedKey, Eth2Enr, }; -pub use enr_ext::{peer_id_to_node_id, CombinedKeyExt, EnrExt}; -pub use libp2p::core::identity::{Keypair, PublicKey}; - -use crate::{config, metrics}; -use crate::{error, Enr, NetworkConfig, NetworkGlobals, SubnetDiscovery}; -use discv5::{enr::NodeId, Discv5, Discv5Event}; use enr::{BITFIELD_ENR_KEY, ETH2_ENR_KEY}; +pub use enr_ext::{peer_id_to_node_id, CombinedKeyExt, EnrExt}; use futures::prelude::*; use futures::stream::FuturesUnordered; -use libp2p::core::PeerId; +pub use libp2p::{ + core::{ + connection::ConnectionId, + identity::{Keypair, PublicKey}, + ConnectedPoint, Multiaddr, PeerId, + }, + swarm::{ + protocols_handler::ProtocolsHandler, NetworkBehaviour, NetworkBehaviourAction as NBAction, + NotifyHandler, PollParameters, SubstreamProtocol, + }, +}; use lru::LruCache; use slog::{crit, debug, error, info, warn}; use ssz::{Decode, Encode}; @@ -885,9 +897,68 @@ impl Discovery { } None } +} + +/* NetworkBehaviour Implementation */ + +impl NetworkBehaviour for Discovery { + // Discovery is not a real NetworkBehaviour... + type ProtocolsHandler = libp2p::swarm::protocols_handler::DummyProtocolsHandler; + type OutEvent = DiscoveryEvent; + + fn new_handler(&mut self) -> Self::ProtocolsHandler { + libp2p::swarm::protocols_handler::DummyProtocolsHandler::default() + } - // Main execution loop to be driven by the peer manager. - pub fn poll(&mut self, cx: &mut Context) -> Poll { + // Handles the libp2p request to obtain multiaddrs for peer_id's in order to dial them. + fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { + if let Some(enr) = self.enr_of_peer(peer_id) { + // ENR's may have multiple Multiaddrs. The multi-addr associated with the UDP + // port is removed, which is assumed to be associated with the discv5 protocol (and + // therefore irrelevant for other libp2p components). + enr.multiaddr_tcp() + } else { + // PeerId is not known + Vec::new() + } + } + + fn inject_connected(&mut self, _peer_id: &PeerId) {} + fn inject_disconnected(&mut self, _peer_id: &PeerId) {} + fn inject_connection_established( + &mut self, + _: &PeerId, + _: &ConnectionId, + _connected_point: &ConnectedPoint, + ) { + } + fn inject_connection_closed( + &mut self, + _: &PeerId, + _: &ConnectionId, + _connected_point: &ConnectedPoint, + ) { + } + fn inject_event( + &mut self, + _: PeerId, + _: ConnectionId, + _: ::OutEvent, + ) { + } + + fn inject_dial_failure(&mut self, peer_id: &PeerId) { + // set peer as disconnected in discovery DHT + debug!(self.log, "Marking peer disconnected in DHT"; "peer_id" => %peer_id); + self.disconnect_peer(peer_id); + } + + // Main execution loop to drive the behaviour + fn poll( + &mut self, + cx: &mut Context, + _: &mut impl PollParameters, + ) -> Poll::InEvent, Self::OutEvent>> { if !self.started { return Poll::Pending; } @@ -898,7 +969,9 @@ impl Discovery { // Drive the queries and return any results from completed queries if let Some(results) = self.poll_queries(cx) { // return the result to the peer manager - return Poll::Ready(DiscoveryEvent::QueryResult(results)); + return Poll::Ready(NBAction::GenerateEvent(DiscoveryEvent::QueryResult( + results, + ))); } // Process the server event stream @@ -946,7 +1019,9 @@ impl Discovery { enr::save_enr_to_disk(Path::new(&self.enr_dir), &enr, &self.log); // update network globals *self.network_globals.local_enr.write() = enr; - return Poll::Ready(DiscoveryEvent::SocketUpdated(socket)); + return Poll::Ready(NBAction::GenerateEvent( + DiscoveryEvent::SocketUpdated(socket), + )); } Discv5Event::EnrAdded { .. } | Discv5Event::TalkRequest(_) diff --git a/beacon_node/eth2_libp2p/src/peer_manager/mod.rs b/beacon_node/eth2_libp2p/src/peer_manager/mod.rs index 591e837ad51..bfe8150f5f0 100644 --- a/beacon_node/eth2_libp2p/src/peer_manager/mod.rs +++ b/beacon_node/eth2_libp2p/src/peer_manager/mod.rs @@ -1,20 +1,19 @@ -//! Implementation of a Lighthouse's peer management system. +//! Implementation of Lighthouse's peer management system. pub use self::peerdb::*; -use crate::discovery::{subnet_predicate, Discovery, DiscoveryEvent, TARGET_SUBNET_PEERS}; use crate::rpc::{GoodbyeReason, MetaData, Protocol, RPCError, RPCResponseErrorCode}; use crate::types::SyncState; use crate::{error, metrics, Gossipsub}; -use crate::{EnrExt, NetworkConfig, NetworkGlobals, PeerId, SubnetDiscovery}; +use crate::{NetworkConfig, NetworkGlobals, PeerId}; +use discv5::Enr; use futures::prelude::*; use futures::Stream; use hashset_delay::HashSetDelay; -use libp2p::core::multiaddr::Protocol as MProtocol; +use libp2p::core::ConnectedPoint; use libp2p::identify::IdentifyInfo; -use slog::{crit, debug, error, trace, warn}; +use slog::{crit, debug, error, warn}; use smallvec::SmallVec; use std::{ - net::SocketAddr, pin::Pin, sync::Arc, task::{Context, Poll}, @@ -36,6 +35,7 @@ pub use peer_sync_status::{PeerSyncStatus, SyncInfo}; use score::{PeerAction, ReportSource, ScoreState}; use std::cmp::Ordering; use std::collections::HashMap; +use std::net::IpAddr; /// The time in seconds between re-status's peers. const STATUS_INTERVAL: u64 = 300; @@ -79,20 +79,22 @@ pub struct PeerManager { target_peers: usize, /// The maximum number of peers we allow (exceptions for subnet peers) max_peers: usize, - /// The discovery service. - discovery: Discovery, /// The heartbeat interval to perform routine maintenance. heartbeat: tokio::time::Interval, + /// Keeps track of whether the discovery service is enabled or not. + discovery_enabled: bool, /// The logger associated with the `PeerManager`. log: slog::Logger, } /// The events that the `PeerManager` outputs (requests). pub enum PeerManagerEvent { - /// Dial a PeerId. - Dial(PeerId), - /// Inform libp2p that our external socket addr has been updated. - SocketUpdated(Multiaddr), + /// A peer has dialed us. + PeerConnectedIncoming(PeerId), + /// A peer has been dialed. + PeerConnectedOutgoing(PeerId), + /// A peer has disconnected. + PeerDisconnected(PeerId), /// Sends a STATUS to a peer. Status(PeerId), /// Sends a PING to a peer. @@ -101,22 +103,22 @@ pub enum PeerManagerEvent { MetaData(PeerId), /// The peer should be disconnected. DisconnectPeer(PeerId, GoodbyeReason), + /// Inform the behaviour to ban this peer and associated ip addresses. + Banned(PeerId, Vec), + /// The peer should be unbanned with the associated ip addresses. + UnBanned(PeerId, Vec), + /// Request the behaviour to discover more peers. + DiscoverPeers, } impl PeerManager { // NOTE: Must be run inside a tokio executor. pub async fn new( - local_key: &Keypair, config: &NetworkConfig, network_globals: Arc>, log: &slog::Logger, ) -> error::Result { - // start the discovery service - let mut discovery = Discovery::new(local_key, config, network_globals.clone(), log).await?; - - // start searching for peers - discovery.discover_peers(); - + // Set up the peer manager heartbeat interval let heartbeat = tokio::time::interval(tokio::time::Duration::from_secs(HEARTBEAT_INTERVAL)); Ok(PeerManager { @@ -127,22 +129,14 @@ impl PeerManager { status_peers: HashSetDelay::new(Duration::from_secs(STATUS_INTERVAL)), target_peers: config.target_peers, max_peers: (config.target_peers as f32 * (1.0 + PEER_EXCESS_FACTOR)).ceil() as usize, - discovery, heartbeat, + discovery_enabled: !config.disable_discovery, log: log.clone(), }) } /* Public accessible functions */ - /// Attempts to connect to a peer. - /// - /// Returns true if the peer was accepted into the database. - pub fn dial_peer(&mut self, peer_id: &PeerId) -> bool { - self.events.push(PeerManagerEvent::Dial(*peer_id)); - self.connect_peer(peer_id, ConnectingType::Dialing) - } - /// The application layer wants to disconnect from a peer for a particular reason. /// /// All instant disconnections are fatal and we ban the associated peer. @@ -217,66 +211,52 @@ impl PeerManager { self.ban_and_unban_peers(to_ban_peers, to_unban_peers); } - /* Discovery Requests */ - - /// Provides a reference to the underlying discovery service. - pub fn discovery(&self) -> &Discovery { - &self.discovery - } - - /// Provides a mutable reference to the underlying discovery service. - pub fn discovery_mut(&mut self) -> &mut Discovery { - &mut self.discovery - } - - /// A request to find peers on a given subnet. - pub fn discover_subnet_peers(&mut self, subnets_to_discover: Vec) { - // If discovery is not started or disabled, ignore the request - if !self.discovery.started { - return; - } + /// Peers that have been returned by discovery requests that are suitable for dialing are + /// returned here. + /// + /// NOTE: By dialing `PeerId`s and not multiaddrs, libp2p requests the multiaddr associated + /// with a new `PeerId` which involves a discovery routing table lookup. We could dial the + /// multiaddr here, however this could relate to duplicate PeerId's etc. If the lookup + /// proves resource constraining, we should switch to multiaddr dialling here. + #[allow(clippy::mutable_key_type)] + pub fn peers_discovered(&mut self, results: HashMap>) -> Vec { + let mut to_dial_peers = Vec::new(); - let filtered: Vec = subnets_to_discover - .into_iter() - .filter(|s| { - // Extend min_ttl of connected peers on required subnets - if let Some(min_ttl) = s.min_ttl { + let connected_or_dialing = self.network_globals.connected_or_dialing_peers(); + for (peer_id, min_ttl) in results { + // we attempt a connection if this peer is a subnet peer or if the max peer count + // is not yet filled (including dialing peers) + if (min_ttl.is_some() || connected_or_dialing + to_dial_peers.len() < self.max_peers) + && self.network_globals.peers.read().should_dial(&peer_id) + { + // This should be updated with the peer dialing. In fact created once the peer is + // dialed + if let Some(min_ttl) = min_ttl { self.network_globals .peers .write() - .extend_peers_on_subnet(s.subnet_id, min_ttl); - } - // Already have target number of peers, no need for subnet discovery - let peers_on_subnet = self - .network_globals - .peers - .read() - .good_peers_on_subnet(s.subnet_id) - .count(); - if peers_on_subnet >= TARGET_SUBNET_PEERS { - trace!( - self.log, - "Discovery query ignored"; - "subnet_id" => ?s.subnet_id, - "reason" => "Already connected to desired peers", - "connected_peers_on_subnet" => peers_on_subnet, - "target_subnet_peers" => TARGET_SUBNET_PEERS, - ); - false - // Queue an outgoing connection request to the cached peers that are on `s.subnet_id`. - // If we connect to the cached peers before the discovery query starts, then we potentially - // save a costly discovery query. - } else { - self.dial_cached_enrs_in_subnet(s.subnet_id); - true + .update_min_ttl(&peer_id, min_ttl); } - }) - .collect(); + to_dial_peers.push(peer_id); + } + } + + // Queue another discovery if we need to + let peer_count = self.network_globals.connected_or_dialing_peers(); + let outbound_only_peer_count = self.network_globals.connected_outbound_only_peers(); + let min_outbound_only_target = + (self.target_peers as f32 * MIN_OUTBOUND_ONLY_FACTOR).ceil() as usize; - // request the subnet query from discovery - if !filtered.is_empty() { - self.discovery.discover_subnet_peers(filtered); + if self.discovery_enabled + && (peer_count < self.target_peers.saturating_sub(to_dial_peers.len()) + || outbound_only_peer_count < min_outbound_only_target) + { + // We need more peers, re-queue a discovery lookup. + debug!(self.log, "Starting a new peer discovery query"; "connected_peers" => peer_count, "target_peers" => self.target_peers); + self.events.push(PeerManagerEvent::DiscoverPeers); } + + to_dial_peers } /// A STATUS message has been received from a peer. This resets the status timer. @@ -307,19 +287,144 @@ impl PeerManager { /* Notifications from the Swarm */ - /// Updates the state of the peer as disconnected. - /// - /// This is also called when dialing a peer fails. - pub fn notify_disconnect(&mut self, peer_id: &PeerId) { - self.network_globals - .peers - .write() - .notify_disconnect(peer_id); + // A peer is being dialed. + pub fn inject_dialing(&mut self, peer_id: &PeerId, enr: Option) { + self.inject_peer_connection(peer_id, ConnectingType::Dialing, enr); + } - // remove the ping and status timer for the peer - self.inbound_ping_peers.remove(peer_id); - self.outbound_ping_peers.remove(peer_id); - self.status_peers.remove(peer_id); + pub fn inject_connection_established( + &mut self, + peer_id: PeerId, + endpoint: ConnectedPoint, + num_established: std::num::NonZeroU32, + enr: Option, + ) { + // Log the connection + match &endpoint { + ConnectedPoint::Listener { .. } => { + debug!(self.log, "Connection established"; "peer_id" => %peer_id, "connection" => "Incoming", "connections" => %num_established); + } + ConnectedPoint::Dialer { .. } => { + debug!(self.log, "Connection established"; "peer_id" => %peer_id, "connection" => "Outgoing", "connections" => %num_established); + } + } + + // Should not be able to connect to a banned peer. Double check here + if self.is_banned(&peer_id) { + warn!(self.log, "Connected to a banned peer"; "peer_id" => %peer_id); + self.events.push(PeerManagerEvent::DisconnectPeer( + peer_id, + GoodbyeReason::Banned, + )); + self.network_globals + .peers + .write() + .notify_disconnecting(&peer_id, true); + return; + } + + // Check the connection limits + if self.peer_limit_reached() + && self + .network_globals + .peers + .read() + .peer_info(&peer_id) + .map_or(true, |peer| !peer.has_future_duty()) + { + self.events.push(PeerManagerEvent::DisconnectPeer( + peer_id, + GoodbyeReason::TooManyPeers, + )); + self.network_globals + .peers + .write() + .notify_disconnecting(&peer_id, false); + return; + } + + // Register the newly connected peer (regardless if we are about to disconnect them). + // NOTE: We don't register peers that we are disconnecting immediately. The network service + // does not need to know about these peers. + match endpoint { + ConnectedPoint::Listener { send_back_addr, .. } => { + self.inject_connect_ingoing(&peer_id, send_back_addr, enr); + if num_established == std::num::NonZeroU32::new(1).expect("valid") { + self.events + .push(PeerManagerEvent::PeerConnectedIncoming(peer_id)); + } + } + ConnectedPoint::Dialer { address } => { + self.inject_connect_outgoing(&peer_id, address, enr); + if num_established == std::num::NonZeroU32::new(1).expect("valid") { + self.events + .push(PeerManagerEvent::PeerConnectedOutgoing(peer_id)); + } + } + } + + // increment prometheus metrics + metrics::inc_counter(&metrics::PEER_CONNECT_EVENT_COUNT); + metrics::set_gauge( + &metrics::PEERS_CONNECTED, + self.network_globals.connected_peers() as i64, + ); + } + + pub fn inject_connection_closed( + &mut self, + peer_id: PeerId, + _endpoint: ConnectedPoint, + num_established: u32, + ) { + if num_established == 0 { + // There are no more connections + + // Remove all subnet subscriptions from the peer_db + self.remove_all_subscriptions(&peer_id); + + if self + .network_globals + .peers + .read() + .is_connected_or_disconnecting(&peer_id) + { + // We are disconnecting the peer or the peer has already been connected. + // Both these cases, the peer has been previously registered by the peer manager and + // potentially the application layer. + // Inform the application. + self.events + .push(PeerManagerEvent::PeerDisconnected(peer_id)); + debug!(self.log, "Peer disconnected"; "peer_id" => %peer_id); + + // Decrement the PEERS_PER_CLIENT metric + if let Some(kind) = self + .network_globals + .peers + .read() + .peer_info(&peer_id) + .map(|info| info.client.kind.clone()) + { + if let Some(v) = + metrics::get_int_gauge(&metrics::PEERS_PER_CLIENT, &[&kind.to_string()]) + { + v.dec() + }; + } + } + + // NOTE: It may be the case that a rejected node, due to too many peers is disconnected + // here and the peer manager has no knowledge of its connection. We insert it here for + // reference so that peer manager can track this peer. + self.inject_disconnect(&peer_id); + + // Update the prometheus metrics + metrics::inc_counter(&metrics::PEER_DISCONNECT_EVENT_COUNT); + metrics::set_gauge( + &metrics::PEERS_CONNECTED, + self.network_globals.connected_peers() as i64, + ); + } } /// A dial attempt has failed. @@ -327,27 +432,12 @@ impl PeerManager { /// NOTE: It can be the case that we are dialing a peer and during the dialing process the peer /// connects and the dial attempt later fails. To handle this, we only update the peer_db if /// the peer is not already connected. - pub fn notify_dial_failure(&mut self, peer_id: &PeerId) { + pub fn inject_dial_failure(&mut self, peer_id: &PeerId) { if !self.network_globals.peers.read().is_connected(peer_id) { - self.notify_disconnect(peer_id); - // set peer as disconnected in discovery DHT - debug!(self.log, "Marking peer disconnected in DHT"; "peer_id" => %peer_id); - self.discovery.disconnect_peer(peer_id); + self.inject_disconnect(peer_id); } } - /// Sets a peer as connected as long as their reputation allows it - /// Informs if the peer was accepted - pub fn connect_ingoing(&mut self, peer_id: &PeerId, multiaddr: Multiaddr) -> bool { - self.connect_peer(peer_id, ConnectingType::IngoingConnected { multiaddr }) - } - - /// Sets a peer as connected as long as their reputation allows it - /// Informs if the peer was accepted - pub fn connect_outgoing(&mut self, peer_id: &PeerId, multiaddr: Multiaddr) -> bool { - self.connect_peer(peer_id, ConnectingType::OutgoingConnected { multiaddr }) - } - /// Reports if a peer is banned or not. /// /// This is used to determine if we should accept incoming connections. @@ -483,6 +573,7 @@ impl PeerManager { }, }, RPCError::NegotiationTimeout => PeerAction::LowToleranceError, + RPCError::Disconnected => return, // No penalty for a graceful disconnection }; self.report_peer(peer_id, peer_action, ReportSource::RPC); @@ -574,19 +665,6 @@ impl PeerManager { } } - // Handles the libp2p request to obtain multiaddrs for peer_id's in order to dial them. - pub fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - if let Some(enr) = self.discovery.enr_of_peer(peer_id) { - // ENR's may have multiple Multiaddrs. The multi-addr associated with the UDP - // port is removed, which is assumed to be associated with the discv5 protocol (and - // therefore irrelevant for other libp2p components). - enr.multiaddr_tcp() - } else { - // PeerId is not known - Vec::new() - } - } - pub(crate) fn update_gossipsub_scores(&mut self, gossipsub: &Gossipsub) { let mut to_ban_peers = Vec::new(); let mut to_unban_peers = Vec::new(); @@ -642,73 +720,49 @@ impl PeerManager { /* Internal functions */ - // The underlying discovery server has updated our external IP address. We send this up to - // notify libp2p. - fn socket_updated(&mut self, socket: SocketAddr) { - // Build a multiaddr to report to libp2p - let mut multiaddr = Multiaddr::from(socket.ip()); - // NOTE: This doesn't actually track the external TCP port. More sophisticated NAT handling - // should handle this. - multiaddr.push(MProtocol::Tcp(self.network_globals.listen_port_tcp())); - self.events.push(PeerManagerEvent::SocketUpdated(multiaddr)); + /// Sets a peer as connected as long as their reputation allows it + /// Informs if the peer was accepted + fn inject_connect_ingoing( + &mut self, + peer_id: &PeerId, + multiaddr: Multiaddr, + enr: Option, + ) -> bool { + self.inject_peer_connection(peer_id, ConnectingType::IngoingConnected { multiaddr }, enr) } - /// Dial cached enrs in discovery service that are in the given `subnet_id` and aren't - /// in Connected, Dialing or Banned state. - fn dial_cached_enrs_in_subnet(&mut self, subnet_id: SubnetId) { - let predicate = subnet_predicate::(vec![subnet_id], &self.log); - let peers_to_dial: Vec = self - .discovery() - .cached_enrs() - .filter_map(|(peer_id, enr)| { - let peers = self.network_globals.peers.read(); - if predicate(enr) && peers.should_dial(peer_id) { - Some(*peer_id) - } else { - None - } - }) - .collect(); - for peer_id in &peers_to_dial { - debug!(self.log, "Dialing cached ENR peer"; "peer_id" => %peer_id); - // Remove the ENR from the cache to prevent continual re-dialing on disconnects - self.discovery.remove_cached_enr(&peer_id); - self.dial_peer(peer_id); - } + /// Sets a peer as connected as long as their reputation allows it + /// Informs if the peer was accepted + fn inject_connect_outgoing( + &mut self, + peer_id: &PeerId, + multiaddr: Multiaddr, + enr: Option, + ) -> bool { + self.inject_peer_connection( + peer_id, + ConnectingType::OutgoingConnected { multiaddr }, + enr, + ) } - /// Peers that have been returned by discovery requests are dialed here if they are suitable. + /// Updates the state of the peer as disconnected. /// - /// NOTE: By dialing `PeerId`s and not multiaddrs, libp2p requests the multiaddr associated - /// with a new `PeerId` which involves a discovery routing table lookup. We could dial the - /// multiaddr here, however this could relate to duplicate PeerId's etc. If the lookup - /// proves resource constraining, we should switch to multiaddr dialling here. - #[allow(clippy::mutable_key_type)] - fn peers_discovered(&mut self, results: HashMap>) { - let mut to_dial_peers = Vec::new(); - - let connected_or_dialing = self.network_globals.connected_or_dialing_peers(); - for (peer_id, min_ttl) in results { - // we attempt a connection if this peer is a subnet peer or if the max peer count - // is not yet filled (including dialing peers) - if (min_ttl.is_some() || connected_or_dialing + to_dial_peers.len() < self.max_peers) - && self.network_globals.peers.read().should_dial(&peer_id) - { - // This should be updated with the peer dialing. In fact created once the peer is - // dialed - if let Some(min_ttl) = min_ttl { - self.network_globals - .peers - .write() - .update_min_ttl(&peer_id, min_ttl); - } - to_dial_peers.push(peer_id); - } - } - for peer_id in to_dial_peers { - debug!(self.log, "Dialing discovered peer"; "peer_id" => %peer_id); - self.dial_peer(&peer_id); + /// This is also called when dialing a peer fails. + fn inject_disconnect(&mut self, peer_id: &PeerId) { + if self + .network_globals + .peers + .write() + .inject_disconnect(peer_id) + { + self.ban_peer(peer_id); } + + // remove the ping and status timer for the peer + self.inbound_ping_peers.remove(peer_id); + self.outbound_ping_peers.remove(peer_id); + self.status_peers.remove(peer_id); } /// Registers a peer as connected. The `ingoing` parameter determines if the peer is being @@ -717,7 +771,12 @@ impl PeerManager { /// This is called by `connect_ingoing` and `connect_outgoing`. /// /// Informs if the peer was accepted in to the db or not. - fn connect_peer(&mut self, peer_id: &PeerId, connection: ConnectingType) -> bool { + fn inject_peer_connection( + &mut self, + peer_id: &PeerId, + connection: ConnectingType, + enr: Option, + ) -> bool { { let mut peerdb = self.network_globals.peers.write(); if peerdb.is_banned(&peer_id) { @@ -725,8 +784,6 @@ impl PeerManager { slog::crit!(self.log, "Connection has been allowed to a banned peer"; "peer_id" => %peer_id); } - let enr = self.discovery.enr_of_peer(peer_id); - match connection { ConnectingType::Dialing => { peerdb.dialing_peer(peer_id, enr); @@ -773,6 +830,8 @@ impl PeerManager { true } + /// This handles score transitions between states. It transitions peers states from + /// disconnected/banned/connected. fn handle_score_transitions( previous_state: ScoreState, peer_id: &PeerId, @@ -813,6 +872,7 @@ impl PeerManager { } } + /// Updates the state of banned peers. fn ban_and_unban_peers(&mut self, to_ban_peers: Vec, to_unban_peers: Vec) { // process banning peers for peer_id in to_ban_peers { @@ -882,7 +942,9 @@ impl PeerManager { }) .unwrap_or_default(); - self.discovery.ban_peer(&peer_id, banned_ip_addresses); + // Inform the Swarm to ban the peer + self.events + .push(PeerManagerEvent::Banned(*peer_id, banned_ip_addresses)); } /// Unbans a peer. @@ -898,7 +960,9 @@ impl PeerManager { .map(|info| info.seen_addresses().collect::>()) .unwrap_or_default(); - self.discovery.unban_peer(&peer_id, seen_ip_addresses); + // Inform the Swarm to unban the peer + self.events + .push(PeerManagerEvent::UnBanned(*peer_id, seen_ip_addresses)); Ok(()) } @@ -914,12 +978,13 @@ impl PeerManager { let min_outbound_only_target = (self.target_peers as f32 * MIN_OUTBOUND_ONLY_FACTOR).ceil() as usize; - if peer_count < self.target_peers || outbound_only_peer_count < min_outbound_only_target { + if self.discovery_enabled + && (peer_count < self.target_peers + || outbound_only_peer_count < min_outbound_only_target) + { // If we need more peers, queue a discovery lookup. - if self.discovery.started { - debug!(self.log, "Starting a new peer discovery query"; "connected_peers" => peer_count, "target_peers" => self.target_peers); - self.discovery.discover_peers(); - } + debug!(self.log, "Starting a new peer discovery query"; "connected_peers" => peer_count, "target_peers" => self.target_peers); + self.events.push(PeerManagerEvent::DiscoverPeers); } // Updates peer's scores. @@ -958,7 +1023,7 @@ impl PeerManager { let mut peer_db = self.network_globals.peers.write(); for peer_id in disconnecting_peers { - peer_db.notify_disconnecting(&peer_id); + peer_db.notify_disconnecting(&peer_id, false); self.events.push(PeerManagerEvent::DisconnectPeer( peer_id, GoodbyeReason::TooManyPeers, @@ -976,14 +1041,6 @@ impl Stream for PeerManager { self.heartbeat(); } - // handle any discovery events - while let Poll::Ready(event) = self.discovery.poll(cx) { - match event { - DiscoveryEvent::SocketUpdated(socket_addr) => self.socket_updated(socket_addr), - DiscoveryEvent::QueryResult(results) => self.peers_discovered(results), - } - } - // poll the timeouts for pings and status' loop { match self.inbound_ping_peers.poll_next_unpin(cx) { @@ -1107,7 +1164,7 @@ mod tests { vec![], &log, ); - PeerManager::new(&keypair, &config, Arc::new(globals), &log) + PeerManager::new(&config, Arc::new(globals), &log) .await .unwrap() } @@ -1124,11 +1181,19 @@ mod tests { let outbound_only_peer1 = PeerId::random(); let outbound_only_peer2 = PeerId::random(); - peer_manager.connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_ingoing(&peer1, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_ingoing(&peer2, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_outgoing(&outbound_only_peer1, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_outgoing(&outbound_only_peer2, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap(), None); + peer_manager.inject_connect_ingoing(&peer1, "/ip4/0.0.0.0".parse().unwrap(), None); + peer_manager.inject_connect_ingoing(&peer2, "/ip4/0.0.0.0".parse().unwrap(), None); + peer_manager.inject_connect_outgoing( + &outbound_only_peer1, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); + peer_manager.inject_connect_outgoing( + &outbound_only_peer2, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); // Set the outbound-only peers to have the lowest score. peer_manager @@ -1180,13 +1245,17 @@ mod tests { // Connect to 20 ingoing-only peers. for _i in 0..19 { let peer = PeerId::random(); - peer_manager.connect_ingoing(&peer, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing(&peer, "/ip4/0.0.0.0".parse().unwrap(), None); } // Connect an outbound-only peer. // Give it the lowest score so that it is evaluated first in the disconnect list iterator. let outbound_only_peer = PeerId::random(); - peer_manager.connect_ingoing(&outbound_only_peer, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing( + &outbound_only_peer, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); peer_manager .network_globals .peers @@ -1212,12 +1281,20 @@ mod tests { let inbound_only_peer1 = PeerId::random(); let outbound_only_peer1 = PeerId::random(); - peer_manager.connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_outgoing(&peer0, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap(), None); + peer_manager.inject_connect_outgoing(&peer0, "/ip4/0.0.0.0".parse().unwrap(), None); // Connect to two peers that are on the threshold of being disconnected. - peer_manager.connect_ingoing(&inbound_only_peer1, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_outgoing(&outbound_only_peer1, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing( + &inbound_only_peer1, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); + peer_manager.inject_connect_outgoing( + &outbound_only_peer1, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); peer_manager .network_globals .peers @@ -1267,12 +1344,20 @@ mod tests { let inbound_only_peer1 = PeerId::random(); let outbound_only_peer1 = PeerId::random(); - peer_manager.connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_ingoing(&peer1, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap(), None); + peer_manager.inject_connect_ingoing(&peer1, "/ip4/0.0.0.0".parse().unwrap(), None); // Connect to two peers that are on the threshold of being disconnected. - peer_manager.connect_ingoing(&inbound_only_peer1, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_outgoing(&outbound_only_peer1, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing( + &inbound_only_peer1, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); + peer_manager.inject_connect_outgoing( + &outbound_only_peer1, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); peer_manager .network_globals .peers @@ -1319,12 +1404,20 @@ mod tests { let inbound_only_peer1 = PeerId::random(); let outbound_only_peer1 = PeerId::random(); - peer_manager.connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_ingoing(&peer1, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_ingoing(&peer2, "/ip4/0.0.0.0".parse().unwrap()); - peer_manager.connect_outgoing(&outbound_only_peer1, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap(), None); + peer_manager.inject_connect_ingoing(&peer1, "/ip4/0.0.0.0".parse().unwrap(), None); + peer_manager.inject_connect_ingoing(&peer2, "/ip4/0.0.0.0".parse().unwrap(), None); + peer_manager.inject_connect_outgoing( + &outbound_only_peer1, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); // Have one peer be on the verge of disconnection. - peer_manager.connect_ingoing(&inbound_only_peer1, "/ip4/0.0.0.0".parse().unwrap()); + peer_manager.inject_connect_ingoing( + &inbound_only_peer1, + "/ip4/0.0.0.0".parse().unwrap(), + None, + ); peer_manager .network_globals .peers diff --git a/beacon_node/eth2_libp2p/src/peer_manager/peer_info.rs b/beacon_node/eth2_libp2p/src/peer_manager/peer_info.rs index 43570c5aeee..c9eeae94726 100644 --- a/beacon_node/eth2_libp2p/src/peer_manager/peer_info.rs +++ b/beacon_node/eth2_libp2p/src/peer_manager/peer_info.rs @@ -198,25 +198,16 @@ impl PeerInfo { // Setters /// Modifies the status to Disconnected and sets the last seen instant to now. Returns None if - /// no changes were made. Returns Some(bool) where the bool represents if peer became banned or - /// simply just disconnected. + /// no changes were made. Returns Some(bool) where the bool represents if peer is to now be + /// baned pub fn notify_disconnect(&mut self) -> Option { match self.connection_status { Banned { .. } | Disconnected { .. } => None, Disconnecting { to_ban } => { - // If we are disconnecting this peer in the process of banning, we now ban the - // peer. - if to_ban { - self.connection_status = Banned { - since: Instant::now(), - }; - Some(true) - } else { - self.connection_status = Disconnected { - since: Instant::now(), - }; - Some(false) - } + self.connection_status = Disconnected { + since: Instant::now(), + }; + Some(to_ban) } Connected { .. } | Dialing { .. } | Unknown => { self.connection_status = Disconnected { @@ -227,11 +218,8 @@ impl PeerInfo { } } - /// Notify the we are currently disconnecting this peer, after which the peer will be - /// considered banned. - // This intermediate state is required to inform the network behaviours that the sub-protocols - // are aware this peer exists and it is in the process of being banned. Compared to nodes that - // try to connect to us and are already banned (sub protocols do not know of these peers). + /// Notify the we are currently disconnecting this peer. Optionally ban the peer after the + /// disconnect. pub fn disconnecting(&mut self, to_ban: bool) { self.connection_status = Disconnecting { to_ban } } diff --git a/beacon_node/eth2_libp2p/src/peer_manager/peerdb.rs b/beacon_node/eth2_libp2p/src/peer_manager/peerdb.rs index e96ca3a81d0..760d0245ded 100644 --- a/beacon_node/eth2_libp2p/src/peer_manager/peerdb.rs +++ b/beacon_node/eth2_libp2p/src/peer_manager/peerdb.rs @@ -453,28 +453,31 @@ impl PeerDB { self.connect(peer_id, multiaddr, enr, ConnectionDirection::Outgoing) } - /// Sets the peer as disconnected. A banned peer remains banned - pub fn notify_disconnect(&mut self, peer_id: &PeerId) { + /// Sets the peer as disconnected. A banned peer remains banned. If the node has become banned, + /// this returns true, otherwise this is false. + pub fn inject_disconnect(&mut self, peer_id: &PeerId) -> bool { // Note that it could be the case we prevent new nodes from joining. In this instance, // we don't bother tracking the new node. if let Some(info) = self.peers.get_mut(peer_id) { - if let Some(became_banned) = info.notify_disconnect() { - if became_banned { - self.banned_peers_count - .add_banned_peer(info.seen_addresses()); - } else { - self.disconnected_peers += 1; - } + if !matches!( + info.connection_status(), + PeerConnectionStatus::Disconnected { .. } | PeerConnectionStatus::Banned { .. } + ) { + self.disconnected_peers += 1; } + let result = info.notify_disconnect().unwrap_or(false); self.shrink_to_fit(); + result + } else { + false } } - /// Notifies the peer manager that the peer is undergoing a normal disconnect (without banning - /// afterwards. - pub fn notify_disconnecting(&mut self, peer_id: &PeerId) { + /// Notifies the peer manager that the peer is undergoing a normal disconnect. Optionally tag + /// the peer to be banned after the disconnect. + pub fn notify_disconnecting(&mut self, peer_id: &PeerId, to_ban_afterwards: bool) { if let Some(info) = self.peers.get_mut(peer_id) { - info.disconnecting(false); + info.disconnecting(to_ban_afterwards); } } @@ -505,15 +508,17 @@ impl PeerDB { PeerConnectionStatus::Disconnected { .. } => { // It is possible to ban a peer that has a disconnected score, if there are many // events that score it poorly and are processed after it has disconnected. - debug!(log_ref, "Banning a disconnected peer"; "peer_id" => %peer_id); self.disconnected_peers = self.disconnected_peers.saturating_sub(1); info.ban(); self.banned_peers_count .add_banned_peer(info.seen_addresses()); + self.shrink_to_fit(); false } PeerConnectionStatus::Disconnecting { .. } => { - warn!(log_ref, "Banning peer that is currently disconnecting"; "peer_id" => %peer_id); + // NOTE: This can occur due a rapid downscore of a peer. It goes through the + // disconnection phase and straight into banning in a short time-frame. + debug!(log_ref, "Banning peer that is currently disconnecting"; "peer_id" => %peer_id); info.disconnecting(true); false } @@ -532,6 +537,7 @@ impl PeerDB { self.banned_peers_count .add_banned_peer(info.seen_addresses()); info.ban(); + self.shrink_to_fit(); false } } @@ -726,7 +732,7 @@ mod tests { assert_eq!(pdb.disconnected_peers, 0); for p in pdb.connected_peer_ids().cloned().collect::>() { - pdb.notify_disconnect(&p); + pdb.inject_disconnect(&p); } assert_eq!(pdb.disconnected_peers, MAX_DC_PEERS); @@ -744,7 +750,8 @@ mod tests { for p in pdb.connected_peer_ids().cloned().collect::>() { pdb.disconnect_and_ban(&p); - pdb.notify_disconnect(&p); + pdb.inject_disconnect(&p); + pdb.disconnect_and_ban(&p); } assert_eq!(pdb.banned_peers_count.banned_peers(), MAX_BANNED_PEERS); @@ -804,23 +811,24 @@ mod tests { pdb.connect_ingoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); pdb.connect_outgoing(&random_peer, "/ip4/0.0.0.0".parse().unwrap(), None); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); pdb.disconnect_and_ban(&random_peer); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); + pdb.disconnect_and_ban(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); } @@ -835,6 +843,10 @@ mod tests { let random_peer1 = PeerId::random(); let random_peer2 = PeerId::random(); let random_peer3 = PeerId::random(); + println!("{}", random_peer); + println!("{}", random_peer1); + println!("{}", random_peer2); + println!("{}", random_peer3); pdb.connect_ingoing(&random_peer, multiaddr.clone(), None); pdb.connect_ingoing(&random_peer1, multiaddr.clone(), None); @@ -846,10 +858,17 @@ mod tests { pdb.banned_peers().count() ); + println!("1:{}", pdb.disconnected_peers); + pdb.connect_ingoing(&random_peer, multiaddr.clone(), None); - pdb.notify_disconnect(&random_peer1); + pdb.inject_disconnect(&random_peer1); + println!("2:{}", pdb.disconnected_peers); + pdb.disconnect_and_ban(&random_peer2); + println!("3:{}", pdb.disconnected_peers); + pdb.inject_disconnect(&random_peer2); + println!("4:{}", pdb.disconnected_peers); pdb.disconnect_and_ban(&random_peer2); - pdb.notify_disconnect(&random_peer2); + println!("5:{}", pdb.disconnected_peers); pdb.connect_ingoing(&random_peer3, multiaddr.clone(), None); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!( @@ -857,7 +876,16 @@ mod tests { pdb.banned_peers().count() ); pdb.disconnect_and_ban(&random_peer1); - pdb.notify_disconnect(&random_peer1); + println!("6:{}", pdb.disconnected_peers); + pdb.inject_disconnect(&random_peer1); + println!("7:{}", pdb.disconnected_peers); + pdb.disconnect_and_ban(&random_peer1); + println!("8:{}", pdb.disconnected_peers); + println!( + "{}, {:?}", + pdb.disconnected_peers, + pdb.disconnected_peers().collect::>() + ); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!( pdb.banned_peers_count.banned_peers(), @@ -871,7 +899,8 @@ mod tests { pdb.banned_peers().count() ); pdb.disconnect_and_ban(&random_peer3); - pdb.notify_disconnect(&random_peer3); + pdb.inject_disconnect(&random_peer3); + pdb.disconnect_and_ban(&random_peer3); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!( pdb.banned_peers_count.banned_peers(), @@ -879,32 +908,34 @@ mod tests { ); pdb.disconnect_and_ban(&random_peer3); - pdb.notify_disconnect(&random_peer3); + pdb.inject_disconnect(&random_peer3); + pdb.disconnect_and_ban(&random_peer3); pdb.connect_ingoing(&random_peer1, multiaddr.clone(), None); - pdb.notify_disconnect(&random_peer2); + pdb.inject_disconnect(&random_peer2); + pdb.disconnect_and_ban(&random_peer3); + pdb.inject_disconnect(&random_peer3); pdb.disconnect_and_ban(&random_peer3); - pdb.notify_disconnect(&random_peer3); pdb.connect_ingoing(&random_peer, multiaddr, None); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!( pdb.banned_peers_count.banned_peers(), pdb.banned_peers().count() ); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!( pdb.banned_peers_count.banned_peers(), pdb.banned_peers().count() ); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); assert_eq!( pdb.banned_peers_count.banned_peers(), pdb.banned_peers().count() ); pdb.disconnect_and_ban(&random_peer); - pdb.notify_disconnect(&random_peer); + pdb.inject_disconnect(&random_peer); assert_eq!(pdb.disconnected_peers, pdb.disconnected_peers().count()); } @@ -950,7 +981,8 @@ mod tests { for p in &peers[..BANNED_PEERS_PER_IP_THRESHOLD + 1] { pdb.disconnect_and_ban(p); - pdb.notify_disconnect(p); + pdb.inject_disconnect(p); + pdb.disconnect_and_ban(p); } //check that ip1 and ip2 are banned but ip3-5 not @@ -962,7 +994,8 @@ mod tests { //ban also the last peer in peers pdb.disconnect_and_ban(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]); - pdb.notify_disconnect(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]); + pdb.inject_disconnect(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]); + pdb.disconnect_and_ban(&peers[BANNED_PEERS_PER_IP_THRESHOLD + 1]); //check that ip1-ip4 are banned but ip5 not assert!(pdb.is_banned(&p1)); @@ -1012,7 +1045,8 @@ mod tests { // ban all peers for p in &peers { pdb.disconnect_and_ban(p); - pdb.notify_disconnect(p); + pdb.inject_disconnect(p); + pdb.disconnect_and_ban(p); } // check ip is banned @@ -1033,7 +1067,8 @@ mod tests { for p in &peers { pdb.connect_ingoing(&p, socker_addr.clone(), None); pdb.disconnect_and_ban(p); - pdb.notify_disconnect(p); + pdb.inject_disconnect(p); + pdb.disconnect_and_ban(p); } // both IP's are now banned @@ -1049,7 +1084,8 @@ mod tests { // reban every peer except one for p in &peers[1..] { pdb.disconnect_and_ban(p); - pdb.notify_disconnect(p); + pdb.inject_disconnect(p); + pdb.disconnect_and_ban(p); } // nothing is banned @@ -1058,7 +1094,8 @@ mod tests { //reban last peer pdb.disconnect_and_ban(&peers[0]); - pdb.notify_disconnect(&peers[0]); + pdb.inject_disconnect(&peers[0]); + pdb.disconnect_and_ban(&peers[0]); //Ip's are banned again assert!(pdb.is_banned(&p1)); diff --git a/beacon_node/eth2_libp2p/src/rpc/handler.rs b/beacon_node/eth2_libp2p/src/rpc/handler.rs index 55b40182264..554e6787f83 100644 --- a/beacon_node/eth2_libp2p/src/rpc/handler.rs +++ b/beacon_node/eth2_libp2p/src/rpc/handler.rs @@ -1,8 +1,10 @@ #![allow(clippy::type_complexity)] #![allow(clippy::cognitive_complexity)] -use super::methods::{RPCCodedResponse, RPCResponseErrorCode, RequestId, ResponseTermination}; -use super::protocol::{Protocol, RPCError, RPCProtocol}; +use super::methods::{ + GoodbyeReason, RPCCodedResponse, RPCResponseErrorCode, RequestId, ResponseTermination, +}; +use super::protocol::{InboundRequest, Protocol, RPCError, RPCProtocol}; use super::{RPCReceived, RPCSend}; use crate::rpc::outbound::{OutboundFramed, OutboundRequest}; use crate::rpc::protocol::InboundFramed; @@ -221,13 +223,14 @@ where } } - /// Initiates the handler's shutdown process, sending an optional last message to the peer. - pub fn shutdown(&mut self, final_msg: Option<(RequestId, OutboundRequest)>) { + /// Initiates the handler's shutdown process, sending an optional Goodbye message to the + /// peer. + fn shutdown(&mut self, goodbye_reason: Option) { if matches!(self.state, HandlerState::Active) { if !self.dial_queue.is_empty() { debug!(self.log, "Starting handler shutdown"; "unsent_queued_requests" => self.dial_queue.len()); } - // we now drive to completion communications already dialed/established + // We now drive to completion communications already dialed/established while let Some((id, req)) = self.dial_queue.pop() { self.events_out.push(Err(HandlerErr::Outbound { error: RPCError::HandlerRejected, @@ -236,9 +239,10 @@ where })); } - // Queue our final message, if any - if let Some((id, req)) = final_msg { - self.dial_queue.push((id, req)); + // Queue our goodbye message. + if let Some(reason) = goodbye_reason { + self.dial_queue + .push((RequestId::Router, OutboundRequest::Goodbye(reason))); } self.state = HandlerState::ShuttingDown(Box::new(sleep_until( @@ -345,6 +349,11 @@ where ); } + // If we received a goodbye, shutdown the connection. + if let InboundRequest::Goodbye(_) = req { + self.shutdown(None); + } + self.events_out.push(Ok(RPCReceived::Request( self.current_inbound_substream_id, req, @@ -412,6 +421,7 @@ where match rpc_event { RPCSend::Request(id, req) => self.send_request(id, req), RPCSend::Response(inbound_id, response) => self.send_response(inbound_id, response), + RPCSend::Shutdown(reason) => self.shutdown(Some(reason)), } } @@ -512,6 +522,9 @@ where if delay.is_elapsed() { self.state = HandlerState::Deactivated; debug!(self.log, "Handler deactivated"); + return Poll::Ready(ProtocolsHandlerEvent::Close(RPCError::InternalError( + "Shutdown timeout", + ))); } } @@ -864,6 +877,19 @@ where protocol: SubstreamProtocol::new(req.clone(), ()).map_info(|()| (id, req)), }); } + + // Check if we have completed sending a goodbye, disconnect. + if let HandlerState::ShuttingDown(_) = self.state { + if self.dial_queue.is_empty() + && self.outbound_substreams.is_empty() + && self.inbound_substreams.is_empty() + && self.events_out.is_empty() + && self.dial_negotiated == 0 + { + return Poll::Ready(ProtocolsHandlerEvent::Close(RPCError::Disconnected)); + } + } + Poll::Pending } } diff --git a/beacon_node/eth2_libp2p/src/rpc/methods.rs b/beacon_node/eth2_libp2p/src/rpc/methods.rs index 8facac48af9..e24b6e980b2 100644 --- a/beacon_node/eth2_libp2p/src/rpc/methods.rs +++ b/beacon_node/eth2_libp2p/src/rpc/methods.rs @@ -149,9 +149,9 @@ impl From for GoodbyeReason { } } -impl Into for GoodbyeReason { - fn into(self) -> u64 { - self as u64 +impl From for u64 { + fn from(reason: GoodbyeReason) -> u64 { + reason as u64 } } diff --git a/beacon_node/eth2_libp2p/src/rpc/mod.rs b/beacon_node/eth2_libp2p/src/rpc/mod.rs index b91ca71fa34..702e3e20dfe 100644 --- a/beacon_node/eth2_libp2p/src/rpc/mod.rs +++ b/beacon_node/eth2_libp2p/src/rpc/mod.rs @@ -52,6 +52,8 @@ pub enum RPCSend { /// peer. The second parameter is a single chunk of a response. These go over *inbound* /// connections. Response(SubstreamId, RPCCodedResponse), + /// Lighthouse has requested to terminate the connection with a goodbye message. + Shutdown(GoodbyeReason), } /// RPC events received from outside Lighthouse. @@ -77,6 +79,7 @@ impl std::fmt::Display for RPCSend { match self { RPCSend::Request(id, req) => write!(f, "RPC Request(id: {:?}, {})", id, req), RPCSend::Response(id, res) => write!(f, "RPC Response(id: {:?}, {})", id, res), + RPCSend::Shutdown(reason) => write!(f, "Sending Goodbye: {}", reason), } } } @@ -115,11 +118,7 @@ impl RPC { methods::MAX_REQUEST_BLOCKS, Duration::from_secs(10), ) - .n_every( - Protocol::BlocksByRoot, - methods::MAX_REQUEST_BLOCKS, - Duration::from_secs(10), - ) + .n_every(Protocol::BlocksByRoot, 128, Duration::from_secs(10)) .build() .expect("Configuration parameters are valid"); RPC { @@ -160,6 +159,16 @@ impl RPC { event: RPCSend::Request(request_id, event), }); } + + /// Lighthouse wishes to disconnect from this peer by sending a Goodbye message. This + /// gracefully terminates the RPC behaviour with a goodbye message. + pub fn shutdown(&mut self, peer_id: PeerId, reason: GoodbyeReason) { + self.events.push(NetworkBehaviourAction::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: RPCSend::Shutdown(reason), + }); + } } impl NetworkBehaviour for RPC diff --git a/beacon_node/eth2_libp2p/src/rpc/protocol.rs b/beacon_node/eth2_libp2p/src/rpc/protocol.rs index 44e180fb598..031246ba16c 100644 --- a/beacon_node/eth2_libp2p/src/rpc/protocol.rs +++ b/beacon_node/eth2_libp2p/src/rpc/protocol.rs @@ -452,6 +452,8 @@ pub enum RPCError { NegotiationTimeout, /// Handler rejected this request. HandlerRejected, + /// We have intentionally disconnected. + Disconnected, } impl From for RPCError { @@ -490,6 +492,7 @@ impl std::fmt::Display for RPCError { RPCError::InternalError(ref err) => write!(f, "Internal error: {}", err), RPCError::NegotiationTimeout => write!(f, "Negotiation timeout"), RPCError::HandlerRejected => write!(f, "Handler rejected the request"), + RPCError::Disconnected => write!(f, "Gracefully Disconnected"), } } } @@ -508,6 +511,7 @@ impl std::error::Error for RPCError { RPCError::ErrorResponse(_, _) => None, RPCError::NegotiationTimeout => None, RPCError::HandlerRejected => None, + RPCError::Disconnected => None, } } } diff --git a/beacon_node/eth2_libp2p/src/service.rs b/beacon_node/eth2_libp2p/src/service.rs index b1dbde56816..9eb0e067bcc 100644 --- a/beacon_node/eth2_libp2p/src/service.rs +++ b/beacon_node/eth2_libp2p/src/service.rs @@ -266,6 +266,7 @@ impl Service { pub fn report_peer(&mut self, peer_id: &PeerId, action: PeerAction, source: ReportSource) { self.swarm .behaviour_mut() + .peer_manager_mut() .report_peer(peer_id, action, source); } @@ -285,19 +286,48 @@ impl Service { pub async fn next_event(&mut self) -> Libp2pEvent { loop { - match self.swarm.next_event().await { - SwarmEvent::Behaviour(behaviour) => return Libp2pEvent::Behaviour(behaviour), - SwarmEvent::ConnectionEstablished { .. } => { - // A connection could be established with a banned peer. This is - // handled inside the behaviour. + match self.swarm.select_next_some().await { + SwarmEvent::Behaviour(behaviour) => { + // Handle banning here + match &behaviour { + BehaviourEvent::PeerBanned(peer_id) => { + self.swarm.ban_peer_id(*peer_id); + } + BehaviourEvent::PeerUnbanned(peer_id) => { + self.swarm.unban_peer_id(*peer_id); + } + _ => {} + } + return Libp2pEvent::Behaviour(behaviour); + } + SwarmEvent::ConnectionEstablished { + peer_id, + endpoint, + num_established, + } => { + // Inform the peer manager. + // We require the ENR to inject into the peer db, if it exists. + let enr = self + .swarm + .behaviour_mut() + .discovery_mut() + .enr_of_peer(&peer_id); + self.swarm + .behaviour_mut() + .peer_manager_mut() + .inject_connection_established(peer_id, endpoint, num_established, enr); } SwarmEvent::ConnectionClosed { peer_id, - cause, - endpoint: _, + cause: _, + endpoint, num_established, } => { - trace!(self.log, "Connection closed"; "peer_id" => %peer_id, "cause" => ?cause, "connections" => num_established); + // Inform the peer manager. + self.swarm + .behaviour_mut() + .peer_manager_mut() + .inject_connection_closed(peer_id, endpoint, num_established); } SwarmEvent::NewListenAddr(multiaddr) => { return Libp2pEvent::NewListenAddr(multiaddr) @@ -313,10 +343,10 @@ impl Service { send_back_addr, error, } => { - debug!(self.log, "Failed incoming connection"; "our_addr" => %local_addr, "from" => %send_back_addr, "error" => %error) + debug!(self.log, "Failed incoming connection"; "our_addr" => %local_addr, "from" => %send_back_addr, "error" => %error); } - SwarmEvent::BannedPeer { .. } => { - // We do not ban peers at the swarm layer, so this should never occur. + SwarmEvent::BannedPeer { peer_id, .. } => { + debug!(self.log, "Banned peer connection rejected"; "peer_id" => %peer_id); } SwarmEvent::UnreachableAddr { peer_id, @@ -325,6 +355,10 @@ impl Service { attempts_remaining, } => { debug!(self.log, "Failed to dial address"; "peer_id" => %peer_id, "address" => %address, "error" => %error, "attempts_remaining" => attempts_remaining); + self.swarm + .behaviour_mut() + .peer_manager_mut() + .inject_dial_failure(&peer_id); } SwarmEvent::UnknownPeerUnreachableAddr { address, error } => { debug!(self.log, "Peer not known at dialed address"; "address" => %address, "error" => %error); @@ -346,7 +380,16 @@ impl Service { } } SwarmEvent::Dialing(peer_id) => { - debug!(self.log, "Dialing peer"; "peer_id" => %peer_id); + // We require the ENR to inject into the peer db, if it exists. + let enr = self + .swarm + .behaviour_mut() + .discovery_mut() + .enr_of_peer(&peer_id); + self.swarm + .behaviour_mut() + .peer_manager_mut() + .inject_dialing(&peer_id, enr); } } } diff --git a/beacon_node/eth2_libp2p/src/types/topics.rs b/beacon_node/eth2_libp2p/src/types/topics.rs index f8e2b676888..6bacfcf383c 100644 --- a/beacon_node/eth2_libp2p/src/types/topics.rs +++ b/beacon_node/eth2_libp2p/src/types/topics.rs @@ -144,19 +144,19 @@ impl GossipTopic { } } -impl Into for GossipTopic { - fn into(self) -> Topic { - Topic::new(self) +impl From for Topic { + fn from(topic: GossipTopic) -> Topic { + Topic::new(topic) } } -impl Into for GossipTopic { - fn into(self) -> String { - let encoding = match self.encoding { +impl From for String { + fn from(topic: GossipTopic) -> String { + let encoding = match topic.encoding { GossipEncoding::SSZSnappy => SSZ_SNAPPY_ENCODING_POSTFIX, }; - let kind = match self.kind { + let kind = match topic.kind { GossipKind::BeaconBlock => BEACON_BLOCK_TOPIC.into(), GossipKind::BeaconAggregateAndProof => BEACON_AGGREGATE_AND_PROOF_TOPIC.into(), GossipKind::VoluntaryExit => VOLUNTARY_EXIT_TOPIC.into(), @@ -167,7 +167,7 @@ impl Into for GossipTopic { format!( "/{}/{}/{}/{}", TOPIC_PREFIX, - hex::encode(self.fork_digest), + hex::encode(topic.fork_digest), kind, encoding ) diff --git a/beacon_node/eth2_libp2p/tests/rpc_tests.rs b/beacon_node/eth2_libp2p/tests/rpc_tests.rs index 1aafba46f03..d621bf31cc8 100644 --- a/beacon_node/eth2_libp2p/tests/rpc_tests.rs +++ b/beacon_node/eth2_libp2p/tests/rpc_tests.rs @@ -53,7 +53,7 @@ fn test_status_rpc() { let sender_future = async { loop { match sender.next_event().await { - Libp2pEvent::Behaviour(BehaviourEvent::PeerDialed(peer_id)) => { + Libp2pEvent::Behaviour(BehaviourEvent::PeerConnectedOutgoing(peer_id)) => { // Send a STATUS message debug!(log, "Sending RPC"); sender.swarm.behaviour_mut().send_request( @@ -149,7 +149,7 @@ fn test_blocks_by_range_chunked_rpc() { let sender_future = async { loop { match sender.next_event().await { - Libp2pEvent::Behaviour(BehaviourEvent::PeerDialed(peer_id)) => { + Libp2pEvent::Behaviour(BehaviourEvent::PeerConnectedOutgoing(peer_id)) => { // Send a STATUS message debug!(log, "Sending RPC"); sender.swarm.behaviour_mut().send_request( @@ -263,7 +263,7 @@ fn test_blocks_by_range_chunked_rpc_terminates_correctly() { let sender_future = async { loop { match sender.next_event().await { - Libp2pEvent::Behaviour(BehaviourEvent::PeerDialed(peer_id)) => { + Libp2pEvent::Behaviour(BehaviourEvent::PeerConnectedOutgoing(peer_id)) => { // Send a STATUS message debug!(log, "Sending RPC"); sender.swarm.behaviour_mut().send_request( @@ -395,7 +395,7 @@ fn test_blocks_by_range_single_empty_rpc() { let sender_future = async { loop { match sender.next_event().await { - Libp2pEvent::Behaviour(BehaviourEvent::PeerDialed(peer_id)) => { + Libp2pEvent::Behaviour(BehaviourEvent::PeerConnectedOutgoing(peer_id)) => { // Send a STATUS message debug!(log, "Sending RPC"); sender.swarm.behaviour_mut().send_request( @@ -510,7 +510,7 @@ fn test_blocks_by_root_chunked_rpc() { let sender_future = async { loop { match sender.next_event().await { - Libp2pEvent::Behaviour(BehaviourEvent::PeerDialed(peer_id)) => { + Libp2pEvent::Behaviour(BehaviourEvent::PeerConnectedOutgoing(peer_id)) => { // Send a STATUS message debug!(log, "Sending RPC"); sender.swarm.behaviour_mut().send_request( @@ -631,7 +631,7 @@ fn test_blocks_by_root_chunked_rpc_terminates_correctly() { let sender_future = async { loop { match sender.next_event().await { - Libp2pEvent::Behaviour(BehaviourEvent::PeerDialed(peer_id)) => { + Libp2pEvent::Behaviour(BehaviourEvent::PeerConnectedOutgoing(peer_id)) => { // Send a STATUS message debug!(log, "Sending RPC"); sender.swarm.behaviour_mut().send_request( @@ -746,7 +746,7 @@ fn test_goodbye_rpc() { let sender_future = async { loop { match sender.next_event().await { - Libp2pEvent::Behaviour(BehaviourEvent::PeerDialed(peer_id)) => { + Libp2pEvent::Behaviour(BehaviourEvent::PeerConnectedOutgoing(peer_id)) => { // Send a goodbye and disconnect debug!(log, "Sending RPC"); sender.swarm.behaviour_mut().goodbye_peer( diff --git a/beacon_node/network/src/beacon_processor/worker/mod.rs b/beacon_node/network/src/beacon_processor/worker/mod.rs index e79889274fd..ec2cce0ee6f 100644 --- a/beacon_node/network/src/beacon_processor/worker/mod.rs +++ b/beacon_node/network/src/beacon_processor/worker/mod.rs @@ -1,7 +1,7 @@ use super::work_reprocessing_queue::ReprocessQueueMessage; use crate::{service::NetworkMessage, sync::SyncMessage}; use beacon_chain::{BeaconChain, BeaconChainTypes}; -use slog::{error, Logger}; +use slog::{debug, Logger}; use std::sync::Arc; use tokio::sync::mpsc; @@ -27,7 +27,7 @@ impl Worker { /// Creates a log if there is an internal error. fn send_sync_message(&self, message: SyncMessage) { self.sync_tx.send(message).unwrap_or_else(|e| { - error!(self.log, "Could not send message to the sync service"; + debug!(self.log, "Could not send message to the sync service, likely shutdown"; "error" => %e) }); } @@ -37,7 +37,7 @@ impl Worker { /// Creates a log if there is an internal error. fn send_network_message(&self, message: NetworkMessage) { self.network_tx.send(message).unwrap_or_else(|e| { - error!(self.log, "Could not send message to the network service"; + debug!(self.log, "Could not send message to the network service, likely shutdown"; "error" => %e) }); } diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index e604aee7326..f3c7cb078b4 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -313,7 +313,7 @@ fn spawn_service( service.upnp_mappings = (tcp_socket.map(|s| s.port()), udp_socket.map(|s| s.port())); // If there is an external TCP port update, modify our local ENR. if let Some(tcp_socket) = tcp_socket { - if let Err(e) = service.libp2p.swarm.behaviour_mut().peer_manager().discovery_mut().update_enr_tcp_port(tcp_socket.port()) { + if let Err(e) = service.libp2p.swarm.behaviour_mut().discovery_mut().update_enr_tcp_port(tcp_socket.port()) { warn!(service.log, "Failed to update ENR"; "error" => e); } } @@ -321,7 +321,7 @@ fn spawn_service( // UPnP mappings if !service.discovery_auto_update { if let Some(udp_socket) = udp_socket { - if let Err(e) = service.libp2p.swarm.behaviour_mut().peer_manager().discovery_mut().update_enr_udp_socket(udp_socket) { + if let Err(e) = service.libp2p.swarm.behaviour_mut().discovery_mut().update_enr_udp_socket(udp_socket) { warn!(service.log, "Failed to update ENR"; "error" => e); } } @@ -427,17 +427,15 @@ fn spawn_service( // poll the swarm match libp2p_event { Libp2pEvent::Behaviour(event) => match event { - - BehaviourEvent::PeerDialed(peer_id) => { + BehaviourEvent::PeerConnectedOutgoing(peer_id) => { let _ = service .router_send .send(RouterMessage::PeerDialed(peer_id)) .map_err(|_| { debug!(service.log, "Failed to send peer dialed to router"); }); }, - BehaviourEvent::PeerConnected(_peer_id) => { - // A peer has connected to us - // We currently do not perform any action here. + BehaviourEvent::PeerConnectedIncoming(_) | BehaviourEvent::PeerBanned(_) | BehaviourEvent::PeerUnbanned(_) => { + // No action required for these events. }, BehaviourEvent::PeerDisconnected(peer_id) => { let _ = service