Silky smooth discovery (#1274)

* Initial structural re-write

* Improving discovery update and correcting attestation service logic

* Rework discovery.mod

* Handling lifetimes of query futures

* Discovery update first draft

* format fixes

* Stabalise discv5 update

* Formatting corrections

* Limit FindPeers queries and bug correction

* Update to stable release discv5

* Remove unnecessary pin

* formatting
This commit is contained in:
Age Manning 2020-06-19 14:13:23 +10:00 committed by GitHub
parent 06a72614cb
commit e379ad0f4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1168 additions and 1045 deletions

282
Cargo.lock generated
View File

@ -41,9 +41,9 @@ dependencies = [
[[package]] [[package]]
name = "adler32" name = "adler32"
version = "1.0.4" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
[[package]] [[package]]
name = "aes-ctr" name = "aes-ctr"
@ -132,9 +132,9 @@ dependencies = [
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "0.4.6" version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
[[package]] [[package]]
name = "arrayref" name = "arrayref"
@ -225,13 +225,14 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.48" version = "0.3.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c"
dependencies = [ dependencies = [
"addr2line", "addr2line",
"cfg-if", "cfg-if",
"libc", "libc",
"miniz_oxide",
"object", "object",
"rustc-demangle", "rustc-demangle",
] ]
@ -260,9 +261,9 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.12.1" version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67"
[[package]] [[package]]
name = "beacon_chain" name = "beacon_chain"
@ -320,8 +321,8 @@ dependencies = [
"ctrlc", "ctrlc",
"dirs", "dirs",
"environment", "environment",
"eth2-libp2p",
"eth2_config", "eth2_config",
"eth2_libp2p",
"eth2_ssz", "eth2_ssz",
"eth2_testnet_config", "eth2_testnet_config",
"exit-future", "exit-future",
@ -621,8 +622,8 @@ dependencies = [
"environment", "environment",
"error-chain", "error-chain",
"eth1", "eth1",
"eth2-libp2p",
"eth2_config", "eth2_config",
"eth2_libp2p",
"eth2_ssz", "eth2_ssz",
"futures 0.3.5", "futures 0.3.5",
"genesis", "genesis",
@ -843,12 +844,13 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-queue" name = "crossbeam-queue"
version = "0.2.2" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2" checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"maybe-uninit",
] ]
[[package]] [[package]]
@ -990,9 +992,9 @@ dependencies = [
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.7" version = "0.99.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2127768764f1556535c01b5326ef94bd60ff08dcfbdc544d53e69ed155610f5d" checksum = "bc655351f820d774679da6cdc23355a93de496867d8203496675162e17b1d671"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1020,11 +1022,10 @@ dependencies = [
[[package]] [[package]]
name = "dirs-sys" name = "dirs-sys"
version = "0.3.4" version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
dependencies = [ dependencies = [
"cfg-if",
"libc", "libc",
"redox_users", "redox_users",
"winapi 0.3.8", "winapi 0.3.8",
@ -1038,8 +1039,9 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]] [[package]]
name = "discv5" name = "discv5"
version = "0.1.0-alpha.2" version = "0.1.0-alpha.5"
source = "git+https://github.com/sigp/discv5?rev=7b3bd40591b62b8c002ffdb85de008aa9f82e2e5#7b3bd40591b62b8c002ffdb85de008aa9f82e2e5" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66319abef3e2f4dc434bf0c9bcb5dee5907d7fece3327dfd7da82db905d02441"
dependencies = [ dependencies = [
"arrayvec 0.5.1", "arrayvec 0.5.1",
"digest", "digest",
@ -1048,10 +1050,15 @@ dependencies = [
"futures 0.3.5", "futures 0.3.5",
"hex 0.4.2", "hex 0.4.2",
"hkdf", "hkdf",
"lazy_static",
"libp2p-core",
"libsecp256k1", "libsecp256k1",
"log 0.4.8", "log 0.4.8",
"lru_time_cache",
"multihash",
"net2", "net2",
"openssl", "openssl",
"parking_lot 0.10.2",
"rand 0.7.3", "rand 0.7.3",
"rlp", "rlp",
"sha2", "sha2",
@ -1069,9 +1076,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "0.4.5" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
[[package]] [[package]]
name = "ed25519-dalek" name = "ed25519-dalek"
@ -1129,7 +1136,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3cd1bccf1bd78eee44d89c0f81b60008b40153db2b99c0fc01abf353781e13" checksum = "ca3cd1bccf1bd78eee44d89c0f81b60008b40153db2b99c0fc01abf353781e13"
dependencies = [ dependencies = [
"base64 0.12.1", "base64 0.12.2",
"bs58", "bs58",
"ed25519-dalek", "ed25519-dalek",
"hex 0.4.2", "hex 0.4.2",
@ -1160,6 +1167,7 @@ name = "environment"
version = "0.1.2" version = "0.1.2"
dependencies = [ dependencies = [
"ctrlc", "ctrlc",
"discv5",
"eth2_config", "eth2_config",
"eth2_testnet_config", "eth2_testnet_config",
"exit-future", "exit-future",
@ -1228,49 +1236,6 @@ dependencies = [
"web3", "web3",
] ]
[[package]]
name = "eth2-libp2p"
version = "0.1.2"
dependencies = [
"base64 0.12.1",
"dirs",
"discv5",
"environment",
"error-chain",
"eth2_ssz",
"eth2_ssz_derive",
"eth2_ssz_types",
"exit-future",
"fnv",
"futures 0.3.5",
"hashset_delay",
"hex 0.4.2",
"lazy_static",
"libp2p",
"libp2p-tcp",
"lighthouse_metrics",
"lru 0.5.1",
"parking_lot 0.10.2",
"serde",
"serde_derive",
"sha2",
"slog",
"slog-async",
"slog-stdlog",
"slog-term",
"smallvec 1.4.0",
"snap",
"tempdir",
"tiny-keccak 2.0.2",
"tokio 0.2.21",
"tokio-io-timeout",
"tokio-util",
"types",
"unsigned-varint 0.3.3 (git+https://github.com/sigp/unsigned-varint?branch=latest-codecs)",
"version",
"void",
]
[[package]] [[package]]
name = "eth2_config" name = "eth2_config"
version = "0.2.0" version = "0.2.0"
@ -1296,7 +1261,7 @@ dependencies = [
name = "eth2_interop_keypairs" name = "eth2_interop_keypairs"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"base64 0.12.1", "base64 0.12.2",
"eth2_hashing", "eth2_hashing",
"hex 0.4.2", "hex 0.4.2",
"lazy_static", "lazy_static",
@ -1335,6 +1300,49 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "eth2_libp2p"
version = "0.1.2"
dependencies = [
"base64 0.12.2",
"dirs",
"discv5",
"environment",
"error-chain",
"eth2_ssz",
"eth2_ssz_derive",
"eth2_ssz_types",
"exit-future",
"fnv",
"futures 0.3.5",
"hashset_delay",
"hex 0.4.2",
"lazy_static",
"libp2p",
"libp2p-tcp",
"lighthouse_metrics",
"lru 0.5.1",
"parking_lot 0.10.2",
"serde",
"serde_derive",
"sha2",
"slog",
"slog-async",
"slog-stdlog",
"slog-term",
"smallvec 1.4.0",
"snap",
"tempdir",
"tiny-keccak 2.0.2",
"tokio 0.2.21",
"tokio-io-timeout",
"tokio-util",
"types",
"unsigned-varint 0.3.3 (git+https://github.com/sigp/unsigned-varint?branch=latest-codecs)",
"version",
"void",
]
[[package]] [[package]]
name = "eth2_ssz" name = "eth2_ssz"
version = "0.1.2" version = "0.1.2"
@ -1850,9 +1858,9 @@ dependencies = [
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.13" version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -2166,9 +2174,9 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.5" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
@ -2238,8 +2246,8 @@ dependencies = [
"deposit_contract", "deposit_contract",
"dirs", "dirs",
"environment", "environment",
"eth2-libp2p",
"eth2_keystore", "eth2_keystore",
"eth2_libp2p",
"eth2_ssz", "eth2_ssz",
"eth2_testnet_config", "eth2_testnet_config",
"futures 0.3.5", "futures 0.3.5",
@ -2708,6 +2716,12 @@ dependencies = [
"linked-hash-map", "linked-hash-map",
] ]
[[package]]
name = "lru_time_cache"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb241df5c4caeb888755363fc95f8a896618dc0d435e9e775f7930cb099beab"
[[package]] [[package]]
name = "mach" name = "mach"
version = "0.3.2" version = "0.3.2"
@ -2795,9 +2809,9 @@ dependencies = [
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.3.6" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [ dependencies = [
"adler32", "adler32",
] ]
@ -2841,7 +2855,7 @@ checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
dependencies = [ dependencies = [
"log 0.4.8", "log 0.4.8",
"mio", "mio",
"miow 0.3.4", "miow 0.3.5",
"winapi 0.3.8", "winapi 0.3.8",
] ]
@ -2870,9 +2884,9 @@ dependencies = [
[[package]] [[package]]
name = "miow" name = "miow"
version = "0.3.4" version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22dfdd1d51b2639a5abd17ed07005c3af05fb7a2a3b1a1d0d7af1000a520c1c7" checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
dependencies = [ dependencies = [
"socket2", "socket2",
"winapi 0.3.8", "winapi 0.3.8",
@ -2949,7 +2963,7 @@ dependencies = [
"beacon_chain", "beacon_chain",
"environment", "environment",
"error-chain", "error-chain",
"eth2-libp2p", "eth2_libp2p",
"eth2_ssz", "eth2_ssz",
"exit-future", "exit-future",
"fnv", "fnv",
@ -3058,9 +3072,9 @@ dependencies = [
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.42" version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [ dependencies = [
"autocfg 1.0.0", "autocfg 1.0.0",
"num-traits", "num-traits",
@ -3068,9 +3082,9 @@ dependencies = [
[[package]] [[package]]
name = "num-iter" name = "num-iter"
version = "0.1.40" version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f"
dependencies = [ dependencies = [
"autocfg 1.0.0", "autocfg 1.0.0",
"num-integer", "num-integer",
@ -3079,9 +3093,9 @@ dependencies = [
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.11" version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [ dependencies = [
"autocfg 1.0.0", "autocfg 1.0.0",
] ]
@ -3098,9 +3112,9 @@ dependencies = [
[[package]] [[package]]
name = "object" name = "object"
version = "0.19.0" version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
@ -3113,9 +3127,9 @@ dependencies = [
[[package]] [[package]]
name = "oorandom" name = "oorandom"
version = "11.1.1" version = "11.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358" checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
@ -3145,9 +3159,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.57" version = "0.9.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7410fef80af8ac071d4f63755c0ab89ac3df0fd1ea91f1d1f37cf5cec4395990" checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
dependencies = [ dependencies = [
"autocfg 1.0.0", "autocfg 1.0.0",
"cc", "cc",
@ -3292,18 +3306,18 @@ dependencies = [
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "0.4.19" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a1acf4a3e70849f8a673497ef984f043f95d2d8252dcdf74d54e6a1e47e8a" checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17"
dependencies = [ dependencies = [
"pin-project-internal", "pin-project-internal",
] ]
[[package]] [[package]]
name = "pin-project-internal" name = "pin-project-internal"
version = "0.4.19" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194e88048b71a3e02eb4ee36a6995fed9b8236c11a7bb9f7247a9d9835b3f265" checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3373,9 +3387,9 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
[[package]] [[package]]
name = "proc-macro-nested" name = "proc-macro-nested"
version = "0.1.4" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
@ -3543,9 +3557,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.6" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -3680,10 +3694,11 @@ dependencies = [
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.3.0" version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
dependencies = [ dependencies = [
"autocfg 1.0.0",
"crossbeam-deque", "crossbeam-deque",
"either", "either",
"rayon-core", "rayon-core",
@ -3691,9 +3706,9 @@ dependencies = [
[[package]] [[package]]
name = "rayon-core" name = "rayon-core"
version = "1.7.0" version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
dependencies = [ dependencies = [
"crossbeam-deque", "crossbeam-deque",
"crossbeam-queue", "crossbeam-queue",
@ -3775,9 +3790,9 @@ dependencies = [
[[package]] [[package]]
name = "remove_dir_all" name = "remove_dir_all"
version = "0.5.2" version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [ dependencies = [
"winapi 0.3.8", "winapi 0.3.8",
] ]
@ -3788,7 +3803,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680"
dependencies = [ dependencies = [
"base64 0.12.1", "base64 0.12.2",
"bytes 0.5.4", "bytes 0.5.4",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@ -3826,8 +3841,8 @@ dependencies = [
"bls", "bls",
"bus", "bus",
"environment", "environment",
"eth2-libp2p",
"eth2_config", "eth2_config",
"eth2_libp2p",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive", "eth2_ssz_derive",
"futures 0.3.5", "futures 0.3.5",
@ -4142,18 +4157,18 @@ checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.111" version = "1.0.112"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.111" version = "1.0.112"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4170,9 +4185,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.53" version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -4181,9 +4196,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_repr" name = "serde_repr"
version = "0.1.5" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573" checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4204,9 +4219,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.8.12" version = "0.8.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
dependencies = [ dependencies = [
"dtoa", "dtoa",
"linked-hash-map", "linked-hash-map",
@ -4697,9 +4712,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.30" version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4708,9 +4723,9 @@ dependencies = [
[[package]] [[package]]
name = "synstructure" name = "synstructure"
version = "0.12.3" version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4792,18 +4807,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.19" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344" checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.19" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479" checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4925,6 +4940,12 @@ dependencies = [
"serde_json", "serde_json",
] ]
[[package]]
name = "tinyvec"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "0.1.22" version = "0.1.22"
@ -5428,11 +5449,11 @@ dependencies = [
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
version = "0.1.12" version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
dependencies = [ dependencies = [
"smallvec 1.4.0", "tinyvec",
] ]
[[package]] [[package]]
@ -5566,9 +5587,9 @@ dependencies = [
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.9" version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55d1e41d56121e07f1e223db0a4def204e45c85425f6a16d462fd07c8d10d74c" checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
@ -5760,10 +5781,11 @@ dependencies = [
[[package]] [[package]]
name = "web3" name = "web3"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/tomusdrw/rust-web3#69d5746f124033dee922d7d36acef9321c1df0b0" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a681e8d15deced7c510db88c59133d2eafa7b6298b6e91b545e2a3fed93b3fe"
dependencies = [ dependencies = [
"arrayvec 0.5.1", "arrayvec 0.5.1",
"base64 0.12.1", "base64 0.12.2",
"derive_more", "derive_more",
"ethabi", "ethabi",
"ethereum-types", "ethereum-types",

View File

@ -5,7 +5,7 @@ members = [
"beacon_node/beacon_chain", "beacon_node/beacon_chain",
"beacon_node/client", "beacon_node/client",
"beacon_node/eth1", "beacon_node/eth1",
"beacon_node/eth2-libp2p", "beacon_node/eth2_libp2p",
"beacon_node/network", "beacon_node/network",
"beacon_node/rest_api", "beacon_node/rest_api",
"beacon_node/store", "beacon_node/store",
@ -72,4 +72,3 @@ eth2_ssz = { path = "consensus/ssz" }
eth2_ssz_derive = { path = "consensus/ssz_derive" } eth2_ssz_derive = { path = "consensus/ssz_derive" }
eth2_ssz_types = { path = "consensus/ssz_types" } eth2_ssz_types = { path = "consensus/ssz_types" }
eth2_hashing = { path = "crypto/eth2_hashing" } eth2_hashing = { path = "crypto/eth2_hashing" }
web3 = { git = "https://github.com/tomusdrw/rust-web3" }

View File

@ -35,7 +35,7 @@ futures = "0.3.5"
environment = { path = "../lighthouse/environment" } environment = { path = "../lighthouse/environment" }
genesis = { path = "genesis" } genesis = { path = "genesis" }
eth2_testnet_config = { path = "../common/eth2_testnet_config" } eth2_testnet_config = { path = "../common/eth2_testnet_config" }
eth2-libp2p = { path = "./eth2-libp2p" } eth2_libp2p = { path = "./eth2_libp2p" }
eth2_ssz = "0.1.2" eth2_ssz = "0.1.2"
toml = "0.5.6" toml = "0.5.6"
serde = "1.0.110" serde = "1.0.110"

View File

@ -13,7 +13,7 @@ beacon_chain = { path = "../beacon_chain" }
store = { path = "../store" } store = { path = "../store" }
network = { path = "../network" } network = { path = "../network" }
timer = { path = "../timer" } timer = { path = "../timer" }
eth2-libp2p = { path = "../eth2-libp2p" } eth2_libp2p = { path = "../eth2_libp2p" }
rest_api = { path = "../rest_api" } rest_api = { path = "../rest_api" }
parking_lot = "0.10.2" parking_lot = "0.10.2"
websocket_server = { path = "../websocket_server" } websocket_server = { path = "../websocket_server" }

View File

@ -1,628 +0,0 @@
///! This manages the discovery and management of peers.
pub(crate) mod enr;
pub mod enr_ext;
// Allow external use of the lighthouse ENR builder
pub use enr::{build_enr, CombinedKey, Keypair};
pub use enr_ext::{CombinedKeyExt, EnrExt};
use crate::metrics;
use crate::{error, Enr, NetworkConfig, NetworkGlobals};
use discv5::{enr::NodeId, Discv5, Discv5Event, QueryId};
use enr::{Eth2Enr, BITFIELD_ENR_KEY, ETH2_ENR_KEY};
use futures::prelude::*;
use libp2p::core::{connection::ConnectionId, Multiaddr, PeerId};
use libp2p::multiaddr::Protocol;
use libp2p::swarm::{
protocols_handler::DummyProtocolsHandler, DialPeerCondition, NetworkBehaviour,
NetworkBehaviourAction, PollParameters, ProtocolsHandler,
};
use lru::LruCache;
use slog::{crit, debug, info, trace, warn};
use ssz::{Decode, Encode};
use ssz_types::BitVector;
use std::{
collections::{HashMap, HashSet, VecDeque},
net::SocketAddr,
path::Path,
sync::Arc,
task::{Context, Poll},
time::{Duration, Instant},
};
use tokio::time::{delay_until, Delay};
use types::{EnrForkId, EthSpec, SubnetId};
mod subnet_predicate;
use subnet_predicate::subnet_predicate;
/// Maximum seconds before searching for extra peers.
const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 120;
/// Initial delay between peer searches.
const INITIAL_SEARCH_DELAY: u64 = 5;
/// The number of peers we must be connected to before increasing the discovery delay.
const MINIMUM_PEERS_BEFORE_DELAY_INCREASE: usize = 5;
/// Local ENR storage filename.
pub const ENR_FILENAME: &str = "enr.dat";
/// Number of peers we'd like to have connected to a given long-lived subnet.
const TARGET_SUBNET_PEERS: usize = 3;
/// Number of times to attempt a discovery request
const MAX_DISCOVERY_RETRY: u64 = 3;
/// A struct representing the information associated with a single discovery request,
/// which can be retried with multiple queries
#[derive(Clone, Debug)]
pub struct Request {
pub query_id: Option<QueryId>,
pub min_ttl: Option<Instant>,
pub retries: u64,
}
/// Lighthouse discovery behaviour. This provides peer management and discovery using the Discv5
/// libp2p protocol.
pub struct Discovery<TSpec: EthSpec> {
/// Events to be processed by the behaviour.
events: VecDeque<NetworkBehaviourAction<void::Void, Discv5Event>>,
/// A collection of seen live ENRs for quick lookup and to map peer-id's to ENRs.
cached_enrs: LruCache<PeerId, Enr>,
/// The currently banned peers.
banned_peers: HashSet<PeerId>,
/// The target number of connected peers on the libp2p interface.
max_peers: usize,
/// The directory where the ENR is stored.
enr_dir: String,
/// The delay between peer discovery searches.
peer_discovery_delay: Delay,
/// Tracks the last discovery delay. The delay is doubled each round until the max
/// time is reached.
past_discovery_delay: u64,
/// The TCP port for libp2p. Used to convert an updated IP address to a multiaddr. Note: This
/// assumes that the external TCP port is the same as the internal TCP port if behind a NAT.
//TODO: Improve NAT handling limit the above restriction
tcp_port: u16,
/// The discovery behaviour used to discover new peers.
discovery: Discv5,
/// A collection of network constants that can be read from other threads.
network_globals: Arc<NetworkGlobals<TSpec>>,
/// A mapping of SubnetId that we are currently searching for to all information associated with each request.
subnet_queries: HashMap<SubnetId, Request>,
/// Logger for the discovery behaviour.
log: slog::Logger,
}
impl<TSpec: EthSpec> Discovery<TSpec> {
pub fn new(
local_key: &Keypair,
config: &NetworkConfig,
network_globals: Arc<NetworkGlobals<TSpec>>,
log: &slog::Logger,
) -> error::Result<Self> {
let log = log.clone();
let enr_dir = match config.network_dir.to_str() {
Some(path) => String::from(path),
None => String::from(""),
};
let local_enr = network_globals.local_enr.read().clone();
info!(log, "ENR Initialised"; "enr" => local_enr.to_base64(), "seq" => local_enr.seq(), "id"=> format!("{}",local_enr.node_id()), "ip" => format!("{:?}", local_enr.ip()), "udp"=> format!("{:?}", local_enr.udp()), "tcp" => format!("{:?}", local_enr.tcp()));
let listen_socket = SocketAddr::new(config.listen_address, config.discovery_port);
// convert the keypair into an ENR key
let enr_key: CombinedKey = CombinedKey::from_libp2p(&local_key)?;
let mut discovery = Discv5::new(
local_enr,
enr_key,
config.discv5_config.clone(),
listen_socket,
)
.map_err(|e| format!("Discv5 service failed. Error: {:?}", e))?;
// Add bootnodes to routing table
for bootnode_enr in config.boot_nodes.clone() {
debug!(
log,
"Adding node to routing table";
"node_id" => format!("{}", bootnode_enr.node_id()),
"peer_id" => format!("{}", bootnode_enr.peer_id()),
"ip" => format!("{:?}", bootnode_enr.ip()),
"udp" => format!("{:?}", bootnode_enr.udp()),
"tcp" => format!("{:?}", bootnode_enr.tcp())
);
let _ = discovery.add_enr(bootnode_enr).map_err(|e| {
warn!(
log,
"Could not add peer to the local routing table";
"error" => format!("{}", e)
)
});
}
Ok(Self {
events: VecDeque::with_capacity(16),
cached_enrs: LruCache::new(50),
banned_peers: HashSet::new(),
max_peers: config.max_peers,
peer_discovery_delay: delay_until(tokio::time::Instant::now()),
past_discovery_delay: INITIAL_SEARCH_DELAY,
tcp_port: config.libp2p_port,
discovery,
network_globals,
subnet_queries: HashMap::new(),
log,
enr_dir,
})
}
/// Return the nodes local ENR.
pub fn local_enr(&self) -> &Enr {
self.discovery.local_enr()
}
/// Manually search for peers. This restarts the discovery round, sparking multiple rapid
/// queries.
pub fn discover_peers(&mut self) {
self.past_discovery_delay = INITIAL_SEARCH_DELAY;
self.find_peers();
}
/// Add an ENR to the routing table of the discovery mechanism.
pub fn add_enr(&mut self, enr: Enr) {
// add the enr to seen caches
self.cached_enrs.put(enr.peer_id(), enr.clone());
let _ = self.discovery.add_enr(enr).map_err(|e| {
warn!(
self.log,
"Could not add peer to the local routing table";
"error" => format!("{}", e)
)
});
}
/// The peer has been banned. Add this peer to the banned list to prevent any future
/// re-connections.
// TODO: Remove the peer from the DHT if present
pub fn peer_banned(&mut self, peer_id: PeerId) {
self.banned_peers.insert(peer_id);
}
pub fn peer_unbanned(&mut self, peer_id: &PeerId) {
self.banned_peers.remove(peer_id);
}
/// Returns an iterator over all enr entries in the DHT.
pub fn enr_entries(&mut self) -> impl Iterator<Item = &Enr> {
self.discovery.enr_entries()
}
/// Returns the ENR of a known peer if it exists.
pub fn enr_of_peer(&mut self, peer_id: &PeerId) -> Option<Enr> {
// first search the local cache
if let Some(enr) = self.cached_enrs.get(peer_id) {
return Some(enr.clone());
}
// not in the local cache, look in the routing table
if let Ok(_node_id) = enr_ext::peer_id_to_node_id(peer_id) {
// TODO: Need to update discv5
// self.discovery.find_enr(&node_id)
return None;
} else {
return None;
}
}
/// Adds/Removes a subnet from the ENR Bitfield
pub fn update_enr_bitfield(&mut self, subnet_id: SubnetId, value: bool) -> Result<(), String> {
let id = *subnet_id as usize;
let local_enr = self.discovery.local_enr();
let mut current_bitfield = local_enr.bitfield::<TSpec>()?;
if id >= current_bitfield.len() {
return Err(format!(
"Subnet id: {} is outside the ENR bitfield length: {}",
id,
current_bitfield.len()
));
}
if current_bitfield
.get(id)
.map_err(|_| String::from("Subnet ID out of bounds"))?
== value
{
return Err(format!(
"Subnet id: {} already in the local ENR already has value: {}",
id, value
));
}
// set the subnet bitfield in the ENR
current_bitfield
.set(id, value)
.map_err(|_| String::from("Subnet ID out of bounds, could not set subnet ID"))?;
// insert the bitfield into the ENR record
let _ = self
.discovery
.enr_insert(BITFIELD_ENR_KEY, current_bitfield.as_ssz_bytes());
// replace the global version
*self.network_globals.local_enr.write() = self.discovery.local_enr().clone();
Ok(())
}
/// Updates the `eth2` field of our local ENR.
pub fn update_eth2_enr(&mut self, enr_fork_id: EnrForkId) {
// to avoid having a reference to the spec constant, for the logging we assume
// FAR_FUTURE_EPOCH is u64::max_value()
let next_fork_epoch_log = if enr_fork_id.next_fork_epoch == u64::max_value() {
String::from("No other fork")
} else {
format!("{:?}", enr_fork_id.next_fork_epoch)
};
info!(self.log, "Updating the ENR fork version";
"fork_digest" => format!("{:?}", enr_fork_id.fork_digest),
"next_fork_version" => format!("{:?}", enr_fork_id.next_fork_version),
"next_fork_epoch" => next_fork_epoch_log,
);
let _ = self
.discovery
.enr_insert(ETH2_ENR_KEY.into(), enr_fork_id.as_ssz_bytes())
.map_err(|e| {
warn!(
self.log,
"Could not update eth2 ENR field";
"error" => format!("{:?}", e)
)
});
// replace the global version with discovery version
*self.network_globals.local_enr.write() = self.discovery.local_enr().clone();
}
/// A request to find peers on a given subnet.
pub fn discover_subnet_peers(&mut self, subnet_id: SubnetId, min_ttl: Option<Instant>) {
// TODO: Extend this to an event once discovery becomes a thread managed by the peer
// manager
if let Some(min_ttl) = min_ttl {
self.network_globals
.peers
.write()
.extend_peers_on_subnet(subnet_id, min_ttl);
}
// If there is already a discovery request in process for this subnet, ignore this request,
// but update the min_ttl.
if let Some(request) = self.subnet_queries.get_mut(&subnet_id) {
// update the min_ttl if required
if let Some(min_ttl) = min_ttl {
if request.min_ttl < Some(min_ttl) {
request.min_ttl = Some(min_ttl);
}
}
return;
}
// Insert a request and start a query for the subnet
self.subnet_queries.insert(
subnet_id.clone(),
Request {
query_id: None,
min_ttl,
retries: 0,
},
);
self.run_subnet_query(subnet_id);
}
/// Runs a discovery request for a given subnet_id if one already exists.
fn run_subnet_query(&mut self, subnet_id: SubnetId) {
let mut request = match self.subnet_queries.remove(&subnet_id) {
Some(v) => v,
None => return, // request doesn't exist
};
// increment the retry count
request.retries += 1;
let peers_on_subnet = self
.network_globals
.peers
.read()
.peers_on_subnet(subnet_id)
.count();
if peers_on_subnet > TARGET_SUBNET_PEERS {
trace!(self.log, "Discovery ignored";
"reason" => "Already connected to desired peers",
"connected_peers_on_subnet" => peers_on_subnet,
"target_subnet_peers" => TARGET_SUBNET_PEERS,
);
return;
}
// remove the entry and complete the query if greater than the maximum search count
if request.retries >= MAX_DISCOVERY_RETRY {
debug!(
self.log,
"Subnet peer discovery did not find sufficient peers. Reached max retry limit"
);
return;
}
let target_peers = TARGET_SUBNET_PEERS - peers_on_subnet;
debug!(self.log, "Searching for peers for subnet";
"subnet_id" => *subnet_id,
"connected_peers_on_subnet" => peers_on_subnet,
"target_subnet_peers" => TARGET_SUBNET_PEERS,
"peers_to_find" => target_peers,
"attempt" => request.retries,
);
// start the query, and update the queries map if necessary
let subnet_predicate = subnet_predicate::<TSpec>(subnet_id, &self.log);
if let Some(query_id) = self.start_query(subnet_predicate, target_peers) {
request.query_id = Some(query_id);
} else {
// ENR is not present remove the query
return;
}
self.subnet_queries.insert(subnet_id, request);
}
/* Internal Functions */
/// Run a standard query to search for more peers.
///
/// This searches for the standard kademlia bucket size (16) peers.
fn find_peers(&mut self) {
debug!(self.log, "Searching for peers");
self.start_query(|_| true, 16);
}
/// Search for a specified number of new peers using the underlying discovery mechanism.
///
/// This can optionally search for peers for a given predicate. Regardless of the predicate
/// given, this will only search for peers on the same enr_fork_id as specified in the local
/// ENR.
fn start_query<F>(&mut self, enr_predicate: F, num_nodes: usize) -> Option<QueryId>
where
F: Fn(&Enr) -> bool + Send + 'static + Clone,
{
// pick a random NodeId
let random_node = NodeId::random();
let enr_fork_id = match self.local_enr().eth2() {
Ok(v) => v,
Err(e) => {
crit!(self.log, "Local ENR has no fork id"; "error" => e);
return None;
}
};
// predicate for finding nodes with a matching fork
let eth2_fork_predicate = move |enr: &Enr| enr.eth2() == Ok(enr_fork_id.clone());
let predicate = move |enr: &Enr| eth2_fork_predicate(enr) && enr_predicate(enr);
// general predicate
Some(
self.discovery
.find_enr_predicate(random_node, predicate, num_nodes),
)
}
/// Peers that are found during discovery are optionally dialed.
// TODO: Shift to peer manager. As its own service, discovery should spit out discovered nodes
// and the peer manager should decide about who to connect to.
fn dial_discovered_peers(&mut self, peers: Vec<Enr>, min_ttl: Option<Instant>) {
for enr in peers {
// cache known peers
let peer_id = enr.peer_id();
self.cached_enrs.put(enr.peer_id(), enr);
// if we need more peers, attempt a connection
if self.network_globals.connected_or_dialing_peers() < self.max_peers
&& !self
.network_globals
.peers
.read()
.is_connected_or_dialing(&peer_id)
&& !self.banned_peers.contains(&peer_id)
{
debug!(self.log, "Connecting to discovered peer"; "peer_id"=> peer_id.to_string());
// TODO: Update output
// 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);
}
self.events.push_back(NetworkBehaviourAction::DialPeer {
peer_id,
condition: DialPeerCondition::Disconnected,
});
}
}
}
}
// Build a dummy Network behaviour around the discv5 server
impl<TSpec: EthSpec> NetworkBehaviour for Discovery<TSpec> {
type ProtocolsHandler = DummyProtocolsHandler;
type OutEvent = Discv5Event;
fn new_handler(&mut self) -> Self::ProtocolsHandler {
DummyProtocolsHandler::default()
}
fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> {
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).
let mut out_list = enr.multiaddr();
out_list.retain(|addr| {
addr.iter()
.find(|v| match v {
Protocol::Udp(_) => true,
_ => false,
})
.is_none()
});
out_list
} else {
// PeerId is not known
Vec::new()
}
}
// ignore libp2p connections/streams
fn inject_connected(&mut self, _: &PeerId) {}
// ignore libp2p connections/streams
fn inject_disconnected(&mut self, _: &PeerId) {}
// no libp2p discv5 events - event originate from the session_service.
fn inject_event(
&mut self,
_: PeerId,
_: ConnectionId,
_event: <Self::ProtocolsHandler as ProtocolsHandler>::OutEvent,
) {
void::unreachable(_event)
}
fn poll(
&mut self,
cx: &mut Context,
_: &mut impl PollParameters,
) -> Poll<
NetworkBehaviourAction<
<Self::ProtocolsHandler as ProtocolsHandler>::InEvent,
Self::OutEvent,
>,
> {
// search for peers if it is time
loop {
match self.peer_discovery_delay.poll_unpin(cx) {
Poll::Ready(_) => {
if self.network_globals.connected_peers() < self.max_peers {
self.find_peers();
}
// Set to maximum, and update to earlier, once we get our results back.
self.peer_discovery_delay.reset(
tokio::time::Instant::now()
+ Duration::from_secs(MAX_TIME_BETWEEN_PEER_SEARCHES),
);
}
Poll::Pending => break,
}
}
// Poll discovery
loop {
match self.discovery.poll_next_unpin(cx) {
Poll::Ready(Some(event)) => {
match event {
Discv5Event::Discovered(_enr) => {
// peers that get discovered during a query but are not contactable or
// don't match a predicate can end up here. For debugging purposes we
// log these to see if we are unnecessarily dropping discovered peers
/*
if enr.eth2() == self.local_enr().eth2() {
trace!(self.log, "Peer found in process of query"; "peer_id" => format!("{}", enr.peer_id()), "tcp_socket" => enr.tcp_socket());
} else {
// this is temporary warning for debugging the DHT
warn!(self.log, "Found peer during discovery not on correct fork"; "peer_id" => format!("{}", enr.peer_id()), "tcp_socket" => enr.tcp_socket());
}
*/
}
Discv5Event::SocketUpdated(socket) => {
info!(self.log, "Address updated"; "ip" => format!("{}",socket.ip()), "udp_port" => format!("{}", socket.port()));
metrics::inc_counter(&metrics::ADDRESS_UPDATE_COUNT);
let mut address = Multiaddr::from(socket.ip());
address.push(Protocol::Tcp(self.tcp_port));
let enr = self.discovery.local_enr();
enr::save_enr_to_disk(Path::new(&self.enr_dir), enr, &self.log);
return Poll::Ready(NetworkBehaviourAction::ReportObservedAddr {
address,
});
}
Discv5Event::FindNodeResult {
closer_peers,
query_id,
..
} => {
debug!(self.log, "Discovery query completed"; "peers_found" => closer_peers.len());
// update the time to the next query
if self.past_discovery_delay < MAX_TIME_BETWEEN_PEER_SEARCHES
&& self.network_globals.connected_or_dialing_peers()
> MINIMUM_PEERS_BEFORE_DELAY_INCREASE
{
self.past_discovery_delay *= 2;
}
let delay = std::cmp::max(
self.past_discovery_delay,
MAX_TIME_BETWEEN_PEER_SEARCHES,
);
self.peer_discovery_delay
.reset(tokio::time::Instant::now() + Duration::from_secs(delay));
// if this is a subnet query, run it to completion
if let Some((subnet_id, min_ttl)) = self
.subnet_queries
.iter()
.find(|(_, request)| request.query_id == Some(query_id))
.map(|(subnet_id, request)| {
(subnet_id.clone(), request.min_ttl.clone())
})
{
debug!(self.log, "Peer subnet discovery request completed"; "peers_found" => closer_peers.len(), "subnet_id" => *subnet_id);
self.dial_discovered_peers(closer_peers, min_ttl);
self.run_subnet_query(subnet_id);
} else {
if closer_peers.is_empty() {
debug!(self.log, "Peer Discovery request yielded no results.");
} else {
self.dial_discovered_peers(closer_peers, None);
}
}
}
_ => {}
}
}
// discv5 does not output any other NetworkBehaviourAction
Poll::Ready(_) => {}
Poll::Pending => break,
}
}
// process any queued events
if let Some(event) = self.events.pop_front() {
return Poll::Ready(event);
}
Poll::Pending
}
}

View File

@ -1,5 +1,5 @@
[package] [package]
name = "eth2-libp2p" name = "eth2_libp2p"
version = "0.1.2" version = "0.1.2"
authors = ["Age Manning <Age@AgeManning.com>"] authors = ["Age Manning <Age@AgeManning.com>"]
edition = "2018" edition = "2018"
@ -32,8 +32,7 @@ snap = "1.0.0"
void = "1.0.2" void = "1.0.2"
tokio-io-timeout = "0.4.0" tokio-io-timeout = "0.4.0"
tokio-util = { version = "0.3.1", features = ["codec", "compat"] } tokio-util = { version = "0.3.1", features = ["codec", "compat"] }
# Patched for quick updates discv5 = { version = "0.1.0-alpha.5", features = ["libp2p"] }
discv5 = { git = "https://github.com/sigp/discv5", rev = "7b3bd40591b62b8c002ffdb85de008aa9f82e2e5" }
tiny-keccak = "2.0.2" tiny-keccak = "2.0.2"
environment = { path = "../../lighthouse/environment" } environment = { path = "../../lighthouse/environment" }
libp2p-tcp = { version = "0.19.1", default-features = false, features = ["tokio"] } libp2p-tcp = { version = "0.19.1", default-features = false, features = ["tokio"] }

View File

@ -1,4 +1,3 @@
use crate::discovery::Discovery;
use crate::rpc::*; use crate::rpc::*;
use libp2p::{ use libp2p::{
core::either::{EitherError, EitherOutput}, core::either::{EitherError, EitherOutput},
@ -19,7 +18,6 @@ use types::EthSpec;
type GossipHandler = <Gossipsub as NetworkBehaviour>::ProtocolsHandler; type GossipHandler = <Gossipsub as NetworkBehaviour>::ProtocolsHandler;
type RPCHandler<TSpec> = <RPC<TSpec> as NetworkBehaviour>::ProtocolsHandler; type RPCHandler<TSpec> = <RPC<TSpec> as NetworkBehaviour>::ProtocolsHandler;
type IdentifyHandler = <Identify as NetworkBehaviour>::ProtocolsHandler; type IdentifyHandler = <Identify as NetworkBehaviour>::ProtocolsHandler;
type DiscoveryHandler<TSpec> = <Discovery<TSpec> as NetworkBehaviour>::ProtocolsHandler;
/// Handler that combines Lighthouse's Behaviours' handlers in a delegating manner. /// Handler that combines Lighthouse's Behaviours' handlers in a delegating manner.
pub(super) struct DelegatingHandler<TSpec: EthSpec> { pub(super) struct DelegatingHandler<TSpec: EthSpec> {
@ -29,22 +27,14 @@ pub(super) struct DelegatingHandler<TSpec: EthSpec> {
rpc_handler: RPCHandler<TSpec>, rpc_handler: RPCHandler<TSpec>,
/// Handler for the Identify protocol. /// Handler for the Identify protocol.
identify_handler: IdentifyHandler, identify_handler: IdentifyHandler,
/// Handler for the Discovery protocol.
discovery_handler: DiscoveryHandler<TSpec>,
} }
impl<TSpec: EthSpec> DelegatingHandler<TSpec> { impl<TSpec: EthSpec> DelegatingHandler<TSpec> {
pub fn new( pub fn new(gossipsub: &mut Gossipsub, rpc: &mut RPC<TSpec>, identify: &mut Identify) -> Self {
gossipsub: &mut Gossipsub,
rpc: &mut RPC<TSpec>,
identify: &mut Identify,
discovery: &mut Discovery<TSpec>,
) -> Self {
DelegatingHandler { DelegatingHandler {
gossip_handler: gossipsub.new_handler(), gossip_handler: gossipsub.new_handler(),
rpc_handler: rpc.new_handler(), rpc_handler: rpc.new_handler(),
identify_handler: identify.new_handler(), identify_handler: identify.new_handler(),
discovery_handler: discovery.new_handler(),
} }
} }
@ -73,7 +63,6 @@ pub enum DelegateIn<TSpec: EthSpec> {
Gossipsub(<GossipHandler as ProtocolsHandler>::InEvent), Gossipsub(<GossipHandler as ProtocolsHandler>::InEvent),
RPC(<RPCHandler<TSpec> as ProtocolsHandler>::InEvent), RPC(<RPCHandler<TSpec> as ProtocolsHandler>::InEvent),
Identify(<IdentifyHandler as ProtocolsHandler>::InEvent), Identify(<IdentifyHandler as ProtocolsHandler>::InEvent),
Discovery(<DiscoveryHandler<TSpec> as ProtocolsHandler>::InEvent),
} }
/// Wrapper around the `ProtocolsHandler::OutEvent` types of the handlers. /// Wrapper around the `ProtocolsHandler::OutEvent` types of the handlers.
@ -82,7 +71,6 @@ pub enum DelegateOut<TSpec: EthSpec> {
Gossipsub(<GossipHandler as ProtocolsHandler>::OutEvent), Gossipsub(<GossipHandler as ProtocolsHandler>::OutEvent),
RPC(<RPCHandler<TSpec> as ProtocolsHandler>::OutEvent), RPC(<RPCHandler<TSpec> as ProtocolsHandler>::OutEvent),
Identify(<IdentifyHandler as ProtocolsHandler>::OutEvent), Identify(<IdentifyHandler as ProtocolsHandler>::OutEvent),
Discovery(<DiscoveryHandler<TSpec> as ProtocolsHandler>::OutEvent),
} }
/// Wrapper around the `ProtocolsHandler::Error` types of the handlers. /// Wrapper around the `ProtocolsHandler::Error` types of the handlers.
@ -92,7 +80,6 @@ pub enum DelegateError<TSpec: EthSpec> {
Gossipsub(<GossipHandler as ProtocolsHandler>::Error), Gossipsub(<GossipHandler as ProtocolsHandler>::Error),
RPC(<RPCHandler<TSpec> as ProtocolsHandler>::Error), RPC(<RPCHandler<TSpec> as ProtocolsHandler>::Error),
Identify(<IdentifyHandler as ProtocolsHandler>::Error), Identify(<IdentifyHandler as ProtocolsHandler>::Error),
Discovery(<DiscoveryHandler<TSpec> as ProtocolsHandler>::Error),
} }
impl<TSpec: EthSpec> std::error::Error for DelegateError<TSpec> {} impl<TSpec: EthSpec> std::error::Error for DelegateError<TSpec> {}
@ -106,7 +93,6 @@ impl<TSpec: EthSpec> std::fmt::Display for DelegateError<TSpec> {
DelegateError::Gossipsub(err) => err.fmt(formater), DelegateError::Gossipsub(err) => err.fmt(formater),
DelegateError::RPC(err) => err.fmt(formater), DelegateError::RPC(err) => err.fmt(formater),
DelegateError::Identify(err) => err.fmt(formater), DelegateError::Identify(err) => err.fmt(formater),
DelegateError::Discovery(err) => err.fmt(formater),
} }
} }
} }
@ -115,10 +101,7 @@ pub type DelegateInProto<TSpec> = SelectUpgrade<
<GossipHandler as ProtocolsHandler>::InboundProtocol, <GossipHandler as ProtocolsHandler>::InboundProtocol,
SelectUpgrade< SelectUpgrade<
<RPCHandler<TSpec> as ProtocolsHandler>::InboundProtocol, <RPCHandler<TSpec> as ProtocolsHandler>::InboundProtocol,
SelectUpgrade< <IdentifyHandler as ProtocolsHandler>::InboundProtocol,
<IdentifyHandler as ProtocolsHandler>::InboundProtocol,
<DiscoveryHandler<TSpec> as ProtocolsHandler>::InboundProtocol,
>,
>, >,
>; >;
@ -126,10 +109,7 @@ pub type DelegateOutProto<TSpec> = EitherUpgrade<
<GossipHandler as ProtocolsHandler>::OutboundProtocol, <GossipHandler as ProtocolsHandler>::OutboundProtocol,
EitherUpgrade< EitherUpgrade<
<RPCHandler<TSpec> as ProtocolsHandler>::OutboundProtocol, <RPCHandler<TSpec> as ProtocolsHandler>::OutboundProtocol,
EitherUpgrade< <IdentifyHandler as ProtocolsHandler>::OutboundProtocol,
<IdentifyHandler as ProtocolsHandler>::OutboundProtocol,
<DiscoveryHandler<TSpec> as ProtocolsHandler>::OutboundProtocol,
>,
>, >,
>; >;
@ -138,10 +118,7 @@ pub type DelegateOutInfo<TSpec> = EitherOutput<
<GossipHandler as ProtocolsHandler>::OutboundOpenInfo, <GossipHandler as ProtocolsHandler>::OutboundOpenInfo,
EitherOutput< EitherOutput<
<RPCHandler<TSpec> as ProtocolsHandler>::OutboundOpenInfo, <RPCHandler<TSpec> as ProtocolsHandler>::OutboundOpenInfo,
EitherOutput< <IdentifyHandler as ProtocolsHandler>::OutboundOpenInfo,
<IdentifyHandler as ProtocolsHandler>::OutboundOpenInfo,
<DiscoveryHandler<TSpec> as ProtocolsHandler>::OutboundOpenInfo,
>,
>, >,
>; >;
@ -157,24 +134,16 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
let gossip_proto = self.gossip_handler.listen_protocol(); let gossip_proto = self.gossip_handler.listen_protocol();
let rpc_proto = self.rpc_handler.listen_protocol(); let rpc_proto = self.rpc_handler.listen_protocol();
let identify_proto = self.identify_handler.listen_protocol(); let identify_proto = self.identify_handler.listen_protocol();
let discovery_proto = self.discovery_handler.listen_protocol();
let timeout = gossip_proto let timeout = gossip_proto
.timeout() .timeout()
.max(rpc_proto.timeout()) .max(rpc_proto.timeout())
.max(identify_proto.timeout()) .max(identify_proto.timeout())
.max(discovery_proto.timeout())
.clone(); .clone();
let select = SelectUpgrade::new( let select = SelectUpgrade::new(
gossip_proto.into_upgrade().1, gossip_proto.into_upgrade().1,
SelectUpgrade::new( SelectUpgrade::new(rpc_proto.into_upgrade().1, identify_proto.into_upgrade().1),
rpc_proto.into_upgrade().1,
SelectUpgrade::new(
identify_proto.into_upgrade().1,
discovery_proto.into_upgrade().1,
),
),
); );
SubstreamProtocol::new(select).with_timeout(timeout) SubstreamProtocol::new(select).with_timeout(timeout)
@ -192,13 +161,9 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
self.rpc_handler.inject_fully_negotiated_inbound(out) self.rpc_handler.inject_fully_negotiated_inbound(out)
} }
// Identify // Identify
EitherOutput::Second(EitherOutput::Second(EitherOutput::First(out))) => { EitherOutput::Second(EitherOutput::Second(out)) => {
self.identify_handler.inject_fully_negotiated_inbound(out) self.identify_handler.inject_fully_negotiated_inbound(out)
} }
// Discovery
EitherOutput::Second(EitherOutput::Second(EitherOutput::Second(out))) => {
self.discovery_handler.inject_fully_negotiated_inbound(out)
}
} }
} }
@ -221,18 +186,11 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
.inject_fully_negotiated_outbound(protocol, info), .inject_fully_negotiated_outbound(protocol, info),
// Identify // Identify
( (
EitherOutput::Second(EitherOutput::Second(EitherOutput::First(protocol))), EitherOutput::Second(EitherOutput::Second(protocol)),
EitherOutput::Second(EitherOutput::Second(EitherOutput::First(info))), EitherOutput::Second(EitherOutput::Second(info)),
) => self ) => self
.identify_handler .identify_handler
.inject_fully_negotiated_outbound(protocol, info), .inject_fully_negotiated_outbound(protocol, info),
// Discovery
(
EitherOutput::Second(EitherOutput::Second(EitherOutput::Second(protocol))),
EitherOutput::Second(EitherOutput::Second(EitherOutput::Second(info))),
) => self
.discovery_handler
.inject_fully_negotiated_outbound(protocol, info),
// Reaching here means we got a protocol and info for different behaviours // Reaching here means we got a protocol and info for different behaviours
_ => unreachable!("output and protocol don't match"), _ => unreachable!("output and protocol don't match"),
} }
@ -243,7 +201,6 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
DelegateIn::Gossipsub(ev) => self.gossip_handler.inject_event(ev), DelegateIn::Gossipsub(ev) => self.gossip_handler.inject_event(ev),
DelegateIn::RPC(ev) => self.rpc_handler.inject_event(ev), DelegateIn::RPC(ev) => self.rpc_handler.inject_event(ev),
DelegateIn::Identify(ev) => self.identify_handler.inject_event(ev), DelegateIn::Identify(ev) => self.identify_handler.inject_event(ev),
DelegateIn::Discovery(ev) => self.discovery_handler.inject_event(ev),
} }
} }
@ -305,7 +262,7 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
} }
}, },
// Identify // Identify
EitherOutput::Second(EitherOutput::Second(EitherOutput::First(info))) => match error { EitherOutput::Second(EitherOutput::Second(info)) => match error {
ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)) => { ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)) => {
self.identify_handler.inject_dial_upgrade_error( self.identify_handler.inject_dial_upgrade_error(
info, info,
@ -319,7 +276,7 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
.identify_handler .identify_handler
.inject_dial_upgrade_error(info, ProtocolsHandlerUpgrErr::Timeout), .inject_dial_upgrade_error(info, ProtocolsHandlerUpgrErr::Timeout),
ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(EitherError::B( ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(EitherError::B(
EitherError::B(EitherError::A(err)), EitherError::B(err),
))) => self.identify_handler.inject_dial_upgrade_error( ))) => self.identify_handler.inject_dial_upgrade_error(
info, info,
ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(err)), ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(err)),
@ -328,30 +285,6 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
unreachable!("info and error don't match") unreachable!("info and error don't match")
} }
}, },
// Discovery
EitherOutput::Second(EitherOutput::Second(EitherOutput::Second(info))) => match error {
ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)) => {
self.discovery_handler.inject_dial_upgrade_error(
info,
ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Select(err)),
)
}
ProtocolsHandlerUpgrErr::Timer => self
.discovery_handler
.inject_dial_upgrade_error(info, ProtocolsHandlerUpgrErr::Timer),
ProtocolsHandlerUpgrErr::Timeout => self
.discovery_handler
.inject_dial_upgrade_error(info, ProtocolsHandlerUpgrErr::Timeout),
ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(EitherError::B(
EitherError::B(EitherError::B(err)),
))) => self.discovery_handler.inject_dial_upgrade_error(
info,
ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(err)),
),
ProtocolsHandlerUpgrErr::Upgrade(UpgradeError::Apply(_)) => {
unreachable!("info and error don't match")
}
},
} }
} }
@ -360,7 +293,6 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
.connection_keep_alive() .connection_keep_alive()
.max(self.rpc_handler.connection_keep_alive()) .max(self.rpc_handler.connection_keep_alive())
.max(self.identify_handler.connection_keep_alive()) .max(self.identify_handler.connection_keep_alive())
.max(self.discovery_handler.connection_keep_alive())
} }
fn poll( fn poll(
@ -417,28 +349,8 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
} }
Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol, info }) => { Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol, info }) => {
return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest {
protocol: protocol protocol: protocol.map_upgrade(|u| EitherUpgrade::B(EitherUpgrade::B(u))),
.map_upgrade(|u| EitherUpgrade::B(EitherUpgrade::B(EitherUpgrade::A(u)))), info: EitherOutput::Second(EitherOutput::Second(info)),
info: EitherOutput::Second(EitherOutput::Second(EitherOutput::First(info))),
});
}
Poll::Pending => (),
};
match self.discovery_handler.poll(cx) {
Poll::Ready(ProtocolsHandlerEvent::Custom(event)) => {
return Poll::Ready(ProtocolsHandlerEvent::Custom(DelegateOut::Discovery(event)));
}
Poll::Ready(ProtocolsHandlerEvent::Close(event)) => {
return Poll::Ready(ProtocolsHandlerEvent::Close(DelegateError::Discovery(
event,
)));
}
Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol, info }) => {
return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest {
protocol: protocol
.map_upgrade(|u| EitherUpgrade::B(EitherUpgrade::B(EitherUpgrade::B(u)))),
info: EitherOutput::Second(EitherOutput::Second(EitherOutput::Second(info))),
}); });
} }
Poll::Pending => (), Poll::Pending => (),

View File

@ -1,4 +1,3 @@
use crate::discovery::Discovery;
use crate::rpc::*; use crate::rpc::*;
use delegate::DelegatingHandler; use delegate::DelegatingHandler;
pub(super) use delegate::{ pub(super) use delegate::{
@ -27,14 +26,9 @@ pub struct BehaviourHandler<TSpec: EthSpec> {
} }
impl<TSpec: EthSpec> BehaviourHandler<TSpec> { impl<TSpec: EthSpec> BehaviourHandler<TSpec> {
pub fn new( pub fn new(gossipsub: &mut Gossipsub, rpc: &mut RPC<TSpec>, identify: &mut Identify) -> Self {
gossipsub: &mut Gossipsub,
rpc: &mut RPC<TSpec>,
identify: &mut Identify,
discovery: &mut Discovery<TSpec>,
) -> Self {
BehaviourHandler { BehaviourHandler {
delegate: DelegatingHandler::new(gossipsub, rpc, identify, discovery), delegate: DelegatingHandler::new(gossipsub, rpc, identify),
shutting_down: false, shutting_down: false,
} }
} }

View File

@ -1,9 +1,8 @@
use crate::discovery::{enr::Eth2Enr, Discovery};
use crate::peer_manager::{PeerManager, PeerManagerEvent}; use crate::peer_manager::{PeerManager, PeerManagerEvent};
use crate::rpc::*; use crate::rpc::*;
use crate::types::{GossipEncoding, GossipKind, GossipTopic}; use crate::types::{GossipEncoding, GossipKind, GossipTopic};
use crate::Eth2Enr;
use crate::{error, Enr, NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash}; use crate::{error, Enr, NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash};
use discv5::Discv5Event;
use futures::prelude::*; use futures::prelude::*;
use handler::{BehaviourHandler, BehaviourHandlerIn, BehaviourHandlerOut, DelegateIn, DelegateOut}; use handler::{BehaviourHandler, BehaviourHandlerIn, BehaviourHandlerOut, DelegateIn, DelegateOut};
use libp2p::{ use libp2p::{
@ -46,8 +45,6 @@ pub struct Behaviour<TSpec: EthSpec> {
// TODO: Using id for initial interop. This will be removed by mainnet. // TODO: Using id for initial interop. This will be removed by mainnet.
/// Provides IP addresses and peer information. /// Provides IP addresses and peer information.
identify: Identify, identify: Identify,
/// Discovery behaviour.
discovery: Discovery<TSpec>,
/// The peer manager that keeps track of peer's reputation and status. /// The peer manager that keeps track of peer's reputation and status.
peer_manager: PeerManager<TSpec>, peer_manager: PeerManager<TSpec>,
/// The events generated by this behaviour to be consumed in the swarm poll. /// The events generated by this behaviour to be consumed in the swarm poll.
@ -76,7 +73,6 @@ macro_rules! delegate_to_behaviours {
$self.gossipsub.$fn($($arg),*); $self.gossipsub.$fn($($arg),*);
$self.eth2_rpc.$fn($($arg),*); $self.eth2_rpc.$fn($($arg),*);
$self.identify.$fn($($arg),*); $self.identify.$fn($($arg),*);
$self.discovery.$fn($($arg),*);
}; };
} }
@ -85,21 +81,11 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
type OutEvent = BehaviourEvent<TSpec>; type OutEvent = BehaviourEvent<TSpec>;
fn new_handler(&mut self) -> Self::ProtocolsHandler { fn new_handler(&mut self) -> Self::ProtocolsHandler {
BehaviourHandler::new( BehaviourHandler::new(&mut self.gossipsub, &mut self.eth2_rpc, &mut self.identify)
&mut self.gossipsub,
&mut self.eth2_rpc,
&mut self.identify,
&mut self.discovery,
)
} }
fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> { fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> {
let mut out = Vec::new(); self.peer_manager.addresses_of_peer(peer_id)
out.extend(self.gossipsub.addresses_of_peer(peer_id));
out.extend(self.eth2_rpc.addresses_of_peer(peer_id));
out.extend(self.identify.addresses_of_peer(peer_id));
out.extend(self.discovery.addresses_of_peer(peer_id));
out
} }
fn inject_connected(&mut self, peer_id: &PeerId) { fn inject_connected(&mut self, peer_id: &PeerId) {
@ -178,7 +164,6 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
DelegateOut::Gossipsub(ev) => self.gossipsub.inject_event(peer_id, conn_id, ev), 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::RPC(ev) => self.eth2_rpc.inject_event(peer_id, conn_id, ev),
DelegateOut::Identify(ev) => self.identify.inject_event(peer_id, conn_id, ev), DelegateOut::Identify(ev) => self.identify.inject_event(peer_id, conn_id, ev),
DelegateOut::Discovery(ev) => self.discovery.inject_event(peer_id, conn_id, ev),
}, },
/* Custom events sent BY the handler */ /* Custom events sent BY the handler */
BehaviourHandlerOut::Custom => { BehaviourHandlerOut::Custom => {
@ -240,7 +225,6 @@ impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
poll_behaviour!(gossipsub, on_gossip_event, DelegateIn::Gossipsub); poll_behaviour!(gossipsub, on_gossip_event, DelegateIn::Gossipsub);
poll_behaviour!(eth2_rpc, on_rpc_event, DelegateIn::RPC); poll_behaviour!(eth2_rpc, on_rpc_event, DelegateIn::RPC);
poll_behaviour!(identify, on_identify_event, DelegateIn::Identify); poll_behaviour!(identify, on_identify_event, DelegateIn::Identify);
poll_behaviour!(discovery, on_discovery_event, DelegateIn::Discovery);
self.custom_poll(cx) self.custom_poll(cx)
} }
@ -264,14 +248,12 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
); );
let enr_fork_id = network_globals let enr_fork_id = network_globals
.local_enr .local_enr()
.read()
.eth2() .eth2()
.expect("Local ENR must have a fork id"); .expect("Local ENR must have a fork id");
let attnets = network_globals let attnets = network_globals
.local_enr .local_enr()
.read()
.bitfield::<TSpec>() .bitfield::<TSpec>()
.expect("Local ENR must have subnet bitfield"); .expect("Local ENR must have subnet bitfield");
@ -283,9 +265,8 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
Ok(Behaviour { Ok(Behaviour {
eth2_rpc: RPC::new(log.clone()), eth2_rpc: RPC::new(log.clone()),
gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()), gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
discovery: Discovery::new(local_key, net_conf, network_globals.clone(), log)?,
identify, identify,
peer_manager: PeerManager::new(network_globals.clone(), log), peer_manager: PeerManager::new(local_key, net_conf, network_globals.clone(), log)?,
events: Vec::new(), events: Vec::new(),
peers_to_dc: Vec::new(), peers_to_dc: Vec::new(),
seen_gossip_messages: LruCache::new(100_000), seen_gossip_messages: LruCache::new(100_000),
@ -296,9 +277,9 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
}) })
} }
/// Obtain a reference to the discovery protocol. /// Returns the local ENR of the node.
pub fn discovery(&self) -> &Discovery<TSpec> { pub fn local_enr(&self) -> Enr {
&self.discovery self.network_globals.local_enr()
} }
/// Obtain a reference to the gossipsub protocol. /// Obtain a reference to the gossipsub protocol.
@ -428,33 +409,35 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
) )
} }
/* Discovery / Peer management functions */ /* Peer management functions */
/// Notify discovery that the peer has been banned. /// Notify discovery that the peer has been banned.
pub fn peer_banned(&mut self, peer_id: PeerId) { // TODO: Remove this and integrate all disconnection/banning logic inside the peer manager.
self.discovery.peer_banned(peer_id); pub fn peer_banned(&mut self, _peer_id: PeerId) {}
}
/// Notify discovery that the peer has been unbanned. /// Notify discovery that the peer has been unbanned.
pub fn peer_unbanned(&mut self, peer_id: &PeerId) { // TODO: Remove this and integrate all disconnection/banning logic inside the peer manager.
self.discovery.peer_unbanned(peer_id); pub fn peer_unbanned(&mut self, _peer_id: &PeerId) {}
}
/// Returns an iterator over all enr entries in the DHT. /// Returns an iterator over all enr entries in the DHT.
pub fn enr_entries(&mut self) -> impl Iterator<Item = &Enr> { pub fn enr_entries(&mut self) -> Vec<Enr> {
self.discovery.enr_entries() self.peer_manager.discovery_mut().table_entries_enr()
} }
/// Add an ENR to the routing table of the discovery mechanism. /// Add an ENR to the routing table of the discovery mechanism.
pub fn add_enr(&mut self, enr: Enr) { pub fn add_enr(&mut self, enr: Enr) {
self.discovery.add_enr(enr); self.peer_manager.discovery_mut().add_enr(enr);
} }
/// Updates a subnet value to the ENR bitfield. /// Updates a subnet value to the ENR bitfield.
/// ///
/// The `value` is `true` if a subnet is being added and false otherwise. /// 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) { pub fn update_enr_subnet(&mut self, subnet_id: SubnetId, value: bool) {
if let Err(e) = self.discovery.update_enr_bitfield(subnet_id, value) { if let Err(e) = self
.peer_manager
.discovery_mut()
.update_enr_bitfield(subnet_id, value)
{
crit!(self.log, "Could not update ENR bitfield"; "error" => e); crit!(self.log, "Could not update ENR bitfield"; "error" => e);
} }
// update the local meta data which informs our peers of the update during PINGS // update the local meta data which informs our peers of the update during PINGS
@ -464,12 +447,14 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
/// Attempts to discover new peers for a given subnet. The `min_ttl` gives the time at which we /// 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. /// would like to retain the peers for.
pub fn discover_subnet_peers(&mut self, subnet_id: SubnetId, min_ttl: Option<Instant>) { pub fn discover_subnet_peers(&mut self, subnet_id: SubnetId, min_ttl: Option<Instant>) {
self.discovery.discover_subnet_peers(subnet_id, min_ttl) self.peer_manager.discover_subnet_peers(subnet_id, min_ttl)
} }
/// Updates the local ENR's "eth2" field with the latest EnrForkId. /// Updates the local ENR's "eth2" field with the latest EnrForkId.
pub fn update_fork_version(&mut self, enr_fork_id: EnrForkId) { pub fn update_fork_version(&mut self, enr_fork_id: EnrForkId) {
self.discovery.update_eth2_enr(enr_fork_id.clone()); self.peer_manager
.discovery_mut()
.update_eth2_enr(enr_fork_id.clone());
// unsubscribe from all gossip topics and re-subscribe to their new fork counterparts // unsubscribe from all gossip topics and re-subscribe to their new fork counterparts
let subscribed_topics = self let subscribed_topics = self
@ -497,11 +482,12 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
/* Private internal functions */ /* Private internal functions */
/// Updates the current meta data of the node. /// Updates the current meta data of the node to match the local ENR.
fn update_metadata(&mut self) { fn update_metadata(&mut self) {
self.meta_data.seq_number += 1; self.meta_data.seq_number += 1;
self.meta_data.attnets = self self.meta_data.attnets = self
.discovery .peer_manager
.discovery()
.local_enr() .local_enr()
.bitfield::<TSpec>() .bitfield::<TSpec>()
.expect("Local discovery must have bitfield"); .expect("Local discovery must have bitfield");
@ -764,6 +750,15 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
loop { loop {
match self.peer_manager.poll_next_unpin(cx) { match self.peer_manager.poll_next_unpin(cx) {
Poll::Ready(Some(event)) => match event { 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 });
}
PeerManagerEvent::Status(peer_id) => { PeerManagerEvent::Status(peer_id) => {
// it's time to status. We don't keep a beacon chain reference here, so we inform // 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 // the network to send a status to this peer
@ -835,10 +830,6 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
IdentifyEvent::Error { .. } => {} IdentifyEvent::Error { .. } => {}
} }
} }
fn on_discovery_event(&mut self, _event: Discv5Event) {
// discv5 has no events to inject
}
} }
/* Public API types */ /* Public API types */

View File

@ -109,14 +109,15 @@ impl Default for Config {
// discv5 configuration // discv5 configuration
let discv5_config = Discv5ConfigBuilder::new() let discv5_config = Discv5ConfigBuilder::new()
.enable_packet_filter()
.session_cache_capacity(100)
.request_timeout(Duration::from_secs(4)) .request_timeout(Duration::from_secs(4))
.request_retries(2) .request_retries(2)
.enr_update(true) // update IP based on PONG responses
.enr_peer_update_min(2) // prevents NAT's should be raised for mainnet .enr_peer_update_min(2) // prevents NAT's should be raised for mainnet
.query_parallelism(5) .query_parallelism(5)
.query_timeout(Duration::from_secs(60)) .query_timeout(Duration::from_secs(60))
.query_peer_timeout(Duration::from_secs(2)) .query_peer_timeout(Duration::from_secs(2))
.ip_limit(false) // limits /24 IP's in buckets. Enable for mainnet .ip_limit() // limits /24 IP's in buckets.
.ping_interval(Duration::from_secs(300)) .ping_interval(Duration::from_secs(300))
.build(); .build();

View File

@ -7,7 +7,6 @@ extern crate lazy_static;
pub mod behaviour; pub mod behaviour;
mod config; mod config;
pub mod discovery;
mod metrics; mod metrics;
mod peer_manager; mod peer_manager;
pub mod rpc; pub mod rpc;
@ -17,9 +16,14 @@ pub mod types;
pub use crate::types::{error, Enr, GossipTopic, NetworkGlobals, PubsubMessage}; pub use crate::types::{error, Enr, GossipTopic, NetworkGlobals, PubsubMessage};
pub use behaviour::{BehaviourEvent, PeerRequestId, Request, Response}; pub use behaviour::{BehaviourEvent, PeerRequestId, Request, Response};
pub use config::Config as NetworkConfig; pub use config::Config as NetworkConfig;
pub use discovery::enr_ext::{CombinedKeyExt, EnrExt}; pub use discv5;
pub use libp2p::gossipsub::{MessageId, Topic, TopicHash}; pub use libp2p::gossipsub::{MessageId, Topic, TopicHash};
pub use libp2p::{core::ConnectedPoint, PeerId, Swarm}; pub use libp2p::{core::ConnectedPoint, PeerId, Swarm};
pub use libp2p::{multiaddr, Multiaddr}; pub use libp2p::{multiaddr, Multiaddr};
pub use peer_manager::{client::Client, PeerDB, PeerInfo, PeerSyncStatus, SyncInfo}; pub use peer_manager::discovery;
pub use peer_manager::{
client::Client,
discovery::{CombinedKeyExt, EnrExt, Eth2Enr},
PeerDB, PeerInfo, PeerSyncStatus, SyncInfo,
};
pub use service::{Libp2pEvent, Service, NETWORK_KEY_FILENAME}; pub use service::{Libp2pEvent, Service, NETWORK_KEY_FILENAME};

View File

@ -17,4 +17,12 @@ lazy_static! {
"libp2p_peer_disconnect_event_total", "libp2p_peer_disconnect_event_total",
"Count of libp2p peer disconnect events" "Count of libp2p peer disconnect events"
); );
pub static ref DISCOVERY_QUEUE: Result<IntGauge> = try_create_int_gauge(
"discovery_queue_size",
"The number of discovery queries awaiting execution"
);
pub static ref DISCOVERY_REQS: Result<IntGauge> = try_create_int_gauge(
"discovery_requests",
"The number of unsolicited discovery requests per second"
);
} }

View File

@ -1,12 +1,12 @@
//! Helper functions and an extension trait for Ethereum 2 ENRs. //! Helper functions and an extension trait for Ethereum 2 ENRs.
pub use discv5::enr::{self, CombinedKey, EnrBuilder}; pub use discv5::enr::{self, CombinedKey, EnrBuilder};
pub use libp2p::core::identity::Keypair;
use super::enr_ext::CombinedKeyExt;
use super::ENR_FILENAME; use super::ENR_FILENAME;
use crate::types::{Enr, EnrBitfield}; use crate::types::{Enr, EnrBitfield};
use crate::CombinedKeyExt;
use crate::NetworkConfig; use crate::NetworkConfig;
use libp2p::core::identity::Keypair;
use slog::{debug, warn}; use slog::{debug, warn};
use ssz::{Decode, Encode}; use ssz::{Decode, Encode};
use ssz_types::BitVector; use ssz_types::BitVector;

View File

@ -0,0 +1,632 @@
///! This manages the discovery and management of peers.
pub(crate) mod enr;
pub mod enr_ext;
// Allow external use of the lighthouse ENR builder
pub use enr::{build_enr, CombinedKey, Eth2Enr};
pub use enr_ext::{CombinedKeyExt, EnrExt};
pub use libp2p::core::identity::Keypair;
use crate::metrics;
use crate::{error, Enr, NetworkConfig, NetworkGlobals};
use discv5::{enr::NodeId, Discv5, Discv5Event};
use enr::{BITFIELD_ENR_KEY, ETH2_ENR_KEY};
use futures::prelude::*;
use libp2p::core::PeerId;
// use libp2p::multiaddr::Protocol;
use futures::stream::FuturesUnordered;
use lru::LruCache;
use slog::{crit, debug, info, trace, warn};
use ssz::{Decode, Encode};
use ssz_types::BitVector;
use std::{
collections::VecDeque,
net::SocketAddr,
path::Path,
pin::Pin,
sync::Arc,
task::{Context, Poll},
time::Instant,
};
use tokio::sync::mpsc;
use types::{EnrForkId, EthSpec, SubnetId};
mod subnet_predicate;
use subnet_predicate::subnet_predicate;
/// Local ENR storage filename.
pub const ENR_FILENAME: &str = "enr.dat";
/// Target number of peers we'd like to have connected to a given long-lived subnet.
const TARGET_SUBNET_PEERS: usize = 3;
/// Number of times to attempt a discovery request
const MAX_DISCOVERY_RETRY: usize = 3;
/// The maximum number of concurrent discovery queries.
const MAX_CONCURRENT_QUERIES: usize = 1;
/// The number of closest peers to search for when doing a regular peer search.
///
/// We could reduce this constant to speed up queries however at the cost of security. It will
/// make it easier to peers to eclipse this node. Kademlia suggests a value of 16.
const FIND_NODE_QUERY_CLOSEST_PEERS: usize = 16;
/// The events emitted by polling discovery.
pub enum DiscoveryEvent {
/// A query has completed. The first parameter is the `min_ttl` of the peers if it is specified
/// and the second parameter are the discovered peers.
QueryResult(Option<Instant>, Box<Vec<Enr>>),
/// This indicates that our local UDP socketaddr has been updated and we should inform libp2p.
SocketUpdated(SocketAddr),
}
#[derive(Debug, Clone, PartialEq)]
enum QueryType {
/// We are searching for subnet peers.
Subnet {
subnet_id: SubnetId,
min_ttl: Option<Instant>,
retries: usize,
},
/// We are searching for more peers without ENR or time constraints.
FindPeers,
}
impl QueryType {
/// Returns true if this query has expired.
pub fn expired(&self) -> bool {
match self {
Self::FindPeers => false,
Self::Subnet { min_ttl, .. } => {
if let Some(ttl) = min_ttl {
ttl > &Instant::now()
} else {
true
}
}
}
}
/// Returns the min_ttl of the query if one exists
///
/// This is required for returning to the peer manager. The peer manager will update newly
/// connected peers with this `min_ttl`
pub fn min_ttl(&self) -> Option<Instant> {
match self {
Self::FindPeers => None,
Self::Subnet { min_ttl, .. } => min_ttl.clone(),
}
}
}
/// The result of a query.
struct QueryResult(QueryType, Result<Vec<Enr>, discv5::QueryError>);
// Awaiting the event stream future
enum EventStream {
/// Awaiting an event stream to be generated. This is required due to the poll nature of
/// `Discovery`
Awaiting(
Pin<
Box<
dyn Future<Output = Result<mpsc::Receiver<Discv5Event>, discv5::Discv5Error>>
+ Send,
>,
>,
),
/// The future has completed.
Present(mpsc::Receiver<Discv5Event>),
// The future has failed, there are no events from discv5.
Failed,
}
pub struct Discovery<TSpec: EthSpec> {
/// A collection of seen live ENRs for quick lookup and to map peer-id's to ENRs.
cached_enrs: LruCache<PeerId, Enr>,
/// The directory where the ENR is stored.
enr_dir: String,
/// The handle for the underlying discv5 Server.
///
/// This is behind a Reference counter to allow for futures to be spawned and polled with a
/// static lifetime.
discv5: Discv5,
/// A collection of network constants that can be read from other threads.
network_globals: Arc<NetworkGlobals<TSpec>>,
/// Indicates if we are actively searching for peers. We only allow a single FindPeers query at
/// a time, regardless of the query concurrency.
find_peer_active: bool,
/// A queue of discovery queries to be processed.
queued_queries: VecDeque<QueryType>,
/// Active discovery queries.
active_queries: FuturesUnordered<std::pin::Pin<Box<dyn Future<Output = QueryResult> + Send>>>,
/// The discv5 event stream.
event_stream: EventStream,
/// Logger for the discovery behaviour.
log: slog::Logger,
}
impl<TSpec: EthSpec> Discovery<TSpec> {
/// NOTE: Creating discovery requires running within a tokio execution environment.
pub fn new(
local_key: &Keypair,
config: &NetworkConfig,
network_globals: Arc<NetworkGlobals<TSpec>>,
log: &slog::Logger,
) -> error::Result<Self> {
let log = log.clone();
let enr_dir = match config.network_dir.to_str() {
Some(path) => String::from(path),
None => String::from(""),
};
let local_enr = network_globals.local_enr.read().clone();
info!(log, "ENR Initialised"; "enr" => local_enr.to_base64(), "seq" => local_enr.seq(), "id"=> format!("{}",local_enr.node_id()), "ip" => format!("{:?}", local_enr.ip()), "udp"=> format!("{:?}", local_enr.udp()), "tcp" => format!("{:?}", local_enr.tcp()));
let listen_socket = SocketAddr::new(config.listen_address, config.discovery_port);
// convert the keypair into an ENR key
let enr_key: CombinedKey = CombinedKey::from_libp2p(&local_key)?;
let mut discv5 = Discv5::new(local_enr, enr_key, config.discv5_config.clone())
.map_err(|e| format!("Discv5 service failed. Error: {:?}", e))?;
// Add bootnodes to routing table
for bootnode_enr in config.boot_nodes.clone() {
debug!(
log,
"Adding node to routing table";
"node_id" => format!("{}", bootnode_enr.node_id()),
"peer_id" => format!("{}", bootnode_enr.peer_id()),
"ip" => format!("{:?}", bootnode_enr.ip()),
"udp" => format!("{:?}", bootnode_enr.udp()),
"tcp" => format!("{:?}", bootnode_enr.tcp())
);
let _ = discv5.add_enr(bootnode_enr).map_err(|e| {
warn!(
log,
"Could not add peer to the local routing table";
"error" => format!("{}", e)
)
});
}
// start the discv5 service.
discv5.start(listen_socket);
debug!(log, "Discovery service started");
// obtain the event stream
let event_stream = EventStream::Awaiting(Box::pin(discv5.event_stream()));
Ok(Self {
cached_enrs: LruCache::new(50),
network_globals,
find_peer_active: false,
queued_queries: VecDeque::with_capacity(10),
active_queries: FuturesUnordered::new(),
discv5,
event_stream,
log,
enr_dir,
})
}
/// Return the nodes local ENR.
pub fn local_enr(&self) -> Enr {
self.discv5.local_enr()
}
/// This adds a new `FindPeers` query to the queue if one doesn't already exist.
pub fn discover_peers(&mut self) {
// If there is not already a find peer's query queued, add one
let query = QueryType::FindPeers;
if !self.queued_queries.contains(&query) {
trace!(self.log, "Queuing a peer discovery request");
self.queued_queries.push_back(query);
// update the metrics
metrics::set_gauge(&metrics::DISCOVERY_QUEUE, self.queued_queries.len() as i64);
}
}
/// Processes a request to search for more peers on a subnet.
pub fn discover_subnet_peers(&mut self, subnet_id: SubnetId, min_ttl: Option<Instant>) {
self.add_subnet_query(subnet_id, min_ttl, 0);
}
/// Adds a subnet query if one doesn't exist. If a subnet query already exists, this
/// updates the min_ttl field.
fn add_subnet_query(&mut self, subnet_id: SubnetId, min_ttl: Option<Instant>, retries: usize) {
// remove the entry and complete the query if greater than the maximum search count
if retries >= MAX_DISCOVERY_RETRY {
debug!(
self.log,
"Subnet peer discovery did not find sufficient peers. Reached max retry limit"
);
return;
}
// Search through any queued requests and update the timeout if a query for this subnet
// already exists
let mut found = false;
for query in self.queued_queries.iter_mut() {
if let QueryType::Subnet {
subnet_id: ref mut q_subnet_id,
min_ttl: ref mut q_min_ttl,
retries: ref mut q_retries,
} = query
{
if *q_subnet_id == subnet_id {
if *q_min_ttl < min_ttl {
*q_min_ttl = min_ttl;
}
// update the number of retries
*q_retries = retries;
// mimic an `Iter::Find()` and short-circuit the loop
found = true;
break;
}
}
}
if !found {
// Set up the query and add it to the queue
let query = QueryType::Subnet {
subnet_id,
min_ttl,
retries,
};
// update the metrics and insert into the queue.
metrics::set_gauge(&metrics::DISCOVERY_QUEUE, self.queued_queries.len() as i64);
self.queued_queries.push_back(query);
}
}
/// Add an ENR to the routing table of the discovery mechanism.
pub fn add_enr(&mut self, enr: Enr) {
// add the enr to seen caches
self.cached_enrs.put(enr.peer_id(), enr.clone());
if let Err(e) = self.discv5.add_enr(enr) {
warn!(
self.log,
"Could not add peer to the local routing table";
"error" => format!("{}", e)
)
}
}
/// Returns an iterator over all enr entries in the DHT.
pub fn table_entries_enr(&mut self) -> Vec<Enr> {
self.discv5.table_entries_enr()
}
/// Returns the ENR of a known peer if it exists.
pub fn enr_of_peer(&mut self, peer_id: &PeerId) -> Option<Enr> {
// first search the local cache
if let Some(enr) = self.cached_enrs.get(peer_id) {
return Some(enr.clone());
}
// not in the local cache, look in the routing table
if let Ok(node_id) = enr_ext::peer_id_to_node_id(peer_id) {
self.discv5.find_enr(&node_id)
} else {
None
}
}
/// Adds/Removes a subnet from the ENR Bitfield
pub fn update_enr_bitfield(&mut self, subnet_id: SubnetId, value: bool) -> Result<(), String> {
let id = *subnet_id as usize;
let local_enr = self.discv5.local_enr();
let mut current_bitfield = local_enr.bitfield::<TSpec>()?;
if id >= current_bitfield.len() {
return Err(format!(
"Subnet id: {} is outside the ENR bitfield length: {}",
id,
current_bitfield.len()
));
}
if current_bitfield
.get(id)
.map_err(|_| String::from("Subnet ID out of bounds"))?
== value
{
return Err(format!(
"Subnet id: {} already in the local ENR already has value: {}",
id, value
));
}
// set the subnet bitfield in the ENR
current_bitfield
.set(id, value)
.map_err(|_| String::from("Subnet ID out of bounds, could not set subnet ID"))?;
// insert the bitfield into the ENR record
let _ = self
.discv5
.enr_insert(BITFIELD_ENR_KEY, current_bitfield.as_ssz_bytes());
// replace the global version
*self.network_globals.local_enr.write() = self.discv5.local_enr().clone();
Ok(())
}
/// Updates the `eth2` field of our local ENR.
pub fn update_eth2_enr(&mut self, enr_fork_id: EnrForkId) {
// to avoid having a reference to the spec constant, for the logging we assume
// FAR_FUTURE_EPOCH is u64::max_value()
let next_fork_epoch_log = if enr_fork_id.next_fork_epoch == u64::max_value() {
String::from("No other fork")
} else {
format!("{:?}", enr_fork_id.next_fork_epoch)
};
info!(self.log, "Updating the ENR fork version";
"fork_digest" => format!("{:?}", enr_fork_id.fork_digest),
"next_fork_version" => format!("{:?}", enr_fork_id.next_fork_version),
"next_fork_epoch" => next_fork_epoch_log,
);
let _ = self
.discv5
.enr_insert(ETH2_ENR_KEY.into(), enr_fork_id.as_ssz_bytes())
.map_err(|e| {
warn!(
self.log,
"Could not update eth2 ENR field";
"error" => format!("{:?}", e)
)
});
// replace the global version with discovery version
*self.network_globals.local_enr.write() = self.discv5.local_enr().clone();
}
/* Internal Functions */
/// Consume the discovery queue and initiate queries when applicable.
///
/// This also sanitizes the queue removing out-dated queries.
fn process_queue(&mut self) {
// Sanitize the queue, removing any out-dated subnet queries
self.queued_queries.retain(|query| !query.expired());
// Check that we are within our query concurrency limit
while !self.at_capacity() && !self.queued_queries.is_empty() {
// consume and process the query queue
match self.queued_queries.pop_front() {
Some(QueryType::FindPeers) => {
// Only permit one FindPeers query at a time
if self.find_peer_active {
self.queued_queries.push_back(QueryType::FindPeers);
continue;
}
// This is a regular request to find additional peers
debug!(self.log, "Searching for new peers");
self.find_peer_active = true;
self.start_query(QueryType::FindPeers, FIND_NODE_QUERY_CLOSEST_PEERS);
}
Some(QueryType::Subnet {
subnet_id,
min_ttl,
retries,
}) => {
// This query is for searching for peers of a particular subnet
self.start_subnet_query(subnet_id, min_ttl, retries);
}
None => {} // Queue is empty
}
}
}
// Returns a boolean indicating if we are currently processing the maximum number of
// concurrent queries or not.
fn at_capacity(&self) -> bool {
if self.active_queries.len() >= MAX_CONCURRENT_QUERIES {
true
} else {
false
}
}
/// Runs a discovery request for a given subnet_id if one already exists.
fn start_subnet_query(
&mut self,
subnet_id: SubnetId,
min_ttl: Option<Instant>,
retries: usize,
) {
// Determine if we have sufficient peers, which may make this discovery unnecessary.
let peers_on_subnet = self
.network_globals
.peers
.read()
.peers_on_subnet(subnet_id)
.count();
if peers_on_subnet > TARGET_SUBNET_PEERS {
trace!(self.log, "Discovery ignored";
"reason" => "Already connected to desired peers",
"connected_peers_on_subnet" => peers_on_subnet,
"target_subnet_peers" => TARGET_SUBNET_PEERS,
);
return;
}
let target_peers = TARGET_SUBNET_PEERS - peers_on_subnet;
debug!(self.log, "Searching for peers for subnet";
"subnet_id" => *subnet_id,
"connected_peers_on_subnet" => peers_on_subnet,
"target_subnet_peers" => TARGET_SUBNET_PEERS,
"peers_to_find" => target_peers,
"attempt" => retries,
);
// start the query, and update the queries map if necessary
let query = QueryType::Subnet {
subnet_id,
min_ttl,
retries,
};
self.start_query(query, target_peers);
}
/// Search for a specified number of new peers using the underlying discovery mechanism.
///
/// This can optionally search for peers for a given predicate. Regardless of the predicate
/// given, this will only search for peers on the same enr_fork_id as specified in the local
/// ENR.
fn start_query(&mut self, query: QueryType, target_peers: usize) {
// Generate a random target node id.
let random_node = NodeId::random();
let enr_fork_id = match self.local_enr().eth2() {
Ok(v) => v,
Err(e) => {
crit!(self.log, "Local ENR has no fork id"; "error" => e);
return;
}
};
// predicate for finding nodes with a matching fork
let eth2_fork_predicate = move |enr: &Enr| enr.eth2() == Ok(enr_fork_id.clone());
// General predicate
let predicate: Box<dyn Fn(&Enr) -> bool + Send> = match &query {
QueryType::FindPeers => Box::new(eth2_fork_predicate),
QueryType::Subnet { subnet_id, .. } => {
// build the subnet predicate as a combination of the eth2_fork_predicate and the
// subnet predicate
let subnet_predicate = subnet_predicate::<TSpec>(subnet_id.clone(), &self.log);
Box::new(move |enr: &Enr| eth2_fork_predicate(enr) && subnet_predicate(enr))
}
};
// Build the future
let query_future = self
.discv5
.find_node_predicate(random_node, predicate, target_peers)
.map(|v| QueryResult(query, v));
// Add the future to active queries, to be executed.
self.active_queries.push(Box::pin(query_future));
}
/// Drives the queries returning any results from completed queries.
fn poll_queries(&mut self, cx: &mut Context) -> Option<(Option<Instant>, Vec<Enr>)> {
while let Poll::Ready(Some(query_future)) = self.active_queries.poll_next_unpin(cx) {
match query_future.0 {
QueryType::FindPeers => {
self.find_peer_active = false;
match query_future.1 {
Ok(r) if r.is_empty() => {
debug!(self.log, "Discovery query yielded no results.");
}
Ok(r) => {
debug!(self.log, "Discovery query completed"; "peers_found" => r.len());
return Some((None, r));
}
Err(e) => {
warn!(self.log, "Discovery query failed"; "error" => e.to_string());
}
}
}
QueryType::Subnet {
subnet_id,
min_ttl,
retries,
} => {
match query_future.1 {
Ok(r) if r.is_empty() => {
debug!(self.log, "Subnet discovery query yielded no results."; "subnet_id" => *subnet_id, "retries" => retries);
}
Ok(r) => {
debug!(self.log, "Peer subnet discovery request completed"; "peers_found" => r.len(), "subnet_id" => *subnet_id);
// A subnet query has completed. Add back to the queue, incrementing retries.
self.add_subnet_query(subnet_id, min_ttl, retries + 1);
// Report the results back to the peer manager.
return Some((query_future.0.min_ttl(), r));
}
Err(e) => {
warn!(self.log,"Subnet Discovery query failed"; "subnet_id" => *subnet_id, "error" => e.to_string());
}
}
}
}
}
None
}
// Main execution loop to be driven by the peer manager.
pub fn poll(&mut self, cx: &mut Context) -> Poll<DiscoveryEvent> {
// Process the query queue
self.process_queue();
// Drive the queries and return any results from completed queries
if let Some((min_ttl, result)) = self.poll_queries(cx) {
// cache the found ENR's
for enr in result.iter().cloned() {
self.cached_enrs.put(enr.peer_id(), enr);
}
// return the result to the peer manager
return Poll::Ready(DiscoveryEvent::QueryResult(min_ttl, Box::new(result)));
}
// Process the server event stream
match self.event_stream {
EventStream::Awaiting(ref mut fut) => {
// Still awaiting the event stream, poll it
if let Poll::Ready(event_stream) = fut.poll_unpin(cx) {
match event_stream {
Ok(stream) => self.event_stream = EventStream::Present(stream),
Err(e) => {
slog::crit!(self.log, "Discv5 event stream failed"; "error" => e.to_string());
self.event_stream = EventStream::Failed;
}
}
}
}
EventStream::Failed => {} // ignore checking the stream
EventStream::Present(ref mut stream) => {
while let Ok(event) = stream.try_recv() {
match event {
// We filter out unwanted discv5 events here and only propagate useful results to
// the peer manager.
Discv5Event::Discovered(_enr) => {
// Peers that get discovered during a query but are not contactable or
// don't match a predicate can end up here. For debugging purposes we
// log these to see if we are unnecessarily dropping discovered peers
/*
if enr.eth2() == self.local_enr().eth2() {
trace!(self.log, "Peer found in process of query"; "peer_id" => format!("{}", enr.peer_id()), "tcp_socket" => enr.tcp_socket());
} else {
// this is temporary warning for debugging the DHT
warn!(self.log, "Found peer during discovery not on correct fork"; "peer_id" => format!("{}", enr.peer_id()), "tcp_socket" => enr.tcp_socket());
}
*/
}
Discv5Event::SocketUpdated(socket) => {
info!(self.log, "Address updated"; "ip" => format!("{}",socket.ip()), "udp_port" => format!("{}", socket.port()));
metrics::inc_counter(&metrics::ADDRESS_UPDATE_COUNT);
// Discv5 will have updated our local ENR. We save the updated version
// to disk.
let enr = self.discv5.local_enr();
enr::save_enr_to_disk(Path::new(&self.enr_dir), &enr, &self.log);
return Poll::Ready(DiscoveryEvent::SocketUpdated(socket));
}
_ => {} // Ignore all other discv5 server events
}
}
}
}
Poll::Pending
}
}

View File

@ -5,7 +5,7 @@ use super::*;
pub fn subnet_predicate<TSpec>( pub fn subnet_predicate<TSpec>(
subnet_id: SubnetId, subnet_id: SubnetId,
log: &slog::Logger, log: &slog::Logger,
) -> impl Fn(&Enr) -> bool + Send + 'static + Clone ) -> impl Fn(&Enr) -> bool + Send
where where
TSpec: EthSpec, TSpec: EthSpec,
{ {

View File

@ -1,27 +1,35 @@
//! Implementation of a Lighthouse's peer management system. //! Implementation of a Lighthouse's peer management system.
pub use self::peerdb::*; pub use self::peerdb::*;
use crate::metrics;
use crate::rpc::{MetaData, Protocol, RPCError, RPCResponseErrorCode}; use crate::rpc::{MetaData, Protocol, RPCError, RPCResponseErrorCode};
use crate::{NetworkGlobals, PeerId}; use crate::{error, metrics};
use crate::{Enr, EnrExt, NetworkConfig, NetworkGlobals, PeerId};
use futures::prelude::*; use futures::prelude::*;
use futures::Stream; use futures::Stream;
use hashset_delay::HashSetDelay; use hashset_delay::HashSetDelay;
use libp2p::core::multiaddr::Protocol as MProtocol;
use libp2p::identify::IdentifyInfo; use libp2p::identify::IdentifyInfo;
use slog::{crit, debug, error, warn}; use slog::{crit, debug, error};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::convert::TryInto; use std::{
use std::pin::Pin; net::SocketAddr,
use std::sync::Arc; pin::Pin,
use std::task::{Context, Poll}; sync::Arc,
use std::time::{Duration, Instant}; task::{Context, Poll},
use types::EthSpec; time::{Duration, Instant},
};
use types::{EthSpec, SubnetId};
pub use libp2p::core::{identity::Keypair, Multiaddr};
pub mod client; pub mod client;
pub mod discovery;
mod peer_info; mod peer_info;
mod peer_sync_status; mod peer_sync_status;
mod peerdb; mod peerdb;
use discovery::{Discovery, DiscoveryEvent};
pub use peer_info::{PeerConnectionStatus::*, PeerInfo}; pub use peer_info::{PeerConnectionStatus::*, PeerInfo};
pub use peer_sync_status::{PeerSyncStatus, SyncInfo}; pub use peer_sync_status::{PeerSyncStatus, SyncInfo};
/// The minimum reputation before a peer is disconnected. /// The minimum reputation before a peer is disconnected.
@ -33,18 +41,26 @@ const STATUS_INTERVAL: u64 = 300;
/// this time frame (Seconds) /// this time frame (Seconds)
const PING_INTERVAL: u64 = 30; const PING_INTERVAL: u64 = 30;
/// The heartbeat performs regular updates such as updating reputations and performing discovery
/// requests. This defines the interval in seconds.
const HEARTBEAT_INTERVAL: u64 = 30;
/// The main struct that handles peer's reputation and connection status. /// The main struct that handles peer's reputation and connection status.
pub struct PeerManager<TSpec: EthSpec> { pub struct PeerManager<TSpec: EthSpec> {
/// Storage of network globals to access the `PeerDB`. /// Storage of network globals to access the `PeerDB`.
network_globals: Arc<NetworkGlobals<TSpec>>, network_globals: Arc<NetworkGlobals<TSpec>>,
/// A queue of events that the `PeerManager` is waiting to produce. /// A queue of events that the `PeerManager` is waiting to produce.
events: SmallVec<[PeerManagerEvent; 5]>, events: SmallVec<[PeerManagerEvent; 16]>,
/// A collection of peers awaiting to be Ping'd. /// A collection of peers awaiting to be Ping'd.
ping_peers: HashSetDelay<PeerId>, ping_peers: HashSetDelay<PeerId>,
/// A collection of peers awaiting to be Status'd. /// A collection of peers awaiting to be Status'd.
status_peers: HashSetDelay<PeerId>, status_peers: HashSetDelay<PeerId>,
/// Last updated moment. /// The target number of peers we would like to connect to.
_last_updated: Instant, target_peers: usize,
/// The discovery service.
discovery: Discovery<TSpec>,
/// The heartbeat interval to perform routine maintenance.
heartbeat: tokio::time::Interval,
/// The logger associated with the `PeerManager`. /// The logger associated with the `PeerManager`.
log: slog::Logger, log: slog::Logger,
} }
@ -89,6 +105,10 @@ impl PeerAction {
/// The events that the `PeerManager` outputs (requests). /// The events that the `PeerManager` outputs (requests).
pub enum PeerManagerEvent { pub enum PeerManagerEvent {
/// Dial a PeerId.
Dial(PeerId),
/// Inform libp2p that our external socket addr has been updated.
SocketUpdated(Multiaddr),
/// Sends a STATUS to a peer. /// Sends a STATUS to a peer.
Status(PeerId), Status(PeerId),
/// Sends a PING to a peer. /// Sends a PING to a peer.
@ -100,99 +120,59 @@ pub enum PeerManagerEvent {
} }
impl<TSpec: EthSpec> PeerManager<TSpec> { impl<TSpec: EthSpec> PeerManager<TSpec> {
pub fn new(network_globals: Arc<NetworkGlobals<TSpec>>, log: &slog::Logger) -> Self { // NOTE: Must be run inside a tokio executor.
PeerManager { pub fn new(
local_key: &Keypair,
config: &NetworkConfig,
network_globals: Arc<NetworkGlobals<TSpec>>,
log: &slog::Logger,
) -> error::Result<Self> {
// start the discovery service
let mut discovery = Discovery::new(local_key, config, network_globals.clone(), log)?;
// start searching for peers
discovery.discover_peers();
let heartbeat = tokio::time::interval(tokio::time::Duration::from_secs(HEARTBEAT_INTERVAL));
Ok(PeerManager {
network_globals, network_globals,
events: SmallVec::new(), events: SmallVec::new(),
_last_updated: Instant::now(),
ping_peers: HashSetDelay::new(Duration::from_secs(PING_INTERVAL)), ping_peers: HashSetDelay::new(Duration::from_secs(PING_INTERVAL)),
status_peers: HashSetDelay::new(Duration::from_secs(STATUS_INTERVAL)), status_peers: HashSetDelay::new(Duration::from_secs(STATUS_INTERVAL)),
target_peers: config.max_peers, //TODO: Add support for target peers and max peers
discovery,
heartbeat,
log: log.clone(), log: log.clone(),
} })
} }
/* Public accessible functions */ /* Public accessible functions */
/// A ping request has been received. /* Discovery Requests */
// NOTE: The behaviour responds with a PONG automatically
// TODO: Update last seen
pub fn ping_request(&mut self, peer_id: &PeerId, seq: u64) {
if let Some(peer_info) = self.network_globals.peers.read().peer_info(peer_id) {
// received a ping
// reset the to-ping timer for this peer
debug!(self.log, "Received a ping request"; "peer_id" => peer_id.to_string(), "seq_no" => seq);
self.ping_peers.insert(peer_id.clone());
// if the sequence number is unknown send an update the meta data of the peer. /// Provides a reference to the underlying discovery service.
if let Some(meta_data) = &peer_info.meta_data { pub fn discovery(&self) -> &Discovery<TSpec> {
if meta_data.seq_number < seq { &self.discovery
debug!(self.log, "Requesting new metadata from peer";
"peer_id" => peer_id.to_string(), "known_seq_no" => meta_data.seq_number, "ping_seq_no" => seq);
self.events
.push(PeerManagerEvent::MetaData(peer_id.clone()));
}
} else {
// if we don't know the meta-data, request it
debug!(self.log, "Requesting first metadata from peer";
"peer_id" => peer_id.to_string());
self.events
.push(PeerManagerEvent::MetaData(peer_id.clone()));
}
} else {
crit!(self.log, "Received a PING from an unknown peer";
"peer_id" => peer_id.to_string());
}
} }
/// A PONG has been returned from a peer. /// Provides a mutable reference to the underlying discovery service.
// TODO: Update last seen pub fn discovery_mut(&mut self) -> &mut Discovery<TSpec> {
pub fn pong_response(&mut self, peer_id: &PeerId, seq: u64) { &mut self.discovery
if let Some(peer_info) = self.network_globals.peers.read().peer_info(peer_id) {
// received a pong
// if the sequence number is unknown send update the meta data of the peer.
if let Some(meta_data) = &peer_info.meta_data {
if meta_data.seq_number < seq {
debug!(self.log, "Requesting new metadata from peer";
"peer_id" => peer_id.to_string(), "known_seq_no" => meta_data.seq_number, "pong_seq_no" => seq);
self.events
.push(PeerManagerEvent::MetaData(peer_id.clone()));
}
} else {
// if we don't know the meta-data, request it
debug!(self.log, "Requesting first metadata from peer";
"peer_id" => peer_id.to_string());
self.events
.push(PeerManagerEvent::MetaData(peer_id.clone()));
}
} else {
crit!(self.log, "Received a PONG from an unknown peer"; "peer_id" => peer_id.to_string());
}
} }
/// Received a metadata response from a peer. /// A request to find peers on a given subnet.
// TODO: Update last seen pub fn discover_subnet_peers(&mut self, subnet_id: SubnetId, min_ttl: Option<Instant>) {
pub fn meta_data_response(&mut self, peer_id: &PeerId, meta_data: MetaData<TSpec>) { // Extend the time to maintain peers if required.
if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) { if let Some(min_ttl) = min_ttl {
if let Some(known_meta_data) = &peer_info.meta_data { self.network_globals
if known_meta_data.seq_number < meta_data.seq_number { .peers
debug!(self.log, "Updating peer's metadata"; .write()
"peer_id" => peer_id.to_string(), "known_seq_no" => known_meta_data.seq_number, "new_seq_no" => meta_data.seq_number); .extend_peers_on_subnet(subnet_id, min_ttl);
peer_info.meta_data = Some(meta_data);
} else {
debug!(self.log, "Received old metadata";
"peer_id" => peer_id.to_string(), "known_seq_no" => known_meta_data.seq_number, "new_seq_no" => meta_data.seq_number);
}
} else {
// we have no meta-data for this peer, update
debug!(self.log, "Obtained peer's metadata";
"peer_id" => peer_id.to_string(), "new_seq_no" => meta_data.seq_number);
peer_info.meta_data = Some(meta_data);
}
} else {
crit!(self.log, "Received METADATA from an unknown peer";
"peer_id" => peer_id.to_string());
} }
// request the subnet query from discovery
self.discovery.discover_subnet_peers(subnet_id, min_ttl);
} }
/// A STATUS message has been received from a peer. This resets the status timer. /// A STATUS message has been received from a peer. This resets the status timer.
@ -320,8 +300,158 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
self.report_peer(peer_id, peer_action); self.report_peer(peer_id, peer_action);
} }
/// A ping request has been received.
// NOTE: The behaviour responds with a PONG automatically
// TODO: Update last seen
pub fn ping_request(&mut self, peer_id: &PeerId, seq: u64) {
if let Some(peer_info) = self.network_globals.peers.read().peer_info(peer_id) {
// received a ping
// reset the to-ping timer for this peer
debug!(self.log, "Received a ping request"; "peer_id" => peer_id.to_string(), "seq_no" => seq);
self.ping_peers.insert(peer_id.clone());
// if the sequence number is unknown send an update the meta data of the peer.
if let Some(meta_data) = &peer_info.meta_data {
if meta_data.seq_number < seq {
debug!(self.log, "Requesting new metadata from peer";
"peer_id" => peer_id.to_string(), "known_seq_no" => meta_data.seq_number, "ping_seq_no" => seq);
self.events
.push(PeerManagerEvent::MetaData(peer_id.clone()));
}
} else {
// if we don't know the meta-data, request it
debug!(self.log, "Requesting first metadata from peer";
"peer_id" => peer_id.to_string());
self.events
.push(PeerManagerEvent::MetaData(peer_id.clone()));
}
} else {
crit!(self.log, "Received a PING from an unknown peer";
"peer_id" => peer_id.to_string());
}
}
/// A PONG has been returned from a peer.
// TODO: Update last seen
pub fn pong_response(&mut self, peer_id: &PeerId, seq: u64) {
if let Some(peer_info) = self.network_globals.peers.read().peer_info(peer_id) {
// received a pong
// if the sequence number is unknown send update the meta data of the peer.
if let Some(meta_data) = &peer_info.meta_data {
if meta_data.seq_number < seq {
debug!(self.log, "Requesting new metadata from peer";
"peer_id" => peer_id.to_string(), "known_seq_no" => meta_data.seq_number, "pong_seq_no" => seq);
self.events
.push(PeerManagerEvent::MetaData(peer_id.clone()));
}
} else {
// if we don't know the meta-data, request it
debug!(self.log, "Requesting first metadata from peer";
"peer_id" => peer_id.to_string());
self.events
.push(PeerManagerEvent::MetaData(peer_id.clone()));
}
} else {
crit!(self.log, "Received a PONG from an unknown peer"; "peer_id" => peer_id.to_string());
}
}
/// Received a metadata response from a peer.
// TODO: Update last seen
pub fn meta_data_response(&mut self, peer_id: &PeerId, meta_data: MetaData<TSpec>) {
if let Some(peer_info) = self.network_globals.peers.write().peer_info_mut(peer_id) {
if let Some(known_meta_data) = &peer_info.meta_data {
if known_meta_data.seq_number < meta_data.seq_number {
debug!(self.log, "Updating peer's metadata";
"peer_id" => peer_id.to_string(), "known_seq_no" => known_meta_data.seq_number, "new_seq_no" => meta_data.seq_number);
peer_info.meta_data = Some(meta_data);
} else {
debug!(self.log, "Received old metadata";
"peer_id" => peer_id.to_string(), "known_seq_no" => known_meta_data.seq_number, "new_seq_no" => meta_data.seq_number);
}
} else {
// we have no meta-data for this peer, update
debug!(self.log, "Obtained peer's metadata";
"peer_id" => peer_id.to_string(), "new_seq_no" => meta_data.seq_number);
peer_info.meta_data = Some(meta_data);
}
} else {
crit!(self.log, "Received METADATA from an unknown peer";
"peer_id" => peer_id.to_string());
}
}
// 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<Multiaddr> {
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).
let mut out_list = enr.multiaddr();
out_list.retain(|addr| {
addr.iter()
.find(|v| match v {
MProtocol::Udp(_) => true,
_ => false,
})
.is_none()
});
out_list
} else {
// PeerId is not known
Vec::new()
}
}
/* Internal functions */ /* 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));
}
/// Peers that have been returned by discovery requests are dialed here if they are suitable.
///
/// 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.
fn peers_discovered(&mut self, peers: Vec<Enr>, min_ttl: Option<Instant>) {
for enr in peers {
let peer_id = enr.peer_id();
// if we need more peers, attempt a connection
if self.network_globals.connected_or_dialing_peers() < self.target_peers
&& !self
.network_globals
.peers
.read()
.is_connected_or_dialing(&peer_id)
&& !self.network_globals.peers.read().peer_banned(&peer_id)
{
debug!(self.log, "Dialing discovered peer"; "peer_id"=> peer_id.to_string());
// TODO: Update output
// 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);
}
self.events.push(PeerManagerEvent::Dial(peer_id));
}
}
}
/// Registers a peer as connected. The `ingoing` parameter determines if the peer is being /// Registers a peer as connected. The `ingoing` parameter determines if the peer is being
/// dialed or connecting to us. /// dialed or connecting to us.
/// ///
@ -376,12 +506,11 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
/// ///
/// A banned(disconnected) peer that gets its rep above(below) MIN_REP_BEFORE_BAN is /// A banned(disconnected) peer that gets its rep above(below) MIN_REP_BEFORE_BAN is
/// now considered a disconnected(banned) peer. /// now considered a disconnected(banned) peer.
// TODO: Implement when reputation is added.
fn _update_reputations(&mut self) { fn _update_reputations(&mut self) {
/*
// avoid locking the peerdb too often // avoid locking the peerdb too often
// TODO: call this on a timer // TODO: call this on a timer
if self._last_updated.elapsed().as_secs() < 30 {
return;
}
let now = Instant::now(); let now = Instant::now();
@ -457,6 +586,28 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
} }
self._last_updated = Instant::now(); self._last_updated = Instant::now();
*/
}
/// The Peer manager's heartbeat maintains the peer count and maintains peer reputations.
///
/// It will request discovery queries if the peer count has not reached the desired number of
/// peers.
///
/// NOTE: Discovery will only add a new query if one isn't already queued.
fn heartbeat(&mut self) {
// TODO: Provide a back-off time for discovery queries. I.e Queue many initially, then only
// perform discoveries over a larger fixed interval. Perhaps one every 6 heartbeats
let peer_count = self.network_globals.connected_or_dialing_peers();
if peer_count < self.target_peers {
// If we need more peers, queue a discovery lookup.
self.discovery.discover_peers();
}
// TODO: If we have too many peers, remove peers that are not required for subnet
// validation.
// TODO: Perform peer reputation maintenance here
} }
} }
@ -464,6 +615,21 @@ impl<TSpec: EthSpec> Stream for PeerManager<TSpec> {
type Item = PeerManagerEvent; type Item = PeerManagerEvent;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// perform the heartbeat when necessary
while let Poll::Ready(Some(_)) = self.heartbeat.poll_next_unpin(cx) {
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(min_ttl, peers) => {
self.peers_discovered(*peers, min_ttl)
}
}
}
// poll the timeouts for pings and status' // poll the timeouts for pings and status'
loop { loop {
match self.ping_peers.poll_next_unpin(cx) { match self.ping_peers.poll_next_unpin(cx) {

View File

@ -106,6 +106,14 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
} }
} }
/// Returns true if the Peer is banned.
pub fn peer_banned(&self, peer_id: &PeerId) -> bool {
match self.peers.get(peer_id).map(|info| &info.connection_status) {
Some(status) => status.is_banned(),
None => false,
}
}
/// Gives the ids of all known connected peers. /// Gives the ids of all known connected peers.
pub fn connected_peers(&self) -> impl Iterator<Item = (&PeerId, &PeerInfo<TSpec>)> { pub fn connected_peers(&self) -> impl Iterator<Item = (&PeerId, &PeerInfo<TSpec>)> {
self.peers self.peers

View File

@ -4,7 +4,7 @@ use crate::rpc::methods::MetaData;
use crate::types::SyncState; use crate::types::SyncState;
use crate::Client; use crate::Client;
use crate::EnrExt; use crate::EnrExt;
use crate::{discovery::enr::Eth2Enr, Enr, GossipTopic, Multiaddr, PeerId}; use crate::{Enr, Eth2Enr, GossipTopic, Multiaddr, PeerId};
use parking_lot::RwLock; use parking_lot::RwLock;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::atomic::{AtomicU16, Ordering};

View File

@ -115,7 +115,7 @@ pub fn build_libp2p_instance(
#[allow(dead_code)] #[allow(dead_code)]
pub fn get_enr(node: &LibP2PService<E>) -> Enr { pub fn get_enr(node: &LibP2PService<E>) -> Enr {
let enr = node.swarm.discovery().local_enr().clone(); let enr = node.swarm.local_enr().clone();
enr enr
} }
@ -153,7 +153,7 @@ pub async fn build_node_pair(log: &slog::Logger) -> (Libp2pInstance, Libp2pInsta
let mut sender = build_libp2p_instance(vec![], None, sender_log); let mut sender = build_libp2p_instance(vec![], None, sender_log);
let mut receiver = build_libp2p_instance(vec![], None, receiver_log); let mut receiver = build_libp2p_instance(vec![], None, receiver_log);
let receiver_multiaddr = receiver.swarm.discovery().local_enr().clone().multiaddr()[1].clone(); let receiver_multiaddr = receiver.swarm.local_enr().multiaddr()[1].clone();
// let the two nodes set up listeners // let the two nodes set up listeners
let sender_fut = async { let sender_fut = async {

View File

@ -15,7 +15,7 @@ exit-future = "0.2.0"
[dependencies] [dependencies]
beacon_chain = { path = "../beacon_chain" } beacon_chain = { path = "../beacon_chain" }
store = { path = "../store" } store = { path = "../store" }
eth2-libp2p = { path = "../eth2-libp2p" } eth2_libp2p = { path = "../eth2_libp2p" }
hashset_delay = { path = "../../common/hashset_delay" } hashset_delay = { path = "../../common/hashset_delay" }
rest_types = { path = "../../common/rest_types" } rest_types = { path = "../../common/rest_types" }
types = { path = "../../consensus/types" } types = { path = "../../consensus/types" }

View File

@ -71,17 +71,20 @@ impl PartialEq for AttServiceMessage {
subnet_id: other_subnet_id, subnet_id: other_subnet_id,
min_ttl: other_min_ttl, min_ttl: other_min_ttl,
}, },
) => match (min_ttl, other_min_ttl) { ) => {
(Some(min_ttl_instant), Some(other_min_ttl_instant)) => { subnet_id == other_subnet_id
min_ttl_instant.saturating_duration_since(other_min_ttl_instant) && match (min_ttl, other_min_ttl) {
< DURATION_DIFFERENCE (Some(min_ttl_instant), Some(other_min_ttl_instant)) => {
&& other_min_ttl_instant.saturating_duration_since(min_ttl_instant) min_ttl_instant.saturating_duration_since(other_min_ttl_instant)
< DURATION_DIFFERENCE < DURATION_DIFFERENCE
&& subnet_id == other_subnet_id && other_min_ttl_instant.saturating_duration_since(min_ttl_instant)
} < DURATION_DIFFERENCE
(None, None) => subnet_id == other_subnet_id, }
_ => false, (None, None) => true,
}, (None, Some(_)) => true,
(Some(_), None) => true,
}
}
_ => false, _ => false,
} }
} }
@ -353,12 +356,12 @@ impl<T: BeaconChainTypes> AttestationService<T> {
*other_min_ttl = min_ttl; *other_min_ttl = min_ttl;
} }
} }
(None, Some(_)) => { (None, Some(_)) => {} // Keep the current one as it has an actual min_ttl
// Update the min_ttl to None, because the new message is longer-lived. (Some(min_ttl), None) => {
*other_min_ttl = None; // Update the request to include a min_ttl.
*other_min_ttl = Some(min_ttl);
} }
(Some(_), None) => {} // Don't replace this because the existing message is for a longer-lived peer. (None, None) => {} // Duplicate message, do nothing.
(None, None) => {} // Duplicate message, do nothing.
} }
is_duplicate = true; is_duplicate = true;
return; return;

View File

@ -11,7 +11,7 @@ use eth2_libp2p::{
rpc::{RPCResponseErrorCode, RequestId}, rpc::{RPCResponseErrorCode, RequestId},
Libp2pEvent, PeerRequestId, PubsubMessage, Request, Response, Libp2pEvent, PeerRequestId, PubsubMessage, Request, Response,
}; };
use eth2_libp2p::{BehaviourEvent, Enr, MessageId, NetworkGlobals, PeerId}; use eth2_libp2p::{BehaviourEvent, MessageId, NetworkGlobals, PeerId};
use futures::prelude::*; use futures::prelude::*;
use rest_types::ValidatorSubscription; use rest_types::ValidatorSubscription;
use slog::{debug, error, info, o, trace}; use slog::{debug, error, info, o, trace};
@ -136,7 +136,7 @@ fn spawn_service<T: BeaconChainTypes>(
// handle network shutdown // handle network shutdown
_ = (&mut exit_rx) => { _ = (&mut exit_rx) => {
// network thread is terminating // network thread is terminating
let enrs: Vec<Enr> = service.libp2p.swarm.enr_entries().cloned().collect(); let enrs = service.libp2p.swarm.enr_entries();
debug!( debug!(
service.log, service.log,
"Persisting DHT to store"; "Persisting DHT to store";

View File

@ -10,7 +10,7 @@ bls = { path = "../../crypto/bls" }
rest_types = { path = "../../common/rest_types" } rest_types = { path = "../../common/rest_types" }
beacon_chain = { path = "../beacon_chain" } beacon_chain = { path = "../beacon_chain" }
network = { path = "../network" } network = { path = "../network" }
eth2-libp2p = { path = "../eth2-libp2p" } eth2_libp2p = { path = "../eth2_libp2p" }
store = { path = "../store" } store = { path = "../store" }
version = { path = "../version" } version = { path = "../version" }
serde = { version = "1.0.110", features = ["derive"] } serde = { version = "1.0.110", features = ["derive"] }

View File

@ -82,6 +82,8 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
let db_path = client_config.create_db_path()?; let db_path = client_config.create_db_path()?;
let freezer_db_path_res = client_config.create_freezer_db_path(); let freezer_db_path_res = client_config.create_freezer_db_path();
let executor = context.executor.clone();
let builder = ClientBuilder::new(context.eth_spec_instance.clone()) let builder = ClientBuilder::new(context.eth_spec_instance.clone())
.runtime_context(context) .runtime_context(context)
.chain_spec(spec) .chain_spec(spec)
@ -119,6 +121,9 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
.system_time_slot_clock()? .system_time_slot_clock()?
.tee_event_handler(client_config.websocket_server.clone())?; .tee_event_handler(client_config.websocket_server.clone())?;
// Inject the executor into the discv5 network config.
client_config.network.discv5_config.executor = Some(Box::new(executor));
let builder = builder let builder = builder
.build_beacon_chain()? .build_beacon_chain()?
.network(&mut client_config.network)? .network(&mut client_config.network)?

View File

@ -28,7 +28,7 @@ deposit_contract = { path = "../common/deposit_contract" }
tree_hash = "0.1.0" tree_hash = "0.1.0"
tokio = { version = "0.2.21", features = ["full"] } tokio = { version = "0.2.21", features = ["full"] }
clap_utils = { path = "../common/clap_utils" } clap_utils = { path = "../common/clap_utils" }
eth2-libp2p = { path = "../beacon_node/eth2-libp2p" } eth2_libp2p = { path = "../beacon_node/eth2_libp2p" }
validator_dir = { path = "../common/validator_dir", features = ["insecure_keys"] } validator_dir = { path = "../common/validator_dir", features = ["insecure_keys"] }
rand = "0.7.2" rand = "0.7.2"
eth2_keystore = { path = "../crypto/eth2_keystore" } eth2_keystore = { path = "../crypto/eth2_keystore" }

View File

@ -21,3 +21,4 @@ slog-json = "2.3.0"
exit-future = "0.2.0" exit-future = "0.2.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
lighthouse_metrics = { path = "../../common/lighthouse_metrics" } lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
discv5 = "0.1.0-alpha.5"

View File

@ -126,3 +126,9 @@ impl TaskExecutor {
&self.log &self.log
} }
} }
impl discv5::Executor for TaskExecutor {
fn spawn(&self, future: std::pin::Pin<Box<dyn Future<Output = ()> + Send>>) {
self.spawn(future, "discv5")
}
}