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 handshake::{EthHandshake, EthRlpxHandshake},
15 EthNetworkPrimitives, HelloMessage, HelloMessageWithProtocols, NetworkPrimitives, Status,
16};
17use reth_ethereum_forks::{ForkFilter, Head};
18use reth_network_peers::{mainnet_nodes, pk2id, sepolia_nodes, PeerId, TrustedPeer};
19use reth_network_types::{PeersConfig, SessionsConfig};
20use reth_storage_api::{noop::NoopProvider, BlockNumReader, BlockReader, HeaderProvider};
21use reth_tasks::{TaskSpawner, TokioTaskExecutor};
22use secp256k1::SECP256K1;
23use std::{collections::HashSet, net::SocketAddr, sync::Arc};
24
25use crate::protocol::{IntoRlpxSubProtocol, RlpxSubProtocols};
27pub use secp256k1::SecretKey;
28
29pub fn rng_secret_key() -> SecretKey {
31 SecretKey::new(&mut rand::thread_rng())
32}
33
34#[derive(Debug)]
36pub struct NetworkConfig<C, N: NetworkPrimitives = EthNetworkPrimitives> {
37 pub client: C,
42 pub secret_key: SecretKey,
44 pub boot_nodes: HashSet<TrustedPeer>,
46 pub dns_discovery_config: Option<DnsDiscoveryConfig>,
48 pub discovery_v4_addr: SocketAddr,
50 pub discovery_v4_config: Option<Discv4Config>,
52 pub discovery_v5_config: Option<reth_discv5::Config>,
54 pub listener_addr: SocketAddr,
56 pub peers_config: PeersConfig,
58 pub sessions_config: SessionsConfig,
60 pub chain_id: u64,
62 pub fork_filter: ForkFilter,
69 pub block_import: Box<dyn BlockImport<N::Block>>,
71 pub network_mode: NetworkMode,
73 pub executor: Box<dyn TaskSpawner>,
75 pub status: Status,
77 pub hello_message: HelloMessageWithProtocols,
79 pub extra_protocols: RlpxSubProtocols,
81 pub tx_gossip_disabled: bool,
83 pub transactions_manager_config: TransactionsManagerConfig,
85 pub nat: Option<NatResolver>,
87 pub handshake: Arc<dyn EthRlpxHandshake>,
92}
93
94impl<N: NetworkPrimitives> NetworkConfig<(), N> {
97 pub fn builder(secret_key: SecretKey) -> NetworkConfigBuilder<N> {
99 NetworkConfigBuilder::new(secret_key)
100 }
101
102 pub fn builder_with_rng_secret_key() -> NetworkConfigBuilder<N> {
104 NetworkConfigBuilder::with_rng_secret_key()
105 }
106}
107
108impl<C, N: NetworkPrimitives> NetworkConfig<C, N> {
109 pub fn new(client: C, secret_key: SecretKey) -> Self
111 where
112 C: ChainSpecProvider<ChainSpec: Hardforks>,
113 {
114 NetworkConfig::builder(secret_key).build(client)
115 }
116
117 pub fn apply<F>(self, f: F) -> Self
119 where
120 F: FnOnce(Self) -> Self,
121 {
122 f(self)
123 }
124
125 pub fn set_discovery_v4(mut self, discovery_config: Discv4Config) -> Self {
127 self.discovery_v4_config = Some(discovery_config);
128 self
129 }
130
131 pub const fn set_listener_addr(mut self, listener_addr: SocketAddr) -> Self {
133 self.listener_addr = listener_addr;
134 self
135 }
136
137 pub const fn listener_addr(&self) -> &SocketAddr {
139 &self.listener_addr
140 }
141}
142
143impl<C, N> NetworkConfig<C, N>
144where
145 C: BlockNumReader + 'static,
146 N: NetworkPrimitives,
147{
148 pub async fn manager(self) -> Result<NetworkManager<N>, NetworkError> {
150 NetworkManager::new(self).await
151 }
152}
153
154impl<C, N> NetworkConfig<C, N>
155where
156 N: NetworkPrimitives,
157 C: BlockReader<Block = N::Block, Receipt = N::Receipt, Header = N::BlockHeader>
158 + HeaderProvider
159 + Clone
160 + Unpin
161 + 'static,
162{
163 pub async fn start_network(self) -> Result<NetworkHandle<N>, NetworkError> {
165 let client = self.client.clone();
166 let (handle, network, _txpool, eth) = NetworkManager::builder::<C>(self)
167 .await?
168 .request_handler::<C>(client)
169 .split_with_handle();
170
171 tokio::task::spawn(network);
172 tokio::task::spawn(eth);
173 Ok(handle)
174 }
175}
176
177#[derive(Debug)]
179pub struct NetworkConfigBuilder<N: NetworkPrimitives = EthNetworkPrimitives> {
180 secret_key: SecretKey,
182 dns_discovery_config: Option<DnsDiscoveryConfig>,
184 discovery_v4_builder: Option<Discv4ConfigBuilder>,
186 discovery_v5_builder: Option<reth_discv5::ConfigBuilder>,
188 boot_nodes: HashSet<TrustedPeer>,
190 discovery_addr: Option<SocketAddr>,
192 listener_addr: Option<SocketAddr>,
194 peers_config: Option<PeersConfig>,
196 sessions_config: Option<SessionsConfig>,
198 network_mode: NetworkMode,
200 executor: Option<Box<dyn TaskSpawner>>,
202 hello_message: Option<HelloMessageWithProtocols>,
204 extra_protocols: RlpxSubProtocols,
206 head: Option<Head>,
208 tx_gossip_disabled: bool,
210 block_import: Option<Box<dyn BlockImport<N::Block>>>,
212 transactions_manager_config: TransactionsManagerConfig,
214 nat: Option<NatResolver>,
216 handshake: Arc<dyn EthRlpxHandshake>,
219}
220
221impl NetworkConfigBuilder<EthNetworkPrimitives> {
222 pub fn eth(secret_key: SecretKey) -> Self {
224 Self::new(secret_key)
225 }
226}
227
228#[allow(missing_docs)]
231impl<N: NetworkPrimitives> NetworkConfigBuilder<N> {
232 pub fn with_rng_secret_key() -> Self {
234 Self::new(rng_secret_key())
235 }
236
237 pub fn new(secret_key: SecretKey) -> Self {
239 Self {
240 secret_key,
241 dns_discovery_config: Some(Default::default()),
242 discovery_v4_builder: Some(Default::default()),
243 discovery_v5_builder: None,
244 boot_nodes: Default::default(),
245 discovery_addr: None,
246 listener_addr: None,
247 peers_config: None,
248 sessions_config: None,
249 network_mode: Default::default(),
250 executor: None,
251 hello_message: None,
252 extra_protocols: Default::default(),
253 head: None,
254 tx_gossip_disabled: false,
255 block_import: None,
256 transactions_manager_config: Default::default(),
257 nat: None,
258 handshake: Arc::new(EthHandshake::default()),
259 }
260 }
261
262 pub fn apply<F>(self, f: F) -> Self
264 where
265 F: FnOnce(Self) -> Self,
266 {
267 f(self)
268 }
269
270 pub fn get_peer_id(&self) -> PeerId {
272 pk2id(&self.secret_key.public_key(SECP256K1))
273 }
274
275 pub const fn secret_key(&self) -> &SecretKey {
277 &self.secret_key
278 }
279
280 pub const fn network_mode(mut self, network_mode: NetworkMode) -> Self {
282 self.network_mode = network_mode;
283 self
284 }
285
286 pub const fn with_pow(self) -> Self {
294 self.network_mode(NetworkMode::Work)
295 }
296
297 pub const fn set_head(mut self, head: Head) -> Self {
303 self.head = Some(head);
304 self
305 }
306
307 pub fn hello_message(mut self, hello_message: HelloMessageWithProtocols) -> Self {
318 self.hello_message = Some(hello_message);
319 self
320 }
321
322 pub fn peer_config(mut self, config: PeersConfig) -> Self {
324 self.peers_config = Some(config);
325 self
326 }
327
328 pub fn with_task_executor(mut self, executor: Box<dyn TaskSpawner>) -> Self {
332 self.executor = Some(executor);
333 self
334 }
335
336 pub const fn sessions_config(mut self, config: SessionsConfig) -> Self {
338 self.sessions_config = Some(config);
339 self
340 }
341
342 pub const fn transactions_manager_config(mut self, config: TransactionsManagerConfig) -> Self {
344 self.transactions_manager_config = config;
345 self
346 }
347
348 pub const fn set_addrs(self, addr: SocketAddr) -> Self {
356 self.listener_addr(addr).discovery_addr(addr)
357 }
358
359 pub const fn listener_addr(mut self, listener_addr: SocketAddr) -> Self {
363 self.listener_addr = Some(listener_addr);
364 self
365 }
366
367 pub fn listener_port(mut self, port: u16) -> Self {
371 self.listener_addr.get_or_insert(DEFAULT_DISCOVERY_ADDRESS).set_port(port);
372 self
373 }
374
375 pub const fn discovery_addr(mut self, discovery_addr: SocketAddr) -> Self {
377 self.discovery_addr = Some(discovery_addr);
378 self
379 }
380
381 pub fn discovery_port(mut self, port: u16) -> Self {
385 self.discovery_addr.get_or_insert(DEFAULT_DISCOVERY_ADDRESS).set_port(port);
386 self
387 }
388
389 pub fn with_unused_ports(self) -> Self {
392 self.with_unused_discovery_port().with_unused_listener_port()
393 }
394
395 pub fn with_unused_discovery_port(self) -> Self {
398 self.discovery_port(0)
399 }
400
401 pub fn with_unused_listener_port(self) -> Self {
404 self.listener_port(0)
405 }
406
407 pub fn external_ip_resolver(mut self, resolver: NatResolver) -> Self {
414 self.discovery_v4_builder
415 .get_or_insert_with(Discv4Config::builder)
416 .external_ip_resolver(Some(resolver));
417 self.nat = Some(resolver);
418 self
419 }
420
421 pub fn discovery(mut self, builder: Discv4ConfigBuilder) -> Self {
423 self.discovery_v4_builder = Some(builder);
424 self
425 }
426
427 pub fn discovery_v5(mut self, builder: reth_discv5::ConfigBuilder) -> Self {
429 self.discovery_v5_builder = Some(builder);
430 self
431 }
432
433 pub fn dns_discovery(mut self, config: DnsDiscoveryConfig) -> Self {
435 self.dns_discovery_config = Some(config);
436 self
437 }
438
439 pub fn mainnet_boot_nodes(self) -> Self {
441 self.boot_nodes(mainnet_nodes())
442 }
443
444 pub fn sepolia_boot_nodes(self) -> Self {
446 self.boot_nodes(sepolia_nodes())
447 }
448
449 pub fn boot_nodes<T: Into<TrustedPeer>>(mut self, nodes: impl IntoIterator<Item = T>) -> Self {
451 self.boot_nodes = nodes.into_iter().map(Into::into).collect();
452 self
453 }
454
455 pub fn boot_nodes_iter(&self) -> impl Iterator<Item = &TrustedPeer> + '_ {
457 self.boot_nodes.iter()
458 }
459
460 pub fn disable_dns_discovery(mut self) -> Self {
462 self.dns_discovery_config = None;
463 self
464 }
465
466 pub const fn disable_nat(mut self) -> Self {
468 self.nat = None;
469 self
470 }
471
472 pub fn disable_discovery(self) -> Self {
474 self.disable_discv4_discovery().disable_discv5_discovery().disable_dns_discovery()
475 }
476
477 pub fn disable_discovery_if(self, disable: bool) -> Self {
479 if disable {
480 self.disable_discovery()
481 } else {
482 self
483 }
484 }
485
486 pub fn disable_discv4_discovery(mut self) -> Self {
488 self.discovery_v4_builder = None;
489 self
490 }
491
492 pub fn disable_discv5_discovery(mut self) -> Self {
494 self.discovery_v5_builder = None;
495 self
496 }
497
498 pub fn disable_dns_discovery_if(self, disable: bool) -> Self {
500 if disable {
501 self.disable_dns_discovery()
502 } else {
503 self
504 }
505 }
506
507 pub fn disable_discv4_discovery_if(self, disable: bool) -> Self {
509 if disable {
510 self.disable_discv4_discovery()
511 } else {
512 self
513 }
514 }
515
516 pub fn disable_discv5_discovery_if(self, disable: bool) -> Self {
518 if disable {
519 self.disable_discv5_discovery()
520 } else {
521 self
522 }
523 }
524
525 pub fn add_rlpx_sub_protocol(mut self, protocol: impl IntoRlpxSubProtocol) -> Self {
527 self.extra_protocols.push(protocol);
528 self
529 }
530
531 pub const fn disable_tx_gossip(mut self, disable_tx_gossip: bool) -> Self {
533 self.tx_gossip_disabled = disable_tx_gossip;
534 self
535 }
536
537 pub fn block_import(mut self, block_import: Box<dyn BlockImport<N::Block>>) -> Self {
539 self.block_import = Some(block_import);
540 self
541 }
542
543 pub fn build_with_noop_provider<ChainSpec>(
546 self,
547 chain_spec: Arc<ChainSpec>,
548 ) -> NetworkConfig<NoopProvider<ChainSpec>, N>
549 where
550 ChainSpec: EthChainSpec + Hardforks + 'static,
551 {
552 self.build(NoopProvider::eth(chain_spec))
553 }
554
555 pub const fn add_nat(mut self, nat: Option<NatResolver>) -> Self {
557 self.nat = nat;
558 self
559 }
560
561 pub fn eth_rlpx_handshake(mut self, handshake: Arc<dyn EthRlpxHandshake>) -> Self {
563 self.handshake = handshake;
564 self
565 }
566
567 pub fn build<C>(self, client: C) -> NetworkConfig<C, N>
574 where
575 C: ChainSpecProvider<ChainSpec: Hardforks>,
576 {
577 let peer_id = self.get_peer_id();
578 let chain_spec = client.chain_spec();
579 let Self {
580 secret_key,
581 mut dns_discovery_config,
582 discovery_v4_builder,
583 mut discovery_v5_builder,
584 boot_nodes,
585 discovery_addr,
586 listener_addr,
587 peers_config,
588 sessions_config,
589 network_mode,
590 executor,
591 hello_message,
592 extra_protocols,
593 head,
594 tx_gossip_disabled,
595 block_import,
596 transactions_manager_config,
597 nat,
598 handshake,
599 } = self;
600
601 discovery_v5_builder = discovery_v5_builder.map(|mut builder| {
602 if let Some(network_stack_id) = NetworkStackId::id(&chain_spec) {
603 let fork_id = chain_spec.latest_fork_id();
604 builder = builder.fork(network_stack_id, fork_id)
605 }
606
607 builder
608 });
609
610 let listener_addr = listener_addr.unwrap_or(DEFAULT_DISCOVERY_ADDRESS);
611
612 let mut hello_message =
613 hello_message.unwrap_or_else(|| HelloMessage::builder(peer_id).build());
614 hello_message.port = listener_addr.port();
615
616 let head = head.unwrap_or_else(|| Head {
617 hash: chain_spec.genesis_hash(),
618 number: 0,
619 timestamp: chain_spec.genesis().timestamp,
620 difficulty: chain_spec.genesis().difficulty,
621 total_difficulty: chain_spec.genesis().difficulty,
622 });
623
624 let status = Status::spec_builder(&chain_spec, &head).build();
626
627 let fork_filter = chain_spec.fork_filter(head);
629
630 let chain_id = chain_spec.chain().id();
632
633 if let Some(dns_networks) =
635 dns_discovery_config.as_mut().and_then(|c| c.bootstrap_dns_networks.as_mut())
636 {
637 if dns_networks.is_empty() {
638 if let Some(link) = chain_spec.chain().public_dns_network_protocol() {
639 dns_networks.insert(link.parse().expect("is valid DNS link entry"));
640 }
641 }
642 }
643
644 NetworkConfig {
645 client,
646 secret_key,
647 boot_nodes,
648 dns_discovery_config,
649 discovery_v4_config: discovery_v4_builder.map(|builder| builder.build()),
650 discovery_v5_config: discovery_v5_builder.map(|builder| builder.build()),
651 discovery_v4_addr: discovery_addr.unwrap_or(DEFAULT_DISCOVERY_ADDRESS),
652 listener_addr,
653 peers_config: peers_config.unwrap_or_default(),
654 sessions_config: sessions_config.unwrap_or_default(),
655 chain_id,
656 block_import: block_import.unwrap_or_else(|| Box::<ProofOfStakeBlockImport>::default()),
657 network_mode,
658 executor: executor.unwrap_or_else(|| Box::<TokioTaskExecutor>::default()),
659 status,
660 hello_message,
661 extra_protocols,
662 fork_filter,
663 tx_gossip_disabled,
664 transactions_manager_config,
665 nat,
666 handshake,
667 }
668 }
669}
670
671#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
677#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
678pub enum NetworkMode {
679 Work,
681 #[default]
683 Stake,
684}
685
686impl NetworkMode {
689 pub const fn is_stake(&self) -> bool {
691 matches!(self, Self::Stake)
692 }
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698 use rand::thread_rng;
699 use reth_chainspec::{Chain, MAINNET};
700 use reth_dns_discovery::tree::LinkEntry;
701 use reth_primitives::ForkHash;
702 use reth_storage_api::noop::NoopProvider;
703 use std::sync::Arc;
704
705 fn builder() -> NetworkConfigBuilder {
706 let secret_key = SecretKey::new(&mut thread_rng());
707 NetworkConfigBuilder::new(secret_key)
708 }
709
710 #[test]
711 fn test_network_dns_defaults() {
712 let config = builder().build(NoopProvider::default());
713
714 let dns = config.dns_discovery_config.unwrap();
715 let bootstrap_nodes = dns.bootstrap_dns_networks.unwrap();
716 let mainnet_dns: LinkEntry =
717 Chain::mainnet().public_dns_network_protocol().unwrap().parse().unwrap();
718 assert!(bootstrap_nodes.contains(&mainnet_dns));
719 assert_eq!(bootstrap_nodes.len(), 1);
720 }
721
722 #[test]
723 fn test_network_fork_filter_default() {
724 let mut chain_spec = Arc::clone(&MAINNET);
725
726 Arc::make_mut(&mut chain_spec).hardforks = Default::default();
728
729 let genesis_fork_hash = ForkHash::from(chain_spec.genesis_hash());
731
732 let config = builder().build_with_noop_provider(chain_spec);
734
735 let status = config.status;
736 let fork_filter = config.fork_filter;
737
738 assert_eq!(status.forkid.next, 0);
740
741 assert_eq!(fork_filter.current().next, 0);
743
744 assert_eq!(status.forkid.hash, genesis_fork_hash);
746 assert_eq!(fork_filter.current().hash, genesis_fork_hash);
747 }
748}