From f3d05c15d159391aebdb4a88fca40dd61b2156e9 Mon Sep 17 00:00:00 2001 From: Age Manning Date: Fri, 19 Jun 2020 16:30:07 +1000 Subject: [PATCH] Lighthouse bootnode (#1265) * Initial bootnode structure * Add boot_node subcommand * Add bootnode subcommand * fmt corrections * Extend help message * Move boot_node crate * Update discv5 dep * Improve logging and boot-node logging Co-authored-by: Paul Hauner --- Cargo.lock | 188 ++++++++++-------- Cargo.toml | 3 + .../src/peer_manager/discovery/enr_ext.rs | 45 ++++- boot_node/Cargo.toml | 20 ++ boot_node/src/cli.rs | 60 ++++++ boot_node/src/config.rs | 102 ++++++++++ boot_node/src/lib.rs | 65 ++++++ boot_node/src/server.rs | 89 +++++++++ lighthouse/Cargo.toml | 1 + lighthouse/src/main.rs | 29 ++- 10 files changed, 509 insertions(+), 93 deletions(-) create mode 100644 boot_node/Cargo.toml create mode 100644 boot_node/src/cli.rs create mode 100644 boot_node/src/config.rs create mode 100644 boot_node/src/lib.rs create mode 100644 boot_node/src/server.rs diff --git a/Cargo.lock b/Cargo.lock index 59d4b6699..561f77b42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "adler32" -version = "1.1.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d" +checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" [[package]] name = "aes-ctr" @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "arc-swap" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" +checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" [[package]] name = "arrayref" @@ -225,14 +225,13 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.49" +version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c" +checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide", "object", "rustc-demangle", ] @@ -261,9 +260,9 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "base64" -version = "0.12.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67" +checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" [[package]] name = "beacon_chain" @@ -443,6 +442,25 @@ dependencies = [ "zeroize", ] +[[package]] +name = "boot_node" +version = "0.1.0" +dependencies = [ + "clap", + "discv5", + "eth2_libp2p", + "futures 0.3.5", + "log 0.4.8", + "logging", + "slog", + "slog-async", + "slog-scope", + "slog-stdlog", + "slog-term", + "sloggers", + "tokio 0.2.21", +] + [[package]] name = "bs58" version = "0.3.1" @@ -844,13 +862,12 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.2.3" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +checksum = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2" dependencies = [ "cfg-if", "crossbeam-utils", - "maybe-uninit", ] [[package]] @@ -992,9 +1009,9 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.8" +version = "0.99.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc655351f820d774679da6cdc23355a93de496867d8203496675162e17b1d671" +checksum = "2127768764f1556535c01b5326ef94bd60ff08dcfbdc544d53e69ed155610f5d" dependencies = [ "proc-macro2", "quote", @@ -1022,10 +1039,11 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.5" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" dependencies = [ + "cfg-if", "libc", "redox_users", "winapi 0.3.8", @@ -1076,9 +1094,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dtoa" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" +checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" [[package]] name = "ed25519-dalek" @@ -1136,7 +1154,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3cd1bccf1bd78eee44d89c0f81b60008b40153db2b99c0fc01abf353781e13" dependencies = [ - "base64 0.12.2", + "base64 0.12.1", "bs58", "ed25519-dalek", "hex 0.4.2", @@ -1261,7 +1279,7 @@ dependencies = [ name = "eth2_interop_keypairs" version = "0.2.0" dependencies = [ - "base64 0.12.2", + "base64 0.12.1", "eth2_hashing", "hex 0.4.2", "lazy_static", @@ -1304,7 +1322,7 @@ dependencies = [ name = "eth2_libp2p" version = "0.1.2" dependencies = [ - "base64 0.12.2", + "base64 0.12.1", "dirs", "discv5", "environment", @@ -1858,9 +1876,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.14" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] @@ -2174,9 +2192,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "js-sys" @@ -2620,6 +2638,7 @@ version = "0.1.2" dependencies = [ "account_manager", "beacon_node", + "boot_node", "clap", "clap_utils", "env_logger", @@ -2809,9 +2828,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" dependencies = [ "adler32", ] @@ -2855,7 +2874,7 @@ checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" dependencies = [ "log 0.4.8", "mio", - "miow 0.3.5", + "miow 0.3.4", "winapi 0.3.8", ] @@ -2884,9 +2903,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.5" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +checksum = "22dfdd1d51b2639a5abd17ed07005c3af05fb7a2a3b1a1d0d7af1000a520c1c7" dependencies = [ "socket2", "winapi 0.3.8", @@ -3072,9 +3091,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.43" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ "autocfg 1.0.0", "num-traits", @@ -3082,9 +3101,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.41" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" +checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" dependencies = [ "autocfg 1.0.0", "num-integer", @@ -3093,9 +3112,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ "autocfg 1.0.0", ] @@ -3112,9 +3131,9 @@ dependencies = [ [[package]] name = "object" -version = "0.20.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" +checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" [[package]] name = "once_cell" @@ -3127,9 +3146,9 @@ dependencies = [ [[package]] name = "oorandom" -version = "11.1.2" +version = "11.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c" +checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358" [[package]] name = "opaque-debug" @@ -3159,9 +3178,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.58" +version = "0.9.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +checksum = "7410fef80af8ac071d4f63755c0ab89ac3df0fd1ea91f1d1f37cf5cec4395990" dependencies = [ "autocfg 1.0.0", "cc", @@ -3306,18 +3325,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.22" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" +checksum = "ba3a1acf4a3e70849f8a673497ef984f043f95d2d8252dcdf74d54e6a1e47e8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.22" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" +checksum = "194e88048b71a3e02eb4ee36a6995fed9b8236c11a7bb9f7247a9d9835b3f265" dependencies = [ "proc-macro2", "quote", @@ -3387,9 +3406,9 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "proc-macro-nested" -version = "0.1.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" +checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" [[package]] name = "proc-macro2" @@ -3557,9 +3576,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ "proc-macro2", ] @@ -3694,11 +3713,10 @@ dependencies = [ [[package]] name = "rayon" -version = "1.3.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080" +checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" dependencies = [ - "autocfg 1.0.0", "crossbeam-deque", "either", "rayon-core", @@ -3706,9 +3724,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.7.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" +checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" dependencies = [ "crossbeam-deque", "crossbeam-queue", @@ -3790,9 +3808,9 @@ dependencies = [ [[package]] name = "remove_dir_all" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" dependencies = [ "winapi 0.3.8", ] @@ -3803,7 +3821,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" dependencies = [ - "base64 0.12.2", + "base64 0.12.1", "bytes 0.5.4", "encoding_rs", "futures-core", @@ -4157,18 +4175,18 @@ checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" [[package]] name = "serde" -version = "1.0.112" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243" +checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.112" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57" +checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" dependencies = [ "proc-macro2", "quote", @@ -4185,9 +4203,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.55" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" +checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" dependencies = [ "itoa", "ryu", @@ -4196,9 +4214,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76" +checksum = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573" dependencies = [ "proc-macro2", "quote", @@ -4219,9 +4237,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.13" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5" +checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" dependencies = [ "dtoa", "linked-hash-map", @@ -4712,9 +4730,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.31" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" dependencies = [ "proc-macro2", "quote", @@ -4723,9 +4741,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ "proc-macro2", "quote", @@ -4807,18 +4825,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.20" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.20" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479" dependencies = [ "proc-macro2", "quote", @@ -4940,12 +4958,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "tinyvec" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" - [[package]] name = "tokio" version = "0.1.22" @@ -5449,11 +5461,11 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.13" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "tinyvec", + "smallvec 1.4.0", ] [[package]] @@ -5587,9 +5599,9 @@ dependencies = [ [[package]] name = "vcpkg" -version = "0.2.10" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" +checksum = "55d1e41d56121e07f1e223db0a4def204e45c85425f6a16d462fd07c8d10d74c" [[package]] name = "vec_map" @@ -5785,7 +5797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a681e8d15deced7c510db88c59133d2eafa7b6298b6e91b545e2a3fed93b3fe" dependencies = [ "arrayvec 0.5.1", - "base64 0.12.2", + "base64 0.12.1", "derive_more", "ethabi", "ethereum-types", diff --git a/Cargo.toml b/Cargo.toml index e8aab0ad0..c007f43b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "account_manager", + "beacon_node", "beacon_node/beacon_chain", "beacon_node/client", @@ -13,6 +14,8 @@ members = [ "beacon_node/version", "beacon_node/websocket_server", + "boot_node", + "common/clap_utils", "common/compare_fields", "common/compare_fields_derive", diff --git a/beacon_node/eth2_libp2p/src/peer_manager/discovery/enr_ext.rs b/beacon_node/eth2_libp2p/src/peer_manager/discovery/enr_ext.rs index ba0af4d1e..42a693f05 100644 --- a/beacon_node/eth2_libp2p/src/peer_manager/discovery/enr_ext.rs +++ b/beacon_node/eth2_libp2p/src/peer_manager/discovery/enr_ext.rs @@ -12,6 +12,9 @@ pub trait EnrExt { /// Returns a list of multiaddrs if the ENR has an `ip` and either a `tcp` or `udp` key **or** an `ip6` and either a `tcp6` or `udp6`. /// The vector remains empty if these fields are not defined. fn multiaddr(&self) -> Vec; + + /// Returns the multiaddr with the `PeerId` prepended. + fn multiaddr_p2p(&self) -> Vec; } /// Extend ENR CombinedPublicKey for libp2p types. @@ -34,8 +37,6 @@ impl EnrExt for Enr { /// Returns a list of multiaddrs if the ENR has an `ip` and either a `tcp` or `udp` key **or** an `ip6` and either a `tcp6` or `udp6`. /// The vector remains empty if these fields are not defined. - /// - /// Note: Only available with the `libp2p` feature flag. fn multiaddr(&self) -> Vec { let mut multiaddrs: Vec = Vec::new(); if let Some(ip) = self.ip() { @@ -66,6 +67,46 @@ impl EnrExt for Enr { } multiaddrs } + + /// Returns a list of multiaddrs if the ENR has an `ip` and either a `tcp` or `udp` key **or** an `ip6` and either a `tcp6` or `udp6`. + /// The vector remains empty if these fields are not defined. + /// + /// This also prepends the `PeerId` into each multiaddr with the `P2p` protocol. + fn multiaddr_p2p(&self) -> Vec { + let peer_id = self.peer_id(); + let mut multiaddrs: Vec = Vec::new(); + if let Some(ip) = self.ip() { + if let Some(udp) = self.udp() { + let mut multiaddr: Multiaddr = ip.into(); + multiaddr.push(Protocol::Udp(udp)); + multiaddr.push(Protocol::P2p(peer_id.clone().into())); + multiaddrs.push(multiaddr); + } + + if let Some(tcp) = self.tcp() { + let mut multiaddr: Multiaddr = ip.into(); + multiaddr.push(Protocol::Tcp(tcp)); + multiaddr.push(Protocol::P2p(peer_id.clone().into())); + multiaddrs.push(multiaddr); + } + } + if let Some(ip6) = self.ip6() { + if let Some(udp6) = self.udp6() { + let mut multiaddr: Multiaddr = ip6.into(); + multiaddr.push(Protocol::Udp(udp6)); + multiaddr.push(Protocol::P2p(peer_id.clone().into())); + multiaddrs.push(multiaddr); + } + + if let Some(tcp6) = self.tcp6() { + let mut multiaddr: Multiaddr = ip6.into(); + multiaddr.push(Protocol::Tcp(tcp6)); + multiaddr.push(Protocol::P2p(peer_id.into())); + multiaddrs.push(multiaddr); + } + } + multiaddrs + } } impl CombinedKeyPublicExt for CombinedPublicKey { diff --git a/boot_node/Cargo.toml b/boot_node/Cargo.toml new file mode 100644 index 000000000..90f9f3840 --- /dev/null +++ b/boot_node/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "boot_node" +version = "0.1.0" +authors = ["Age Manning "] +edition = "2018" + +[dependencies] +clap = "2.33.0" +eth2_libp2p = { path = "../beacon_node/eth2_libp2p" } +slog = "2.5.2" +sloggers = "1.0.1" +tokio = "0.2.21" +log = "0.4.8" +slog-term = "2.6.0" +logging = { path = "../common/logging" } +slog-async = "2.5.0" +slog-scope = "4.3.0" +slog-stdlog = "4.0.0" +futures = "0.3.5" +discv5 = "0.1.0-alpha.5" diff --git a/boot_node/src/cli.rs b/boot_node/src/cli.rs new file mode 100644 index 000000000..ac700b5f1 --- /dev/null +++ b/boot_node/src/cli.rs @@ -0,0 +1,60 @@ +//! Simple logic for spawning a Lighthouse BootNode. + +use clap::{App, Arg}; + +// TODO: Add DOS prevention CLI params +pub fn cli_app<'a, 'b>() -> App<'a, 'b> { + App::new("boot_node") + .about("Start a special Lighthouse process that only serves as a discv5 boot-node. This + process will *not* import blocks or perform most typical beacon node functions. Instead, it + will simply run the discv5 service and assist nodes on the network to discover each other. + This is the recommended way to provide a network boot-node since it has a reduced attack + surface compared to a full beacon node.") + .settings(&[clap::AppSettings::ColoredHelp]) + .arg( + Arg::with_name("boot-node-enr-address") + .value_name("IP-ADDRESS") + .help("The external IP address/ DNS address to broadcast to other peers on how to reach this node. \ + If a DNS address is provided, the enr-address is set to the IP address it resolves to and \ + does not auto-update based on PONG responses in discovery.") + .required(true) + .takes_value(true), + ) + .arg( + Arg::with_name("port") + .value_name("PORT") + .help("The UDP port to listen on.") + .default_value("9000") + .takes_value(true), + ) + .arg( + Arg::with_name("listen-address") + .long("listen-address") + .value_name("ADDRESS") + .help("The address the bootnode will listen for UDP connections.") + .default_value("0.0.0.0") + .takes_value(true) + ) + .arg( + Arg::with_name("boot-nodes") + .long("boot-nodes") + .allow_hyphen_values(true) + .value_name("ENR-LIST/Multiaddr") + .help("One or more comma-delimited base64-encoded ENR's or multiaddr strings of peers to initially add to the local routing table") + .takes_value(true), + ) + .arg( + Arg::with_name("enr-port") + .long("enr-port") + .value_name("PORT") + .help("The UDP port of the boot node's ENR. This is the port that external peers will dial to reach this boot node. Set this only if the external port differs from the listening port.") + .takes_value(true), + ) + .arg( + Arg::with_name("enable-enr-auto-update") + .short("x") + .long("enable-enr-auto-update") + .help("Discovery can automatically update the node's local ENR with an external IP address and port as seen by other peers on the network. \ + This enables this feature.") + ) +} diff --git a/boot_node/src/config.rs b/boot_node/src/config.rs new file mode 100644 index 000000000..89bb44d4a --- /dev/null +++ b/boot_node/src/config.rs @@ -0,0 +1,102 @@ +use clap::ArgMatches; +use discv5::{enr::CombinedKey, Enr}; +use std::convert::TryFrom; +use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; + +/// A set of configuration parameters for the bootnode, established from CLI arguments. +pub struct BootNodeConfig { + pub listen_socket: SocketAddr, + // TODO: Generalise to multiaddr + pub boot_nodes: Vec, + pub local_enr: Enr, + pub local_key: CombinedKey, + pub auto_update: bool, +} + +impl TryFrom<&ArgMatches<'_>> for BootNodeConfig { + type Error = String; + + fn try_from(matches: &ArgMatches<'_>) -> Result { + let listen_address = matches + .value_of("listen-address") + .expect("required parameter") + .parse::() + .map_err(|_| format!("Invalid listening address"))?; + + let listen_port = matches + .value_of("port") + .expect("required parameter") + .parse::() + .map_err(|_| format!("Invalid listening port"))?; + + let boot_nodes = { + if let Some(boot_nodes) = matches.value_of("boot-nodes") { + boot_nodes + .split(',') + .map(|enr| enr.parse().map_err(|_| format!("Invalid ENR: {}", enr))) + .collect::, _>>()? + } else { + Vec::new() + } + }; + + let enr_port = { + if let Some(port) = matches.value_of("boot-node-enr-port") { + port.parse::() + .map_err(|_| format!("Invalid ENR port"))? + } else { + listen_port + } + }; + + let enr_address = { + let address_string = matches + .value_of("boot-node-enr-address") + .expect("required parameter"); + resolve_address(address_string.into(), enr_port)? + }; + + let auto_update = matches.is_present("enable-enr_auto_update"); + + // the address to listen on + let listen_socket = SocketAddr::new(listen_address.into(), enr_port); + + // Generate a new key and build a new ENR + let local_key = CombinedKey::generate_secp256k1(); + let local_enr = discv5::enr::EnrBuilder::new("v4") + .ip(enr_address) + .udp(enr_port) + .build(&local_key) + .map_err(|e| format!("Failed to build ENR: {:?}", e))?; + + Ok(BootNodeConfig { + listen_socket, + boot_nodes, + local_enr, + local_key, + auto_update, + }) + } +} + +/// Resolves an IP/DNS string to an IpAddr. +fn resolve_address(address_string: String, port: u16) -> Result { + match address_string.parse::() { + Ok(addr) => Ok(addr), // valid IpAddr + Err(_) => { + let mut addr = address_string.clone(); + // Appending enr-port to the dns hostname to appease `to_socket_addrs()` parsing. + addr.push_str(&format!(":{}", port.to_string())); + // `to_socket_addr()` does the dns resolution + // Note: `to_socket_addrs()` is a blocking call + addr.to_socket_addrs() + .map(|mut resolved_addrs| + // Pick the first ip from the list of resolved addresses + resolved_addrs + .next() + .map(|a| a.ip()) + .ok_or_else(|| format!("Resolved dns addr contains no entries"))) + .map_err(|_| format!("Failed to parse enr-address: {}", address_string))? + } + } +} diff --git a/boot_node/src/lib.rs b/boot_node/src/lib.rs new file mode 100644 index 000000000..41d1baa80 --- /dev/null +++ b/boot_node/src/lib.rs @@ -0,0 +1,65 @@ +//! Creates a simple DISCV5 server which can be used to bootstrap an Eth2 network. +use clap::ArgMatches; +use slog; +use slog::{o, Drain, Level, Logger}; + +use std::convert::TryFrom; +mod cli; +mod config; +mod server; +pub use cli::cli_app; +use config::BootNodeConfig; + +/// Run the bootnode given the CLI configuration. +pub fn run(matches: &ArgMatches<'_>, debug_level: String) { + let debug_level = match debug_level.as_str() { + "trace" => log::Level::Trace, + "debug" => log::Level::Debug, + "info" => log::Level::Info, + "warn" => log::Level::Warn, + "error" => log::Level::Error, + "crit" => log::Level::Error, + _ => unreachable!(), + }; + + // Setting up the initial logger format and building it. + let drain = { + let decorator = slog_term::TermDecorator::new().build(); + let decorator = logging::AlignedTermDecorator::new(decorator, logging::MAX_MESSAGE_WIDTH); + let drain = slog_term::FullFormat::new(decorator).build().fuse(); + slog_async::Async::new(drain).build() + }; + + let drain = match debug_level { + log::Level::Info => drain.filter_level(Level::Info), + log::Level::Debug => drain.filter_level(Level::Debug), + log::Level::Trace => drain.filter_level(Level::Trace), + log::Level::Warn => drain.filter_level(Level::Warning), + log::Level::Error => drain.filter_level(Level::Error), + }; + + let logger = Logger::root(drain.fuse(), o!()); + let _scope_guard = slog_scope::set_global_logger(logger); + let _log_guard = slog_stdlog::init_with_level(debug_level).unwrap(); + + // Run the main function emitting any errors + if let Err(e) = main(matches, slog_scope::logger()) { + slog::crit!(slog_scope::logger(), "{}", e); + } +} + +fn main(matches: &ArgMatches<'_>, log: slog::Logger) -> Result<(), String> { + // Builds a custom executor for the bootnode + let mut runtime = tokio::runtime::Builder::new() + .threaded_scheduler() + .enable_all() + .build() + .map_err(|e| format!("Failed to build runtime: {}", e))?; + + // parse the CLI args into a useable config + let config = BootNodeConfig::try_from(matches)?; + + // Run the boot node + runtime.block_on(server::run(config, log)); + Ok(()) +} diff --git a/boot_node/src/server.rs b/boot_node/src/server.rs new file mode 100644 index 000000000..162b30888 --- /dev/null +++ b/boot_node/src/server.rs @@ -0,0 +1,89 @@ +//! The main bootnode server execution. + +use super::BootNodeConfig; +use discv5::{Discv5, Discv5ConfigBuilder, Discv5Event}; +use eth2_libp2p::EnrExt; +use futures::prelude::*; +use slog::info; + +pub async fn run(config: BootNodeConfig, log: slog::Logger) { + // Print out useful information about the generated ENR + + let enr_socket = config.local_enr.udp_socket().expect("Enr has a UDP socket"); + info!(log, "Configuration parameters"; "listening_address" => format!("{}:{}", config.listen_socket.ip(), config.listen_socket.port()), "broadcast_address" => format!("{}:{}",enr_socket.ip(), enr_socket.port())); + + info!(log, "Identity established"; "peer_id" => config.local_enr.peer_id().to_string(), "node_id" => config.local_enr.node_id().to_string()); + + // build the contactable multiaddr list, adding the p2p protocol + info!(log, "Contact information"; "enr" => config.local_enr.to_base64()); + info!(log, "Contact information"; "multiaddrs" => format!("{:?}", config.local_enr.multiaddr_p2p())); + + // Build the discv5 server + + // default configuration with packet filtering + + let discv5_config = { + let mut builder = Discv5ConfigBuilder::new(); + builder.enable_packet_filter(); + if !config.auto_update { + builder.disable_enr_update(); + } + builder.build() + }; + + // construct the discv5 server + let mut discv5 = Discv5::new(config.local_enr, config.local_key, discv5_config).unwrap(); + + // If there are any bootnodes add them to the routing table + for enr in config.boot_nodes { + info!(log, "Adding bootnode"; "address" => format!("{:?}", enr.udp_socket()), "peer_id" => enr.peer_id().to_string(), "node_id" => enr.node_id().to_string()); + if let Err(e) = discv5.add_enr(enr) { + slog::warn!(log, "Failed adding ENR"; "error" => e.to_string()); + } + } + + // start the server + discv5.start(config.listen_socket); + + // if there are peers in the local routing table, establish a session by running a query + if !discv5.table_entries_id().is_empty() { + info!(log, "Executing bootstrap query..."); + let _ = discv5.find_node(discv5::enr::NodeId::random()).await; + } + + // respond with metrics every 10 seconds + let mut metric_interval = tokio::time::interval(tokio::time::Duration::from_secs(10)); + + // get an event stream + let mut event_stream = match discv5.event_stream().await { + Ok(stream) => stream, + Err(e) => { + slog::crit!(log, "Failed to obtain event stream"; "error" => e.to_string()); + return; + } + }; + + // listen for events + loop { + tokio::select! { + _ = metric_interval.next() => { + // display server metrics + let metrics = discv5.metrics(); + info!(log, "Server metrics"; "connected_peers" => discv5.connected_peers(), "active_sessions" => metrics.active_sessions, "requests/s" => format!("{:.2}", metrics.unsolicited_requests_per_second)); + } + Some(event) = event_stream.recv() => { + match event { + Discv5Event::Discovered(_enr) => { + // An ENR has bee obtained by the server + // Ignore these events here + } + Discv5Event::EnrAdded { .. } => {} // Ignore + Discv5Event::NodeInserted { .. } => {} // Ignore + Discv5Event::SocketUpdated(socket_addr) => { + info!(log, "External socket address updated"; "socket_addr" => format!("{:?}", socket_addr)); + } + } + } + } + } +} diff --git a/lighthouse/Cargo.toml b/lighthouse/Cargo.toml index f4abcddb6..a3100d94b 100644 --- a/lighthouse/Cargo.toml +++ b/lighthouse/Cargo.toml @@ -19,6 +19,7 @@ logging = { path = "../common/logging" } slog-term = "2.5.0" slog-async = "2.5.0" environment = { path = "./environment" } +boot_node = { path = "../boot_node" } futures = "0.3.5" validator_client = { "path" = "../validator_client" } account_manager = { "path" = "../account_manager" } diff --git a/lighthouse/src/main.rs b/lighthouse/src/main.rs index 2a8b98855..bb6529e0a 100644 --- a/lighthouse/src/main.rs +++ b/lighthouse/src/main.rs @@ -18,13 +18,11 @@ pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml"; pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml"; fn main() { - // Debugging output for libp2p and external crates. - Builder::from_env(Env::default()).init(); - // Parse the CLI parameters. let matches = App::new("Lighthouse") .version(crate_version!()) .author("Sigma Prime ") + .setting(clap::AppSettings::ColoredHelp) .about( "Ethereum 2.0 client by Sigma Prime. Provides a full-featured beacon \ node, a validator client and utilities for managing validator accounts.", @@ -40,6 +38,13 @@ fn main() { .global(true) .default_value("mainnet"), ) + .arg( + Arg::with_name("env_log") + .short("l") + .help("Enables environment logging giving access to sub-protocol logs such as discv5 and libp2p", + ) + .takes_value(false), + ) .arg( Arg::with_name("logfile") .long("logfile") @@ -64,6 +69,7 @@ fn main() { .help("The verbosity level for emitting logs.") .takes_value(true) .possible_values(&["info", "debug", "trace", "warn", "error", "crit"]) + .global(true) .default_value("info"), ) .arg( @@ -89,10 +95,27 @@ fn main() { .global(true), ) .subcommand(beacon_node::cli_app()) + .subcommand(boot_node::cli_app()) .subcommand(validator_client::cli_app()) .subcommand(account_manager::cli_app()) .get_matches(); + // boot node subcommand circumvents the environment + if let Some(bootnode_matches) = matches.subcommand_matches("boot_node") { + // The bootnode uses the main debug-level flag + let debug_info = matches + .value_of("debug-level") + .expect("Debug-level must be present") + .into(); + boot_node::run(bootnode_matches, debug_info); + return; + } + + // Debugging output for libp2p and external crates. + if matches.is_present("env_log") { + Builder::from_env(Env::default()).init(); + } + macro_rules! run_with_spec { ($env_builder: expr) => { run($env_builder, &matches)