1use crate::{
4 error::NetworkError,
5 import::{BlockImport, ProofOfStakeBlockImport},
6 transactions::TransactionsManagerConfig,
7 NetworkHandle, NetworkManager,
8};
9use reth_chainspec::{ChainSpecProvider, EthChainSpec, Hardforks};
10use reth_discv4::{Discv4Config, Discv4ConfigBuilder, NatResolver, DEFAULT_DISCOVERY_ADDRESS};
11use reth_discv5::NetworkStackId;
12use reth_dns_discovery::DnsDiscoveryConfig;
13use reth_eth_wire::{
14 EthNetworkPrimitives, HelloMessage, HelloMessageWithProtocols, NetworkPrimitives, Status,
15};
16use reth_ethereum_forks::{ForkFilter, Head};
17use reth_network_peers::{mainnet_nodes, pk2id, sepolia_nodes, PeerId, TrustedPeer};
18use reth_network_types::{PeersConfig, SessionsConfig};
19use reth_storage_api::{noop::NoopProvider, BlockNumReader, BlockReader, HeaderProvider};
20use reth_tasks::{TaskSpawner, TokioTaskExecutor};
21use secp256k1::SECP256K1;
22use std::{collections::HashSet, net::SocketAddr, sync::Arc};
23
24use crate::protocol::{IntoRlpxSubProtocol, RlpxSubProtocols};
26pub use secp256k1::SecretKey;
27
28pub fn rng_secret_key() -> SecretKey {
30 SecretKey::new(&mut rand::thread_rng())
31}
32
33#[derive(Debug)]
35pub struct NetworkConfig<C, N: NetworkPrimitives = EthNetworkPrimitives> {
36 pub client: C,
41 pub secret_key: SecretKey,
43 pub boot_nodes: HashSet<TrustedPeer>,
45 pub dns_discovery_config: Option<DnsDiscoveryConfig>,
47 pub discovery_v4_addr: SocketAddr,
49 pub discovery_v4_config: Option<Discv4Config>,
51 pub discovery_v5_config: Option<reth_discv5::Config>,
53 pub listener_addr: SocketAddr,
55 pub peers_config: PeersConfig,
57 pub sessions_config: SessionsConfig,
59 pub chain_id: u64,
61 pub fork_filter: ForkFilter,
68 pub block_import: Box<dyn BlockImport<N::Block>>,
70 pub network_mode: NetworkMode,
72 pub executor: Box<dyn TaskSpawner>,
74 pub status: Status,
76 pub hello_message: HelloMessageWithProtocols,
78 pub extra_protocols: RlpxSubProtocols,
80 pub tx_gossip_disabled: bool,
82 pub transactions_manager_config: TransactionsManagerConfig,
84 pub nat: Option<NatResolver>,
86}
87
88impl<N: NetworkPrimitives> NetworkConfig<(), N> {
91 pub fn builder(secret_key: SecretKey) -> NetworkConfigBuilder<N> {
93 NetworkConfigBuilder::new(secret_key)
94 }
95
96 pub fn builder_with_rng_secret_key() -> NetworkConfigBuilder<N> {
98 NetworkConfigBuilder::with_rng_secret_key()
99 }
100}
101
102impl<C, N: NetworkPrimitives> NetworkConfig<C, N> {
103 pub fn new(client: C, secret_key: SecretKey) -> Self
105 where
106 C: ChainSpecProvider<ChainSpec: Hardforks>,
107 {
108 NetworkConfig::builder(secret_key).build(client)
109 }
110
111 pub fn apply<F>(self, f: F) -> Self
113 where
114 F: FnOnce(Self) -> Self,
115 {
116 f(self)
117 }
118
119 pub fn set_discovery_v4(mut self, discovery_config: Discv4Config) -> Self {
121 self.discovery_v4_config = Some(discovery_config);
122 self
123 }
124
125 pub const fn set_listener_addr(mut self, listener_addr: SocketAddr) -> Self {
127 self.listener_addr = listener_addr;
128 self
129 }
130
131 pub const fn listener_addr(&self) -> &SocketAddr {
133 &self.listener_addr
134 }
135}
136
137impl<C, N> NetworkConfig<C, N>
138where
139 C: BlockNumReader + 'static,
140 N: NetworkPrimitives,
141{
142 pub async fn manager(self) -> Result<NetworkManager<N>, NetworkError> {
144 NetworkManager::new(self).await
145 }
146}
147
148impl<C, N> NetworkConfig<C, N>
149where
150 N: NetworkPrimitives,
151 C: BlockReader<Block = N::Block, Receipt = reth_primitives::Receipt, Header = N::BlockHeader>
152 + HeaderProvider
153 + Clone
154 + Unpin
155 + 'static,
156{
157 pub async fn start_network(self) -> Result<NetworkHandle<N>, NetworkError> {
159 let client = self.client.clone();
160 let (handle, network, _txpool, eth) = NetworkManager::builder::<C>(self)
161 .await?
162 .request_handler::<C>(client)
163 .split_with_handle();
164
165 tokio::task::spawn(network);
166 tokio::task::spawn(eth);
167 Ok(handle)
168 }
169}
170
171#[derive(Debug)]
173pub struct NetworkConfigBuilder<N: NetworkPrimitives = EthNetworkPrimitives> {
174 secret_key: SecretKey,
176 dns_discovery_config: Option<DnsDiscoveryConfig>,
178 discovery_v4_builder: Option<Discv4ConfigBuilder>,
180 discovery_v5_builder: Option<reth_discv5::ConfigBuilder>,
182 boot_nodes: HashSet<TrustedPeer>,
184 discovery_addr: Option<SocketAddr>,
186 listener_addr: Option<SocketAddr>,
188 peers_config: Option<PeersConfig>,
190 sessions_config: Option<SessionsConfig>,
192 network_mode: NetworkMode,
194 executor: Option<Box<dyn TaskSpawner>>,
196 hello_message: Option<HelloMessageWithProtocols>,
198 extra_protocols: RlpxSubProtocols,
200 head: Option<Head>,
202 tx_gossip_disabled: bool,
204 block_import: Option<Box<dyn BlockImport<N::Block>>>,
206 transactions_manager_config: TransactionsManagerConfig,
208 nat: Option<NatResolver>,
210}
211
212#[allow(missing_docs)]
215impl<N: NetworkPrimitives> NetworkConfigBuilder<N> {
216 pub fn with_rng_secret_key() -> Self {
218 Self::new(rng_secret_key())
219 }
220
221 pub fn new(secret_key: SecretKey) -> Self {
223 Self {
224 secret_key,
225 dns_discovery_config: Some(Default::default()),
226 discovery_v4_builder: Some(Default::default()),
227 discovery_v5_builder: None,
228 boot_nodes: Default::default(),
229 discovery_addr: None,
230 listener_addr: None,
231 peers_config: None,
232 sessions_config: None,
233 network_mode: Default::default(),
234 executor: None,
235 hello_message: None,
236 extra_protocols: Default::default(),
237 head: None,
238 tx_gossip_disabled: false,
239 block_import: None,
240 transactions_manager_config: Default::default(),
241 nat: None,
242 }
243 }
244
245 pub fn apply<F>(self, f: F) -> Self
247 where
248 F: FnOnce(Self) -> Self,
249 {
250 f(self)
251 }
252
253 pub fn get_peer_id(&self) -> PeerId {
255 pk2id(&self.secret_key.public_key(SECP256K1))
256 }
257
258 pub const fn secret_key(&self) -> &SecretKey {
260 &self.secret_key
261 }
262
263 pub const fn network_mode(mut self, network_mode: NetworkMode) -> Self {
265 self.network_mode = network_mode;
266 self
267 }
268
269 pub const fn set_head(mut self, head: Head) -> Self {
275 self.head = Some(head);
276 self
277 }
278
279 pub fn hello_message(mut self, hello_message: HelloMessageWithProtocols) -> Self {
290 self.hello_message = Some(hello_message);
291 self
292 }
293
294 pub fn peer_config(mut self, config: PeersConfig) -> Self {
296 self.peers_config = Some(config);
297 self
298 }
299
300 pub fn with_task_executor(mut self, executor: Box<dyn TaskSpawner>) -> Self {
304 self.executor = Some(executor);
305 self
306 }
307
308 pub const fn sessions_config(mut self, config: SessionsConfig) -> Self {
310 self.sessions_config = Some(config);
311 self
312 }
313
314 pub const fn transactions_manager_config(mut self, config: TransactionsManagerConfig) -> Self {
316 self.transactions_manager_config = config;
317 self
318 }
319
320 pub const fn set_addrs(self, addr: SocketAddr) -> Self {
328 self.listener_addr(addr).discovery_addr(addr)
329 }
330
331 pub const fn listener_addr(mut self, listener_addr: SocketAddr) -> Self {
335 self.listener_addr = Some(listener_addr);
336 self
337 }
338
339 pub fn listener_port(mut self, port: u16) -> Self {
343 self.listener_addr.get_or_insert(DEFAULT_DISCOVERY_ADDRESS).set_port(port);
344 self
345 }
346
347 pub const fn discovery_addr(mut self, discovery_addr: SocketAddr) -> Self {
349 self.discovery_addr = Some(discovery_addr);
350 self
351 }
352
353 pub fn discovery_port(mut self, port: u16) -> Self {
357 self.discovery_addr.get_or_insert(DEFAULT_DISCOVERY_ADDRESS).set_port(port);
358 self
359 }
360
361 pub fn with_unused_discovery_port(self) -> Self {
364 self.discovery_port(0)
365 }
366
367 pub fn with_unused_listener_port(self) -> Self {
370 self.listener_port(0)
371 }
372
373 pub fn external_ip_resolver(mut self, resolver: NatResolver) -> Self {
380 self.discovery_v4_builder
381 .get_or_insert_with(Discv4Config::builder)
382 .external_ip_resolver(Some(resolver));
383 self.nat = Some(resolver);
384 self
385 }
386
387 pub fn discovery(mut self, builder: Discv4ConfigBuilder) -> Self {
389 self.discovery_v4_builder = Some(builder);
390 self
391 }
392
393 pub fn discovery_v5(mut self, builder: reth_discv5::ConfigBuilder) -> Self {
395 self.discovery_v5_builder = Some(builder);
396 self
397 }
398
399 pub fn dns_discovery(mut self, config: DnsDiscoveryConfig) -> Self {
401 self.dns_discovery_config = Some(config);
402 self
403 }
404
405 pub fn mainnet_boot_nodes(self) -> Self {
407 self.boot_nodes(mainnet_nodes())
408 }
409
410 pub fn sepolia_boot_nodes(self) -> Self {
412 self.boot_nodes(sepolia_nodes())
413 }
414
415 pub fn boot_nodes<T: Into<TrustedPeer>>(mut self, nodes: impl IntoIterator<Item = T>) -> Self {
417 self.boot_nodes = nodes.into_iter().map(Into::into).collect();
418 self
419 }
420
421 pub fn boot_nodes_iter(&self) -> impl Iterator<Item = &TrustedPeer> + '_ {
423 self.boot_nodes.iter()
424 }
425
426 pub fn disable_dns_discovery(mut self) -> Self {
428 self.dns_discovery_config = None;
429 self
430 }
431
432 pub const fn disable_nat(mut self) -> Self {
434 self.nat = None;
435 self
436 }
437
438 pub fn disable_discovery(self) -> Self {
440 self.disable_discv4_discovery().disable_dns_discovery().disable_nat()
441 }
442
443 pub fn disable_discovery_if(self, disable: bool) -> Self {
445 if disable {
446 self.disable_discovery()
447 } else {
448 self
449 }
450 }
451
452 pub fn disable_discv4_discovery(mut self) -> Self {
454 self.discovery_v4_builder = None;
455 self
456 }
457
458 pub fn disable_dns_discovery_if(self, disable: bool) -> Self {
460 if disable {
461 self.disable_dns_discovery()
462 } else {
463 self
464 }
465 }
466
467 pub fn disable_discv4_discovery_if(self, disable: bool) -> Self {
469 if disable {
470 self.disable_discv4_discovery()
471 } else {
472 self
473 }
474 }
475
476 pub fn add_rlpx_sub_protocol(mut self, protocol: impl IntoRlpxSubProtocol) -> Self {
478 self.extra_protocols.push(protocol);
479 self
480 }
481
482 pub const fn disable_tx_gossip(mut self, disable_tx_gossip: bool) -> Self {
484 self.tx_gossip_disabled = disable_tx_gossip;
485 self
486 }
487
488 pub fn block_import(mut self, block_import: Box<dyn BlockImport<N::Block>>) -> Self {
490 self.block_import = Some(block_import);
491 self
492 }
493
494 pub fn build_with_noop_provider<ChainSpec>(
497 self,
498 chain_spec: Arc<ChainSpec>,
499 ) -> NetworkConfig<NoopProvider<ChainSpec>, N>
500 where
501 ChainSpec: EthChainSpec + Hardforks + 'static,
502 {
503 self.build(NoopProvider::eth(chain_spec))
504 }
505
506 pub const fn add_nat(mut self, nat: Option<NatResolver>) -> Self {
508 self.nat = nat;
509 self
510 }
511
512 pub fn build<C>(self, client: C) -> NetworkConfig<C, N>
519 where
520 C: ChainSpecProvider<ChainSpec: Hardforks>,
521 {
522 let peer_id = self.get_peer_id();
523 let chain_spec = client.chain_spec();
524 let Self {
525 secret_key,
526 mut dns_discovery_config,
527 discovery_v4_builder,
528 mut discovery_v5_builder,
529 boot_nodes,
530 discovery_addr,
531 listener_addr,
532 peers_config,
533 sessions_config,
534 network_mode,
535 executor,
536 hello_message,
537 extra_protocols,
538 head,
539 tx_gossip_disabled,
540 block_import,
541 transactions_manager_config,
542 nat,
543 } = self;
544
545 discovery_v5_builder = discovery_v5_builder.map(|mut builder| {
546 if let Some(network_stack_id) = NetworkStackId::id(&chain_spec) {
547 let fork_id = chain_spec.latest_fork_id();
548 builder = builder.fork(network_stack_id, fork_id)
549 }
550
551 builder
552 });
553
554 let listener_addr = listener_addr.unwrap_or(DEFAULT_DISCOVERY_ADDRESS);
555
556 let mut hello_message =
557 hello_message.unwrap_or_else(|| HelloMessage::builder(peer_id).build());
558 hello_message.port = listener_addr.port();
559
560 let head = head.unwrap_or_else(|| Head {
561 hash: chain_spec.genesis_hash(),
562 number: 0,
563 timestamp: chain_spec.genesis().timestamp,
564 difficulty: chain_spec.genesis().difficulty,
565 total_difficulty: chain_spec.genesis().difficulty,
566 });
567
568 let status = Status::spec_builder(&chain_spec, &head).build();
570
571 let fork_filter = chain_spec.fork_filter(head);
573
574 let chain_id = chain_spec.chain().id();
576
577 if let Some(dns_networks) =
579 dns_discovery_config.as_mut().and_then(|c| c.bootstrap_dns_networks.as_mut())
580 {
581 if dns_networks.is_empty() {
582 if let Some(link) = chain_spec.chain().public_dns_network_protocol() {
583 dns_networks.insert(link.parse().expect("is valid DNS link entry"));
584 }
585 }
586 }
587
588 NetworkConfig {
589 client,
590 secret_key,
591 boot_nodes,
592 dns_discovery_config,
593 discovery_v4_config: discovery_v4_builder.map(|builder| builder.build()),
594 discovery_v5_config: discovery_v5_builder.map(|builder| builder.build()),
595 discovery_v4_addr: discovery_addr.unwrap_or(DEFAULT_DISCOVERY_ADDRESS),
596 listener_addr,
597 peers_config: peers_config.unwrap_or_default(),
598 sessions_config: sessions_config.unwrap_or_default(),
599 chain_id,
600 block_import: block_import.unwrap_or_else(|| Box::<ProofOfStakeBlockImport>::default()),
601 network_mode,
602 executor: executor.unwrap_or_else(|| Box::<TokioTaskExecutor>::default()),
603 status,
604 hello_message,
605 extra_protocols,
606 fork_filter,
607 tx_gossip_disabled,
608 transactions_manager_config,
609 nat,
610 }
611 }
612}
613
614#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
620#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
621pub enum NetworkMode {
622 Work,
624 #[default]
626 Stake,
627}
628
629impl NetworkMode {
632 pub const fn is_stake(&self) -> bool {
634 matches!(self, Self::Stake)
635 }
636}
637
638#[cfg(test)]
639mod tests {
640 use super::*;
641 use rand::thread_rng;
642 use reth_chainspec::{Chain, MAINNET};
643 use reth_dns_discovery::tree::LinkEntry;
644 use reth_primitives::ForkHash;
645 use reth_storage_api::noop::NoopProvider;
646 use std::sync::Arc;
647
648 fn builder() -> NetworkConfigBuilder {
649 let secret_key = SecretKey::new(&mut thread_rng());
650 NetworkConfigBuilder::new(secret_key)
651 }
652
653 #[test]
654 fn test_network_dns_defaults() {
655 let config = builder().build(NoopProvider::default());
656
657 let dns = config.dns_discovery_config.unwrap();
658 let bootstrap_nodes = dns.bootstrap_dns_networks.unwrap();
659 let mainnet_dns: LinkEntry =
660 Chain::mainnet().public_dns_network_protocol().unwrap().parse().unwrap();
661 assert!(bootstrap_nodes.contains(&mainnet_dns));
662 assert_eq!(bootstrap_nodes.len(), 1);
663 }
664
665 #[test]
666 fn test_network_fork_filter_default() {
667 let mut chain_spec = Arc::clone(&MAINNET);
668
669 Arc::make_mut(&mut chain_spec).hardforks = Default::default();
671
672 let genesis_fork_hash = ForkHash::from(chain_spec.genesis_hash());
674
675 let config = builder().build_with_noop_provider(chain_spec);
677
678 let status = config.status;
679 let fork_filter = config.fork_filter;
680
681 assert_eq!(status.forkid.next, 0);
683
684 assert_eq!(fork_filter.current().next, 0);
686
687 assert_eq!(status.forkid.hash, genesis_fork_hash);
689 assert_eq!(fork_filter.current().hash, genesis_fork_hash);
690 }
691}