From 3cafd586cf2be4984067a9074befbd1d5196a91b Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Wed, 3 Apr 2024 14:27:46 +0200 Subject: [PATCH] feat(discv5): config via cli (#7394) --- Cargo.lock | 3 + crates/net/discv4/src/lib.rs | 10 +++ crates/net/discv5/src/config.rs | 2 +- crates/net/discv5/src/enr.rs | 4 +- crates/net/discv5/src/lib.rs | 3 +- crates/net/network/Cargo.toml | 2 + crates/net/network/src/config.rs | 76 ++++++++++++++++++++++- crates/node-core/Cargo.toml | 3 + crates/node-core/src/args/network_args.rs | 32 +++++++++- crates/node-core/src/node_config.rs | 26 +++++++- crates/primitives/src/lib.rs | 2 +- crates/primitives/src/net.rs | 2 +- 12 files changed, 149 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d74ed3ae..7f286483a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6380,6 +6380,7 @@ dependencies = [ "auto_impl", "criterion", "derive_more", + "discv5", "enr", "ethers-core", "ethers-providers", @@ -6395,6 +6396,7 @@ dependencies = [ "pprof", "rand 0.8.5", "reth-discv4", + "reth-discv5", "reth-dns-discovery", "reth-ecies", "reth-eth-wire", @@ -6515,6 +6517,7 @@ dependencies = [ "const-str", "derive_more", "dirs-next", + "discv5", "eyre", "futures", "humantime", diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index d144cdef8..b919c8277 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -94,6 +94,16 @@ pub const DEFAULT_DISCOVERY_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); /// Note: the default TCP port is the same. pub const DEFAULT_DISCOVERY_PORT: u16 = 30303; +/// The default address for discv5 via UDP. +/// +/// Note: the default TCP address is the same. +pub const DEFAULT_DISCOVERY_V5_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); + +/// The default port for discv5 via UDP. +/// +/// Default is port 9000. +pub const DEFAULT_DISCOVERY_V5_PORT: u16 = 9000; + /// The default address for discv4 via UDP: "0.0.0.0:30303" /// /// Note: The default TCP address is the same. diff --git a/crates/net/discv5/src/config.rs b/crates/net/discv5/src/config.rs index 1fe25c482..fb47276ed 100644 --- a/crates/net/discv5/src/config.rs +++ b/crates/net/discv5/src/config.rs @@ -100,7 +100,7 @@ impl ConfigBuilder { } /// Adds boot nodes in the form a list of [`NodeRecord`]s, parsed enodes. - pub fn add_unsigned_boot_nodes(mut self, enodes: Vec) -> Self { + pub fn add_unsigned_boot_nodes(mut self, enodes: impl Iterator) -> Self { for node in enodes { if let Ok(node) = BootNode::from_unsigned(node) { self.bootstrap_nodes.insert(node); diff --git a/crates/net/discv5/src/enr.rs b/crates/net/discv5/src/enr.rs index 51323b040..502233794 100644 --- a/crates/net/discv5/src/enr.rs +++ b/crates/net/discv5/src/enr.rs @@ -60,7 +60,7 @@ impl From for Enr { mod tests { use alloy_rlp::Encodable; use discv5::enr::{CombinedKey, EnrKey}; - use reth_primitives::{pk_to_id, Hardfork, NodeRecord, MAINNET}; + use reth_primitives::{Hardfork, NodeRecord, MAINNET}; use super::*; @@ -105,7 +105,7 @@ mod tests { address: IP.parse().unwrap(), tcp_port: TCP_PORT, udp_port: UDP_PORT, - id: pk_to_id(&enr.public_key()) + id: pk2id(&enr.public_key()) }, node_record ) diff --git a/crates/net/discv5/src/lib.rs b/crates/net/discv5/src/lib.rs index 360bed68b..5f0809cb2 100644 --- a/crates/net/discv5/src/lib.rs +++ b/crates/net/discv5/src/lib.rs @@ -376,8 +376,7 @@ impl Discv5 { // node has been inserted into kbuckets - // `replaced` covers `reth_discv4::DiscoveryUpdate::Removed(_)` .. but we can't get - // a `PeerId` from a `NodeId` + // `replaced` partly covers `reth_discv4::DiscoveryUpdate::Removed(_)` self.metrics.discovered_peers.increment_kbucket_insertions(1); diff --git a/crates/net/network/Cargo.toml b/crates/net/network/Cargo.toml index 7332fb9ea..1df806764 100644 --- a/crates/net/network/Cargo.toml +++ b/crates/net/network/Cargo.toml @@ -18,6 +18,7 @@ reth-primitives.workspace = true reth-net-common.workspace = true reth-network-api.workspace = true reth-discv4.workspace = true +reth-discv5.workspace = true reth-dns-discovery.workspace = true reth-eth-wire.workspace = true reth-ecies.workspace = true @@ -30,6 +31,7 @@ reth-tokio-util.workspace = true # ethereum enr = { workspace = true, features = ["rust-secp256k1"], optional = true } alloy-rlp.workspace = true +discv5.workspace = true # async/futures futures.workspace = true diff --git a/crates/net/network/src/config.rs b/crates/net/network/src/config.rs index 39f12b298..85812dee5 100644 --- a/crates/net/network/src/config.rs +++ b/crates/net/network/src/config.rs @@ -9,10 +9,12 @@ use crate::{ NetworkHandle, NetworkManager, }; use reth_discv4::{Discv4Config, Discv4ConfigBuilder, DEFAULT_DISCOVERY_ADDRESS}; +use reth_discv5::config::OPSTACK; use reth_dns_discovery::DnsDiscoveryConfig; use reth_eth_wire::{HelloMessage, HelloMessageWithProtocols, Status}; use reth_primitives::{ - mainnet_nodes, pk2id, sepolia_nodes, ChainSpec, ForkFilter, Head, NodeRecord, PeerId, MAINNET, + mainnet_nodes, pk2id, sepolia_nodes, Chain, ChainSpec, ForkFilter, Head, NamedChain, + NodeRecord, PeerId, MAINNET, }; use reth_provider::{BlockReader, HeaderProvider}; use reth_tasks::{TaskSpawner, TokioTaskExecutor}; @@ -43,6 +45,8 @@ pub struct NetworkConfig { pub dns_discovery_config: Option, /// How to set up discovery. pub discovery_v4_config: Option, + /// How to set up discovery version 5. + pub discovery_v5_config: Option, /// Address to use for discovery pub discovery_addr: SocketAddr, /// Address to listen for incoming connections @@ -110,6 +114,54 @@ impl NetworkConfig { self } + /// Sets the config to use for the discovery v5 protocol, with help of the + /// [`reth_discv5::ConfigBuilder`]. + /// ``` + /// use reth_network::NetworkConfigBuilder; + /// use secp256k1::{rand::thread_rng, SecretKey}; + /// + /// let sk = SecretKey::new(&mut thread_rng()); + /// let network_config = NetworkConfigBuilder::new(sk).build(()); + /// let fork_id = network_config.status.forkid; + /// let network_config = network_config + /// .discovery_v5_with_config_builder(|builder| builder.fork(b"eth", fork_id).build()); + /// ``` + + pub fn discovery_v5_with_config_builder( + self, + f: impl FnOnce(reth_discv5::ConfigBuilder) -> reth_discv5::Config, + ) -> Self { + let rlpx_port = self.listener_addr.port(); + let chain = self.chain_spec.chain; + let fork_id = self.status.forkid; + let boot_nodes = self.boot_nodes.clone(); + + let mut builder = + reth_discv5::Config::builder(rlpx_port).add_unsigned_boot_nodes(boot_nodes.into_iter()); // todo: store discv5 peers in separate file + + if chain.is_optimism() { + builder = builder.fork(OPSTACK, fork_id) + } + + if chain == Chain::optimism_mainnet() || chain == Chain::base_mainnet() { + builder = builder.add_optimism_mainnet_boot_nodes() + } else if chain == Chain::from_named(NamedChain::OptimismSepolia) || + chain == Chain::from_named(NamedChain::BaseSepolia) + { + builder = builder.add_optimism_sepolia_boot_nodes() + } + + self.set_discovery_v5(f(builder)) + } + + /// Sets the config to use for the discovery v5 protocol. + + pub fn set_discovery_v5(mut self, discv5_config: reth_discv5::Config) -> Self { + self.discovery_v5_config = Some(discv5_config); + self.discovery_addr = self.discovery_v5_config.as_ref().unwrap().discovery_socket(); + self + } + /// Sets the address for the incoming connection listener. pub fn set_listener_addr(mut self, listener_addr: SocketAddr) -> Self { self.listener_addr = listener_addr; @@ -142,8 +194,10 @@ pub struct NetworkConfigBuilder { secret_key: SecretKey, /// How to configure discovery over DNS. dns_discovery_config: Option, - /// How to set up discovery. + /// How to set up discovery version 4. discovery_v4_builder: Option, + /// Whether to enable discovery version 5. Disabled by default. + enable_discovery_v5: bool, /// All boot nodes to start network discovery with. boot_nodes: HashSet, /// Address to use for discovery @@ -198,6 +252,7 @@ impl NetworkConfigBuilder { secret_key, dns_discovery_config: Some(Default::default()), discovery_v4_builder: Some(Default::default()), + enable_discovery_v5: false, boot_nodes: Default::default(), discovery_addr: None, listener_addr: None, @@ -326,11 +381,18 @@ impl NetworkConfigBuilder { } /// Sets the discv4 config to use. + // pub fn discovery(mut self, builder: Discv4ConfigBuilder) -> Self { self.discovery_v4_builder = Some(builder); self } + /// Allows discv5 discovery. + pub fn discovery_v5(mut self) -> Self { + self.enable_discovery_v5 = true; + self + } + /// Sets the dns discovery config to use. pub fn dns_discovery(mut self, config: DnsDiscoveryConfig) -> Self { self.dns_discovery_config = Some(config); @@ -379,6 +441,12 @@ impl NetworkConfigBuilder { self } + /// Enable the Discv5 discovery. + pub fn enable_discv5_discovery(mut self) -> Self { + self.enable_discovery_v5 = true; + self + } + /// Disable the DNS discovery if the given condition is true. pub fn disable_dns_discovery_if(self, disable: bool) -> Self { if disable { @@ -442,6 +510,7 @@ impl NetworkConfigBuilder { secret_key, mut dns_discovery_config, discovery_v4_builder, + enable_discovery_v5: _, boot_nodes, discovery_addr, listener_addr, @@ -497,6 +566,7 @@ impl NetworkConfigBuilder { boot_nodes, dns_discovery_config, discovery_v4_config: discovery_v4_builder.map(|builder| builder.build()), + discovery_v5_config: None, discovery_addr: discovery_addr.unwrap_or(DEFAULT_DISCOVERY_ADDRESS), listener_addr, peers_config: peers_config.unwrap_or_default(), @@ -546,7 +616,7 @@ mod tests { use super::*; use rand::thread_rng; use reth_dns_discovery::tree::LinkEntry; - use reth_primitives::{Chain, ForkHash}; + use reth_primitives::ForkHash; use reth_provider::test_utils::NoopProvider; use std::collections::BTreeMap; diff --git a/crates/node-core/Cargo.toml b/crates/node-core/Cargo.toml index 022a824f5..7dfaa9c44 100644 --- a/crates/node-core/Cargo.toml +++ b/crates/node-core/Cargo.toml @@ -41,6 +41,9 @@ reth-prune.workspace = true reth-blockchain-tree.workspace = true reth-static-file.workspace = true +# ethereum +discv5.workspace = true + # async tokio.workspace = true diff --git a/crates/node-core/src/args/network_args.rs b/crates/node-core/src/args/network_args.rs index 23670be8c..88f448ee9 100644 --- a/crates/node-core/src/args/network_args.rs +++ b/crates/node-core/src/args/network_args.rs @@ -3,7 +3,10 @@ use crate::version::P2P_CLIENT_VERSION; use clap::Args; use reth_config::Config; -use reth_discv4::{DEFAULT_DISCOVERY_ADDR, DEFAULT_DISCOVERY_PORT}; +use reth_discv4::{ + DEFAULT_DISCOVERY_ADDR, DEFAULT_DISCOVERY_PORT, DEFAULT_DISCOVERY_V5_ADDR, + DEFAULT_DISCOVERY_V5_PORT, +}; use reth_net_nat::NatResolver; use reth_network::{ transactions::{ @@ -211,13 +214,27 @@ pub struct DiscoveryArgs { #[arg(long, conflicts_with = "disable_discovery")] pub disable_discv4_discovery: bool, - /// The UDP address to use for P2P discovery/networking + /// Enable Discv5 discovery. + #[arg(long, conflicts_with = "disable_discovery")] + pub enable_discv5_discovery: bool, + + /// The UDP address to use for devp2p peer discovery version 4. #[arg(id = "discovery.addr", long = "discovery.addr", value_name = "DISCOVERY_ADDR", default_value_t = DEFAULT_DISCOVERY_ADDR)] pub addr: IpAddr, - /// The UDP port to use for P2P discovery/networking + /// The UDP port to use for devp2p peer discovery version 4. #[arg(id = "discovery.port", long = "discovery.port", value_name = "DISCOVERY_PORT", default_value_t = DEFAULT_DISCOVERY_PORT)] pub port: u16, + + /// The UDP address to use for devp2p peer discovery version 5. + #[arg(id = "discovery.v5.addr", long = "discovery.v5.addr", value_name = "DISCOVERY_V5_ADDR", + default_value_t = DEFAULT_DISCOVERY_V5_ADDR)] + pub discv5_addr: IpAddr, + + /// The UDP port to use for devp2p peer discovery version 5. + #[arg(id = "discovery.v5.port", long = "discovery.v5.port", value_name = "DISCOVERY_V5_PORT", + default_value_t = DEFAULT_DISCOVERY_V5_PORT)] + pub discv5_port: u16, } impl DiscoveryArgs { @@ -233,6 +250,11 @@ impl DiscoveryArgs { if self.disable_discovery || self.disable_discv4_discovery { network_config_builder = network_config_builder.disable_discv4_discovery(); } + + if !self.disable_discovery && (self.enable_discv5_discovery || cfg!(feature = "optimism")) { + network_config_builder = network_config_builder.enable_discv5_discovery(); + } + network_config_builder } @@ -250,8 +272,11 @@ impl Default for DiscoveryArgs { disable_discovery: false, disable_dns_discovery: false, disable_discv4_discovery: false, + enable_discv5_discovery: cfg!(feature = "optimism"), addr: DEFAULT_DISCOVERY_ADDR, port: DEFAULT_DISCOVERY_PORT, + discv5_addr: DEFAULT_DISCOVERY_V5_ADDR, + discv5_port: DEFAULT_DISCOVERY_V5_PORT, } } } @@ -315,6 +340,7 @@ mod tests { ); } + #[cfg(not(feature = "optimism"))] #[test] fn network_args_default_sanity_test() { let default_args = NetworkArgs::default(); diff --git a/crates/node-core/src/node_config.rs b/crates/node-core/src/node_config.rs index 4078c29d9..aff0ee4c3 100644 --- a/crates/node-core/src/node_config.rs +++ b/crates/node-core/src/node_config.rs @@ -2,14 +2,15 @@ use crate::{ args::{ - get_secret_key, DatabaseArgs, DebugArgs, DevArgs, NetworkArgs, PayloadBuilderArgs, - PruningArgs, RpcServerArgs, TxPoolArgs, + get_secret_key, DatabaseArgs, DebugArgs, DevArgs, DiscoveryArgs, NetworkArgs, + PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs, }, cli::config::RethTransactionPoolConfig, dirs::{ChainPath, DataDirPath}, metrics::prometheus_exporter, utils::{get_single_header, write_peers_to_file}, }; +use discv5::ListenConfig; use metrics_exporter_prometheus::PrometheusHandle; use once_cell::sync::Lazy; use reth_auto_seal_consensus::{AutoSealConsensus, MiningMode}; @@ -162,6 +163,7 @@ pub struct NodeConfig { /// /// Changes to the following port numbers: /// - DISCOVERY_PORT: default + `instance` - 1 + /// - DISCOVERY_V5_PORT: default + `instance` - 1 /// - AUTH_PORT: default + `instance` * 100 - 100 /// - HTTP_RPC_PORT: default - `instance` + 1 /// - WS_RPC_PORT: default + `instance` * 2 - 2 @@ -768,7 +770,25 @@ impl NodeConfig { self.network.discovery.port + self.instance - 1, )); - cfg_builder.build(client) + let config = cfg_builder.build(client); + + if !self.network.discovery.enable_discv5_discovery { + return config + } + // work around since discv5 config builder can't be integrated into network config builder + // due to unsatisfied trait bounds + config.discovery_v5_with_config_builder(|builder| { + let DiscoveryArgs { discv5_addr, discv5_port, .. } = self.network.discovery; + builder + .discv5_config( + discv5::ConfigBuilder::new(ListenConfig::from(Into::::into(( + discv5_addr, + discv5_port + self.instance - 1, + )))) + .build(), + ) + .build() + }) } /// Builds the [Pipeline] with the given [ProviderFactory] and downloaders. diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 8210e1a8c..0f858c914 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -71,7 +71,7 @@ pub use header::{Header, HeaderValidationError, HeadersDirection, SealedHeader}; pub use integer_list::IntegerList; pub use log::{logs_bloom, Log}; pub use net::{ - goerli_nodes, holesky_nodes, mainnet_nodes, parse_nodes, pk_to_id, sepolia_nodes, NodeRecord, + goerli_nodes, holesky_nodes, mainnet_nodes, parse_nodes, sepolia_nodes, NodeRecord, NodeRecordParseError, GOERLI_BOOTNODES, HOLESKY_BOOTNODES, MAINNET_BOOTNODES, SEPOLIA_BOOTNODES, }; diff --git a/crates/primitives/src/net.rs b/crates/primitives/src/net.rs index 2e0b77d50..7e582d081 100644 --- a/crates/primitives/src/net.rs +++ b/crates/primitives/src/net.rs @@ -1,4 +1,4 @@ -pub use reth_rpc_types::{pk_to_id, NodeRecord, NodeRecordParseError}; +pub use reth_rpc_types::{NodeRecord, NodeRecordParseError}; //