Skip to main content

reth_network/
manager.rs

1//! High level network management.
2//!
3//! The [`NetworkManager`] contains the state of the network as a whole. It controls how connections
4//! are handled and keeps track of connections to peers.
5//!
6//! ## Capabilities
7//!
8//! The network manages peers depending on their announced capabilities via their `RLPx` sessions. Most importantly the [Ethereum Wire Protocol](https://github.com/ethereum/devp2p/blob/master/caps/eth.md)(`eth`).
9//!
10//! ## Overview
11//!
12//! The [`NetworkManager`] is responsible for advancing the state of the `network`. The `network` is
13//! made up of peer-to-peer connections between nodes that are available on the same network.
14//! Responsible for peer discovery is ethereum's discovery protocol (discv4, discv5). If the address
15//! (IP+port) of our node is published via discovery, remote peers can initiate inbound connections
16//! to the local node. Once a (tcp) connection is established, both peers start to authenticate a [RLPx session](https://github.com/ethereum/devp2p/blob/master/rlpx.md) via a handshake. If the handshake was successful, both peers announce their capabilities and are now ready to exchange sub-protocol messages via the `RLPx` session.
17
18use crate::{
19    budget::{DEFAULT_BUDGET_TRY_DRAIN_NETWORK_HANDLE_CHANNEL, DEFAULT_BUDGET_TRY_DRAIN_SWARM},
20    config::NetworkConfig,
21    discovery::Discovery,
22    error::{NetworkError, ServiceKind},
23    eth_requests::IncomingEthRequest,
24    import::{BlockImport, BlockImportEvent, BlockImportOutcome, BlockValidation, NewBlockEvent},
25    listener::ConnectionListener,
26    message::{NewBlockMessage, PeerMessage},
27    metrics::{
28        BackedOffPeersMetrics, ClosedSessionsMetrics, DirectionalDisconnectMetrics, NetworkMetrics,
29        PendingSessionFailureMetrics,
30    },
31    network::{NetworkHandle, NetworkHandleMessage},
32    peers::{BackoffReason, PeersManager},
33    poll_nested_stream_with_budget,
34    protocol::IntoRlpxSubProtocol,
35    required_block_filter::RequiredBlockFilter,
36    session::SessionManager,
37    state::NetworkState,
38    swarm::{Swarm, SwarmEvent},
39    transactions::NetworkTransactionEvent,
40    FetchClient, NetworkBuilder,
41};
42use futures::{Future, StreamExt};
43use parking_lot::Mutex;
44use reth_chainspec::EnrForkIdEntry;
45use reth_eth_wire::{DisconnectReason, EthNetworkPrimitives, NetworkPrimitives};
46use reth_fs_util::{self as fs, FsPathError};
47use reth_metrics::common::mpsc::MemoryBoundedSender;
48use reth_network_api::{
49    events::{PeerEvent, SessionInfo},
50    test_utils::PeersHandle,
51    EthProtocolInfo, NetworkEvent, NetworkStatus, PeerInfo, PeerRequest,
52};
53use reth_network_peers::{NodeRecord, PeerId};
54use reth_network_types::ReputationChangeKind;
55use reth_storage_api::BlockNumReader;
56use reth_tasks::shutdown::GracefulShutdown;
57use reth_tokio_util::EventSender;
58use secp256k1::SecretKey;
59use std::{
60    net::SocketAddr,
61    path::Path,
62    pin::Pin,
63    sync::{
64        atomic::{AtomicU64, AtomicUsize, Ordering},
65        Arc,
66    },
67    task::{Context, Poll},
68    time::{Duration, Instant},
69};
70use tokio::sync::mpsc::{self, error::TrySendError};
71use tokio_stream::wrappers::UnboundedReceiverStream;
72use tracing::{debug, error, trace, warn};
73
74#[cfg_attr(doc, aquamarine::aquamarine)]
75// TODO: Inlined diagram due to a bug in aquamarine library, should become an include when it's
76// fixed. See https://github.com/mersinvald/aquamarine/issues/50
77// include_mmd!("docs/mermaid/network-manager.mmd")
78/// Manages the _entire_ state of the network.
79///
80/// This is an endless [`Future`] that consistently drives the state of the entire network forward.
81///
82/// The [`NetworkManager`] is the container type for all parts involved with advancing the network.
83///
84/// ```mermaid
85/// graph TB
86///   handle(NetworkHandle)
87///   events(NetworkEvents)
88///   transactions(Transactions Task)
89///   ethrequest(ETH Request Task)
90///   discovery(Discovery Task)
91///   subgraph NetworkManager
92///     direction LR
93///     subgraph Swarm
94///         direction TB
95///         B1[(Session Manager)]
96///         B2[(Connection Listener)]
97///         B3[(Network State)]
98///     end
99///  end
100///  handle <--> |request response channel| NetworkManager
101///  NetworkManager --> |Network events| events
102///  transactions <--> |transactions| NetworkManager
103///  ethrequest <--> |ETH request handing| NetworkManager
104///  discovery --> |Discovered peers| NetworkManager
105/// ```
106#[derive(Debug)]
107#[must_use = "The NetworkManager does nothing unless polled"]
108pub struct NetworkManager<N: NetworkPrimitives = EthNetworkPrimitives> {
109    /// The type that manages the actual network part, which includes connections.
110    swarm: Swarm<N>,
111    /// Underlying network handle that can be shared.
112    handle: NetworkHandle<N>,
113    /// Receiver half of the command channel set up between this type and the [`NetworkHandle`]
114    from_handle_rx: UnboundedReceiverStream<NetworkHandleMessage<N>>,
115    /// Handles block imports according to the `eth` protocol.
116    block_import: Box<dyn BlockImport<N::NewBlockPayload>>,
117    /// Sender for high level network events.
118    event_sender: EventSender<NetworkEvent<PeerRequest<N>>>,
119    /// Sender half to send events to the
120    /// [`TransactionsManager`](crate::transactions::TransactionsManager) task, if configured.
121    to_transactions_manager: Option<MemoryBoundedSender<NetworkTransactionEvent<N>>>,
122    /// Sender half to send events to the
123    /// [`EthRequestHandler`](crate::eth_requests::EthRequestHandler) task, if configured.
124    ///
125    /// The channel that originally receives and bundles all requests from all sessions is already
126    /// bounded. However, since handling an eth request is more I/O intensive than delegating
127    /// them from the bounded channel to the eth-request channel, it is possible that this
128    /// builds up if the node is flooded with requests.
129    ///
130    /// Even though nonmalicious requests are relatively cheap, it's possible to craft
131    /// body requests with bogus data up until the allowed max message size limit.
132    /// Thus, we use a bounded channel here to avoid unbounded build up if the node is flooded with
133    /// requests. This channel size is set at
134    /// [`ETH_REQUEST_CHANNEL_CAPACITY`](crate::builder::ETH_REQUEST_CHANNEL_CAPACITY)
135    to_eth_request_handler: Option<mpsc::Sender<IncomingEthRequest<N>>>,
136    /// Tracks the number of active session (connected peers).
137    ///
138    /// This is updated via internal events and shared via `Arc` with the [`NetworkHandle`]
139    /// Updated by the `NetworkWorker` and loaded by the `NetworkService`.
140    num_active_peers: Arc<AtomicUsize>,
141    /// Metrics for the Network
142    metrics: NetworkMetrics,
143    /// Disconnect metrics for the Network, split by connection direction.
144    disconnect_metrics: DirectionalDisconnectMetrics,
145    /// Closed sessions metrics, split by direction.
146    closed_sessions_metrics: ClosedSessionsMetrics,
147    /// Pending session failure metrics, split by direction.
148    pending_session_failure_metrics: PendingSessionFailureMetrics,
149    /// Backed off peers metrics, split by reason.
150    backed_off_peers_metrics: BackedOffPeersMetrics,
151}
152
153impl NetworkManager {
154    /// Creates the manager of a new network with [`EthNetworkPrimitives`] types.
155    ///
156    /// ```no_run
157    /// # async fn f() {
158    /// use reth_chainspec::MAINNET;
159    /// use reth_network::{NetworkConfig, NetworkManager};
160    /// use reth_tasks::Runtime;
161    /// let config = NetworkConfig::builder_with_rng_secret_key(Runtime::test())
162    ///     .build_with_noop_provider(MAINNET.clone());
163    /// let manager = NetworkManager::eth(config).await;
164    /// # }
165    /// ```
166    pub async fn eth<C: BlockNumReader + 'static>(
167        config: NetworkConfig<C, EthNetworkPrimitives>,
168    ) -> Result<Self, NetworkError> {
169        Self::new(config).await
170    }
171}
172
173impl<N: NetworkPrimitives> NetworkManager<N> {
174    /// Sets the dedicated channel for events intended for the
175    /// [`TransactionsManager`](crate::transactions::TransactionsManager).
176    pub fn with_transactions(
177        mut self,
178        tx: MemoryBoundedSender<NetworkTransactionEvent<N>>,
179    ) -> Self {
180        self.set_transactions(tx);
181        self
182    }
183
184    /// Sets the dedicated channel for events intended for the
185    /// [`TransactionsManager`](crate::transactions::TransactionsManager).
186    pub fn set_transactions(&mut self, tx: MemoryBoundedSender<NetworkTransactionEvent<N>>) {
187        self.to_transactions_manager = Some(tx);
188    }
189
190    /// Sets the dedicated channel for events intended for the
191    /// [`EthRequestHandler`](crate::eth_requests::EthRequestHandler).
192    pub fn with_eth_request_handler(mut self, tx: mpsc::Sender<IncomingEthRequest<N>>) -> Self {
193        self.set_eth_request_handler(tx);
194        self
195    }
196
197    /// Sets the dedicated channel for events intended for the
198    /// [`EthRequestHandler`](crate::eth_requests::EthRequestHandler).
199    pub fn set_eth_request_handler(&mut self, tx: mpsc::Sender<IncomingEthRequest<N>>) {
200        self.to_eth_request_handler = Some(tx);
201    }
202
203    /// Adds an additional protocol handler to the `RLPx` sub-protocol list.
204    pub fn add_rlpx_sub_protocol(&mut self, protocol: impl IntoRlpxSubProtocol) {
205        self.swarm.add_rlpx_sub_protocol(protocol)
206    }
207
208    /// Returns the [`NetworkHandle`] that can be cloned and shared.
209    ///
210    /// The [`NetworkHandle`] can be used to interact with this [`NetworkManager`]
211    pub const fn handle(&self) -> &NetworkHandle<N> {
212        &self.handle
213    }
214
215    /// Returns the secret key used for authenticating sessions.
216    pub const fn secret_key(&self) -> SecretKey {
217        self.swarm.sessions().secret_key()
218    }
219
220    #[inline]
221    fn update_poll_metrics(&self, start: Instant, poll_durations: NetworkManagerPollDurations) {
222        let metrics = &self.metrics;
223
224        let NetworkManagerPollDurations { acc_network_handle, acc_swarm } = poll_durations;
225
226        // update metrics for whole poll function
227        metrics.duration_poll_network_manager.set(start.elapsed().as_secs_f64());
228        // update poll metrics for nested items
229        metrics.acc_duration_poll_network_handle.set(acc_network_handle.as_secs_f64());
230        metrics.acc_duration_poll_swarm.set(acc_swarm.as_secs_f64());
231    }
232
233    /// Creates the manager of a new network.
234    ///
235    /// The [`NetworkManager`] is an endless future that needs to be polled in order to advance the
236    /// state of the entire network.
237    pub async fn new<C: BlockNumReader + 'static>(
238        config: NetworkConfig<C, N>,
239    ) -> Result<Self, NetworkError> {
240        let NetworkConfig {
241            client,
242            secret_key,
243            discovery_v4_addr,
244            mut discovery_v4_config,
245            mut discovery_v5_config,
246            listener_addr,
247            peers_config,
248            sessions_config,
249            chain_id,
250            block_import,
251            network_mode,
252            boot_nodes,
253            executor,
254            hello_message,
255            status,
256            fork_filter,
257            dns_discovery_config,
258            extra_protocols,
259            tx_gossip_disabled,
260            transactions_manager_config: _,
261            nat,
262            handshake,
263            eth_max_message_size,
264            p2p_max_payload_size,
265            required_block_hashes,
266        } = config;
267
268        let peers_manager = PeersManager::new(peers_config);
269        let peers_handle = peers_manager.handle();
270
271        let incoming = ConnectionListener::bind(listener_addr).await.map_err(|err| {
272            NetworkError::from_io_error(err, ServiceKind::Listener(listener_addr))
273        })?;
274
275        // retrieve the tcp address of the socket
276        let listener_addr = incoming.local_address();
277
278        // resolve boot nodes
279        let resolved_boot_nodes =
280            futures::future::try_join_all(boot_nodes.iter().map(|record| record.resolve())).await?;
281
282        if let Some(disc_config) = discovery_v4_config.as_mut() {
283            // merge configured boot nodes
284            disc_config.bootstrap_nodes.extend(resolved_boot_nodes.clone());
285            // add the forkid entry for EIP-868, but wrap it in an `EnrForkIdEntry` for proper
286            // encoding
287            disc_config.add_eip868_pair("eth", EnrForkIdEntry::from(status.forkid));
288        }
289
290        if let Some(discv5) = discovery_v5_config.as_mut() {
291            // merge configured boot nodes
292            discv5.extend_unsigned_boot_nodes(resolved_boot_nodes)
293        }
294
295        let discovery = Discovery::new(
296            listener_addr,
297            discovery_v4_addr,
298            secret_key,
299            discovery_v4_config,
300            discovery_v5_config,
301            dns_discovery_config,
302            nat.clone(),
303        )
304        .await?;
305        // need to retrieve the addr here since provided port could be `0`
306        let local_peer_id = discovery.local_id();
307        let discv4 = discovery.discv4();
308        let discv5 = discovery.discv5();
309
310        let num_active_peers = Arc::new(AtomicUsize::new(0));
311
312        let sessions = SessionManager::new(
313            secret_key,
314            sessions_config,
315            executor,
316            status,
317            hello_message,
318            fork_filter,
319            extra_protocols,
320            handshake,
321            eth_max_message_size,
322            p2p_max_payload_size,
323            network_mode.is_stake(),
324        );
325
326        let state = NetworkState::new(
327            crate::state::BlockNumReader::new(client),
328            discovery,
329            peers_manager,
330            Arc::clone(&num_active_peers),
331        );
332
333        let swarm = Swarm::new(incoming, sessions, state);
334
335        let (to_manager_tx, from_handle_rx) = mpsc::unbounded_channel();
336
337        let event_sender: EventSender<NetworkEvent<PeerRequest<N>>> = Default::default();
338
339        let handle = NetworkHandle::new(
340            Arc::clone(&num_active_peers),
341            Arc::new(Mutex::new(listener_addr)),
342            to_manager_tx,
343            secret_key,
344            local_peer_id,
345            peers_handle,
346            network_mode,
347            Arc::new(AtomicU64::new(chain_id)),
348            tx_gossip_disabled,
349            discv4,
350            discv5,
351            event_sender.clone(),
352            nat,
353        );
354
355        // Spawn required block peer filter if configured
356        if !required_block_hashes.is_empty() {
357            let filter = RequiredBlockFilter::new(handle.clone(), required_block_hashes);
358            filter.spawn();
359        }
360
361        Ok(Self {
362            swarm,
363            handle,
364            from_handle_rx: UnboundedReceiverStream::new(from_handle_rx),
365            block_import,
366            event_sender,
367            to_transactions_manager: None,
368            to_eth_request_handler: None,
369            num_active_peers,
370            metrics: Default::default(),
371            disconnect_metrics: Default::default(),
372            closed_sessions_metrics: Default::default(),
373            pending_session_failure_metrics: Default::default(),
374            backed_off_peers_metrics: Default::default(),
375        })
376    }
377
378    /// Create a new [`NetworkManager`] instance and start a [`NetworkBuilder`] to configure all
379    /// components of the network
380    ///
381    /// ```
382    /// use reth_network::{
383    ///     config::rng_secret_key, EthNetworkPrimitives, NetworkConfig, NetworkManager,
384    /// };
385    /// use reth_network_peers::mainnet_nodes;
386    /// use reth_storage_api::noop::NoopProvider;
387    /// use reth_tasks::Runtime;
388    /// use reth_transaction_pool::TransactionPool;
389    /// async fn launch<Pool: TransactionPool>(pool: Pool) {
390    ///     // This block provider implementation is used for testing purposes.
391    ///     let client = NoopProvider::default();
392    ///
393    ///     // The key that's used for encrypting sessions and to identify our node.
394    ///     let local_key = rng_secret_key();
395    ///
396    ///     let config = NetworkConfig::<_, EthNetworkPrimitives>::builder(local_key, Runtime::test())
397    ///         .boot_nodes(mainnet_nodes())
398    ///         .build(client.clone());
399    ///     let transactions_manager_config = config.transactions_manager_config.clone();
400    ///
401    ///     // create the network instance
402    ///     let (handle, network, transactions, request_handler) = NetworkManager::builder(config)
403    ///         .await
404    ///         .unwrap()
405    ///         .transactions(pool, transactions_manager_config)
406    ///         .request_handler(client)
407    ///         .split_with_handle();
408    /// }
409    /// ```
410    pub async fn builder<C: BlockNumReader + 'static>(
411        config: NetworkConfig<C, N>,
412    ) -> Result<NetworkBuilder<(), (), N>, NetworkError> {
413        let network = Self::new(config).await?;
414        Ok(network.into_builder())
415    }
416
417    /// Create a [`NetworkBuilder`] to configure all components of the network
418    pub const fn into_builder(self) -> NetworkBuilder<(), (), N> {
419        NetworkBuilder { network: self, transactions: (), request_handler: () }
420    }
421
422    /// Returns the [`SocketAddr`] that listens for incoming tcp connections.
423    pub const fn local_addr(&self) -> SocketAddr {
424        self.swarm.listener().local_address()
425    }
426
427    /// How many peers we're currently connected to.
428    pub fn num_connected_peers(&self) -> usize {
429        self.swarm.state().num_active_peers()
430    }
431
432    /// Returns the [`PeerId`] used in the network.
433    pub fn peer_id(&self) -> &PeerId {
434        self.handle.peer_id()
435    }
436
437    /// Returns an iterator over all peers in the peer set.
438    pub fn all_peers(&self) -> impl Iterator<Item = NodeRecord> + '_ {
439        self.swarm.peers().iter_peers()
440    }
441
442    /// Returns the number of peers in the peer set.
443    pub fn num_known_peers(&self) -> usize {
444        self.swarm.peers().num_known_peers()
445    }
446
447    /// Returns a new [`PeersHandle`] that can be cloned and shared.
448    ///
449    /// The [`PeersHandle`] can be used to interact with the network's peer set.
450    pub fn peers_handle(&self) -> PeersHandle {
451        self.swarm.peers().handle()
452    }
453
454    /// Collect the peers from the [`NetworkManager`] and write them to the given
455    /// `persistent_peers_file`.
456    ///
457    /// Only persists peers that are not currently backed off or banned. Includes metadata like
458    /// peer kind, fork ID, and reputation.
459    pub fn write_peers_to_file(&self, persistent_peers_file: &Path) -> Result<(), FsPathError> {
460        let peers = self.swarm.peers().persistable_peers().collect::<Vec<_>>();
461        persistent_peers_file.parent().map(fs::create_dir_all).transpose()?;
462        reth_fs_util::write_json_file(persistent_peers_file, &peers)?;
463        Ok(())
464    }
465
466    /// Returns a new [`FetchClient`] that can be cloned and shared.
467    ///
468    /// The [`FetchClient`] is the entrypoint for sending requests to the network, including
469    /// `snap/2` requests via its [`SnapClient`](reth_network_p2p::snap::client::SnapClient) impl.
470    pub fn fetch_client(&self) -> FetchClient<N> {
471        self.swarm.state().fetch_client()
472    }
473
474    /// Returns the current [`NetworkStatus`] for the local node.
475    pub fn status(&self) -> NetworkStatus {
476        let sessions = self.swarm.sessions();
477        let status = sessions.status();
478        let hello_message = sessions.hello_message();
479
480        #[expect(deprecated)]
481        NetworkStatus {
482            client_version: hello_message.client_version,
483            protocol_version: hello_message.protocol_version as u64,
484            eth_protocol_info: EthProtocolInfo {
485                difficulty: None,
486                head: status.blockhash,
487                network: status.chain.id(),
488                genesis: status.genesis,
489                config: Default::default(),
490            },
491            capabilities: hello_message
492                .protocols
493                .into_iter()
494                .map(|protocol| protocol.cap)
495                .collect(),
496        }
497    }
498
499    /// Sends an event to the [`TransactionsManager`](crate::transactions::TransactionsManager) if
500    /// configured.
501    fn notify_tx_manager(&self, event: NetworkTransactionEvent<N>) {
502        if let Some(ref tx) = self.to_transactions_manager &&
503            let Err(e) = tx.try_send(event)
504        {
505            match e {
506                TrySendError::Full(_) => {
507                    trace!(target: "net", "Transaction events channel at capacity, dropping event");
508                    self.metrics.total_dropped_tx_events_at_full_capacity.increment(1);
509                }
510                TrySendError::Closed(_) => {}
511            }
512        }
513    }
514
515    /// Sends an event to the [`EthRequestManager`](crate::eth_requests::EthRequestHandler) if
516    /// configured.
517    fn delegate_eth_request(&self, event: IncomingEthRequest<N>) {
518        if let Some(ref reqs) = self.to_eth_request_handler {
519            let _ = reqs.try_send(event).map_err(|e| {
520                if let TrySendError::Full(_) = e {
521                    debug!(target:"net", "EthRequestHandler channel is full!");
522                    self.metrics.total_dropped_eth_requests_at_full_capacity.increment(1);
523                }
524            });
525        }
526    }
527
528    /// Handle an incoming request from the peer
529    fn on_eth_request(&self, peer_id: PeerId, req: PeerRequest<N>) {
530        match req {
531            PeerRequest::GetBlockHeaders { request, response } => {
532                self.delegate_eth_request(IncomingEthRequest::GetBlockHeaders {
533                    peer_id,
534                    request,
535                    response,
536                })
537            }
538            PeerRequest::GetBlockBodies { request, response } => {
539                self.delegate_eth_request(IncomingEthRequest::GetBlockBodies {
540                    peer_id,
541                    request,
542                    response,
543                })
544            }
545            PeerRequest::GetNodeData { request, response } => {
546                self.delegate_eth_request(IncomingEthRequest::GetNodeData {
547                    peer_id,
548                    request,
549                    response,
550                })
551            }
552            PeerRequest::GetReceipts { request, response } => {
553                self.delegate_eth_request(IncomingEthRequest::GetReceipts {
554                    peer_id,
555                    request,
556                    response,
557                })
558            }
559            PeerRequest::GetReceipts69 { request, response } => {
560                self.delegate_eth_request(IncomingEthRequest::GetReceipts69 {
561                    peer_id,
562                    request,
563                    response,
564                })
565            }
566            PeerRequest::GetReceipts70 { request, response } => {
567                self.delegate_eth_request(IncomingEthRequest::GetReceipts70 {
568                    peer_id,
569                    request,
570                    response,
571                })
572            }
573            PeerRequest::GetBlockAccessLists { request, response } => {
574                self.delegate_eth_request(IncomingEthRequest::GetBlockAccessLists {
575                    peer_id,
576                    request,
577                    response,
578                })
579            }
580            PeerRequest::GetCells { request, response } => self
581                .delegate_eth_request(IncomingEthRequest::GetCells { peer_id, request, response }),
582            PeerRequest::GetPooledTransactions { request, response } => {
583                self.notify_tx_manager(NetworkTransactionEvent::GetPooledTransactions {
584                    peer_id,
585                    request,
586                    response,
587                });
588            }
589            PeerRequest::GetSnap { request, response } => self
590                .delegate_eth_request(IncomingEthRequest::GetSnap { peer_id, request, response }),
591        }
592    }
593
594    /// Invoked after a `NewBlock` message from the peer was validated
595    fn on_block_import_result(&mut self, event: BlockImportEvent<N::NewBlockPayload>) {
596        match event {
597            BlockImportEvent::Announcement(validation) => match validation {
598                BlockValidation::ValidHeader { block } => {
599                    self.swarm.state_mut().announce_new_block(block);
600                }
601                BlockValidation::ValidBlock { block } => {
602                    self.swarm.state_mut().announce_new_block_hash(block);
603                }
604            },
605            BlockImportEvent::Outcome(outcome) => {
606                let BlockImportOutcome { peer, result } = outcome;
607                match result {
608                    Ok(validated_block) => match validated_block {
609                        BlockValidation::ValidHeader { block } => {
610                            self.swarm.state_mut().update_peer_block(
611                                &peer,
612                                block.hash,
613                                block.number(),
614                            );
615                            self.swarm.state_mut().announce_new_block(block);
616                        }
617                        BlockValidation::ValidBlock { block } => {
618                            self.swarm.state_mut().announce_new_block_hash(block);
619                        }
620                    },
621                    Err(_err) => {
622                        self.swarm
623                            .state_mut()
624                            .peers_mut()
625                            .apply_reputation_change(&peer, ReputationChangeKind::BadBlock);
626                    }
627                }
628            }
629        }
630    }
631
632    /// Enforces [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p) consensus rules for the network protocol
633    ///
634    /// Depending on the mode of the network:
635    ///    - disconnect peer if in POS
636    ///    - execute the closure if in POW
637    fn within_pow_or_disconnect<F>(&mut self, peer_id: PeerId, only_pow: F)
638    where
639        F: FnOnce(&mut Self),
640    {
641        // reject message in POS
642        if self.handle.mode().is_stake() {
643            // connections to peers which send invalid messages should be terminated
644            self.swarm
645                .sessions_mut()
646                .disconnect(peer_id, Some(DisconnectReason::SubprotocolSpecific));
647        } else {
648            only_pow(self);
649        }
650    }
651
652    /// Handles a received Message from the peer's session.
653    fn on_peer_message(&mut self, peer_id: PeerId, msg: PeerMessage<N>) {
654        match msg {
655            PeerMessage::NewBlockHashes(hashes) => {
656                self.within_pow_or_disconnect(peer_id, |this| {
657                    // update peer's state, to track what blocks this peer has seen
658                    this.swarm.state_mut().on_new_block_hashes(peer_id, hashes.to_vec());
659                    // start block import process for the hashes
660                    this.block_import.on_new_block(peer_id, NewBlockEvent::Hashes(hashes));
661                })
662            }
663            PeerMessage::NewBlock(block) => {
664                self.within_pow_or_disconnect(peer_id, move |this| {
665                    this.swarm.state_mut().on_new_block(peer_id, block.hash);
666                    // start block import process
667                    this.block_import.on_new_block(peer_id, NewBlockEvent::Block(block));
668                });
669            }
670            PeerMessage::PooledTransactions(msg) => {
671                self.notify_tx_manager(NetworkTransactionEvent::IncomingPooledTransactionHashes {
672                    peer_id,
673                    msg,
674                });
675            }
676            PeerMessage::EthRequest(req) => {
677                self.on_eth_request(peer_id, req);
678            }
679            PeerMessage::ReceivedTransaction(msg) => {
680                self.notify_tx_manager(NetworkTransactionEvent::IncomingTransactions {
681                    peer_id,
682                    msg,
683                });
684            }
685            PeerMessage::SendTransactions(_) | PeerMessage::SendBroadcastPoolTransactions(_) => {
686                unreachable!("Not emitted by session")
687            }
688            PeerMessage::BlockRangeUpdated(_) => {}
689            PeerMessage::Other(other) => {
690                debug!(target: "net", message_id=%other.id, "Ignoring unsupported message");
691            }
692        }
693    }
694
695    /// Handler for received messages from a handle
696    fn on_handle_message(&mut self, msg: NetworkHandleMessage<N>) {
697        match msg {
698            NetworkHandleMessage::DiscoveryListener(tx) => {
699                self.swarm.state_mut().discovery_mut().add_listener(tx);
700            }
701            NetworkHandleMessage::AnnounceBlock(block, hash) => {
702                if self.handle.mode().is_stake() {
703                    // See [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p)
704                    warn!(target: "net", "Peer performed block propagation, but it is not supported in proof of stake (EIP-3675)");
705                    return
706                }
707                let msg = NewBlockMessage { hash, block: Arc::new(block) };
708                self.swarm.state_mut().announce_new_block(msg);
709            }
710            NetworkHandleMessage::EthRequest { peer_id, request } => {
711                self.swarm.sessions_mut().send_message(&peer_id, PeerMessage::EthRequest(request))
712            }
713            NetworkHandleMessage::SendTransaction { peer_id, msg } => {
714                self.swarm.sessions_mut().send_message(&peer_id, PeerMessage::SendTransactions(msg))
715            }
716            NetworkHandleMessage::SendBroadcastPoolTransactions { peer_id, msg } => self
717                .swarm
718                .sessions_mut()
719                .send_message(&peer_id, PeerMessage::SendBroadcastPoolTransactions(msg)),
720            NetworkHandleMessage::SendPooledTransactionHashes { peer_id, msg } => self
721                .swarm
722                .sessions_mut()
723                .send_message(&peer_id, PeerMessage::PooledTransactions(msg)),
724            NetworkHandleMessage::AddTrustedPeerId(peer_id) => {
725                self.swarm.state_mut().add_trusted_peer_id(peer_id);
726            }
727            NetworkHandleMessage::AddTrustedPeerNode(trusted_peer) => {
728                if !self.swarm.is_shutting_down() {
729                    self.swarm.state_mut().add_trusted_peer_node(trusted_peer);
730                }
731            }
732            NetworkHandleMessage::AddPeerAddress(peer, kind, addr) => {
733                // only add peer if we are not shutting down
734                if !self.swarm.is_shutting_down() {
735                    self.swarm.state_mut().add_peer_kind(peer, kind, addr);
736                }
737            }
738            NetworkHandleMessage::RemovePeer(peer_id, kind) => {
739                self.swarm.state_mut().remove_peer_kind(peer_id, kind);
740            }
741            NetworkHandleMessage::DisconnectPeer(peer_id, reason) => {
742                self.swarm.sessions_mut().disconnect(peer_id, reason);
743            }
744            NetworkHandleMessage::BanPeer(peer_id) => {
745                self.swarm.peers_mut().ban_peer_by_admin(peer_id);
746            }
747            NetworkHandleMessage::UnbanPeer(peer_id) => {
748                self.swarm.peers_mut().unban_peer_by_admin(peer_id);
749            }
750            NetworkHandleMessage::ConnectPeer(peer_id, kind, addr) => {
751                self.swarm.state_mut().add_and_connect(peer_id, kind, addr);
752            }
753            NetworkHandleMessage::SetNetworkState(net_state) => {
754                // Sets network connection state between Active and Hibernate.
755                // If hibernate stops the node to fill new outbound
756                // connections, this is beneficial for sync stages that do not require a network
757                // connection.
758                self.swarm.on_network_state_change(net_state);
759            }
760
761            NetworkHandleMessage::Shutdown(tx) => {
762                self.perform_network_shutdown();
763                let _ = tx.send(());
764            }
765            NetworkHandleMessage::ReputationChange(peer_id, kind) => {
766                self.swarm.peers_mut().apply_reputation_change(&peer_id, kind);
767            }
768            NetworkHandleMessage::GetReputationById(peer_id, tx) => {
769                let _ = tx.send(self.swarm.peers().get_reputation(&peer_id));
770            }
771            NetworkHandleMessage::FetchClient(tx) => {
772                let _ = tx.send(self.fetch_client());
773            }
774            NetworkHandleMessage::GetStatus(tx) => {
775                let _ = tx.send(self.status());
776            }
777            NetworkHandleMessage::StatusUpdate { head } => {
778                if let Some(transition) = self.swarm.sessions_mut().on_status_update(head) {
779                    self.swarm.state_mut().update_fork_id(transition.current);
780                }
781            }
782            NetworkHandleMessage::SetForkFilter { fork_filter } => {
783                let fork_id = self.swarm.sessions_mut().set_fork_filter(fork_filter);
784                self.swarm.state_mut().update_fork_id(fork_id);
785            }
786            NetworkHandleMessage::GetPeerInfos(tx) => {
787                let _ = tx.send(self.get_peer_infos());
788            }
789            NetworkHandleMessage::GetPeerInfoById(peer_id, tx) => {
790                let _ = tx.send(self.get_peer_info_by_id(peer_id));
791            }
792            NetworkHandleMessage::GetPeerInfosByIds(peer_ids, tx) => {
793                let _ = tx.send(self.get_peer_infos_by_ids(peer_ids));
794            }
795            NetworkHandleMessage::GetPeerInfosByPeerKind(kind, tx) => {
796                let peer_ids = self.swarm.peers().peers_by_kind(kind);
797                let _ = tx.send(self.get_peer_infos_by_ids(peer_ids));
798            }
799            NetworkHandleMessage::AddRlpxSubProtocol(proto) => self.add_rlpx_sub_protocol(proto),
800            NetworkHandleMessage::GetTransactionsHandle(tx) => {
801                if let Some(ref tx_inner) = self.to_transactions_manager {
802                    let _ = tx_inner.try_send(NetworkTransactionEvent::GetTransactionsHandle(tx));
803                } else {
804                    let _ = tx.send(None);
805                }
806            }
807            NetworkHandleMessage::InternalBlockRangeUpdate(block_range_update) => {
808                self.swarm.sessions_mut().update_advertised_block_range(block_range_update);
809            }
810            NetworkHandleMessage::EthMessage { peer_id, message } => {
811                self.swarm.sessions_mut().send_message(&peer_id, message)
812            }
813        }
814    }
815
816    fn on_swarm_event(&mut self, event: SwarmEvent<N>) {
817        // handle event
818        match event {
819            SwarmEvent::ValidMessage { peer_id, message } => self.on_peer_message(peer_id, message),
820            SwarmEvent::TcpListenerClosed { remote_addr } => {
821                trace!(target: "net", ?remote_addr, "TCP listener closed.");
822            }
823            SwarmEvent::TcpListenerError(err) => {
824                trace!(target: "net", %err, "TCP connection error.");
825            }
826            SwarmEvent::IncomingTcpConnection { remote_addr, session_id } => {
827                trace!(target: "net", ?session_id, ?remote_addr, "Incoming connection");
828                self.metrics.total_incoming_connections.increment(1);
829                self.update_pending_connection_metrics();
830                self.metrics
831                    .incoming_connections
832                    .set(self.swarm.peers().num_inbound_connections() as f64);
833            }
834            SwarmEvent::OutgoingTcpConnection { remote_addr, peer_id } => {
835                trace!(target: "net", ?remote_addr, ?peer_id, "Starting outbound connection.");
836                self.metrics.total_outgoing_connections.increment(1);
837                self.update_pending_connection_metrics()
838            }
839            SwarmEvent::SessionEstablished {
840                peer_id,
841                remote_addr,
842                client_version,
843                capabilities,
844                version,
845                messages,
846                status,
847                direction,
848            } => {
849                let total_active = self.num_active_peers.fetch_add(1, Ordering::Relaxed) + 1;
850                self.metrics.connected_peers.set(total_active as f64);
851                debug!(
852                    target: "net",
853                    ?remote_addr,
854                    %client_version,
855                    ?peer_id,
856                    ?total_active,
857                    kind=%direction,
858                    peer_enode=%NodeRecord::new(remote_addr, peer_id),
859                    "Session established"
860                );
861
862                if direction.is_incoming() {
863                    self.swarm
864                        .state_mut()
865                        .peers_mut()
866                        .on_incoming_session_established(peer_id, remote_addr);
867                }
868
869                if direction.is_outgoing() {
870                    self.swarm.peers_mut().on_active_outgoing_established(peer_id);
871                }
872
873                self.update_active_connection_metrics();
874                self.update_pending_connection_metrics();
875
876                let peer_kind = self
877                    .swarm
878                    .state()
879                    .peers()
880                    .peer_by_id(peer_id)
881                    .map(|(_, kind)| kind)
882                    .unwrap_or_default();
883                let session_info = SessionInfo {
884                    peer_id,
885                    remote_addr,
886                    client_version,
887                    capabilities,
888                    status,
889                    version,
890                    peer_kind,
891                };
892
893                self.event_sender
894                    .notify(NetworkEvent::ActivePeerSession { info: session_info, messages });
895            }
896            SwarmEvent::PeerAdded(peer_id) => {
897                trace!(target: "net", ?peer_id, "Peer added");
898                self.event_sender.notify(NetworkEvent::Peer(PeerEvent::PeerAdded(peer_id)));
899                self.metrics.tracked_peers.set(self.swarm.peers().num_known_peers() as f64);
900            }
901            SwarmEvent::PeerRemoved(peer_id) => {
902                trace!(target: "net", ?peer_id, "Peer dropped");
903                self.event_sender.notify(NetworkEvent::Peer(PeerEvent::PeerRemoved(peer_id)));
904                self.metrics.tracked_peers.set(self.swarm.peers().num_known_peers() as f64);
905            }
906            SwarmEvent::SessionClosed { peer_id, remote_addr, error } => {
907                let total_active = self.num_active_peers.fetch_sub(1, Ordering::Relaxed) - 1;
908                self.metrics.connected_peers.set(total_active as f64);
909                trace!(
910                    target: "net",
911                    ?remote_addr,
912                    ?peer_id,
913                    ?total_active,
914                    ?error,
915                    "Session disconnected"
916                );
917
918                // Capture direction before state is reset to Idle
919                let is_inbound = self.swarm.peers().is_inbound_peer(&peer_id);
920
921                let reason = if let Some(ref err) = error {
922                    // If the connection was closed due to an error, we report
923                    // the peer
924                    self.swarm.peers_mut().on_active_session_dropped(&remote_addr, &peer_id, err);
925                    self.backed_off_peers_metrics.increment_for_reason(
926                        BackoffReason::from_disconnect(err.as_disconnected()),
927                    );
928                    err.as_disconnected()
929                } else {
930                    // Gracefully disconnected
931                    self.swarm.peers_mut().on_active_session_gracefully_closed(peer_id);
932                    self.backed_off_peers_metrics
933                        .increment_for_reason(BackoffReason::GracefulClose);
934                    None
935                };
936                self.closed_sessions_metrics.active.increment(1);
937                self.update_active_connection_metrics();
938
939                if let Some(reason) = reason {
940                    if is_inbound {
941                        self.disconnect_metrics.increment_inbound(reason);
942                    } else {
943                        self.disconnect_metrics.increment_outbound(reason);
944                    }
945                }
946                self.metrics.backed_off_peers.set(self.swarm.peers().num_backed_off_peers() as f64);
947                self.event_sender
948                    .notify(NetworkEvent::Peer(PeerEvent::SessionClosed { peer_id, reason }));
949            }
950            SwarmEvent::IncomingPendingSessionClosed { remote_addr, error } => {
951                trace!(
952                    target: "net",
953                    ?remote_addr,
954                    ?error,
955                    "Incoming pending session failed"
956                );
957
958                if let Some(ref err) = error {
959                    self.swarm
960                        .state_mut()
961                        .peers_mut()
962                        .on_incoming_pending_session_dropped(remote_addr, err);
963                    self.pending_session_failure_metrics.inbound.increment(1);
964                    if let Some(reason) = err.as_disconnected() {
965                        self.disconnect_metrics.increment_inbound(reason);
966                    }
967                } else {
968                    self.swarm
969                        .state_mut()
970                        .peers_mut()
971                        .on_incoming_pending_session_gracefully_closed();
972                }
973                self.closed_sessions_metrics.incoming_pending.increment(1);
974                self.update_pending_connection_metrics();
975                self.metrics
976                    .incoming_connections
977                    .set(self.swarm.peers().num_inbound_connections() as f64);
978            }
979            SwarmEvent::OutgoingPendingSessionClosed { remote_addr, peer_id, error } => {
980                trace!(
981                    target: "net",
982                    ?remote_addr,
983                    ?peer_id,
984                    ?error,
985                    "Outgoing pending session failed"
986                );
987
988                if let Some(ref err) = error {
989                    self.swarm.peers_mut().on_outgoing_pending_session_dropped(
990                        &remote_addr,
991                        &peer_id,
992                        err,
993                    );
994                    self.pending_session_failure_metrics.outbound.increment(1);
995                    self.backed_off_peers_metrics.increment_for_reason(
996                        BackoffReason::from_disconnect(err.as_disconnected()),
997                    );
998                    if let Some(reason) = err.as_disconnected() {
999                        self.disconnect_metrics.increment_outbound(reason);
1000                    }
1001                } else {
1002                    self.swarm
1003                        .state_mut()
1004                        .peers_mut()
1005                        .on_outgoing_pending_session_gracefully_closed(&peer_id);
1006                }
1007                self.closed_sessions_metrics.outgoing_pending.increment(1);
1008                self.update_pending_connection_metrics();
1009                self.metrics.backed_off_peers.set(self.swarm.peers().num_backed_off_peers() as f64);
1010            }
1011            SwarmEvent::OutgoingConnectionError { remote_addr, peer_id, error } => {
1012                trace!(
1013                    target: "net",
1014                    ?remote_addr,
1015                    ?peer_id,
1016                    %error,
1017                    "Outgoing connection error"
1018                );
1019
1020                self.swarm.peers_mut().on_outgoing_connection_failure(
1021                    &remote_addr,
1022                    &peer_id,
1023                    &error,
1024                );
1025
1026                self.backed_off_peers_metrics.increment_for_reason(BackoffReason::ConnectionError);
1027                self.metrics.backed_off_peers.set(self.swarm.peers().num_backed_off_peers() as f64);
1028                self.update_pending_connection_metrics();
1029            }
1030            SwarmEvent::BadMessage { peer_id } => {
1031                self.swarm
1032                    .state_mut()
1033                    .peers_mut()
1034                    .apply_reputation_change(&peer_id, ReputationChangeKind::BadMessage);
1035                self.metrics.invalid_messages_received.increment(1);
1036            }
1037            SwarmEvent::ProtocolBreach { peer_id } => {
1038                self.swarm
1039                    .state_mut()
1040                    .peers_mut()
1041                    .apply_reputation_change(&peer_id, ReputationChangeKind::BadProtocol);
1042            }
1043        }
1044    }
1045
1046    /// Returns [`PeerInfo`] for all connected peers
1047    fn get_peer_infos(&self) -> Vec<PeerInfo> {
1048        self.swarm
1049            .sessions()
1050            .active_sessions()
1051            .iter()
1052            .filter_map(|(&peer_id, session)| {
1053                self.swarm
1054                    .state()
1055                    .peers()
1056                    .peer_by_id(peer_id)
1057                    .map(|(record, kind)| session.peer_info(&record, kind))
1058            })
1059            .collect()
1060    }
1061
1062    /// Returns [`PeerInfo`] for a given peer.
1063    ///
1064    /// Returns `None` if there's no active session to the peer.
1065    fn get_peer_info_by_id(&self, peer_id: PeerId) -> Option<PeerInfo> {
1066        self.swarm.sessions().active_sessions().get(&peer_id).and_then(|session| {
1067            self.swarm
1068                .state()
1069                .peers()
1070                .peer_by_id(peer_id)
1071                .map(|(record, kind)| session.peer_info(&record, kind))
1072        })
1073    }
1074
1075    /// Returns [`PeerInfo`] for a given peers.
1076    ///
1077    /// Ignore the non-active peer.
1078    fn get_peer_infos_by_ids(&self, peer_ids: impl IntoIterator<Item = PeerId>) -> Vec<PeerInfo> {
1079        peer_ids.into_iter().filter_map(|peer_id| self.get_peer_info_by_id(peer_id)).collect()
1080    }
1081
1082    /// Updates the metrics for active,established connections
1083    #[inline]
1084    fn update_active_connection_metrics(&self) {
1085        self.metrics.incoming_connections.set(self.swarm.peers().num_inbound_connections() as f64);
1086        self.metrics.outgoing_connections.set(self.swarm.peers().num_outbound_connections() as f64);
1087    }
1088
1089    /// Updates the metrics for pending connections
1090    #[inline]
1091    fn update_pending_connection_metrics(&self) {
1092        self.metrics
1093            .pending_outgoing_connections
1094            .set(self.swarm.peers().num_pending_outbound_connections() as f64);
1095        self.metrics
1096            .total_pending_connections
1097            .set(self.swarm.sessions().num_pending_connections() as f64);
1098    }
1099
1100    /// Drives the [`NetworkManager`] future until a [`GracefulShutdown`] signal is received.
1101    ///
1102    /// This invokes the given function `shutdown_hook` while holding the graceful shutdown guard.
1103    pub async fn run_until_graceful_shutdown<F, R>(
1104        mut self,
1105        shutdown: GracefulShutdown,
1106        shutdown_hook: F,
1107    ) -> R
1108    where
1109        F: FnOnce(Self) -> R,
1110    {
1111        let mut graceful_guard = None;
1112        tokio::select! {
1113            _ = &mut self => {},
1114            guard = shutdown => {
1115                graceful_guard = Some(guard);
1116            },
1117        }
1118
1119        self.perform_network_shutdown();
1120        let res = shutdown_hook(self);
1121        drop(graceful_guard);
1122        res
1123    }
1124
1125    /// Performs a graceful network shutdown by stopping new connections from being accepted while
1126    /// draining current and pending connections.
1127    fn perform_network_shutdown(&mut self) {
1128        // Set connection status to `Shutdown`. Stops node from accepting
1129        // new incoming connections as well as sending connection requests to newly
1130        // discovered nodes.
1131        self.swarm.on_shutdown_requested();
1132        // Disconnect all active connections
1133        self.swarm.sessions_mut().disconnect_all(Some(DisconnectReason::ClientQuitting));
1134        // drop pending connections
1135        self.swarm.sessions_mut().disconnect_all_pending();
1136    }
1137}
1138
1139impl<N: NetworkPrimitives> Future for NetworkManager<N> {
1140    type Output = ();
1141
1142    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1143        let start = Instant::now();
1144        let mut poll_durations = NetworkManagerPollDurations::default();
1145
1146        let this = self.get_mut();
1147
1148        // poll new block imports (expected to be a noop for POS)
1149        while let Poll::Ready(outcome) = this.block_import.poll(cx) {
1150            this.on_block_import_result(outcome);
1151        }
1152
1153        // These loops drive the entire state of network and does a lot of work. Under heavy load
1154        // (many messages/events), data may arrive faster than it can be processed (incoming
1155        // messages/requests -> events), and it is possible that more data has already arrived by
1156        // the time an internal event is processed. Which could turn this loop into a busy loop.
1157        // Without yielding back to the executor, it can starve other tasks waiting on that
1158        // executor to execute them, or drive underlying resources To prevent this, we
1159        // preemptively return control when the `budget` is exhausted. The value itself is chosen
1160        // somewhat arbitrarily, it is high enough so the swarm can make meaningful progress but
1161        // low enough that this loop does not starve other tasks for too long. If the budget is
1162        // exhausted we manually yield back control to the (coop) scheduler. This manual yield
1163        // point should prevent situations where polling appears to be frozen. See also
1164        // <https://tokio.rs/blog/2020-04-preemption> And tokio's docs on cooperative scheduling
1165        // <https://docs.rs/tokio/latest/tokio/task/#cooperative-scheduling>
1166        //
1167        // Testing has shown that this loop naturally reaches the pending state within 1-5
1168        // iterations in << 100µs in most cases. On average it requires ~50µs, which is inside the
1169        // range of what's recommended as rule of thumb.
1170        // <https://ryhl.io/blog/async-what-is-blocking/>
1171
1172        // process incoming messages from a handle (`TransactionsManager` has one)
1173        //
1174        // will only be closed if the channel was deliberately closed since we always have an
1175        // instance of `NetworkHandle`
1176        let start_network_handle = Instant::now();
1177        let maybe_more_handle_messages = poll_nested_stream_with_budget!(
1178            "net",
1179            "Network message channel",
1180            DEFAULT_BUDGET_TRY_DRAIN_NETWORK_HANDLE_CHANNEL,
1181            this.from_handle_rx.poll_next_unpin(cx),
1182            |msg| this.on_handle_message(msg),
1183            error!("Network channel closed");
1184        );
1185        poll_durations.acc_network_handle = start_network_handle.elapsed();
1186
1187        // process incoming messages from the network
1188        let maybe_more_swarm_events = poll_nested_stream_with_budget!(
1189            "net",
1190            "Swarm events stream",
1191            DEFAULT_BUDGET_TRY_DRAIN_SWARM,
1192            this.swarm.poll_next_unpin(cx),
1193            |event| this.on_swarm_event(event),
1194        );
1195        poll_durations.acc_swarm =
1196            start_network_handle.elapsed() - poll_durations.acc_network_handle;
1197
1198        // all streams are fully drained and import futures pending
1199        if maybe_more_handle_messages || maybe_more_swarm_events {
1200            // make sure we're woken up again
1201            cx.waker().wake_by_ref();
1202            return Poll::Pending
1203        }
1204
1205        this.update_poll_metrics(start, poll_durations);
1206
1207        Poll::Pending
1208    }
1209}
1210
1211#[derive(Debug, Default)]
1212struct NetworkManagerPollDurations {
1213    acc_network_handle: Duration,
1214    acc_swarm: Duration,
1215}
1216
1217#[cfg(test)]
1218mod tests {
1219    use super::*;
1220    use crate::{NetworkConfigBuilder, PeersConfig, SessionsConfig};
1221    use futures::{future::poll_fn, task::noop_waker_ref};
1222    use metrics::{atomics::AtomicU64, Gauge};
1223    use reth_network_types::{PeerAddr, PeerKind, SessionLimits};
1224    use reth_storage_api::noop::NoopProvider;
1225    use reth_tasks::Runtime;
1226    use tokio::net::{TcpListener, TcpStream};
1227
1228    #[tokio::test]
1229    async fn rejected_dials_release_slots_and_refill() {
1230        for limit in [Some(0), Some(1), None] {
1231            let mut networks = Vec::new();
1232            for index in 0..3 {
1233                let sessions = SessionsConfig {
1234                    limits: SessionLimits {
1235                        max_pending_outbound: if index == 0 { limit } else { None },
1236                        ..Default::default()
1237                    },
1238                    ..Default::default()
1239                };
1240                let config = NetworkConfigBuilder::<EthNetworkPrimitives>::new(
1241                    SecretKey::new(&mut rand::rng()),
1242                    Runtime::test(),
1243                )
1244                .disable_discovery()
1245                .listener_addr("127.0.0.1:0".parse().unwrap())
1246                .peer_config(PeersConfig::test().with_max_outbound(2))
1247                .sessions_config(sessions)
1248                .build(NoopProvider::default());
1249                networks.push(NetworkManager::new(config).await.unwrap());
1250            }
1251            let (local, remotes) = networks.split_first_mut().unwrap();
1252            for remote in remotes.iter() {
1253                local.swarm.peers_mut().add_and_connect_kind(
1254                    *remote.peer_id(),
1255                    PeerKind::Basic,
1256                    PeerAddr::from_tcp(remote.swarm.listener().local_address()),
1257                    None,
1258                );
1259            }
1260            let mut cx = Context::from_waker(noop_waker_ref());
1261            assert!(Pin::new(&mut *local).poll(&mut cx).is_pending());
1262            assert_eq!(
1263                local.swarm.peers().num_pending_outbound_connections(),
1264                limit.map_or(2, |limit| limit as usize),
1265            );
1266            assert_eq!(local.swarm.peers().num_outbound_connections(), 0);
1267            if limit == Some(0) {
1268                continue
1269            }
1270
1271            tokio::time::timeout(
1272                Duration::from_secs(5),
1273                poll_fn(|cx| {
1274                    assert!(Pin::new(&mut *local).poll(cx).is_pending());
1275                    for remote in remotes.iter_mut() {
1276                        assert!(Pin::new(remote).poll(cx).is_pending());
1277                    }
1278                    if local.swarm.peers().num_outbound_connections() == 2 {
1279                        Poll::Ready(())
1280                    } else {
1281                        Poll::Pending
1282                    }
1283                }),
1284            )
1285            .await
1286            .expect("rejected peers must be retried after session capacity becomes available");
1287            assert_eq!(local.swarm.peers().num_pending_outbound_connections(), 0);
1288        }
1289    }
1290
1291    #[tokio::test]
1292    async fn inbound_replacement_refreshes_pending_connection_metrics() {
1293        let config = || {
1294            NetworkConfigBuilder::<EthNetworkPrimitives>::new(
1295                SecretKey::new(&mut rand::rng()),
1296                Runtime::test(),
1297            )
1298            .disable_discovery()
1299            .listener_addr("127.0.0.1:0".parse().unwrap())
1300            .peer_config(PeersConfig::test())
1301            .build(NoopProvider::default())
1302        };
1303        let mut local = NetworkManager::new(config()).await.unwrap();
1304        let mut remote = NetworkManager::new(config()).await.unwrap();
1305        let stalled = TcpListener::bind("127.0.0.1:0").await.unwrap();
1306        let pending_outgoing = Arc::new(AtomicU64::new(0));
1307        let total_pending = Arc::new(AtomicU64::new(0));
1308        local.metrics.pending_outgoing_connections = Gauge::from_arc(pending_outgoing.clone());
1309        local.metrics.total_pending_connections = Gauge::from_arc(total_pending.clone());
1310        local.swarm.peers_mut().add_and_connect_kind(
1311            *remote.peer_id(),
1312            PeerKind::Basic,
1313            PeerAddr::from_tcp(stalled.local_addr().unwrap()),
1314            None,
1315        );
1316        assert!(futures::poll!(Pin::new(&mut local)).is_pending());
1317        assert_eq!(f64::from_bits(pending_outgoing.load(Ordering::Relaxed)), 1.0);
1318        remote.swarm.peers_mut().add_and_connect_kind(
1319            *local.peer_id(),
1320            PeerKind::Basic,
1321            PeerAddr::from_tcp(local.local_addr()),
1322            None,
1323        );
1324        tokio::time::timeout(
1325            Duration::from_secs(5),
1326            poll_fn(|cx| {
1327                assert!(Pin::new(&mut local).poll(cx).is_pending());
1328                assert!(Pin::new(&mut remote).poll(cx).is_pending());
1329                if local.swarm.peers().num_inbound_connections() == 1 {
1330                    Poll::Ready(())
1331                } else {
1332                    Poll::Pending
1333                }
1334            }),
1335        )
1336        .await
1337        .unwrap();
1338        assert_eq!(local.swarm.peers().num_pending_outbound_connections(), 0);
1339        assert_eq!(f64::from_bits(pending_outgoing.load(Ordering::Relaxed)), 0.0);
1340        assert_eq!(f64::from_bits(total_pending.load(Ordering::Relaxed)), 0.0);
1341    }
1342
1343    #[tokio::test]
1344    async fn incoming_handshake_failure_refreshes_pending_connection_metrics() {
1345        let config = NetworkConfigBuilder::<EthNetworkPrimitives>::new(
1346            SecretKey::new(&mut rand::rng()),
1347            Runtime::test(),
1348        )
1349        .disable_discovery()
1350        .listener_addr("127.0.0.1:0".parse().unwrap())
1351        .peer_config(PeersConfig::test())
1352        .build(NoopProvider::default());
1353        let mut network = NetworkManager::new(config).await.unwrap();
1354        let total_pending = Arc::new(AtomicU64::new(0));
1355        network.metrics.total_pending_connections = Gauge::from_arc(total_pending.clone());
1356        let stream = TcpStream::connect(network.local_addr()).await.unwrap();
1357        tokio::time::timeout(
1358            Duration::from_secs(5),
1359            poll_fn(|cx| {
1360                assert!(Pin::new(&mut network).poll(cx).is_pending());
1361                if network.swarm.sessions().num_pending_connections() == 1 {
1362                    Poll::Ready(())
1363                } else {
1364                    Poll::Pending
1365                }
1366            }),
1367        )
1368        .await
1369        .unwrap();
1370        assert_eq!(f64::from_bits(total_pending.load(Ordering::Relaxed)), 1.0);
1371        drop(stream);
1372        tokio::time::timeout(
1373            Duration::from_secs(5),
1374            poll_fn(|cx| {
1375                assert!(Pin::new(&mut network).poll(cx).is_pending());
1376                if network.swarm.sessions().num_pending_connections() == 0 {
1377                    Poll::Ready(())
1378                } else {
1379                    Poll::Pending
1380                }
1381            }),
1382        )
1383        .await
1384        .unwrap();
1385        assert_eq!(f64::from_bits(total_pending.load(Ordering::Relaxed)), 0.0);
1386    }
1387}