Skip to main content

reth_network/
peers.rs

1//! Peer related implementations
2
3use crate::{
4    cache::LruMap,
5    error::SessionError,
6    session::{Direction, PendingSessionHandshakeError},
7    swarm::NetworkConnectionState,
8    trusted_peers_resolver::TrustedPeersResolver,
9};
10use alloy_primitives::map::{hash_map::Entry, FbBuildHasher, HashMap, HashSet};
11use futures::StreamExt;
12
13use rand::Rng;
14use reth_eth_wire::{
15    errors::{EthStreamError, P2PHandshakeError, P2PStreamError},
16    DisconnectReason,
17};
18use reth_ethereum_forks::ForkId;
19use reth_net_banlist::{is_global, BanList};
20use reth_network_api::test_utils::{PeerCommand, PeersHandle};
21use reth_network_peers::{NodeRecord, PeerId, TrustedPeer};
22use reth_network_types::{
23    is_connection_failed_reputation,
24    peers::{
25        config::{PeerBackoffDurations, PEER_ROTATION_MIN_UPTIME},
26        reputation::{DEFAULT_REPUTATION, MAX_TRUSTED_PEER_REPUTATION_CHANGE},
27    },
28    BackoffKind, ConnectionsConfig, Peer, PeerAddr, PeerConnectionState, PeerKind, PeersConfig,
29    PersistedPeerInfo, ReputationChangeKind, ReputationChangeOutcome, ReputationChangeWeights,
30};
31use std::{
32    collections::VecDeque,
33    fmt::Display,
34    io,
35    net::{IpAddr, SocketAddr},
36    pin::Pin,
37    task::{Context, Poll},
38    time::Duration,
39};
40use thiserror::Error;
41use tokio::{
42    sync::mpsc,
43    time::{Instant, Interval, Sleep},
44};
45use tokio_stream::wrappers::UnboundedReceiverStream;
46use tracing::{trace, warn};
47
48/// Bound admission-time throttling independently of long-lived security bans.
49const MAX_INCOMING_IP_THROTTLE_ENTRIES: u32 = 16_384;
50
51/// Maintains the state of _all_ the peers known to the network.
52///
53/// This is supposed to be owned by the network itself, but can be reached via the [`PeersHandle`].
54/// From this type, connections to peers are established or disconnected, see [`PeerAction`].
55///
56/// The [`PeersManager`] will be notified on peer related changes
57#[derive(Debug)]
58pub struct PeersManager {
59    /// All peers known to the network
60    peers: HashMap<PeerId, Peer, FbBuildHasher<64>>,
61    /// The set of trusted peer ids.
62    ///
63    /// This tracks peer ids that are considered trusted, but for which we don't necessarily have
64    /// an address: [`Self::add_trusted_peer_id`]
65    trusted_peer_ids: HashSet<PeerId, FbBuildHasher<64>>,
66    /// A resolver used to periodically resolve DNS names for trusted peers. This updates the
67    /// peer's address when the DNS records change.
68    trusted_peers_resolver: TrustedPeersResolver,
69    /// Copy of the sender half, so new [`PeersHandle`] can be created on demand.
70    manager_tx: mpsc::UnboundedSender<PeerCommand>,
71    /// Receiver half of the command channel.
72    handle_rx: UnboundedReceiverStream<PeerCommand>,
73    /// Buffered actions until the manager is polled.
74    queued_actions: VecDeque<PeerAction>,
75    /// Interval for triggering connections if there are free slots.
76    refill_slots_interval: Interval,
77    /// How to weigh reputation changes
78    reputation_weights: ReputationChangeWeights,
79    /// Tracks current slot stats.
80    connection_info: ConnectionInfo,
81    /// Tracks unwanted ips/peer ids.
82    ban_list: BanList,
83    /// Bounded recent-source throttle, separate from security bans.
84    incoming_ip_throttle: LruMap<IpAddr, std::time::Instant>,
85    /// Tracks currently backed off peers.
86    backed_off_peers: HashMap<PeerId, std::time::Instant, FbBuildHasher<64>>,
87    /// Interval at which to check for peers to unban and release from the backoff map.
88    release_interval: Interval,
89    /// How long to ban bad peers.
90    ban_duration: Duration,
91    /// How long peers to which we could not connect for non-fatal reasons, e.g.
92    /// [`DisconnectReason::TooManyPeers`], are put in time out.
93    backoff_durations: PeerBackoffDurations,
94    /// If non-trusted peers should be connected to, or the connection from non-trusted
95    /// incoming peers should be accepted.
96    trusted_nodes_only: bool,
97    /// Timestamp of the last time [`Self::tick`] was called.
98    last_tick: Instant,
99    /// Maximum number of backoff attempts before we give up on a peer and dropping.
100    max_backoff_count: u8,
101    /// Tracks the connection state of the node
102    net_connection_state: NetworkConnectionState,
103    /// How long to temporarily ban ip on an incoming connection attempt.
104    incoming_ip_throttle_duration: Duration,
105    /// IP address filter for restricting network connections to specific IP ranges.
106    ip_filter: reth_net_banlist::IpFilter,
107    /// If true, discovered peers without a confirmed ENR fork ID will not be added until their
108    /// fork ID is verified via EIP-868.
109    enforce_enr_fork_id: bool,
110    /// One-shot sleep that fires when it's time to rotate a peer; reset with jitter after each
111    /// fire. `None` when rotation is disabled.
112    peer_rotation_sleep: Option<Pin<Box<Sleep>>>,
113    /// Mean duration for computing jittered rotation intervals. `None` when rotation is disabled.
114    peer_rotation_mean: Option<Duration>,
115}
116
117impl PeersManager {
118    /// Create a new instance with the given config
119    pub fn new(config: PeersConfig) -> Self {
120        let PeersConfig {
121            refill_slots_interval,
122            connection_info,
123            reputation_weights,
124            ban_list,
125            ban_duration,
126            backoff_durations,
127            trusted_nodes,
128            trusted_nodes_only,
129            trusted_nodes_resolution_interval,
130            basic_nodes,
131            persisted_peers,
132            max_backoff_count,
133            incoming_ip_throttle_duration,
134            ip_filter,
135            enforce_enr_fork_id,
136            peer_rotation_interval,
137        } = config;
138        let (manager_tx, handle_rx) = mpsc::unbounded_channel();
139        let now = Instant::now();
140
141        // We use half of the interval to decrease the max duration to `150%` in worst case
142        let unban_interval = ban_duration.min(backoff_durations.low) / 2;
143
144        let mut peers: HashMap<PeerId, Peer, FbBuildHasher<64>> = HashMap::with_capacity_and_hasher(
145            trusted_nodes.len() + basic_nodes.len() + persisted_peers.len(),
146            Default::default(),
147        );
148        let mut trusted_peer_ids: HashSet<PeerId, FbBuildHasher<64>> =
149            HashSet::with_capacity_and_hasher(trusted_nodes.len(), Default::default());
150
151        for trusted_peer in &trusted_nodes {
152            match trusted_peer.resolve_blocking() {
153                Ok(NodeRecord { address, tcp_port, udp_port, id }) => {
154                    trusted_peer_ids.insert(id);
155                    peers.entry(id).or_insert_with(|| {
156                        Peer::trusted(PeerAddr::new_with_ports(address, tcp_port, Some(udp_port)))
157                    });
158                }
159                Err(err) => {
160                    warn!(target: "net::peers", ?err, "Failed to resolve trusted peer");
161                }
162            }
163        }
164
165        for PersistedPeerInfo { record, kind, fork_id, reputation } in persisted_peers {
166            // When enforce_enr_fork_id is enabled, skip persisted peers that don't have a
167            // confirmed fork ID. These were likely accumulated from a different network during
168            // a prior run without the flag.
169            if enforce_enr_fork_id && fork_id.is_none() {
170                continue
171            }
172            let NodeRecord { address, tcp_port, udp_port, id } = record;
173            peers.entry(id).or_insert_with(|| {
174                if kind.is_trusted() {
175                    trusted_peer_ids.insert(id);
176                }
177                let mut peer = Peer::with_kind(
178                    PeerAddr::new_with_ports(address, tcp_port, Some(udp_port)),
179                    kind,
180                );
181                peer.fork_id = fork_id.map(Box::new);
182                peer.reputation = reputation;
183                peer
184            });
185        }
186
187        for NodeRecord { address, tcp_port, udp_port, id } in basic_nodes {
188            peers.entry(id).or_insert_with(|| {
189                Peer::new(PeerAddr::new_with_ports(address, tcp_port, Some(udp_port)))
190            });
191        }
192
193        trace!(target: "net::peers", trusted_peers=?trusted_peer_ids, "Initialized peers manager");
194
195        Self {
196            peers,
197            trusted_peer_ids,
198            trusted_peers_resolver: TrustedPeersResolver::new(
199                trusted_nodes,
200                tokio::time::interval(trusted_nodes_resolution_interval), // 1 hour
201            ),
202            manager_tx,
203            handle_rx: UnboundedReceiverStream::new(handle_rx),
204            queued_actions: Default::default(),
205            reputation_weights,
206            refill_slots_interval: tokio::time::interval(refill_slots_interval),
207            release_interval: tokio::time::interval_at(now + unban_interval, unban_interval),
208            connection_info: ConnectionInfo::new(connection_info),
209            ban_list,
210            incoming_ip_throttle: LruMap::new(MAX_INCOMING_IP_THROTTLE_ENTRIES),
211            backed_off_peers: Default::default(),
212            ban_duration,
213            backoff_durations,
214            trusted_nodes_only,
215            last_tick: Instant::now(),
216            max_backoff_count,
217            net_connection_state: NetworkConnectionState::default(),
218            incoming_ip_throttle_duration,
219            ip_filter,
220            enforce_enr_fork_id,
221            peer_rotation_sleep: peer_rotation_interval
222                .map(|mean| Box::pin(tokio::time::sleep(jitter_rotation_interval(mean)))),
223            peer_rotation_mean: peer_rotation_interval,
224        }
225    }
226
227    /// Returns a new [`PeersHandle`] that can send commands to this type.
228    pub(crate) fn handle(&self) -> PeersHandle {
229        PeersHandle::new(self.manager_tx.clone())
230    }
231
232    /// Returns `true` if discovered peers must have a confirmed ENR fork ID before being added.
233    pub(crate) const fn enforce_enr_fork_id(&self) -> bool {
234        self.enforce_enr_fork_id
235    }
236
237    /// Returns the number of peers in the peer set
238    #[inline]
239    pub(crate) fn num_known_peers(&self) -> usize {
240        self.peers.len()
241    }
242
243    /// Returns an iterator over all peers as [`NodeRecord`]s.
244    pub(crate) fn iter_peers(&self) -> impl Iterator<Item = NodeRecord> + '_ {
245        self.peers.iter().map(|(peer_id, v)| {
246            NodeRecord::new_with_ports(
247                v.addr.tcp().ip(),
248                v.addr.tcp().port(),
249                v.addr.udp().map(|addr| addr.port()),
250                *peer_id,
251            )
252        })
253    }
254
255    /// Returns an iterator over peers suitable for persisting to disk.
256    ///
257    /// Filters out backed-off and banned peers, and includes metadata like kind, fork ID, and
258    /// reputation.
259    pub(crate) fn persistable_peers(&self) -> impl Iterator<Item = PersistedPeerInfo> + '_ {
260        self.peers.iter().filter(|(_, peer)| !peer.is_backed_off() && !peer.is_banned()).map(
261            |(peer_id, peer)| PersistedPeerInfo {
262                record: NodeRecord::new_with_ports(
263                    peer.addr.tcp().ip(),
264                    peer.addr.tcp().port(),
265                    peer.addr.udp().map(|addr| addr.port()),
266                    *peer_id,
267                ),
268                kind: peer.kind,
269                fork_id: peer.fork_id.as_deref().copied(),
270                reputation: peer.reputation,
271            },
272        )
273    }
274
275    /// Returns the `NodeRecord` and `PeerKind` for the given peer id
276    pub(crate) fn peer_by_id(&self, peer_id: PeerId) -> Option<(NodeRecord, PeerKind)> {
277        self.peers.get(&peer_id).map(|v| {
278            (
279                NodeRecord::new_with_ports(
280                    v.addr.tcp().ip(),
281                    v.addr.tcp().port(),
282                    v.addr.udp().map(|addr| addr.port()),
283                    peer_id,
284                ),
285                v.kind,
286            )
287        })
288    }
289
290    /// Returns `true` if the given peer is connected via an inbound session.
291    pub(crate) fn is_inbound_peer(&self, peer_id: &PeerId) -> bool {
292        self.peers.get(peer_id).is_some_and(|p| {
293            matches!(p.state, PeerConnectionState::In | PeerConnectionState::DisconnectingIn)
294        })
295    }
296
297    /// Returns an iterator over all peer ids for peers with the given kind
298    pub(crate) fn peers_by_kind(&self, kind: PeerKind) -> impl Iterator<Item = PeerId> + '_ {
299        self.peers.iter().filter_map(move |(peer_id, peer)| (peer.kind == kind).then_some(*peer_id))
300    }
301
302    /// Returns the number of currently active inbound connections.
303    #[inline]
304    pub(crate) const fn num_inbound_connections(&self) -> usize {
305        self.connection_info.num_inbound
306    }
307
308    /// Returns the number of currently __active__ outbound connections.
309    #[inline]
310    pub(crate) const fn num_outbound_connections(&self) -> usize {
311        self.connection_info.num_outbound
312    }
313
314    /// Returns the number of currently pending outbound connections.
315    #[inline]
316    pub(crate) const fn num_pending_outbound_connections(&self) -> usize {
317        self.connection_info.num_pending_out
318    }
319
320    /// Returns the number of currently backed off peers.
321    #[inline]
322    pub(crate) fn num_backed_off_peers(&self) -> usize {
323        self.backed_off_peers.len()
324    }
325
326    /// Returns the number of idle trusted peers.
327    fn num_idle_trusted_peers(&self) -> usize {
328        self.peers.iter().filter(|(_, peer)| peer.kind.is_trusted() && peer.state.is_idle()).count()
329    }
330
331    /// Invoked when a new _incoming_ tcp connection is accepted.
332    ///
333    /// returns an error if the inbound ip address is on the ban list
334    pub(crate) fn on_incoming_pending_session(
335        &mut self,
336        addr: IpAddr,
337    ) -> Result<(), InboundConnectionError> {
338        // Check if the IP is in the allowed ranges (netrestrict)
339        if !self.ip_filter.is_allowed(&addr) {
340            trace!(target: "net", ?addr, "Rejecting connection from IP not in allowed ranges");
341            return Err(InboundConnectionError::IpBanned)
342        }
343
344        if self.ban_list.is_banned_ip(&addr) ||
345            self.incoming_ip_throttle
346                .get(&addr)
347                .is_some_and(|until| *until > std::time::Instant::now())
348        {
349            return Err(InboundConnectionError::IpBanned)
350        }
351
352        // check if we even have slots for a new incoming connection
353        if !self.connection_info.has_in_capacity() {
354            if self.trusted_peer_ids.is_empty() {
355                // if we don't have any incoming slots and no trusted peers, we don't accept any new
356                // connections
357                return Err(InboundConnectionError::ExceedsCapacity)
358            }
359
360            // there's an edge case here where no incoming connections besides from trusted peers
361            // are allowed (max_inbound == 0), in which case we still need to allow new pending
362            // incoming connections until all trusted peers are connected.
363            let num_idle_trusted_peers = self.num_idle_trusted_peers();
364            if num_idle_trusted_peers <= self.trusted_peer_ids.len() {
365                // we still want to limit concurrent pending connections
366                let max_inbound =
367                    self.trusted_peer_ids.len().max(self.connection_info.config.max_inbound);
368                if self.connection_info.num_pending_in < max_inbound {
369                    // Rate limit these admissions as well, otherwise a single host could keep
370                    // the pending slots occupied once the inbound slots are taken.
371                    self.throttle_incoming_ip(addr);
372                    self.connection_info.inc_pending_in();
373                    return Ok(())
374                }
375            }
376
377            // all trusted peers are either connected or connecting
378            return Err(InboundConnectionError::ExceedsCapacity)
379        }
380
381        // also cap the incoming connections we can process at once
382        if !self.connection_info.has_in_pending_capacity() {
383            return Err(InboundConnectionError::ExceedsCapacity)
384        }
385
386        // apply the rate limit
387        self.throttle_incoming_ip(addr);
388
389        self.connection_info.inc_pending_in();
390        Ok(())
391    }
392
393    /// Invoked when a previous call to [`Self::on_incoming_pending_session`] succeeded but it was
394    /// rejected.
395    pub(crate) const fn on_incoming_pending_session_rejected_internally(&mut self) {
396        self.connection_info.decr_pending_in();
397    }
398
399    /// Invoked when a pending session was closed.
400    pub(crate) const fn on_incoming_pending_session_gracefully_closed(&mut self) {
401        self.connection_info.decr_pending_in()
402    }
403
404    /// Invoked when a pending session was closed.
405    pub(crate) fn on_incoming_pending_session_dropped(
406        &mut self,
407        remote_addr: SocketAddr,
408        err: &PendingSessionHandshakeError,
409    ) {
410        if err.is_fatal_protocol_error() {
411            self.ban_ip(remote_addr.ip());
412
413            if err.merits_discovery_ban() {
414                self.queued_actions
415                    .push_back(PeerAction::DiscoveryBanIp { ip_addr: remote_addr.ip() })
416            }
417        }
418
419        self.connection_info.decr_pending_in();
420    }
421
422    /// Called when a new _incoming_ active session was established to the given peer.
423    ///
424    /// This will update the state of the peer if not yet tracked.
425    ///
426    /// If the reputation of the peer is below the `BANNED_REPUTATION` threshold, a disconnect will
427    /// be scheduled.
428    pub(crate) fn on_incoming_session_established(&mut self, peer_id: PeerId, addr: SocketAddr) {
429        self.connection_info.decr_pending_in();
430
431        // we only need to check the peer id here as the ip address will have been checked at
432        // on_incoming_pending_session. We also check if the peer is in the backoff list here.
433        if self.ban_list.is_banned_peer(&peer_id) {
434            self.queued_actions.push_back(PeerAction::DisconnectBannedIncoming { peer_id });
435            return
436        }
437
438        // check if the peer is trustable or not
439        let is_trusted = self.trusted_peer_ids.contains(&peer_id);
440        if self.trusted_nodes_only && !is_trusted {
441            self.queued_actions.push_back(PeerAction::DisconnectUntrustedIncoming { peer_id });
442            return
443        }
444
445        // start a new tick, so the peer is not immediately rewarded for the time since last tick
446        self.tick();
447
448        match self.peers.entry(peer_id) {
449            Entry::Occupied(mut entry) => {
450                let peer = entry.get_mut();
451                if peer.is_banned() {
452                    self.queued_actions.push_back(PeerAction::DisconnectBannedIncoming { peer_id });
453                    return
454                }
455                // it might be the case that we're also trying to connect to this peer at the same
456                // time, so we need to adjust the state here
457                if peer.state.is_pending_out() {
458                    self.connection_info.decr_state(peer.state);
459                }
460
461                peer.state = PeerConnectionState::In;
462                peer.mark_connected();
463            }
464            Entry::Vacant(entry) => {
465                // peer is missing in the table, we add it but mark it as to be removed after
466                // disconnect, because we only know the outgoing port
467                let mut peer = Peer::with_state(PeerAddr::from_tcp(addr), PeerConnectionState::In);
468                peer.mark_connected();
469                peer.remove_after_disconnect = true;
470                entry.insert(peer);
471                self.queued_actions.push_back(PeerAction::PeerAdded(peer_id));
472            }
473        }
474
475        let has_in_capacity = self.has_inbound_capacity(peer_id);
476        // increment new incoming connection
477        self.connection_info.inc_in();
478
479        // disconnect the peer if we don't have capacity for more inbound connections
480        if !has_in_capacity {
481            self.queued_actions.push_back(PeerAction::Disconnect {
482                peer_id,
483                reason: Some(DisconnectReason::TooManyPeers),
484            });
485        }
486    }
487
488    /// Checks capacity before an incoming session can supersede an outbound attempt.
489    pub(crate) fn has_inbound_capacity(&self, peer_id: PeerId) -> bool {
490        self.connection_info.has_in_capacity() ||
491            self.trusted_peer_ids.contains(&peer_id) ||
492            self.peers.get(&peer_id).is_some_and(|peer| peer.is_trusted())
493    }
494
495    /// Bans the peer temporarily with the configured ban timeout
496    fn ban_peer(&mut self, peer_id: PeerId) {
497        let ban_duration = if let Some(peer) = self.peers.get(&peer_id) &&
498            (peer.is_trusted() || peer.is_static())
499        {
500            // For misbehaving trusted or static peers, we provide a bit more leeway when
501            // penalizing them.
502            self.backoff_durations.low / 2
503        } else {
504            self.ban_duration
505        };
506
507        self.ban_list.ban_peer_until(peer_id, std::time::Instant::now() + ban_duration);
508        self.queued_actions.push_back(PeerAction::BanPeer { peer_id });
509    }
510
511    /// Bans the IP temporarily with the configured ban timeout
512    fn ban_ip(&mut self, ip: IpAddr) {
513        self.ban_list.ban_ip_until(ip, std::time::Instant::now() + self.ban_duration);
514    }
515
516    /// Rate limits recent global sources without allowing the throttle state to grow unbounded.
517    fn throttle_incoming_ip(&mut self, ip: IpAddr) {
518        if is_global(&ip) {
519            // Evict the least recently used throttle when full. Security bans are never evicted,
520            // and a new source still gets throttled without blocking unrelated admissions.
521            self.incoming_ip_throttle
522                .insert(ip, std::time::Instant::now() + self.incoming_ip_throttle_duration);
523        }
524    }
525
526    /// Temporarily puts the peer in timeout by inserting it into the backedoff peers set
527    fn backoff_peer_until(&mut self, peer_id: PeerId, until: std::time::Instant) {
528        trace!(target: "net::peers", ?peer_id, "backing off");
529
530        if let Some(peer) = self.peers.get_mut(&peer_id) {
531            peer.backed_off = true;
532            self.backed_off_peers.insert(peer_id, until);
533        }
534    }
535
536    /// Unbans the peer
537    fn unban_peer(&mut self, peer_id: PeerId) {
538        self.ban_list.unban_peer(&peer_id);
539        self.queued_actions.push_back(PeerAction::UnBanPeer { peer_id });
540    }
541
542    /// Tick function to update reputation of all connected peers.
543    /// Peers are rewarded with reputation increases for the time they are connected since the last
544    /// tick. This is to prevent peers from being disconnected eventually due to slashed
545    /// reputation because of some bad messages (most likely transaction related)
546    fn tick(&mut self) {
547        let now = Instant::now();
548        // Determine the number of seconds since the last tick.
549        // Ensuring that now is always greater than last_tick to account for issues with system
550        // time.
551        let secs_since_last_tick =
552            if self.last_tick > now { 0 } else { (now - self.last_tick).as_secs() as i32 };
553        self.last_tick = now;
554
555        // update reputation via seconds connected
556        for peer in self.peers.iter_mut().filter(|(_, peer)| peer.state.is_connected()) {
557            // update reputation via seconds connected, but keep the target _around_ the default
558            // reputation.
559            if peer.1.reputation < DEFAULT_REPUTATION {
560                peer.1.reputation += secs_since_last_tick;
561            }
562        }
563    }
564
565    /// Returns the tracked reputation for a peer.
566    pub(crate) fn get_reputation(&self, peer_id: &PeerId) -> Option<i32> {
567        self.peers.get(peer_id).map(|peer| peer.reputation)
568    }
569
570    /// Apply the corresponding reputation change to the given peer.
571    ///
572    /// If the peer is a trusted peer, it will be exempt from reputation slashing for certain
573    /// reputation changes that can be attributed to network conditions. If the peer is a
574    /// trusted peer, it will also be less strict with the reputation slashing.
575    pub(crate) fn apply_reputation_change(&mut self, peer_id: &PeerId, rep: ReputationChangeKind) {
576        trace!(target: "net::peers", ?peer_id, reputation=?rep, "applying reputation change");
577
578        let reputation_change = if rep.is_reset() {
579            None
580        } else {
581            let reputation_change = self.reputation_weights.change(rep).as_i32();
582            if reputation_change == 0 {
583                return
584            }
585            Some(reputation_change)
586        };
587
588        let outcome = if let Some(peer) = self.peers.get_mut(peer_id) {
589            if let Some(mut reputation_change) = reputation_change {
590                if peer.is_trusted() || peer.is_static() {
591                    // exempt trusted and static peers from reputation slashing for
592                    if matches!(
593                        rep,
594                        ReputationChangeKind::Dropped |
595                            ReputationChangeKind::BadAnnouncement |
596                            ReputationChangeKind::Timeout |
597                            ReputationChangeKind::AlreadySeenTransaction
598                    ) {
599                        return
600                    }
601
602                    // also be less strict with the reputation slashing for trusted peers
603                    if reputation_change < MAX_TRUSTED_PEER_REPUTATION_CHANGE {
604                        // this caps the reputation change to the maximum allowed for trusted peers
605                        reputation_change = MAX_TRUSTED_PEER_REPUTATION_CHANGE;
606                    }
607                }
608                peer.apply_reputation(reputation_change, rep)
609            } else {
610                peer.reset_reputation()
611            }
612        } else {
613            return
614        };
615
616        match outcome {
617            ReputationChangeOutcome::None => {}
618            ReputationChangeOutcome::Ban => {
619                self.ban_peer(*peer_id);
620            }
621            ReputationChangeOutcome::Unban => self.unban_peer(*peer_id),
622            ReputationChangeOutcome::DisconnectAndBan => {
623                self.queued_actions.push_back(PeerAction::Disconnect {
624                    peer_id: *peer_id,
625                    reason: Some(DisconnectReason::DisconnectRequested),
626                });
627                self.ban_peer(*peer_id);
628            }
629        }
630    }
631
632    /// Gracefully disconnected a pending _outgoing_ session
633    pub(crate) fn on_outgoing_pending_session_gracefully_closed(&mut self, peer_id: &PeerId) {
634        if let Some(peer) = self.peers.get_mut(peer_id) &&
635            peer.state.is_pending_out()
636        {
637            self.connection_info.decr_state(peer.state);
638            peer.state = PeerConnectionState::Idle;
639            peer.mark_disconnected();
640        }
641    }
642
643    /// Invoked when an _outgoing_ pending session was closed during authentication or the
644    /// handshake.
645    pub(crate) fn on_outgoing_pending_session_dropped(
646        &mut self,
647        remote_addr: &SocketAddr,
648        peer_id: &PeerId,
649        err: &PendingSessionHandshakeError,
650    ) {
651        if matches!(
652            err,
653            PendingSessionHandshakeError::Ecies(_) | PendingSessionHandshakeError::Timeout
654        ) || matches!(
655            err,
656            PendingSessionHandshakeError::Eth(EthStreamError::P2PStreamError(
657                P2PStreamError::HandshakeError(
658                    P2PHandshakeError::Timeout | P2PHandshakeError::NoResponse
659                )
660            ))
661        ) || matches!(err, PendingSessionHandshakeError::Eth(error) if error.as_io().is_some())
662        {
663            // ACK, transport, and Hello timeout errors can precede the first authenticated frame.
664            // The outer timeout carries no handshake phase, so it is also treated conservatively.
665            // Back off the attempt without penalizing that identity or its discovery record.
666            if self.peers.get(peer_id).is_some_and(|peer| peer.state.is_pending_out()) {
667                self.on_outgoing_pending_session_gracefully_closed(peer_id);
668                self.backoff_peer_until(
669                    *peer_id,
670                    self.backoff_durations.backoff_until(BackoffKind::Low, 0),
671                );
672                self.fill_outbound_slots();
673            }
674            return
675        }
676        self.on_connection_failure(remote_addr, peer_id, err, ReputationChangeKind::FailedToConnect)
677    }
678
679    /// Gracefully disconnected an active session
680    pub(crate) fn on_active_session_gracefully_closed(&mut self, peer_id: PeerId) {
681        match self.peers.entry(peer_id) {
682            Entry::Occupied(mut entry) => {
683                trace!(target: "net::peers", ?peer_id, direction=?entry.get().state, "active session gracefully closed");
684                self.connection_info.decr_state(entry.get().state);
685
686                if entry.get().remove_after_disconnect && !entry.get().is_trusted() {
687                    // this peer should be removed from the set
688                    entry.remove();
689                    self.queued_actions.push_back(PeerAction::PeerRemoved(peer_id));
690                } else {
691                    let peer = entry.get_mut();
692                    // reset the peer's state
693                    // we reset the backoff counter since we're able to establish a successful
694                    // session to that peer
695                    peer.severe_backoff_counter = 0;
696                    peer.state = PeerConnectionState::Idle;
697                    peer.mark_disconnected();
698
699                    // but we're backing off slightly to avoid dialing the peer again right away, to
700                    // give the remote time to also properly register the closed session and clean
701                    // up and to avoid any issues with ip throttling on the remote in case this
702                    // session was terminated right away.
703                    peer.backed_off = true;
704                    self.backed_off_peers.insert(
705                        peer_id,
706                        std::time::Instant::now() + self.incoming_ip_throttle_duration,
707                    );
708                    trace!(target: "net::peers", ?peer_id, kind=?peer.kind, duration=?self.incoming_ip_throttle_duration, "backing off on gracefully closed session");
709                }
710            }
711            Entry::Vacant(_) => return,
712        }
713
714        self.fill_outbound_slots();
715    }
716
717    /// Called when a _pending_ outbound connection is successful.
718    pub(crate) fn on_active_outgoing_established(&mut self, peer_id: PeerId) {
719        let at_capacity = self.connection_info.is_outbound_at_capacity();
720        if let Some(peer) = self.peers.get_mut(&peer_id) {
721            trace!(target: "net::peers", ?peer_id, "established active outgoing connection");
722            self.connection_info.decr_state(peer.state);
723            self.connection_info.inc_out();
724            peer.state = PeerConnectionState::Out;
725            peer.mark_connected();
726            if at_capacity &&
727                !peer.is_trusted() &&
728                !peer.is_static() &&
729                !self.trusted_peer_ids.contains(&peer_id)
730            {
731                self.queued_actions.push_back(PeerAction::Disconnect {
732                    peer_id,
733                    reason: Some(DisconnectReason::TooManyPeers),
734                });
735            }
736        }
737    }
738
739    /// Called when an _active_ session to a peer was forcefully dropped due to an error.
740    ///
741    /// Depending on whether the error is fatal, the peer will be removed from the peer set
742    /// otherwise its reputation is slashed.
743    pub(crate) fn on_active_session_dropped(
744        &mut self,
745        remote_addr: &SocketAddr,
746        peer_id: &PeerId,
747        err: &EthStreamError,
748    ) {
749        self.on_connection_failure(remote_addr, peer_id, err, ReputationChangeKind::Dropped)
750    }
751
752    /// Called when an attempt to create an _outgoing_ pending session failed while setting up a tcp
753    /// connection.
754    pub(crate) fn on_outgoing_connection_failure(
755        &mut self,
756        remote_addr: &SocketAddr,
757        peer_id: &PeerId,
758        err: &io::Error,
759    ) {
760        // there's a race condition where we accepted an incoming connection while we were trying to
761        // connect to the same peer at the same time. if the outgoing connection failed
762        // after the incoming connection was accepted, we can ignore this error
763        if let Some(peer) = self.peers.get(peer_id) {
764            if peer.state.is_incoming() {
765                // we already have an active connection to the peer, so we can ignore this error
766                return
767            }
768
769            if peer.is_trusted() && is_connection_failed_reputation(peer.reputation) {
770                // trigger resolution task for trusted peer since multiple connection failures
771                // occurred
772                self.trusted_peers_resolver.interval.reset_immediately();
773            }
774        }
775
776        self.on_connection_failure(remote_addr, peer_id, err, ReputationChangeKind::FailedToConnect)
777    }
778
779    fn on_connection_failure(
780        &mut self,
781        remote_addr: &SocketAddr,
782        peer_id: &PeerId,
783        err: impl SessionError,
784        reputation_change: ReputationChangeKind,
785    ) {
786        trace!(target: "net::peers", ?remote_addr, ?peer_id, %err, "handling failed connection");
787
788        if err.is_fatal_protocol_error() {
789            trace!(target: "net::peers", ?remote_addr, ?peer_id, %err, "fatal connection error");
790            // remove the peer to which we can't establish a connection due to protocol related
791            // issues.
792            if let Entry::Occupied(mut entry) = self.peers.entry(*peer_id) {
793                self.connection_info.decr_state(entry.get().state);
794                // only remove if the peer is not trusted
795                if entry.get().is_trusted() {
796                    let peer = entry.get_mut();
797                    peer.state = PeerConnectionState::Idle;
798                    peer.mark_disconnected();
799                } else {
800                    entry.remove();
801                    self.queued_actions.push_back(PeerAction::PeerRemoved(*peer_id));
802                    // If the error is caused by a peer that should be banned from discovery
803                    if err.merits_discovery_ban() {
804                        self.queued_actions.push_back(PeerAction::DiscoveryBanPeerId {
805                            peer_id: *peer_id,
806                            ip_addr: remote_addr.ip(),
807                        })
808                    }
809                }
810            }
811
812            // ban the peer
813            self.ban_peer(*peer_id);
814        } else {
815            let mut backoff_until = None;
816            let mut remove_peer = false;
817
818            if let Some(peer) = self.peers.get_mut(peer_id) {
819                if let Some(kind) = err.should_backoff() {
820                    if peer.is_trusted() || peer.is_static() {
821                        // provide a bit more leeway for trusted peers and use a lower backoff so
822                        // that we keep re-trying them after backing off shortly, but we should at
823                        // least backoff for the low duration to not violate the ip based inbound
824                        // connection throttle that peer has in place, because this peer might not
825                        // have us registered as a trusted peer.
826                        let backoff = self.backoff_durations.low;
827                        backoff_until = Some(std::time::Instant::now() + backoff);
828                        trace!(target: "net::peers", ?peer_id, ?backoff, "backing off trusted peer");
829                    } else {
830                        // Increment peer.backoff_counter
831                        if kind.is_severe() {
832                            peer.severe_backoff_counter =
833                                peer.severe_backoff_counter.saturating_add(1);
834                        }
835                        trace!(target: "net::peers", ?peer_id, ?kind, severe_backoff_counter=peer.severe_backoff_counter, "backing off basic peer");
836
837                        let backoff_time =
838                            self.backoff_durations.backoff_until(kind, peer.severe_backoff_counter);
839
840                        // The peer has signaled that it is currently unable to process any more
841                        // connections, so we will hold off on attempting any new connections for a
842                        // while
843                        backoff_until = Some(backoff_time);
844                    }
845                } else {
846                    // If the error was not a backoff error, we reduce the peer's reputation
847                    let reputation_change = self.reputation_weights.change(reputation_change);
848                    peer.reputation = peer.reputation.saturating_add(reputation_change.as_i32());
849                };
850
851                self.connection_info.decr_state(peer.state);
852                peer.state = PeerConnectionState::Idle;
853                peer.mark_disconnected();
854
855                if peer.severe_backoff_counter > self.max_backoff_count &&
856                    !peer.is_trusted() &&
857                    !peer.is_static()
858                {
859                    // mark peer for removal if it has been backoff too many times and is _not_
860                    // trusted or static
861                    remove_peer = true;
862                }
863            }
864
865            // remove peer if it has been marked for removal
866            if remove_peer {
867                trace!(target: "net", ?peer_id, "removed peer after exceeding backoff counter");
868                let (peer_id, _) = self.peers.remove_entry(peer_id).expect("peer must exist");
869                self.queued_actions.push_back(PeerAction::PeerRemoved(peer_id));
870            } else if let Some(backoff_until) = backoff_until {
871                // otherwise, backoff the peer if marked as such
872                self.backoff_peer_until(*peer_id, backoff_until);
873            }
874        }
875
876        self.fill_outbound_slots();
877    }
878
879    /// Invoked if a pending session was disconnected because there's already a connection to the
880    /// peer.
881    ///
882    /// If the session was an outgoing connection, this means that the peer initiated a connection
883    /// to us at the same time and this connection is already established.
884    pub(crate) const fn on_already_connected(&mut self, direction: Direction) {
885        match direction {
886            Direction::Incoming => {
887                // need to decrement the ingoing counter
888                self.connection_info.decr_pending_in();
889            }
890            Direction::Outgoing(_) => {
891                // cleanup is handled when the incoming active session is activated in
892                // `on_incoming_session_established`
893            }
894        }
895    }
896
897    /// Called for a newly discovered peer.
898    ///
899    /// If the peer already exists, then the address, kind and `fork_id` will be updated.
900    pub(crate) fn add_peer(&mut self, peer_id: PeerId, addr: PeerAddr, fork_id: Option<ForkId>) {
901        self.add_peer_kind(peer_id, None, addr, fork_id)
902    }
903
904    /// Marks the given peer as trusted.
905    pub(crate) fn add_trusted_peer_id(&mut self, peer_id: PeerId) {
906        self.trusted_peer_ids.insert(peer_id);
907        if let Some(peer) = self.peers.get_mut(&peer_id) {
908            peer.kind = PeerKind::Trusted;
909        }
910    }
911
912    /// Called for a newly discovered trusted peer.
913    ///
914    /// If the peer already exists, then the address and kind will be updated.
915    #[cfg_attr(not(test), expect(dead_code))]
916    pub(crate) fn add_trusted_peer(&mut self, peer_id: PeerId, addr: PeerAddr) {
917        self.add_peer_kind(peer_id, Some(PeerKind::Trusted), addr, None)
918    }
919
920    /// Adds a trusted peer that may use a hostname instead of an IP address.
921    pub(crate) fn add_trusted_peer_node(&mut self, trusted: TrustedPeer) {
922        let peer_id = trusted.id;
923        self.trusted_peer_ids.insert(peer_id);
924        self.trusted_peers_resolver.remove(peer_id);
925        self.trusted_peers_resolver.trusted_peers.push(trusted);
926        self.trusted_peers_resolver.interval.reset_immediately();
927    }
928
929    /// Called for a newly discovered peer.
930    ///
931    /// If the peer already exists, then the address, kind and `fork_id` will be updated.
932    /// If the peer exists and a [`PeerKind`] is provided then the peer's kind is updated
933    pub(crate) fn add_peer_kind(
934        &mut self,
935        peer_id: PeerId,
936        kind: Option<PeerKind>,
937        addr: PeerAddr,
938        fork_id: Option<ForkId>,
939    ) {
940        let ip_addr = addr.tcp().ip();
941
942        // Check if the IP is in the allowed ranges (netrestrict)
943        if !self.ip_filter.is_allowed(&ip_addr) {
944            trace!(target: "net", ?peer_id, ?ip_addr, "Skipping peer from IP not in allowed ranges");
945            return
946        }
947
948        if self.ban_list.is_banned(&peer_id, &ip_addr) {
949            return
950        }
951
952        match self.peers.entry(peer_id) {
953            Entry::Occupied(mut entry) => {
954                let peer = entry.get_mut();
955                peer.fork_id = fork_id.map(Box::new);
956                peer.addr = addr;
957
958                if let Some(kind) = kind {
959                    peer.kind = kind;
960                }
961
962                if peer.state.is_incoming() {
963                    // now that we have an actual discovered address, for that peer and not just the
964                    // ip of the incoming connection, we don't need to remove the peer after
965                    // disconnecting, See `on_incoming_session_established`
966                    peer.remove_after_disconnect = false;
967                }
968            }
969            Entry::Vacant(entry) => {
970                trace!(target: "net::peers", ?peer_id, addr=?addr.tcp(), "discovered new node");
971                let mut peer = Peer::with_kind(addr, kind.unwrap_or(PeerKind::Basic));
972                peer.fork_id = fork_id.map(Box::new);
973                entry.insert(peer);
974                self.queued_actions.push_back(PeerAction::PeerAdded(peer_id));
975            }
976        }
977
978        if kind.is_some_and(|kind| kind.is_trusted()) {
979            // also track the peer in the peer id set
980            self.trusted_peer_ids.insert(peer_id);
981        }
982    }
983
984    /// Called for a peer that was explicitly requested, e.g. via `admin_addPeer`.
985    ///
986    /// Same as [`Self::add_peer_kind`], but the peer is dialed right away if there's an outbound
987    /// slot instead of on the next refill tick like discovered peers.
988    pub(crate) fn add_requested_peer(
989        &mut self,
990        peer_id: PeerId,
991        kind: Option<PeerKind>,
992        addr: PeerAddr,
993    ) {
994        self.add_peer_kind(peer_id, kind, addr, None);
995        self.fill_outbound_slots();
996    }
997
998    /// Removes the tracked node from the set.
999    pub(crate) fn remove_peer(&mut self, peer_id: PeerId) {
1000        let Entry::Occupied(entry) = self.peers.entry(peer_id) else { return };
1001        if entry.get().is_trusted() {
1002            return
1003        }
1004        let mut peer = entry.remove();
1005
1006        trace!(target: "net::peers", ?peer_id, "remove discovered node");
1007        self.queued_actions.push_back(PeerAction::PeerRemoved(peer_id));
1008
1009        if peer.state.is_connected() {
1010            trace!(target: "net::peers", ?peer_id, "disconnecting on remove from discovery");
1011            // we terminate the active session here, but only remove the peer after the session
1012            // was disconnected, this prevents the case where the session is scheduled for
1013            // disconnect but the node is immediately rediscovered, See also
1014            // [`Self::on_disconnected()`]
1015            peer.remove_after_disconnect = true;
1016            peer.state.disconnect();
1017            self.peers.insert(peer_id, peer);
1018            self.queued_actions.push_back(PeerAction::Disconnect {
1019                peer_id,
1020                reason: Some(DisconnectReason::DisconnectRequested),
1021            })
1022        }
1023    }
1024
1025    /// Bans the peer indefinitely and removes it from the peer set.
1026    ///
1027    /// This follows [`Self::remove_peer`] and does not override trusted status. Remove trusted
1028    /// peers from the trusted set before banning them.
1029    pub(crate) fn ban_peer_by_admin(&mut self, peer_id: PeerId) {
1030        if self.trusted_peer_ids.contains(&peer_id) ||
1031            self.peers.get(&peer_id).is_some_and(Peer::is_trusted)
1032        {
1033            return
1034        }
1035
1036        self.remove_peer(peer_id);
1037        self.ban_list.ban_peer(peer_id);
1038    }
1039
1040    /// Removes the peer from the ban list and resets its reputation.
1041    pub(crate) fn unban_peer_by_admin(&mut self, peer_id: PeerId) {
1042        self.ban_list.unban_peer(&peer_id);
1043        if let Some(peer) = self.peers.get_mut(&peer_id) &&
1044            peer.is_banned()
1045        {
1046            peer.unban();
1047            self.queued_actions.push_back(PeerAction::UnBanPeer { peer_id });
1048        }
1049    }
1050
1051    /// Connect to the given peer. NOTE: if the maximum number of outbound sessions is reached,
1052    /// this won't do anything. See `reth_network::SessionManager::dial_outbound`.
1053    #[cfg_attr(not(test), expect(dead_code))]
1054    pub(crate) fn add_and_connect(
1055        &mut self,
1056        peer_id: PeerId,
1057        addr: PeerAddr,
1058        fork_id: Option<ForkId>,
1059    ) {
1060        self.add_and_connect_kind(peer_id, PeerKind::Basic, addr, fork_id)
1061    }
1062
1063    /// Connects a peer and its address with the given kind.
1064    ///
1065    /// Note: This is invoked on demand via an external command received by the manager
1066    pub(crate) fn add_and_connect_kind(
1067        &mut self,
1068        peer_id: PeerId,
1069        kind: PeerKind,
1070        addr: PeerAddr,
1071        fork_id: Option<ForkId>,
1072    ) {
1073        let ip_addr = addr.tcp().ip();
1074
1075        // Check if the IP is in the allowed ranges (netrestrict)
1076        if !self.ip_filter.is_allowed(&ip_addr) {
1077            trace!(target: "net", ?peer_id, ?ip_addr, "Skipping outbound connection to IP not in allowed ranges");
1078            return
1079        }
1080
1081        if self.ban_list.is_banned(&peer_id, &ip_addr) {
1082            return
1083        }
1084
1085        match self.peers.entry(peer_id) {
1086            Entry::Occupied(mut entry) => {
1087                let peer = entry.get_mut();
1088                peer.kind = kind;
1089                peer.fork_id = fork_id.map(Box::new);
1090                peer.addr = addr;
1091
1092                if peer.state.is_idle() {
1093                    // Try connecting again.
1094                    peer.state = PeerConnectionState::PendingOut;
1095                    self.connection_info.inc_pending_out();
1096                    self.queued_actions
1097                        .push_back(PeerAction::Connect { peer_id, remote_addr: addr.tcp() });
1098                }
1099            }
1100            Entry::Vacant(entry) => {
1101                trace!(target: "net::peers", ?peer_id, addr=?addr.tcp(), "connects new node");
1102                let mut peer = Peer::with_kind(addr, kind);
1103                peer.state = PeerConnectionState::PendingOut;
1104                peer.fork_id = fork_id.map(Box::new);
1105                entry.insert(peer);
1106                self.connection_info.inc_pending_out();
1107                self.queued_actions
1108                    .push_back(PeerAction::Connect { peer_id, remote_addr: addr.tcp() });
1109            }
1110        }
1111
1112        if kind.is_trusted() {
1113            self.trusted_peer_ids.insert(peer_id);
1114        }
1115    }
1116
1117    /// Removes the tracked node from the trusted set.
1118    pub(crate) fn remove_peer_from_trusted_set(&mut self, peer_id: PeerId) {
1119        self.trusted_peers_resolver.remove(peer_id);
1120        self.trusted_peer_ids.remove(&peer_id);
1121        if let Some(peer) = self.peers.get_mut(&peer_id) &&
1122            peer.is_trusted()
1123        {
1124            peer.kind = PeerKind::Basic;
1125        }
1126    }
1127
1128    /// Returns the best idle peer to connect to.
1129    ///
1130    /// Peers that are `trusted` or `static`, see [`PeerKind`], are prioritized as long as they're
1131    /// not currently marked as banned or backed off.
1132    ///
1133    /// Among remaining peers, the one with the highest reputation is selected. When reputation is
1134    /// equal, a peer with a discovered `fork_id` is preferred since it indicates a compatible fork.
1135    ///
1136    /// If `trusted_nodes_only` is enabled, see [`PeersConfig`], then this will only consider
1137    /// `trusted` peers, including registered trusted IDs whose addresses were discovered before
1138    /// hostname resolution. The same restriction applies when `trusted_only` is set.
1139    ///
1140    /// Returns `None` if no peer is available.
1141    fn best_unconnected(&mut self, trusted_only: bool) -> Option<(PeerId, &mut Peer)> {
1142        let mut unconnected = self.peers.iter_mut().filter(|(peer_id, peer)| {
1143            !peer.is_backed_off() &&
1144                !peer.is_banned() &&
1145                peer.state.is_unconnected() &&
1146                (!(self.trusted_nodes_only || trusted_only) ||
1147                    peer.is_trusted() ||
1148                    self.trusted_peer_ids.contains(*peer_id))
1149        });
1150
1151        // keep track of the best peer, if there's one
1152        let mut best_peer = unconnected.next()?;
1153
1154        if best_peer.1.is_trusted() ||
1155            best_peer.1.is_static() ||
1156            self.trusted_peer_ids.contains(best_peer.0)
1157        {
1158            return Some((*best_peer.0, best_peer.1))
1159        }
1160
1161        for maybe_better in unconnected {
1162            // if the peer is trusted or static, return it immediately
1163            if maybe_better.1.is_trusted() ||
1164                maybe_better.1.is_static() ||
1165                self.trusted_peer_ids.contains(maybe_better.0)
1166            {
1167                return Some((*maybe_better.0, maybe_better.1))
1168            }
1169
1170            // prefer higher reputation, break ties by fork_id presence
1171            match maybe_better.1.reputation.cmp(&best_peer.1.reputation) {
1172                std::cmp::Ordering::Greater => best_peer = maybe_better,
1173                std::cmp::Ordering::Equal
1174                    if maybe_better.1.fork_id.is_some() && best_peer.1.fork_id.is_none() =>
1175                {
1176                    best_peer = maybe_better
1177                }
1178                _ => {}
1179            }
1180        }
1181        Some((*best_peer.0, best_peer.1))
1182    }
1183
1184    /// Disconnects one eligible peer (inbound or outbound) to open a slot for new nodes,
1185    /// mirroring Geth's peer dropper logic:
1186    /// <https://github.com/ethereum/go-ethereum/blob/c5c75977ab55e4d7ea6147cc0e221b588e5e3754/eth/dropper.go#L106-L138>
1187    ///
1188    /// A peer is eligible when its pool (inbound or outbound) is at capacity, the peer is not
1189    /// trusted or static, and it has been connected for at least [`PEER_ROTATION_MIN_UPTIME`].
1190    fn try_rotate_peer(&mut self) {
1191        let outbound_at_capacity = self.connection_info.is_outbound_at_capacity();
1192        let inbound_at_capacity = self.connection_info.is_inbound_at_capacity();
1193
1194        if !outbound_at_capacity && !inbound_at_capacity {
1195            return
1196        }
1197
1198        let now = std::time::Instant::now();
1199
1200        let candidates = self
1201            .peers
1202            .iter()
1203            .filter_map(|(peer_id, peer)| {
1204                let eligible = match peer.state {
1205                    PeerConnectionState::Out => outbound_at_capacity,
1206                    PeerConnectionState::In => inbound_at_capacity,
1207                    _ => false,
1208                };
1209                (eligible &&
1210                    !peer.is_trusted() &&
1211                    !peer.is_static() &&
1212                    !self.trusted_peer_ids.contains(peer_id) &&
1213                    peer.connected_for_at_least(now, PEER_ROTATION_MIN_UPTIME))
1214                .then_some(*peer_id)
1215            })
1216            .collect::<Vec<_>>();
1217
1218        if candidates.is_empty() {
1219            return
1220        }
1221        let peer_id = candidates[rand::rng().random_range(0..candidates.len())];
1222
1223        trace!(target: "net::peers", ?peer_id, "rotating peer to open slot for new nodes");
1224
1225        self.queued_actions.push_back(PeerAction::Disconnect {
1226            peer_id,
1227            reason: Some(DisconnectReason::UselessPeer),
1228        });
1229    }
1230
1231    /// If there's capacity for new outbound connections, this will queue new
1232    /// [`PeerAction::Connect`] actions.
1233    ///
1234    /// New connections are only initiated, if slots are available and appropriate peers are
1235    /// available.
1236    fn fill_outbound_slots(&mut self) {
1237        self.tick();
1238
1239        if !self.net_connection_state.is_active() {
1240            // nothing to fill
1241            return
1242        }
1243
1244        // as long as there are slots available fill them with the best peers
1245        while self.connection_info.has_out_dial_capacity() {
1246            // Trusted peers are dialed even when all outbound slots are taken, so discovered
1247            // peers can never crowd them out; only the concurrent dial limit applies to them.
1248            let has_out_capacity = self.connection_info.has_out_capacity();
1249            let action = {
1250                let Some((peer_id, peer)) = self.best_unconnected(!has_out_capacity) else { break };
1251
1252                trace!(target: "net::peers", ?peer_id, addr=?peer.addr, "schedule outbound connection");
1253
1254                peer.state = PeerConnectionState::PendingOut;
1255                PeerAction::Connect { peer_id, remote_addr: peer.addr.tcp() }
1256            };
1257
1258            self.connection_info.inc_pending_out();
1259
1260            self.queued_actions.push_back(action);
1261        }
1262    }
1263
1264    fn on_resolved_peer(&mut self, peer_id: PeerId, new_record: NodeRecord) {
1265        if !self.trusted_peer_ids.contains(&peer_id) {
1266            trace!(target: "net::peers", ?peer_id, "Ignoring resolved trusted peer after removal");
1267            return
1268        }
1269
1270        let new_addr = PeerAddr::new_with_ports(
1271            new_record.address,
1272            new_record.tcp_port,
1273            Some(new_record.udp_port),
1274        );
1275
1276        if let Some(peer) = self.peers.get_mut(&peer_id) {
1277            if peer.addr != new_addr {
1278                peer.addr = new_addr;
1279                trace!(target: "net::peers", ?peer_id, addr=?peer.addr, "Updated resolved trusted peer address");
1280            }
1281        } else {
1282            trace!(target: "net::peers", ?peer_id, ?new_addr, "Adding trusted peer after first successful resolution");
1283            self.add_peer_kind(peer_id, Some(PeerKind::Trusted), new_addr, None);
1284        }
1285    }
1286
1287    /// Keeps track of network state changes.
1288    pub const fn on_network_state_change(&mut self, state: NetworkConnectionState) {
1289        self.net_connection_state = state;
1290    }
1291
1292    /// Returns the current network connection state.
1293    pub const fn connection_state(&self) -> &NetworkConnectionState {
1294        &self.net_connection_state
1295    }
1296
1297    /// Sets `net_connection_state` to `ShuttingDown`.
1298    pub const fn on_shutdown(&mut self) {
1299        self.net_connection_state = NetworkConnectionState::ShuttingDown;
1300    }
1301
1302    /// Advances the state.
1303    ///
1304    /// Event hooks invoked externally may trigger a new [`PeerAction`] that are buffered until
1305    /// [`PeersManager`] is polled.
1306    pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll<PeerAction> {
1307        loop {
1308            // drain buffered actions
1309            if let Some(action) = self.queued_actions.pop_front() {
1310                return Poll::Ready(action)
1311            }
1312
1313            while let Poll::Ready(Some(cmd)) = self.handle_rx.poll_next_unpin(cx) {
1314                match cmd {
1315                    PeerCommand::Add(peer_id, addr) => {
1316                        self.add_requested_peer(peer_id, None, PeerAddr::from_tcp(addr));
1317                    }
1318                    PeerCommand::Remove(peer) => self.remove_peer(peer),
1319                    PeerCommand::ReputationChange(peer_id, rep) => {
1320                        self.apply_reputation_change(&peer_id, rep)
1321                    }
1322                    PeerCommand::GetPeer(peer, tx) => {
1323                        let _ = tx.send(self.peers.get(&peer).cloned());
1324                    }
1325                    PeerCommand::GetPeers(tx) => {
1326                        let _ = tx.send(self.iter_peers().collect());
1327                    }
1328                }
1329            }
1330
1331            if self.release_interval.poll_tick(cx).is_ready() {
1332                let now = std::time::Instant::now();
1333                let (_, unbanned_peers) = self.ban_list.evict(now);
1334
1335                for peer_id in unbanned_peers {
1336                    if let Some(peer) = self.peers.get_mut(&peer_id) {
1337                        peer.unban();
1338                        self.queued_actions.push_back(PeerAction::UnBanPeer { peer_id });
1339                    }
1340                }
1341
1342                // clear the backoff list of expired backoffs, and mark the relevant peers as
1343                // ready to be dialed
1344                self.backed_off_peers.retain(|peer_id, until| {
1345                    if now > *until {
1346                        if let Some(peer) = self.peers.get_mut(peer_id) {
1347                            peer.backed_off = false;
1348                        }
1349                        return false
1350                    }
1351                    true
1352                })
1353            }
1354
1355            while self.refill_slots_interval.poll_tick(cx).is_ready() {
1356                self.fill_outbound_slots();
1357            }
1358
1359            if let Poll::Ready((peer_id, new_record)) = self.trusted_peers_resolver.poll(cx) {
1360                self.on_resolved_peer(peer_id, new_record);
1361            }
1362
1363            // Poll the jittered rotation timer and rotate one eligible peer when ready.
1364            let rotation_ready = self
1365                .peer_rotation_sleep
1366                .as_mut()
1367                .is_some_and(|sleep| sleep.as_mut().poll(cx).is_ready());
1368            if rotation_ready {
1369                self.try_rotate_peer();
1370                if let Some((mean, sleep)) =
1371                    self.peer_rotation_mean.zip(self.peer_rotation_sleep.as_mut())
1372                {
1373                    sleep
1374                        .as_mut()
1375                        .reset(tokio::time::Instant::now() + jitter_rotation_interval(mean));
1376                }
1377            }
1378
1379            if self.queued_actions.is_empty() {
1380                return Poll::Pending
1381            }
1382        }
1383    }
1384}
1385
1386impl Default for PeersManager {
1387    fn default() -> Self {
1388        Self::new(Default::default())
1389    }
1390}
1391
1392/// Tracks stats about connected nodes
1393#[derive(Debug, Clone, PartialEq, Eq, Default)]
1394pub struct ConnectionInfo {
1395    /// Counter for currently occupied slots for active outbound connections.
1396    num_outbound: usize,
1397    /// Counter for pending outbound connections.
1398    num_pending_out: usize,
1399    /// Counter for currently occupied slots for active inbound connections.
1400    num_inbound: usize,
1401    /// Counter for pending inbound connections.
1402    num_pending_in: usize,
1403    /// Restrictions on number of connections.
1404    config: ConnectionsConfig,
1405}
1406
1407// === impl ConnectionInfo ===
1408
1409impl ConnectionInfo {
1410    /// Returns a new [`ConnectionInfo`] with the given config.
1411    const fn new(config: ConnectionsConfig) -> Self {
1412        Self { config, num_outbound: 0, num_pending_out: 0, num_inbound: 0, num_pending_in: 0 }
1413    }
1414
1415    ///  Returns `true` if there's still capacity to perform an outgoing connection.
1416    const fn has_out_capacity(&self) -> bool {
1417        self.has_out_dial_capacity() &&
1418            self.num_pending_out < self.config.max_outbound.saturating_sub(self.num_outbound)
1419    }
1420
1421    /// Returns `true` if all active outbound slots are occupied (ignoring pending dials).
1422    const fn is_outbound_at_capacity(&self) -> bool {
1423        self.num_outbound >= self.config.max_outbound
1424    }
1425
1426    /// Returns `true` if all active inbound slots are occupied.
1427    const fn is_inbound_at_capacity(&self) -> bool {
1428        self.num_inbound >= self.config.max_inbound
1429    }
1430
1431    ///  Returns `true` if there's still capacity to accept a new incoming connection.
1432    const fn has_in_capacity(&self) -> bool {
1433        self.num_inbound < self.config.max_inbound
1434    }
1435
1436    /// Returns `true` if we can handle an additional incoming pending connection.
1437    const fn has_in_pending_capacity(&self) -> bool {
1438        self.num_pending_in < self.config.max_inbound
1439    }
1440
1441    /// Returns `true` if another outgoing dial can be started without exceeding the concurrent
1442    /// dial limit.
1443    const fn has_out_dial_capacity(&self) -> bool {
1444        self.num_pending_out < self.config.max_concurrent_outbound_dials
1445    }
1446
1447    const fn decr_state(&mut self, state: PeerConnectionState) {
1448        match state {
1449            PeerConnectionState::Idle => {}
1450            PeerConnectionState::DisconnectingIn | PeerConnectionState::In => self.decr_in(),
1451            PeerConnectionState::DisconnectingOut | PeerConnectionState::Out => self.decr_out(),
1452            PeerConnectionState::PendingOut => self.decr_pending_out(),
1453        }
1454    }
1455
1456    const fn decr_out(&mut self) {
1457        self.num_outbound -= 1;
1458    }
1459
1460    const fn inc_out(&mut self) {
1461        self.num_outbound += 1;
1462    }
1463
1464    const fn inc_pending_out(&mut self) {
1465        self.num_pending_out += 1;
1466    }
1467
1468    const fn inc_in(&mut self) {
1469        self.num_inbound += 1;
1470    }
1471
1472    const fn inc_pending_in(&mut self) {
1473        self.num_pending_in += 1;
1474    }
1475
1476    const fn decr_in(&mut self) {
1477        self.num_inbound -= 1;
1478    }
1479
1480    const fn decr_pending_out(&mut self) {
1481        self.num_pending_out -= 1;
1482    }
1483
1484    const fn decr_pending_in(&mut self) {
1485        self.num_pending_in -= 1;
1486    }
1487}
1488
1489/// Actions the peer manager can trigger.
1490#[derive(Debug)]
1491pub enum PeerAction {
1492    /// Start a new connection to a peer.
1493    Connect {
1494        /// The peer to connect to.
1495        peer_id: PeerId,
1496        /// Where to reach the node
1497        remote_addr: SocketAddr,
1498    },
1499    /// Disconnect an existing connection.
1500    Disconnect {
1501        /// The peer ID of the established connection.
1502        peer_id: PeerId,
1503        /// An optional reason for the disconnect.
1504        reason: Option<DisconnectReason>,
1505    },
1506    /// Disconnect an existing incoming connection, because the peers reputation is below the
1507    /// banned threshold or is on the [`BanList`]
1508    DisconnectBannedIncoming {
1509        /// The peer ID of the established connection.
1510        peer_id: PeerId,
1511    },
1512    /// Disconnect an untrusted incoming connection when trust-node-only is enabled.
1513    DisconnectUntrustedIncoming {
1514        /// The peer ID.
1515        peer_id: PeerId,
1516    },
1517    /// Ban the peer in discovery.
1518    DiscoveryBanPeerId {
1519        /// The peer ID.
1520        peer_id: PeerId,
1521        /// The IP address.
1522        ip_addr: IpAddr,
1523    },
1524    /// Ban the IP in discovery.
1525    DiscoveryBanIp {
1526        /// The IP address.
1527        ip_addr: IpAddr,
1528    },
1529    /// Ban the peer temporarily
1530    BanPeer {
1531        /// The peer ID.
1532        peer_id: PeerId,
1533    },
1534    /// Unban the peer temporarily
1535    UnBanPeer {
1536        /// The peer ID.
1537        peer_id: PeerId,
1538    },
1539    /// Emit peerAdded event
1540    PeerAdded(PeerId),
1541    /// Emit peerRemoved event
1542    PeerRemoved(PeerId),
1543}
1544
1545/// Error thrown when an incoming connection is rejected right away
1546#[derive(Debug, Error, PartialEq, Eq)]
1547pub enum InboundConnectionError {
1548    /// The remote's ip address is banned
1549    IpBanned,
1550    /// No capacity for new inbound connections
1551    ExceedsCapacity,
1552}
1553
1554impl Display for InboundConnectionError {
1555    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1556        write!(f, "{self:?}")
1557    }
1558}
1559
1560/// The reason a peer was backed off.
1561#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1562pub enum BackoffReason {
1563    /// The remote peer responded with `TooManyPeers` (0x04).
1564    TooManyPeers,
1565    /// The session was gracefully closed and we're backing off briefly.
1566    GracefulClose,
1567    /// A connection or protocol-level error occurred.
1568    ConnectionError,
1569}
1570
1571impl BackoffReason {
1572    /// Derives the backoff reason from an optional [`DisconnectReason`].
1573    pub const fn from_disconnect(reason: Option<DisconnectReason>) -> Self {
1574        match reason {
1575            Some(DisconnectReason::TooManyPeers) => Self::TooManyPeers,
1576            _ => Self::ConnectionError,
1577        }
1578    }
1579}
1580
1581/// Returns a random duration uniformly distributed in `[mean * 3/5, mean * 7/5]`.
1582///
1583/// With the default 5-minute mean this gives `[3 min, 7 min]`, matching Geth's peer dropper:
1584/// <https://github.com/ethereum/go-ethereum/blob/c5c75977ab55e4d7ea6147cc0e221b588e5e3754/eth/dropper.go#L32-L36>
1585fn jitter_rotation_interval(mean: Duration) -> Duration {
1586    let min_nanos = (mean * 3 / 5).as_nanos() as u64;
1587    let max_nanos = (mean * 7 / 5).as_nanos() as u64;
1588    Duration::from_nanos(rand::rng().random_range(min_nanos..=max_nanos))
1589}
1590
1591#[cfg(test)]
1592mod tests {
1593    use super::*;
1594    use alloy_primitives::B512;
1595    use reth_ecies::ECIESErrorImpl;
1596    use reth_eth_wire::{
1597        errors::{EthHandshakeError, EthStreamError, P2PHandshakeError, P2PStreamError},
1598        DisconnectReason,
1599    };
1600    use reth_ethereum_forks::{ForkHash, ForkId};
1601    use reth_net_banlist::BanList;
1602    use reth_network_api::Direction;
1603    use reth_network_peers::{NodeRecord, PeerId, TrustedPeer};
1604    use reth_network_types::{
1605        peers::reputation::DEFAULT_REPUTATION, BackoffKind, Peer, PeerKind, ReputationChangeKind,
1606    };
1607    use std::{
1608        future::{poll_fn, Future},
1609        io,
1610        net::{IpAddr, Ipv4Addr, SocketAddr},
1611        pin::Pin,
1612        task::{Context, Poll, Waker},
1613        time::Duration,
1614    };
1615    use url::Host;
1616
1617    use crate::{
1618        error::SessionError,
1619        peers::{
1620            ConnectionInfo, InboundConnectionError, PeerAction, PeerAddr, PeerBackoffDurations,
1621            PeerConnectionState, MAX_INCOMING_IP_THROTTLE_ENTRIES,
1622        },
1623        session::PendingSessionHandshakeError,
1624        PeersConfig,
1625    };
1626
1627    struct PeerActionFuture<'a> {
1628        peers: &'a mut PeersManager,
1629    }
1630
1631    impl Future for PeerActionFuture<'_> {
1632        type Output = PeerAction;
1633
1634        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1635            self.get_mut().peers.poll(cx)
1636        }
1637    }
1638
1639    macro_rules! event {
1640        ($peers:expr) => {
1641            PeerActionFuture { peers: &mut $peers }.await
1642        };
1643    }
1644
1645    fn set_connected_at(
1646        peers: &mut PeersManager,
1647        peer_id: PeerId,
1648        connected_at: std::time::Instant,
1649    ) {
1650        peers.peers.get_mut(&peer_id).expect("peer exists").connected_at = Some(connected_at);
1651    }
1652
1653    /// Polls the manager once without waiting for an action.
1654    fn poll_now(peers: &mut PeersManager) -> Poll<PeerAction> {
1655        peers.poll(&mut Context::from_waker(Waker::noop()))
1656    }
1657
1658    #[tokio::test]
1659    async fn test_insert() {
1660        let peer = PeerId::random();
1661        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1662        let mut peers = PeersManager::default();
1663        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
1664
1665        match event!(peers) {
1666            PeerAction::PeerAdded(peer_id) => {
1667                assert_eq!(peer_id, peer);
1668            }
1669            _ => unreachable!(),
1670        }
1671        match event!(peers) {
1672            PeerAction::Connect { peer_id, remote_addr } => {
1673                assert_eq!(peer_id, peer);
1674                assert_eq!(remote_addr, socket_addr);
1675            }
1676            _ => unreachable!(),
1677        }
1678
1679        let (record, _) = peers.peer_by_id(peer).unwrap();
1680        assert_eq!(record.tcp_addr(), socket_addr);
1681        assert_eq!(record.udp_addr(), socket_addr);
1682    }
1683
1684    #[tokio::test]
1685    async fn test_insert_udp() {
1686        let peer = PeerId::random();
1687        let tcp_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1688        let udp_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
1689        let mut peers = PeersManager::default();
1690        peers.add_peer(peer, PeerAddr::new(tcp_addr, Some(udp_addr)), None);
1691
1692        match event!(peers) {
1693            PeerAction::PeerAdded(peer_id) => {
1694                assert_eq!(peer_id, peer);
1695            }
1696            _ => unreachable!(),
1697        }
1698        match event!(peers) {
1699            PeerAction::Connect { peer_id, remote_addr } => {
1700                assert_eq!(peer_id, peer);
1701                assert_eq!(remote_addr, tcp_addr);
1702            }
1703            _ => unreachable!(),
1704        }
1705
1706        let (record, _) = peers.peer_by_id(peer).unwrap();
1707        assert_eq!(record.tcp_addr(), tcp_addr);
1708        assert_eq!(record.udp_addr(), udp_addr);
1709    }
1710
1711    #[tokio::test]
1712    async fn test_ban() {
1713        let peer = PeerId::random();
1714        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1715        let mut peers = PeersManager::default();
1716        peers.ban_peer(peer);
1717        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
1718
1719        match event!(peers) {
1720            PeerAction::BanPeer { peer_id } => {
1721                assert_eq!(peer_id, peer);
1722            }
1723            _ => unreachable!(),
1724        }
1725
1726        poll_fn(|cx| {
1727            assert!(peers.poll(cx).is_pending());
1728            Poll::Ready(())
1729        })
1730        .await;
1731    }
1732
1733    #[tokio::test]
1734    async fn test_unban() {
1735        let peer = PeerId::random();
1736        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1737        let mut peers = PeersManager::default();
1738        peers.ban_peer(peer);
1739        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
1740
1741        match event!(peers) {
1742            PeerAction::BanPeer { peer_id } => {
1743                assert_eq!(peer_id, peer);
1744            }
1745            _ => unreachable!(),
1746        }
1747
1748        poll_fn(|cx| {
1749            assert!(peers.poll(cx).is_pending());
1750            Poll::Ready(())
1751        })
1752        .await;
1753
1754        peers.unban_peer(peer);
1755
1756        match event!(peers) {
1757            PeerAction::UnBanPeer { peer_id } => {
1758                assert_eq!(peer_id, peer);
1759            }
1760            _ => unreachable!(),
1761        }
1762
1763        poll_fn(|cx| {
1764            assert!(peers.poll(cx).is_pending());
1765            Poll::Ready(())
1766        })
1767        .await;
1768    }
1769
1770    #[tokio::test]
1771    async fn test_admin_ban_removes_peer_and_bans_indefinitely() {
1772        let peer = PeerId::random();
1773        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1774        let mut peers = PeersManager::default();
1775        peers.peers.insert(peer, Peer::new(PeerAddr::from_tcp(socket_addr)));
1776
1777        peers.ban_peer_by_admin(peer);
1778
1779        assert!(peers.ban_list.is_banned_peer(&peer));
1780        assert!(peers.peer_by_id(peer).is_none());
1781
1782        match peers.queued_actions.pop_front() {
1783            Some(PeerAction::PeerRemoved(peer_id)) => assert_eq!(peer_id, peer),
1784            other => panic!("unexpected action: {other:?}"),
1785        }
1786
1787        let (_, unbanned_peers) =
1788            peers.ban_list.evict(std::time::Instant::now() + Duration::from_secs(1));
1789        assert!(unbanned_peers.is_empty());
1790    }
1791
1792    #[tokio::test]
1793    async fn test_admin_ban_does_not_override_trusted_peer() {
1794        let peer = PeerId::random();
1795        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1796        let mut peers = PeersManager::default();
1797        peers.peers.insert(peer, Peer::trusted(PeerAddr::from_tcp(socket_addr)));
1798
1799        peers.ban_peer_by_admin(peer);
1800
1801        assert!(!peers.ban_list.is_banned_peer(&peer));
1802        assert!(peers.peer_by_id(peer).is_some());
1803        assert!(peers.queued_actions.is_empty());
1804    }
1805
1806    #[tokio::test]
1807    async fn test_admin_unban_resets_reputation() {
1808        let peer = PeerId::random();
1809        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1810        let mut peers = PeersManager::default();
1811        let mut peer_info = Peer::new(PeerAddr::from_tcp(socket_addr));
1812        peer_info.reputation = i32::MIN;
1813        peers.peers.insert(peer, peer_info);
1814        peers.ban_list.ban_peer(peer);
1815        assert!(peers.peers.get(&peer).is_some_and(Peer::is_banned));
1816
1817        peers.unban_peer_by_admin(peer);
1818
1819        assert!(!peers.ban_list.is_banned_peer(&peer));
1820        assert!(!peers.peers.get(&peer).is_some_and(Peer::is_banned));
1821        assert!(matches!(
1822            peers.queued_actions.pop_front(),
1823            Some(PeerAction::UnBanPeer { peer_id }) if peer_id == peer
1824        ));
1825    }
1826
1827    #[tokio::test]
1828    async fn test_backoff_on_busy() {
1829        let peer = PeerId::random();
1830        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1831
1832        let mut peers = PeersManager::new(PeersConfig::test());
1833        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
1834
1835        match event!(peers) {
1836            PeerAction::PeerAdded(peer_id) => {
1837                assert_eq!(peer_id, peer);
1838            }
1839            _ => unreachable!(),
1840        }
1841        match event!(peers) {
1842            PeerAction::Connect { peer_id, .. } => {
1843                assert_eq!(peer_id, peer);
1844            }
1845            _ => unreachable!(),
1846        }
1847
1848        poll_fn(|cx| {
1849            assert!(peers.poll(cx).is_pending());
1850            Poll::Ready(())
1851        })
1852        .await;
1853
1854        peers.on_active_session_dropped(
1855            &socket_addr,
1856            &peer,
1857            &EthStreamError::P2PStreamError(P2PStreamError::Disconnected(
1858                DisconnectReason::TooManyPeers,
1859            )),
1860        );
1861
1862        poll_fn(|cx| {
1863            assert!(peers.poll(cx).is_pending());
1864            Poll::Ready(())
1865        })
1866        .await;
1867
1868        assert!(peers.backed_off_peers.contains_key(&peer));
1869        assert!(peers.peers.get(&peer).unwrap().is_backed_off());
1870
1871        tokio::time::sleep(peers.backoff_durations.low).await;
1872
1873        match event!(peers) {
1874            PeerAction::Connect { peer_id, .. } => {
1875                assert_eq!(peer_id, peer);
1876            }
1877            _ => unreachable!(),
1878        }
1879
1880        assert!(!peers.backed_off_peers.contains_key(&peer));
1881        assert!(!peers.peers.get(&peer).unwrap().is_backed_off());
1882    }
1883
1884    #[tokio::test]
1885    async fn test_backoff_on_no_response() {
1886        let peer = PeerId::random();
1887        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1888
1889        let backoff_durations = PeerBackoffDurations::test();
1890        let config = PeersConfig { backoff_durations, ..PeersConfig::test() };
1891        let mut peers = PeersManager::new(config);
1892        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
1893
1894        match event!(peers) {
1895            PeerAction::PeerAdded(peer_id) => {
1896                assert_eq!(peer_id, peer);
1897            }
1898            _ => unreachable!(),
1899        }
1900        match event!(peers) {
1901            PeerAction::Connect { peer_id, .. } => {
1902                assert_eq!(peer_id, peer);
1903            }
1904            _ => unreachable!(),
1905        }
1906
1907        poll_fn(|cx| {
1908            assert!(peers.poll(cx).is_pending());
1909            Poll::Ready(())
1910        })
1911        .await;
1912
1913        peers.on_outgoing_pending_session_dropped(
1914            &socket_addr,
1915            &peer,
1916            &PendingSessionHandshakeError::Eth(EthStreamError::EthHandshakeError(
1917                EthHandshakeError::NoResponse,
1918            )),
1919        );
1920
1921        poll_fn(|cx| {
1922            assert!(peers.poll(cx).is_pending());
1923            Poll::Ready(())
1924        })
1925        .await;
1926
1927        assert!(peers.backed_off_peers.contains_key(&peer));
1928        assert!(peers.peers.get(&peer).unwrap().is_backed_off());
1929
1930        tokio::time::sleep(backoff_durations.high).await;
1931
1932        match event!(peers) {
1933            PeerAction::Connect { peer_id, .. } => {
1934                assert_eq!(peer_id, peer);
1935            }
1936            _ => unreachable!(),
1937        }
1938
1939        assert!(!peers.backed_off_peers.contains_key(&peer));
1940        assert!(!peers.peers.get(&peer).unwrap().is_backed_off());
1941    }
1942
1943    #[tokio::test]
1944    async fn test_low_backoff() {
1945        let peer = PeerId::random();
1946        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1947        let config = PeersConfig::test();
1948        let mut peers = PeersManager::new(config);
1949        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
1950        let peer_struct = peers.peers.get_mut(&peer).unwrap();
1951
1952        let backoff_timestamp = peers
1953            .backoff_durations
1954            .backoff_until(BackoffKind::Low, peer_struct.severe_backoff_counter);
1955
1956        let expected = std::time::Instant::now() + peers.backoff_durations.low;
1957        assert!(backoff_timestamp <= expected);
1958    }
1959
1960    #[tokio::test]
1961    async fn test_multiple_backoff_calculations() {
1962        let peer = PeerId::random();
1963        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1964        let config = PeersConfig::default();
1965        let mut peers = PeersManager::new(config);
1966        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
1967        let peer_struct = peers.peers.get_mut(&peer).unwrap();
1968
1969        // Simulate a peer that was already backed off once
1970        peer_struct.severe_backoff_counter = 1;
1971
1972        let now = std::time::Instant::now();
1973
1974        // Simulate the increment that happens in on_connection_failure
1975        peer_struct.severe_backoff_counter += 1;
1976        // Get official backoff time
1977        let backoff_time = peers
1978            .backoff_durations
1979            .backoff_until(BackoffKind::High, peer_struct.severe_backoff_counter);
1980
1981        // Duration of the backoff should be 2 * 15 minutes = 30 minutes
1982        let backoff_duration = std::time::Duration::new(30 * 60, 0);
1983
1984        // We can't use assert_eq! since there is a very small diff in the nano secs
1985        // Usually it is 1800s != 1799.9999996s
1986        assert!(backoff_time.duration_since(now) > backoff_duration);
1987    }
1988
1989    #[tokio::test]
1990    async fn test_ban_on_active_drop() {
1991        let peer = PeerId::random();
1992        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
1993        let mut peers = PeersManager::default();
1994        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
1995
1996        match event!(peers) {
1997            PeerAction::PeerAdded(peer_id) => {
1998                assert_eq!(peer_id, peer);
1999            }
2000            _ => unreachable!(),
2001        }
2002        match event!(peers) {
2003            PeerAction::Connect { peer_id, .. } => {
2004                assert_eq!(peer_id, peer);
2005            }
2006            _ => unreachable!(),
2007        }
2008
2009        poll_fn(|cx| {
2010            assert!(peers.poll(cx).is_pending());
2011            Poll::Ready(())
2012        })
2013        .await;
2014
2015        peers.on_active_session_dropped(
2016            &socket_addr,
2017            &peer,
2018            &EthStreamError::P2PStreamError(P2PStreamError::Disconnected(
2019                DisconnectReason::UselessPeer,
2020            )),
2021        );
2022
2023        match event!(peers) {
2024            PeerAction::PeerRemoved(peer_id) => {
2025                assert_eq!(peer_id, peer);
2026            }
2027            _ => unreachable!(),
2028        }
2029        match event!(peers) {
2030            PeerAction::BanPeer { peer_id } => {
2031                assert_eq!(peer_id, peer);
2032            }
2033            _ => unreachable!(),
2034        }
2035
2036        poll_fn(|cx| {
2037            assert!(peers.poll(cx).is_pending());
2038            Poll::Ready(())
2039        })
2040        .await;
2041
2042        assert!(!peers.peers.contains_key(&peer));
2043    }
2044
2045    #[tokio::test]
2046    async fn test_remove_on_max_backoff_count() {
2047        let peer = PeerId::random();
2048        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2049        let config = PeersConfig::test();
2050        let mut peers = PeersManager::new(config.clone());
2051        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
2052        let peer_struct = peers.peers.get_mut(&peer).unwrap();
2053
2054        // Simulate a peer that was already backed off once
2055        peer_struct.severe_backoff_counter = config.max_backoff_count;
2056
2057        match event!(peers) {
2058            PeerAction::PeerAdded(peer_id) => {
2059                assert_eq!(peer_id, peer);
2060            }
2061            _ => unreachable!(),
2062        }
2063        match event!(peers) {
2064            PeerAction::Connect { peer_id, .. } => {
2065                assert_eq!(peer_id, peer);
2066            }
2067            _ => unreachable!(),
2068        }
2069
2070        poll_fn(|cx| {
2071            assert!(peers.poll(cx).is_pending());
2072            Poll::Ready(())
2073        })
2074        .await;
2075
2076        peers.on_outgoing_connection_failure(
2077            &socket_addr,
2078            &peer,
2079            &io::Error::new(io::ErrorKind::ConnectionRefused, "peer unreachable"),
2080        );
2081
2082        match event!(peers) {
2083            PeerAction::PeerRemoved(peer_id) => {
2084                assert_eq!(peer_id, peer);
2085            }
2086            _ => unreachable!(),
2087        }
2088
2089        poll_fn(|cx| {
2090            assert!(peers.poll(cx).is_pending());
2091            Poll::Ready(())
2092        })
2093        .await;
2094
2095        assert!(!peers.peers.contains_key(&peer));
2096    }
2097
2098    #[tokio::test]
2099    async fn test_ban_on_pending_drop() {
2100        let peer = PeerId::random();
2101        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2102        let mut peers = PeersManager::default();
2103        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
2104
2105        match event!(peers) {
2106            PeerAction::PeerAdded(peer_id) => {
2107                assert_eq!(peer_id, peer);
2108            }
2109            _ => unreachable!(),
2110        }
2111        match event!(peers) {
2112            PeerAction::Connect { peer_id, .. } => {
2113                assert_eq!(peer_id, peer);
2114            }
2115            _ => unreachable!(),
2116        }
2117
2118        poll_fn(|cx| {
2119            assert!(peers.poll(cx).is_pending());
2120            Poll::Ready(())
2121        })
2122        .await;
2123
2124        peers.on_outgoing_pending_session_dropped(
2125            &socket_addr,
2126            &peer,
2127            &PendingSessionHandshakeError::Eth(EthStreamError::P2PStreamError(
2128                P2PStreamError::Disconnected(DisconnectReason::UselessPeer),
2129            )),
2130        );
2131
2132        match event!(peers) {
2133            PeerAction::PeerRemoved(peer_id) => {
2134                assert_eq!(peer_id, peer);
2135            }
2136            _ => unreachable!(),
2137        }
2138        match event!(peers) {
2139            PeerAction::BanPeer { peer_id } => {
2140                assert_eq!(peer_id, peer);
2141            }
2142            _ => unreachable!(),
2143        }
2144
2145        poll_fn(|cx| {
2146            assert!(peers.poll(cx).is_pending());
2147            Poll::Ready(())
2148        })
2149        .await;
2150
2151        assert!(!peers.peers.contains_key(&peer));
2152    }
2153
2154    #[tokio::test]
2155    async fn test_internally_closed_incoming() {
2156        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2157        let mut peers = PeersManager::default();
2158
2159        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2160        assert_eq!(peers.connection_info.num_pending_in, 1);
2161        peers.on_incoming_pending_session_rejected_internally();
2162        assert_eq!(peers.connection_info.num_pending_in, 0);
2163    }
2164
2165    #[tokio::test]
2166    async fn test_reject_incoming_at_pending_capacity() {
2167        let mut peers = PeersManager::default();
2168
2169        for count in 1..=peers.connection_info.config.max_inbound {
2170            let socket_addr =
2171                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, count as u8)), 8008);
2172            assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2173            assert_eq!(peers.connection_info.num_pending_in, count);
2174        }
2175        assert!(peers.connection_info.has_in_capacity());
2176        assert!(!peers.connection_info.has_in_pending_capacity());
2177
2178        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 100)), 8008);
2179        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_err());
2180    }
2181
2182    #[tokio::test]
2183    async fn test_reject_incoming_at_pending_capacity_trusted_peers() {
2184        let mut peers = PeersManager::new(PeersConfig::test().with_max_inbound(2));
2185        let trusted = PeerId::random();
2186        peers.add_trusted_peer_id(trusted);
2187
2188        // connect the trusted peer
2189        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 0)), 8008);
2190        assert!(peers.on_incoming_pending_session(addr.ip()).is_ok());
2191        peers.on_incoming_session_established(trusted, addr);
2192
2193        match event!(peers) {
2194            PeerAction::PeerAdded(id) => {
2195                assert_eq!(id, trusted);
2196            }
2197            _ => unreachable!(),
2198        }
2199
2200        // saturate the remaining inbound slots with untrusted peers
2201        let mut connected_untrusted_peer_ids = Vec::new();
2202        for i in 0..(peers.connection_info.config.max_inbound - 1) {
2203            let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, (i + 1) as u8)), 8008);
2204            assert!(peers.on_incoming_pending_session(addr.ip()).is_ok());
2205            let peer_id = PeerId::random();
2206            peers.on_incoming_session_established(peer_id, addr);
2207            connected_untrusted_peer_ids.push(peer_id);
2208
2209            match event!(peers) {
2210                PeerAction::PeerAdded(id) => {
2211                    assert_eq!(id, peer_id);
2212                }
2213                _ => unreachable!(),
2214            }
2215        }
2216
2217        let mut pending_addrs = Vec::new();
2218
2219        // saturate available slots
2220        for i in 0..2 {
2221            let socket_addr =
2222                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, (i + 10) as u8)), 8008);
2223            assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2224
2225            pending_addrs.push(socket_addr);
2226        }
2227
2228        assert_eq!(peers.connection_info.num_pending_in, 2);
2229
2230        // try to handle additional incoming connections at capacity
2231        for i in 0..2 {
2232            let socket_addr =
2233                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, (i + 20) as u8)), 8008);
2234            assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_err());
2235        }
2236
2237        let err = PendingSessionHandshakeError::Eth(EthStreamError::P2PStreamError(
2238            P2PStreamError::HandshakeError(P2PHandshakeError::Disconnected(
2239                DisconnectReason::UselessPeer,
2240            )),
2241        ));
2242
2243        // Remove all pending peers
2244        for pending_addr in pending_addrs {
2245            peers.on_incoming_pending_session_dropped(pending_addr, &err);
2246        }
2247
2248        println!("num_pending_in: {}", peers.connection_info.num_pending_in);
2249
2250        println!(
2251            "num_inbound: {}, has_in_capacity: {}",
2252            peers.connection_info.num_inbound,
2253            peers.connection_info.has_in_capacity()
2254        );
2255
2256        // disconnect a connected peer
2257        peers.on_active_session_gracefully_closed(connected_untrusted_peer_ids[0]);
2258
2259        println!(
2260            "num_inbound: {}, has_in_capacity: {}",
2261            peers.connection_info.num_inbound,
2262            peers.connection_info.has_in_capacity()
2263        );
2264
2265        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 99)), 8008);
2266        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2267    }
2268
2269    #[tokio::test]
2270    async fn test_closed_incoming() {
2271        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2272        let mut peers = PeersManager::default();
2273
2274        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2275        assert_eq!(peers.connection_info.num_pending_in, 1);
2276        peers.on_incoming_pending_session_gracefully_closed();
2277        assert_eq!(peers.connection_info.num_pending_in, 0);
2278    }
2279
2280    #[tokio::test]
2281    async fn test_dropped_incoming() {
2282        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 0, 1, 2)), 8008);
2283        let ban_duration = Duration::from_millis(500);
2284        let config = PeersConfig {
2285            ban_duration,
2286            incoming_ip_throttle_duration: ban_duration,
2287            ..PeersConfig::test()
2288        };
2289        let mut peers = PeersManager::new(config);
2290
2291        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2292        assert_eq!(peers.connection_info.num_pending_in, 1);
2293        let err = PendingSessionHandshakeError::Eth(EthStreamError::P2PStreamError(
2294            P2PStreamError::HandshakeError(P2PHandshakeError::Disconnected(
2295                DisconnectReason::UselessPeer,
2296            )),
2297        ));
2298
2299        peers.on_incoming_pending_session_dropped(socket_addr, &err);
2300        assert_eq!(peers.connection_info.num_pending_in, 0);
2301        assert!(peers.ban_list.is_banned_ip(&socket_addr.ip()));
2302
2303        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_err());
2304
2305        // unbanned after timeout
2306        tokio::time::sleep(ban_duration).await;
2307
2308        poll_fn(|cx| {
2309            let _ = peers.poll(cx);
2310            Poll::Ready(())
2311        })
2312        .await;
2313
2314        assert!(!peers.ban_list.is_banned_ip(&socket_addr.ip()));
2315        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2316    }
2317
2318    #[tokio::test]
2319    async fn test_reputation_change_connected() {
2320        let peer = PeerId::random();
2321        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2322        let mut peers = PeersManager::default();
2323        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
2324
2325        match event!(peers) {
2326            PeerAction::PeerAdded(peer_id) => {
2327                assert_eq!(peer_id, peer);
2328            }
2329            _ => unreachable!(),
2330        }
2331        match event!(peers) {
2332            PeerAction::Connect { peer_id, remote_addr } => {
2333                assert_eq!(peer_id, peer);
2334                assert_eq!(remote_addr, socket_addr);
2335            }
2336            _ => unreachable!(),
2337        }
2338
2339        let p = peers.peers.get_mut(&peer).unwrap();
2340        assert_eq!(p.state, PeerConnectionState::PendingOut);
2341
2342        peers.apply_reputation_change(&peer, ReputationChangeKind::BadProtocol);
2343
2344        let p = peers.peers.get(&peer).unwrap();
2345        assert_eq!(p.state, PeerConnectionState::PendingOut);
2346        assert!(p.is_banned());
2347
2348        peers.on_active_session_gracefully_closed(peer);
2349
2350        let p = peers.peers.get(&peer).unwrap();
2351        assert_eq!(p.state, PeerConnectionState::Idle);
2352        assert!(p.is_banned());
2353
2354        match event!(peers) {
2355            PeerAction::Disconnect { peer_id, .. } => {
2356                assert_eq!(peer_id, peer);
2357            }
2358            _ => unreachable!(),
2359        }
2360    }
2361
2362    #[tokio::test]
2363    async fn retain_trusted_status() {
2364        let _socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 99)), 8008);
2365        let trusted = PeerId::random();
2366        let mut peers =
2367            PeersManager::new(PeersConfig::test().with_trusted_nodes(vec![TrustedPeer {
2368                host: Host::Ipv4(Ipv4Addr::new(127, 0, 1, 2)),
2369                tcp_port: 8008,
2370                udp_port: 8008,
2371                id: trusted,
2372            }]));
2373        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2374        peers.add_peer(trusted, PeerAddr::from_tcp(socket_addr), None);
2375        assert!(peers.peers.get(&trusted).unwrap().is_trusted());
2376    }
2377
2378    #[tokio::test]
2379    async fn accept_incoming_trusted_unknown_peer_address() {
2380        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 99)), 8008);
2381        let mut peers = PeersManager::new(PeersConfig::test().with_max_inbound(2));
2382        // try to connect trusted peer
2383        let trusted = PeerId::random();
2384        peers.add_trusted_peer_id(trusted);
2385
2386        // saturate the inbound slots
2387        for i in 0..peers.connection_info.config.max_inbound {
2388            let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, i as u8)), 8008);
2389            assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2390            let peer_id = PeerId::random();
2391            peers.on_incoming_session_established(peer_id, addr);
2392
2393            match event!(peers) {
2394                PeerAction::PeerAdded(id) => {
2395                    assert_eq!(id, peer_id);
2396                }
2397                _ => unreachable!(),
2398            }
2399        }
2400
2401        // try to connect untrusted peer
2402        let untrusted = PeerId::random();
2403        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 99)), 8008);
2404        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2405        peers.on_incoming_session_established(untrusted, socket_addr);
2406
2407        match event!(peers) {
2408            PeerAction::PeerAdded(id) => {
2409                assert_eq!(id, untrusted);
2410            }
2411            _ => unreachable!(),
2412        }
2413
2414        match event!(peers) {
2415            PeerAction::Disconnect { peer_id, reason } => {
2416                assert_eq!(peer_id, untrusted);
2417                assert_eq!(reason, Some(DisconnectReason::TooManyPeers));
2418            }
2419            _ => unreachable!(),
2420        }
2421
2422        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 100)), 8008);
2423        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2424        peers.on_incoming_session_established(trusted, socket_addr);
2425
2426        match event!(peers) {
2427            PeerAction::PeerAdded(id) => {
2428                assert_eq!(id, trusted);
2429            }
2430            _ => unreachable!(),
2431        }
2432
2433        poll_fn(|cx| {
2434            assert!(peers.poll(cx).is_pending());
2435            Poll::Ready(())
2436        })
2437        .await;
2438
2439        let peer = peers.peers.get(&trusted).unwrap();
2440        assert_eq!(peer.state, PeerConnectionState::In);
2441    }
2442
2443    #[tokio::test]
2444    async fn test_already_connected() {
2445        let peer = PeerId::random();
2446        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2447        let mut peers = PeersManager::default();
2448
2449        // Attempt to establish an incoming session, expecting `num_pending_in` to increase by 1
2450        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2451        assert_eq!(peers.connection_info.num_pending_in, 1);
2452
2453        // Establish a session with the peer, expecting the peer to be added and the `num_inbound`
2454        // to increase by 1
2455        peers.on_incoming_session_established(peer, socket_addr);
2456        let p = peers.peers.get_mut(&peer).expect("peer not found");
2457        assert_eq!(p.addr.tcp(), socket_addr);
2458        assert_eq!(peers.connection_info.num_pending_in, 0);
2459        assert_eq!(peers.connection_info.num_inbound, 1);
2460
2461        // Attempt to establish another incoming session, expecting the `num_pending_in` to increase
2462        // by 1
2463        assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
2464        assert_eq!(peers.connection_info.num_pending_in, 1);
2465
2466        // Simulate a rejection due to an already established connection, expecting the
2467        // `num_pending_in` to decrease by 1. The peer should remain connected and the `num_inbound`
2468        // should not be changed.
2469        peers.on_already_connected(Direction::Incoming);
2470
2471        let p = peers.peers.get_mut(&peer).expect("peer not found");
2472        assert_eq!(p.addr.tcp(), socket_addr);
2473        assert_eq!(peers.connection_info.num_pending_in, 0);
2474        assert_eq!(peers.connection_info.num_inbound, 1);
2475    }
2476
2477    #[tokio::test]
2478    async fn test_reputation_change_trusted_peer() {
2479        let peer = PeerId::random();
2480        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2481        let mut peers = PeersManager::default();
2482        peers.add_trusted_peer(peer, PeerAddr::from_tcp(socket_addr));
2483
2484        match event!(peers) {
2485            PeerAction::PeerAdded(peer_id) => {
2486                assert_eq!(peer_id, peer);
2487            }
2488            _ => unreachable!(),
2489        }
2490        match event!(peers) {
2491            PeerAction::Connect { peer_id, remote_addr } => {
2492                assert_eq!(peer_id, peer);
2493                assert_eq!(remote_addr, socket_addr);
2494            }
2495            _ => unreachable!(),
2496        }
2497
2498        assert_eq!(peers.peers.get_mut(&peer).unwrap().state, PeerConnectionState::PendingOut);
2499        peers.on_active_outgoing_established(peer);
2500        assert_eq!(peers.peers.get_mut(&peer).unwrap().state, PeerConnectionState::Out);
2501
2502        peers.apply_reputation_change(&peer, ReputationChangeKind::BadMessage);
2503
2504        {
2505            let p = peers.peers.get(&peer).unwrap();
2506            assert_eq!(p.state, PeerConnectionState::Out);
2507            // not banned yet
2508            assert!(!p.is_banned());
2509        }
2510
2511        // ensure peer is banned eventually
2512        loop {
2513            peers.apply_reputation_change(&peer, ReputationChangeKind::BadMessage);
2514
2515            let p = peers.peers.get(&peer).unwrap();
2516            if p.is_banned() {
2517                break
2518            }
2519        }
2520
2521        match event!(peers) {
2522            PeerAction::Disconnect { peer_id, .. } => {
2523                assert_eq!(peer_id, peer);
2524            }
2525            _ => unreachable!(),
2526        }
2527    }
2528
2529    #[tokio::test]
2530    async fn test_reputation_management() {
2531        let peer = PeerId::random();
2532        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2533        let mut peers = PeersManager::default();
2534        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
2535        assert_eq!(peers.get_reputation(&peer), Some(0));
2536
2537        peers.apply_reputation_change(&peer, ReputationChangeKind::Other(1024));
2538        assert_eq!(peers.get_reputation(&peer), Some(1024));
2539
2540        peers.apply_reputation_change(&peer, ReputationChangeKind::Reset);
2541        assert_eq!(peers.get_reputation(&peer), Some(0));
2542    }
2543
2544    #[tokio::test]
2545    async fn test_remove_discovered_active() {
2546        let peer = PeerId::random();
2547        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2548        let mut peers = PeersManager::default();
2549        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
2550
2551        match event!(peers) {
2552            PeerAction::PeerAdded(peer_id) => {
2553                assert_eq!(peer_id, peer);
2554            }
2555            _ => unreachable!(),
2556        }
2557        match event!(peers) {
2558            PeerAction::Connect { peer_id, remote_addr } => {
2559                assert_eq!(peer_id, peer);
2560                assert_eq!(remote_addr, socket_addr);
2561            }
2562            _ => unreachable!(),
2563        }
2564
2565        let p = peers.peers.get(&peer).unwrap();
2566        assert_eq!(p.state, PeerConnectionState::PendingOut);
2567
2568        peers.remove_peer(peer);
2569
2570        match event!(peers) {
2571            PeerAction::PeerRemoved(peer_id) => {
2572                assert_eq!(peer_id, peer);
2573            }
2574            _ => unreachable!(),
2575        }
2576        match event!(peers) {
2577            PeerAction::Disconnect { peer_id, .. } => {
2578                assert_eq!(peer_id, peer);
2579            }
2580            _ => unreachable!(),
2581        }
2582
2583        let p = peers.peers.get(&peer).unwrap();
2584        assert_eq!(p.state, PeerConnectionState::PendingOut);
2585
2586        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
2587        let p = peers.peers.get(&peer).unwrap();
2588        assert_eq!(p.state, PeerConnectionState::PendingOut);
2589
2590        peers.on_active_session_gracefully_closed(peer);
2591        assert!(!peers.peers.contains_key(&peer));
2592    }
2593
2594    #[tokio::test]
2595    async fn test_fatal_outgoing_connection_error_trusted() {
2596        let peer = PeerId::random();
2597        let config = PeersConfig::test()
2598            .with_trusted_nodes(vec![TrustedPeer {
2599                host: Host::Ipv4(Ipv4Addr::new(127, 0, 1, 2)),
2600                tcp_port: 8008,
2601                udp_port: 8008,
2602                id: peer,
2603            }])
2604            .with_trusted_nodes_only(true);
2605        let mut peers = PeersManager::new(config);
2606        let socket_addr = peers.peers.get(&peer).unwrap().addr.tcp();
2607
2608        match event!(peers) {
2609            PeerAction::Connect { peer_id, remote_addr } => {
2610                assert_eq!(peer_id, peer);
2611                assert_eq!(remote_addr, socket_addr);
2612            }
2613            _ => unreachable!(),
2614        }
2615
2616        let p = peers.peers.get(&peer).unwrap();
2617        assert_eq!(p.state, PeerConnectionState::PendingOut);
2618
2619        assert_eq!(peers.num_outbound_connections(), 0);
2620
2621        let err = PendingSessionHandshakeError::Eth(EthStreamError::EthHandshakeError(
2622            EthHandshakeError::NonStatusMessageInHandshake,
2623        ));
2624        assert!(err.is_fatal_protocol_error());
2625
2626        peers.on_outgoing_pending_session_dropped(&socket_addr, &peer, &err);
2627        assert_eq!(peers.num_outbound_connections(), 0);
2628
2629        // try tmp ban peer
2630        match event!(peers) {
2631            PeerAction::BanPeer { peer_id } => {
2632                assert_eq!(peer_id, peer);
2633            }
2634            err => unreachable!("{err:?}"),
2635        }
2636
2637        // ensure we still have trusted peer
2638        assert!(peers.peers.contains_key(&peer));
2639
2640        // await for the ban to expire
2641        tokio::time::sleep(peers.backoff_durations.medium).await;
2642
2643        match event!(peers) {
2644            PeerAction::Connect { peer_id, remote_addr } => {
2645                assert_eq!(peer_id, peer);
2646                assert_eq!(remote_addr, socket_addr);
2647            }
2648            err => unreachable!("{err:?}"),
2649        }
2650    }
2651
2652    #[tokio::test]
2653    async fn test_outgoing_connection_error() {
2654        let peer = PeerId::random();
2655        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2656        let mut peers = PeersManager::default();
2657        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
2658
2659        match event!(peers) {
2660            PeerAction::PeerAdded(peer_id) => {
2661                assert_eq!(peer_id, peer);
2662            }
2663            _ => unreachable!(),
2664        }
2665        match event!(peers) {
2666            PeerAction::Connect { peer_id, remote_addr } => {
2667                assert_eq!(peer_id, peer);
2668                assert_eq!(remote_addr, socket_addr);
2669            }
2670            _ => unreachable!(),
2671        }
2672
2673        let p = peers.peers.get(&peer).unwrap();
2674        assert_eq!(p.state, PeerConnectionState::PendingOut);
2675
2676        assert_eq!(peers.num_outbound_connections(), 0);
2677
2678        peers.on_outgoing_connection_failure(
2679            &socket_addr,
2680            &peer,
2681            &io::Error::new(io::ErrorKind::ConnectionRefused, ""),
2682        );
2683
2684        assert_eq!(peers.num_outbound_connections(), 0);
2685    }
2686
2687    #[tokio::test]
2688    async fn test_outgoing_connection_gracefully_closed() {
2689        let peer = PeerId::random();
2690        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2691        let mut peers = PeersManager::default();
2692        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
2693
2694        match event!(peers) {
2695            PeerAction::PeerAdded(peer_id) => {
2696                assert_eq!(peer_id, peer);
2697            }
2698            _ => unreachable!(),
2699        }
2700        match event!(peers) {
2701            PeerAction::Connect { peer_id, remote_addr } => {
2702                assert_eq!(peer_id, peer);
2703                assert_eq!(remote_addr, socket_addr);
2704            }
2705            _ => unreachable!(),
2706        }
2707
2708        let p = peers.peers.get(&peer).unwrap();
2709        assert_eq!(p.state, PeerConnectionState::PendingOut);
2710
2711        assert_eq!(peers.num_outbound_connections(), 0);
2712
2713        peers.on_outgoing_pending_session_gracefully_closed(&peer);
2714
2715        assert_eq!(peers.num_outbound_connections(), 0);
2716        assert_eq!(peers.connection_info.num_pending_out, 0);
2717    }
2718
2719    #[tokio::test]
2720    async fn test_discovery_ban_list() {
2721        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2));
2722        let socket_addr = SocketAddr::new(ip, 8008);
2723        let ban_list = BanList::new(vec![], vec![ip]);
2724        let config = PeersConfig::default().with_ban_list(ban_list);
2725        let mut peer_manager = PeersManager::new(config);
2726        peer_manager.add_peer(B512::default(), PeerAddr::from_tcp(socket_addr), None);
2727
2728        assert!(peer_manager.peers.is_empty());
2729    }
2730
2731    #[tokio::test]
2732    async fn test_on_pending_ban_list() {
2733        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2));
2734        let socket_addr = SocketAddr::new(ip, 8008);
2735        let ban_list = BanList::new(vec![], vec![ip]);
2736        let config = PeersConfig::test().with_ban_list(ban_list);
2737        let mut peer_manager = PeersManager::new(config);
2738        let a = peer_manager.on_incoming_pending_session(socket_addr.ip());
2739        // because we have no active peers this should be fine for testings
2740        match a {
2741            Ok(_) => panic!(),
2742            Err(err) => match err {
2743                InboundConnectionError::IpBanned => {
2744                    assert_eq!(peer_manager.connection_info.num_pending_in, 0)
2745                }
2746                _ => unreachable!(),
2747            },
2748        }
2749    }
2750
2751    #[tokio::test]
2752    async fn test_on_active_inbound_ban_list() {
2753        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2));
2754        let socket_addr = SocketAddr::new(ip, 8008);
2755        let given_peer_id = PeerId::random();
2756        let ban_list = BanList::new(vec![given_peer_id], vec![]);
2757        let config = PeersConfig::test().with_ban_list(ban_list);
2758        let mut peer_manager = PeersManager::new(config);
2759        assert!(peer_manager.on_incoming_pending_session(socket_addr.ip()).is_ok());
2760        // non-trusted nodes should also increase pending_in
2761        assert_eq!(peer_manager.connection_info.num_pending_in, 1);
2762        peer_manager.on_incoming_session_established(given_peer_id, socket_addr);
2763        // after the connection is established, the peer should be removed, the num_pending_in
2764        // should be decreased, and the num_inbound should not be increased
2765        assert_eq!(peer_manager.connection_info.num_pending_in, 0);
2766        assert_eq!(peer_manager.connection_info.num_inbound, 0);
2767
2768        let Some(PeerAction::DisconnectBannedIncoming { peer_id }) =
2769            peer_manager.queued_actions.pop_front()
2770        else {
2771            panic!()
2772        };
2773
2774        assert_eq!(peer_id, given_peer_id)
2775    }
2776
2777    #[test]
2778    fn test_connection_limits() {
2779        let mut info = ConnectionInfo::default();
2780        info.inc_in();
2781        assert_eq!(info.num_inbound, 1);
2782        assert_eq!(info.num_outbound, 0);
2783        assert!(info.has_in_capacity());
2784
2785        info.decr_in();
2786        assert_eq!(info.num_inbound, 0);
2787        assert_eq!(info.num_outbound, 0);
2788
2789        info.inc_out();
2790        assert_eq!(info.num_inbound, 0);
2791        assert_eq!(info.num_outbound, 1);
2792        assert!(info.has_out_capacity());
2793
2794        info.decr_out();
2795        assert_eq!(info.num_inbound, 0);
2796        assert_eq!(info.num_outbound, 0);
2797    }
2798
2799    #[test]
2800    fn test_connection_peer_state() {
2801        let mut info = ConnectionInfo::default();
2802        info.inc_in();
2803
2804        info.decr_state(PeerConnectionState::In);
2805        assert_eq!(info.num_inbound, 0);
2806        assert_eq!(info.num_outbound, 0);
2807
2808        info.inc_out();
2809
2810        info.decr_state(PeerConnectionState::Out);
2811        assert_eq!(info.num_inbound, 0);
2812        assert_eq!(info.num_outbound, 0);
2813    }
2814
2815    #[tokio::test]
2816    async fn test_trusted_peers_are_prioritized() {
2817        let trusted_peer = PeerId::random();
2818        let trusted_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2819        let config = PeersConfig::test().with_trusted_nodes(vec![TrustedPeer {
2820            host: Host::Ipv4(Ipv4Addr::new(127, 0, 1, 2)),
2821            tcp_port: 8008,
2822            udp_port: 8008,
2823            id: trusted_peer,
2824        }]);
2825        let mut peers = PeersManager::new(config);
2826
2827        let basic_peer = PeerId::random();
2828        let basic_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
2829        peers.add_peer(basic_peer, PeerAddr::from_tcp(basic_sock), None);
2830
2831        match event!(peers) {
2832            PeerAction::PeerAdded(peer_id) => {
2833                assert_eq!(peer_id, basic_peer);
2834            }
2835            _ => unreachable!(),
2836        }
2837        match event!(peers) {
2838            PeerAction::Connect { peer_id, remote_addr } => {
2839                assert_eq!(peer_id, trusted_peer);
2840                assert_eq!(remote_addr, trusted_sock);
2841            }
2842            _ => unreachable!(),
2843        }
2844        match event!(peers) {
2845            PeerAction::Connect { peer_id, remote_addr } => {
2846                assert_eq!(peer_id, basic_peer);
2847                assert_eq!(remote_addr, basic_sock);
2848            }
2849            _ => unreachable!(),
2850        }
2851    }
2852
2853    #[tokio::test]
2854    async fn test_trusted_peers_bypass_outbound_capacity() {
2855        let mut peers = PeersManager::new(PeersConfig::test().with_max_outbound(1));
2856
2857        // an untrusted peer takes the only outbound slot
2858        let basic_peer = PeerId::random();
2859        let basic_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2860        peers.add_peer(basic_peer, PeerAddr::from_tcp(basic_sock), None);
2861        peers.fill_outbound_slots();
2862        peers.on_active_outgoing_established(basic_peer);
2863        assert!(!peers.connection_info.has_out_capacity());
2864        peers.queued_actions.clear();
2865
2866        let static_peer = PeerId::random();
2867        peers.add_peer_kind(
2868            static_peer,
2869            Some(PeerKind::Static),
2870            PeerAddr::from_tcp(basic_sock),
2871            None,
2872        );
2873        assert!(peers.best_unconnected(true).is_none());
2874        peers.queued_actions.clear();
2875
2876        // a trusted peer is dialed regardless
2877        let trusted_peer = PeerId::random();
2878        let trusted_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 3)), 8008);
2879        peers.add_trusted_peer(trusted_peer, PeerAddr::from_tcp(trusted_sock));
2880        peers.queued_actions.clear();
2881        peers.fill_outbound_slots();
2882        match peers.queued_actions.pop_front() {
2883            Some(PeerAction::Connect { peer_id, remote_addr }) => {
2884                assert_eq!(peer_id, trusted_peer);
2885                assert_eq!(remote_addr, trusted_sock);
2886            }
2887            action => panic!("expected the trusted peer to be dialed, got {action:?}"),
2888        }
2889        assert!(peers.queued_actions.is_empty());
2890        peers.on_active_outgoing_established(trusted_peer);
2891
2892        // untrusted peers still wait for a free slot
2893        let other_peer = PeerId::random();
2894        let other_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 4)), 8008);
2895        peers.add_peer(other_peer, PeerAddr::from_tcp(other_sock), None);
2896        peers.queued_actions.clear();
2897        peers.fill_outbound_slots();
2898        assert!(peers.queued_actions.is_empty());
2899        assert!(peers.peers[&other_peer].state.is_idle());
2900    }
2901
2902    #[tokio::test]
2903    async fn test_connect_trusted_nodes_only() {
2904        let trusted_peer = PeerId::random();
2905        let trusted_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
2906        let config = PeersConfig::test()
2907            .with_trusted_nodes(vec![TrustedPeer {
2908                host: Host::Ipv4(Ipv4Addr::new(127, 0, 1, 2)),
2909                tcp_port: 8008,
2910                udp_port: 8008,
2911                id: trusted_peer,
2912            }])
2913            .with_trusted_nodes_only(true);
2914        let mut peers = PeersManager::new(config);
2915
2916        let basic_peer = PeerId::random();
2917        let basic_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
2918        peers.add_peer(basic_peer, PeerAddr::from_tcp(basic_sock), None);
2919
2920        match event!(peers) {
2921            PeerAction::PeerAdded(peer_id) => {
2922                assert_eq!(peer_id, basic_peer);
2923            }
2924            _ => unreachable!(),
2925        }
2926        match event!(peers) {
2927            PeerAction::Connect { peer_id, remote_addr } => {
2928                assert_eq!(peer_id, trusted_peer);
2929                assert_eq!(remote_addr, trusted_sock);
2930            }
2931            _ => unreachable!(),
2932        }
2933        poll_fn(|cx| {
2934            assert!(peers.poll(cx).is_pending());
2935            Poll::Ready(())
2936        })
2937        .await;
2938    }
2939
2940    #[tokio::test]
2941    async fn test_incoming_with_trusted_nodes_only() {
2942        let trusted_peer = PeerId::random();
2943        let config = PeersConfig::test()
2944            .with_trusted_nodes(vec![TrustedPeer {
2945                host: Host::Ipv4(Ipv4Addr::new(127, 0, 1, 2)),
2946                tcp_port: 8008,
2947                udp_port: 8008,
2948                id: trusted_peer,
2949            }])
2950            .with_trusted_nodes_only(true);
2951        let mut peers = PeersManager::new(config);
2952
2953        let basic_peer = PeerId::random();
2954        let basic_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
2955        assert!(peers.on_incoming_pending_session(basic_sock.ip()).is_ok());
2956        // non-trusted nodes should also increase pending_in
2957        assert_eq!(peers.connection_info.num_pending_in, 1);
2958        peers.on_incoming_session_established(basic_peer, basic_sock);
2959        // after the connection is established, the peer should be removed, the num_pending_in
2960        // should be decreased, and the num_inbound mut not be increased
2961        assert_eq!(peers.connection_info.num_pending_in, 0);
2962        assert_eq!(peers.connection_info.num_inbound, 0);
2963
2964        let Some(PeerAction::DisconnectUntrustedIncoming { peer_id }) =
2965            peers.queued_actions.pop_front()
2966        else {
2967            panic!()
2968        };
2969        assert_eq!(basic_peer, peer_id);
2970        assert!(!peers.peers.contains_key(&basic_peer));
2971    }
2972
2973    #[tokio::test]
2974    async fn test_incoming_without_trusted_nodes_only() {
2975        let trusted_peer = PeerId::random();
2976        let config = PeersConfig::test()
2977            .with_trusted_nodes(vec![TrustedPeer {
2978                host: Host::Ipv4(Ipv4Addr::new(127, 0, 1, 2)),
2979                tcp_port: 8008,
2980                udp_port: 8008,
2981                id: trusted_peer,
2982            }])
2983            .with_trusted_nodes_only(false);
2984        let mut peers = PeersManager::new(config);
2985
2986        let basic_peer = PeerId::random();
2987        let basic_sock = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
2988        assert!(peers.on_incoming_pending_session(basic_sock.ip()).is_ok());
2989
2990        // non-trusted nodes should also increase pending_in
2991        assert_eq!(peers.connection_info.num_pending_in, 1);
2992        peers.on_incoming_session_established(basic_peer, basic_sock);
2993        // after the connection is established, the peer should be removed, the num_pending_in
2994        // should be decreased, and the num_inbound must be increased
2995        assert_eq!(peers.connection_info.num_pending_in, 0);
2996        assert_eq!(peers.connection_info.num_inbound, 1);
2997        assert!(peers.peers.contains_key(&basic_peer));
2998    }
2999
3000    #[tokio::test]
3001    async fn test_incoming_at_capacity() {
3002        let mut config = PeersConfig::test();
3003        config.connection_info.max_inbound = 1;
3004        let mut peers = PeersManager::new(config);
3005
3006        let peer = PeerId::random();
3007        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
3008        assert!(peers.on_incoming_pending_session(addr.ip()).is_ok());
3009
3010        peers.on_incoming_session_established(peer, addr);
3011
3012        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
3013        assert_eq!(
3014            peers.on_incoming_pending_session(addr.ip()).unwrap_err(),
3015            InboundConnectionError::ExceedsCapacity
3016        );
3017    }
3018
3019    #[tokio::test]
3020    async fn test_incoming_rate_limit() {
3021        for max_inbound in [0, 1] {
3022            let config = PeersConfig {
3023                incoming_ip_throttle_duration: Duration::from_millis(100),
3024                ..PeersConfig::test().with_max_inbound(max_inbound)
3025            };
3026            let mut peers = PeersManager::new(config);
3027            peers.add_trusted_peer_id(PeerId::random());
3028
3029            let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(168, 0, 1, 2)), 8009);
3030            peers.on_incoming_pending_session(addr.ip()).unwrap();
3031            peers.on_incoming_pending_session_gracefully_closed();
3032            assert_eq!(
3033                peers.on_incoming_pending_session(addr.ip()).unwrap_err(),
3034                InboundConnectionError::IpBanned
3035            );
3036
3037            peers.release_interval.reset_immediately();
3038            tokio::time::sleep(peers.incoming_ip_throttle_duration).await;
3039
3040            // await unban
3041            poll_fn(|cx| loop {
3042                if peers.poll(cx).is_pending() {
3043                    return Poll::Ready(());
3044                }
3045            })
3046            .await;
3047
3048            peers.on_incoming_pending_session(addr.ip()).unwrap();
3049            peers.on_incoming_pending_session_gracefully_closed();
3050            assert_eq!(
3051                peers.on_incoming_pending_session(addr.ip()).unwrap_err(),
3052                InboundConnectionError::IpBanned
3053            );
3054        }
3055    }
3056
3057    #[tokio::test]
3058    async fn test_incoming_throttle_capacity() {
3059        for max_inbound in [0, 1] {
3060            let mut peers = PeersManager::new(PeersConfig::test().with_max_inbound(max_inbound));
3061            peers.add_trusted_peer_id(PeerId::random());
3062            peers.ban_list = BanList::new(
3063                [],
3064                (0..MAX_INCOMING_IP_THROTTLE_ENTRIES)
3065                    .map(|i| IpAddr::V4(Ipv4Addr::from(0x0800_0000 + i))),
3066            );
3067            for i in 0..MAX_INCOMING_IP_THROTTLE_ENTRIES {
3068                peers.throttle_incoming_ip(IpAddr::V4(Ipv4Addr::from(0x0900_0000 + i)));
3069            }
3070            let ip = IpAddr::from([1, 1, 1, 1]);
3071            assert!(peers.on_incoming_pending_session(ip).is_ok());
3072            assert_eq!(peers.connection_info.num_pending_in, 1);
3073            assert_eq!(
3074                peers.incoming_ip_throttle.len(),
3075                usize::try_from(MAX_INCOMING_IP_THROTTLE_ENTRIES).unwrap()
3076            );
3077            peers.on_incoming_pending_session_gracefully_closed();
3078            assert_eq!(peers.connection_info.num_pending_in, 0);
3079            assert_eq!(
3080                peers.on_incoming_pending_session(ip),
3081                Err(InboundConnectionError::IpBanned)
3082            );
3083
3084            // A saturated throttle table must not weaken existing security bans.
3085            assert_eq!(
3086                peers.on_incoming_pending_session(IpAddr::from([8, 0, 0, 1])),
3087                Err(InboundConnectionError::IpBanned)
3088            );
3089        }
3090    }
3091
3092    #[tokio::test]
3093    async fn test_tick() {
3094        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2));
3095        let socket_addr = SocketAddr::new(ip, 8008);
3096        let config = PeersConfig::test();
3097        let mut peer_manager = PeersManager::new(config);
3098        let peer_id = PeerId::random();
3099        peer_manager.add_peer(peer_id, PeerAddr::from_tcp(socket_addr), None);
3100
3101        // Age the last tick directly so each reputation update sees a full elapsed second.
3102        peer_manager.last_tick -= Duration::from_secs(1);
3103        peer_manager.tick();
3104
3105        // still unconnected
3106        assert_eq!(peer_manager.peers.get_mut(&peer_id).unwrap().reputation, DEFAULT_REPUTATION);
3107
3108        // mark as connected
3109        peer_manager.peers.get_mut(&peer_id).unwrap().state = PeerConnectionState::Out;
3110
3111        peer_manager.last_tick -= Duration::from_secs(1);
3112        peer_manager.tick();
3113
3114        // still at default reputation
3115        assert_eq!(peer_manager.peers.get_mut(&peer_id).unwrap().reputation, DEFAULT_REPUTATION);
3116
3117        peer_manager.peers.get_mut(&peer_id).unwrap().reputation -= 1;
3118
3119        peer_manager.last_tick -= Duration::from_secs(1);
3120        peer_manager.tick();
3121
3122        // tick applied
3123        assert!(peer_manager.peers.get_mut(&peer_id).unwrap().reputation >= DEFAULT_REPUTATION);
3124    }
3125
3126    #[tokio::test]
3127    async fn test_remove_incoming_after_disconnect() {
3128        let peer_id = PeerId::random();
3129        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
3130        let mut peers = PeersManager::default();
3131
3132        peers.on_incoming_pending_session(addr.ip()).unwrap();
3133        peers.on_incoming_session_established(peer_id, addr);
3134        let peer = peers.peers.get(&peer_id).unwrap();
3135        assert_eq!(peer.state, PeerConnectionState::In);
3136        assert!(peer.remove_after_disconnect);
3137
3138        peers.on_active_session_gracefully_closed(peer_id);
3139        assert!(!peers.peers.contains_key(&peer_id))
3140    }
3141
3142    #[tokio::test]
3143    async fn test_keep_incoming_after_disconnect_if_discovered() {
3144        let peer_id = PeerId::random();
3145        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
3146        let mut peers = PeersManager::default();
3147
3148        peers.on_incoming_pending_session(addr.ip()).unwrap();
3149        peers.on_incoming_session_established(peer_id, addr);
3150        let peer = peers.peers.get(&peer_id).unwrap();
3151        assert_eq!(peer.state, PeerConnectionState::In);
3152        assert!(peer.remove_after_disconnect);
3153
3154        // trigger discovery manually while the peer is still connected
3155        peers.add_peer(peer_id, PeerAddr::from_tcp(addr), None);
3156
3157        peers.on_active_session_gracefully_closed(peer_id);
3158
3159        let peer = peers.peers.get(&peer_id).unwrap();
3160        assert_eq!(peer.state, PeerConnectionState::Idle);
3161        assert!(!peer.remove_after_disconnect);
3162    }
3163
3164    #[tokio::test]
3165    async fn test_peer_reconnect_after_graceful_close_respects_throttle() {
3166        let throttle_duration = Duration::from_millis(100);
3167        let config =
3168            PeersConfig { incoming_ip_throttle_duration: throttle_duration, ..PeersConfig::test() };
3169        let mut peers = PeersManager::new(config);
3170
3171        let peer_id = PeerId::random();
3172        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
3173
3174        // Add as regular peer
3175        peers.add_peer(peer_id, PeerAddr::from_tcp(addr), None);
3176
3177        match event!(peers) {
3178            PeerAction::PeerAdded(id) => assert_eq!(id, peer_id),
3179            _ => unreachable!(),
3180        }
3181
3182        match event!(peers) {
3183            PeerAction::Connect { .. } => {}
3184            _ => unreachable!(),
3185        }
3186
3187        // Simulate outbound connection established
3188        peers.on_active_outgoing_established(peer_id);
3189        assert_eq!(peers.peers.get(&peer_id).unwrap().state, PeerConnectionState::Out);
3190
3191        // Gracefully close the session
3192        peers.on_active_session_gracefully_closed(peer_id);
3193
3194        let peer = peers.peers.get(&peer_id).unwrap();
3195        assert_eq!(peer.state, PeerConnectionState::Idle);
3196        assert!(peer.backed_off);
3197
3198        // Verify the peer is in the backed_off_peers set
3199        assert!(peers.backed_off_peers.contains_key(&peer_id));
3200
3201        // Immediately try to poll - should not trigger any actions yet
3202        poll_fn(|cx| {
3203            assert!(peers.poll(cx).is_pending());
3204            Poll::Ready(())
3205        })
3206        .await;
3207
3208        // Peer should still be backed off
3209        assert!(peers.backed_off_peers.contains_key(&peer_id));
3210        assert!(peers.peers.get(&peer_id).unwrap().backed_off);
3211
3212        // Sleep for the throttle duration
3213        tokio::time::sleep(throttle_duration).await;
3214
3215        // After throttle duration, event! will poll until we get a Connect action
3216        match event!(peers) {
3217            PeerAction::Connect { peer_id: id, .. } => assert_eq!(id, peer_id),
3218            _ => unreachable!(),
3219        }
3220
3221        // After connection is initiated, peer should no longer be backed off
3222        assert!(!peers.backed_off_peers.contains_key(&peer_id));
3223        assert!(!peers.peers.get(&peer_id).unwrap().backed_off);
3224    }
3225
3226    #[tokio::test]
3227    async fn test_backed_off_peer_can_accept_incoming_connection() {
3228        let throttle_duration = Duration::from_millis(100);
3229        let config =
3230            PeersConfig { incoming_ip_throttle_duration: throttle_duration, ..PeersConfig::test() };
3231        let mut peers = PeersManager::new(config);
3232
3233        let peer_id = PeerId::random();
3234        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
3235
3236        // Add as regular peer
3237        peers.add_peer(peer_id, PeerAddr::from_tcp(addr), None);
3238
3239        match event!(peers) {
3240            PeerAction::PeerAdded(id) => assert_eq!(id, peer_id),
3241            _ => unreachable!(),
3242        }
3243
3244        match event!(peers) {
3245            PeerAction::Connect { .. } => {}
3246            _ => unreachable!(),
3247        }
3248
3249        // Simulate outbound connection established
3250        peers.on_active_outgoing_established(peer_id);
3251        assert_eq!(peers.peers.get(&peer_id).unwrap().state, PeerConnectionState::Out);
3252
3253        // Gracefully close the session - this will back off the peer
3254        peers.on_active_session_gracefully_closed(peer_id);
3255
3256        let peer = peers.peers.get(&peer_id).unwrap();
3257        assert_eq!(peer.state, PeerConnectionState::Idle);
3258        assert!(peer.backed_off);
3259        assert!(peers.backed_off_peers.contains_key(&peer_id));
3260
3261        // Now simulate an incoming connection from the backed-off peer
3262        // First, handle the incoming pending session
3263        assert!(peers.on_incoming_pending_session(addr.ip()).is_ok());
3264        assert_eq!(peers.connection_info.num_pending_in, 1);
3265
3266        // Establish the incoming session
3267        peers.on_incoming_session_established(peer_id, addr);
3268
3269        // Peer should have been added to incoming connections
3270        assert_eq!(peers.peers.get(&peer_id).unwrap().state, PeerConnectionState::In);
3271        assert_eq!(peers.connection_info.num_inbound, 1);
3272
3273        // Peer should still be backed off for outbound connections
3274        assert!(peers.backed_off_peers.contains_key(&peer_id));
3275        assert!(peers.peers.get(&peer_id).unwrap().backed_off);
3276
3277        // Verify we don't try to reconnect outbound while peer is backed off
3278        poll_fn(|cx| {
3279            assert!(peers.poll(cx).is_pending());
3280            Poll::Ready(())
3281        })
3282        .await;
3283
3284        // No outbound connection should be attempted while backed off
3285        assert_eq!(peers.peers.get(&peer_id).unwrap().state, PeerConnectionState::In);
3286
3287        // After throttle duration, the backoff should be cleared
3288        tokio::time::sleep(throttle_duration).await;
3289
3290        poll_fn(|cx| {
3291            let _ = peers.poll(cx);
3292            Poll::Ready(())
3293        })
3294        .await;
3295
3296        // Backoff should be cleared now
3297        assert!(!peers.backed_off_peers.contains_key(&peer_id));
3298        assert!(!peers.peers.get(&peer_id).unwrap().backed_off);
3299
3300        // Peer should still be in incoming state
3301        assert_eq!(peers.peers.get(&peer_id).unwrap().state, PeerConnectionState::In);
3302    }
3303
3304    #[tokio::test]
3305    async fn test_incoming_outgoing_already_connected() {
3306        let peer_id = PeerId::random();
3307        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
3308        let mut peers = PeersManager::default();
3309
3310        peers.on_incoming_pending_session(addr.ip()).unwrap();
3311        peers.add_peer(peer_id, PeerAddr::from_tcp(addr), None);
3312
3313        match event!(peers) {
3314            PeerAction::PeerAdded(_) => {}
3315            _ => unreachable!(),
3316        }
3317
3318        match event!(peers) {
3319            PeerAction::Connect { .. } => {}
3320            _ => unreachable!(),
3321        }
3322
3323        peers.on_incoming_session_established(peer_id, addr);
3324        peers.on_already_connected(Direction::Outgoing(peer_id));
3325        assert_eq!(peers.peers.get(&peer_id).unwrap().state, PeerConnectionState::In);
3326        assert_eq!(peers.connection_info.num_inbound, 1);
3327        assert_eq!(peers.connection_info.num_pending_out, 0);
3328        assert_eq!(peers.connection_info.num_pending_in, 0);
3329        assert_eq!(peers.connection_info.num_outbound, 0);
3330    }
3331
3332    #[tokio::test]
3333    async fn test_already_connected_incoming_outgoing_connection_error() {
3334        let peer_id = PeerId::random();
3335        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8009);
3336        let mut peers = PeersManager::default();
3337
3338        peers.on_incoming_pending_session(addr.ip()).unwrap();
3339        peers.add_peer(peer_id, PeerAddr::from_tcp(addr), None);
3340
3341        match event!(peers) {
3342            PeerAction::PeerAdded(_) => {}
3343            _ => unreachable!(),
3344        }
3345
3346        match event!(peers) {
3347            PeerAction::Connect { .. } => {}
3348            _ => unreachable!(),
3349        }
3350
3351        peers.on_incoming_session_established(peer_id, addr);
3352
3353        peers.on_outgoing_connection_failure(
3354            &addr,
3355            &peer_id,
3356            &io::Error::new(io::ErrorKind::ConnectionRefused, ""),
3357        );
3358        assert_eq!(peers.peers.get(&peer_id).unwrap().state, PeerConnectionState::In);
3359        assert_eq!(peers.connection_info.num_inbound, 1);
3360        assert_eq!(peers.connection_info.num_pending_out, 0);
3361        assert_eq!(peers.connection_info.num_pending_in, 0);
3362        assert_eq!(peers.connection_info.num_outbound, 0);
3363    }
3364
3365    #[tokio::test]
3366    async fn test_max_concurrent_dials() {
3367        let config = PeersConfig::default();
3368        let mut peer_manager = PeersManager::new(config);
3369        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2));
3370        let peer_addr = PeerAddr::from_tcp(SocketAddr::new(ip, 8008));
3371        for _ in 0..peer_manager.connection_info.config.max_concurrent_outbound_dials * 2 {
3372            peer_manager.add_peer(PeerId::random(), peer_addr, None);
3373        }
3374
3375        peer_manager.fill_outbound_slots();
3376        let dials = peer_manager
3377            .queued_actions
3378            .iter()
3379            .filter(|ev| matches!(ev, PeerAction::Connect { .. }))
3380            .count();
3381        assert_eq!(dials, peer_manager.connection_info.config.max_concurrent_outbound_dials);
3382    }
3383
3384    #[tokio::test]
3385    async fn test_max_num_of_pending_dials() {
3386        let config = PeersConfig::default();
3387        let mut peer_manager = PeersManager::new(config);
3388        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2));
3389        let peer_addr = PeerAddr::from_tcp(SocketAddr::new(ip, 8008));
3390
3391        // add more peers than allowed
3392        for _ in 0..peer_manager.connection_info.config.max_concurrent_outbound_dials * 2 {
3393            peer_manager.add_peer(PeerId::random(), peer_addr, None);
3394        }
3395
3396        for _ in 0..peer_manager.connection_info.config.max_concurrent_outbound_dials * 2 {
3397            match event!(peer_manager) {
3398                PeerAction::PeerAdded(_) => {}
3399                _ => unreachable!(),
3400            }
3401        }
3402
3403        for _ in 0..peer_manager.connection_info.config.max_concurrent_outbound_dials {
3404            match event!(peer_manager) {
3405                PeerAction::Connect { .. } => {}
3406                _ => unreachable!(),
3407            }
3408        }
3409
3410        // generate 'Connect' actions
3411        peer_manager.fill_outbound_slots();
3412
3413        // all dialed connections should be in 'PendingOut' state
3414        let dials = peer_manager.connection_info.num_pending_out;
3415        assert_eq!(dials, peer_manager.connection_info.config.max_concurrent_outbound_dials);
3416
3417        let num_pendingout_states = peer_manager
3418            .peers
3419            .iter()
3420            .filter(|(_, peer)| peer.state.is_pending_out())
3421            .map(|(peer_id, _)| *peer_id)
3422            .collect::<Vec<PeerId>>();
3423        assert_eq!(
3424            num_pendingout_states.len(),
3425            peer_manager.connection_info.config.max_concurrent_outbound_dials
3426        );
3427
3428        // establish dialed connections
3429        for peer_id in &num_pendingout_states {
3430            peer_manager.on_active_outgoing_established(*peer_id);
3431        }
3432
3433        // all dialed connections should now be in 'Out' state
3434        for peer_id in &num_pendingout_states {
3435            assert_eq!(peer_manager.peers.get(peer_id).unwrap().state, PeerConnectionState::Out);
3436        }
3437
3438        // no more pending outbound connections
3439        assert_eq!(peer_manager.connection_info.num_pending_out, 0);
3440    }
3441
3442    #[tokio::test]
3443    async fn test_connect() {
3444        let peer = PeerId::random();
3445        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
3446        let mut peers = PeersManager::default();
3447        peers.add_and_connect(peer, PeerAddr::from_tcp(socket_addr), None);
3448        assert_eq!(peers.peers.get(&peer).unwrap().state, PeerConnectionState::PendingOut);
3449
3450        match event!(peers) {
3451            PeerAction::Connect { peer_id, remote_addr } => {
3452                assert_eq!(peer_id, peer);
3453                assert_eq!(remote_addr, socket_addr);
3454            }
3455            _ => unreachable!(),
3456        }
3457
3458        let (record, _) = peers.peer_by_id(peer).unwrap();
3459        assert_eq!(record.tcp_addr(), socket_addr);
3460        assert_eq!(record.udp_addr(), socket_addr);
3461
3462        // connect again
3463        peers.add_and_connect(peer, PeerAddr::from_tcp(socket_addr), None);
3464
3465        let (record, _) = peers.peer_by_id(peer).unwrap();
3466        assert_eq!(record.tcp_addr(), socket_addr);
3467        assert_eq!(record.udp_addr(), socket_addr);
3468    }
3469
3470    #[tokio::test]
3471    async fn test_incoming_connection_from_banned() {
3472        let peer = PeerId::random();
3473        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
3474        let config = PeersConfig::test().with_max_inbound(3);
3475        let mut peers = PeersManager::new(config);
3476        peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None);
3477
3478        match event!(peers) {
3479            PeerAction::PeerAdded(peer_id) => {
3480                assert_eq!(peer_id, peer);
3481            }
3482            _ => unreachable!(),
3483        }
3484        match event!(peers) {
3485            PeerAction::Connect { peer_id, .. } => {
3486                assert_eq!(peer_id, peer);
3487            }
3488            _ => unreachable!(),
3489        }
3490
3491        poll_fn(|cx| {
3492            assert!(peers.poll(cx).is_pending());
3493            Poll::Ready(())
3494        })
3495        .await;
3496
3497        // simulate new connection drops with error
3498        loop {
3499            peers.on_active_session_dropped(
3500                &socket_addr,
3501                &peer,
3502                &EthStreamError::InvalidMessage(reth_eth_wire::message::MessageError::Invalid(
3503                    reth_eth_wire::EthVersion::Eth68,
3504                    reth_eth_wire::EthMessageID::Status,
3505                )),
3506            );
3507
3508            if peers.peers.get(&peer).unwrap().is_banned() {
3509                break;
3510            }
3511
3512            assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
3513            peers.on_incoming_session_established(peer, socket_addr);
3514
3515            match event!(peers) {
3516                PeerAction::Connect { peer_id, .. } => {
3517                    assert_eq!(peer_id, peer);
3518                }
3519                _ => unreachable!(),
3520            }
3521        }
3522
3523        assert!(peers.peers.get(&peer).unwrap().is_banned());
3524
3525        // fill all incoming slots
3526        for _ in 0..peers.connection_info.config.max_inbound {
3527            assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok());
3528            peers.on_incoming_session_established(peer, socket_addr);
3529
3530            match event!(peers) {
3531                PeerAction::DisconnectBannedIncoming { peer_id } => {
3532                    assert_eq!(peer_id, peer);
3533                }
3534                _ => unreachable!(),
3535            }
3536        }
3537
3538        poll_fn(|cx| {
3539            assert!(peers.poll(cx).is_pending());
3540            Poll::Ready(())
3541        })
3542        .await;
3543
3544        assert_eq!(peers.connection_info.num_inbound, 0);
3545
3546        let new_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 3)), 8008);
3547
3548        // Assert we can still accept new connections
3549        assert!(peers.on_incoming_pending_session(new_addr.ip()).is_ok());
3550        assert_eq!(peers.connection_info.num_pending_in, 1);
3551
3552        // the triggered DisconnectBannedIncoming will result in dropped connections, assert that
3553        // connection info is updated via the peer's state which would be a noop here since the
3554        // banned peer's state is idle
3555        peers.on_active_session_gracefully_closed(peer);
3556        assert_eq!(peers.connection_info.num_inbound, 0);
3557    }
3558
3559    #[tokio::test]
3560    async fn test_add_pending_connect() {
3561        let peer = PeerId::random();
3562        let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
3563        let mut peers = PeersManager::default();
3564        peers.add_and_connect(peer, PeerAddr::from_tcp(socket_addr), None);
3565        assert_eq!(peers.peers.get(&peer).unwrap().state, PeerConnectionState::PendingOut);
3566        assert_eq!(peers.connection_info.num_pending_out, 1);
3567    }
3568
3569    #[tokio::test]
3570    async fn test_dns_updates_peer_address() {
3571        let peer_id = PeerId::random();
3572        let initial_socket = SocketAddr::new("1.1.1.1".parse::<IpAddr>().unwrap(), 8008);
3573        let updated_ip = "2.2.2.2".parse::<IpAddr>().unwrap();
3574
3575        let trusted = TrustedPeer {
3576            host: url::Host::Ipv4("2.2.2.2".parse().unwrap()),
3577            tcp_port: 8008,
3578            udp_port: 8008,
3579            id: peer_id,
3580        };
3581
3582        let config = PeersConfig::test().with_trusted_nodes(vec![trusted.clone()]);
3583        let mut manager = PeersManager::new(config);
3584        manager
3585            .trusted_peers_resolver
3586            .set_interval(tokio::time::interval(Duration::from_millis(1)));
3587
3588        manager.peers.insert(
3589            peer_id,
3590            Peer::trusted(PeerAddr::new_with_ports(initial_socket.ip(), 8008, Some(8008))),
3591        );
3592
3593        for _ in 0..100 {
3594            let _ = event!(manager);
3595            if manager.peers.get(&peer_id).unwrap().addr.tcp().ip() == updated_ip {
3596                break;
3597            }
3598            tokio::time::sleep(Duration::from_millis(10)).await;
3599        }
3600
3601        let updated_peer = manager.peers.get(&peer_id).unwrap();
3602        assert_eq!(updated_peer.addr.tcp().ip(), updated_ip);
3603    }
3604
3605    #[tokio::test]
3606    async fn test_ip_filter_blocks_inbound_connection() {
3607        use reth_net_banlist::IpFilter;
3608        use std::net::IpAddr;
3609
3610        // Create a filter that only allows 192.168.0.0/16
3611        let ip_filter = IpFilter::from_cidr_string("192.168.0.0/16").unwrap();
3612        let config = PeersConfig::test().with_ip_filter(ip_filter);
3613        let mut peers = PeersManager::new(config);
3614
3615        // Try to connect from an allowed IP
3616        let allowed_ip: IpAddr = "192.168.1.100".parse().unwrap();
3617        assert!(peers.on_incoming_pending_session(allowed_ip).is_ok());
3618
3619        // Try to connect from a disallowed IP
3620        let disallowed_ip: IpAddr = "10.0.0.1".parse().unwrap();
3621        assert!(peers.on_incoming_pending_session(disallowed_ip).is_err());
3622    }
3623
3624    #[tokio::test]
3625    async fn test_ip_filter_blocks_outbound_connection() {
3626        use reth_net_banlist::IpFilter;
3627        use std::net::SocketAddr;
3628
3629        // Create a filter that only allows 192.168.0.0/16
3630        let ip_filter = IpFilter::from_cidr_string("192.168.0.0/16").unwrap();
3631        let config = PeersConfig::test().with_ip_filter(ip_filter);
3632        let mut peers = PeersManager::new(config);
3633
3634        let peer_id = PeerId::repeat_byte(1);
3635
3636        // Try to add a peer with an allowed IP
3637        let allowed_addr: SocketAddr = "192.168.1.100:30303".parse().unwrap();
3638        peers.add_peer(peer_id, PeerAddr::from_tcp(allowed_addr), None);
3639        assert!(peers.peers.contains_key(&peer_id));
3640
3641        // Try to add a peer with a disallowed IP
3642        let peer_id2 = PeerId::repeat_byte(2);
3643        let disallowed_addr: SocketAddr = "10.0.0.1:30303".parse().unwrap();
3644        peers.add_peer(peer_id2, PeerAddr::from_tcp(disallowed_addr), None);
3645        assert!(!peers.peers.contains_key(&peer_id2));
3646    }
3647
3648    #[tokio::test]
3649    async fn test_ip_filter_ipv6() {
3650        use reth_net_banlist::IpFilter;
3651        use std::net::IpAddr;
3652
3653        // Create a filter that only allows IPv6 range 2001:db8::/32
3654        let ip_filter = IpFilter::from_cidr_string("2001:db8::/32").unwrap();
3655        let config = PeersConfig::test().with_ip_filter(ip_filter);
3656        let mut peers = PeersManager::new(config);
3657
3658        // Try to connect from an allowed IPv6 address
3659        let allowed_ip: IpAddr = "2001:db8::1".parse().unwrap();
3660        assert!(peers.on_incoming_pending_session(allowed_ip).is_ok());
3661
3662        // Try to connect from a disallowed IPv6 address
3663        let disallowed_ip: IpAddr = "2001:db9::1".parse().unwrap();
3664        assert!(peers.on_incoming_pending_session(disallowed_ip).is_err());
3665    }
3666
3667    #[tokio::test]
3668    async fn test_ip_filter_multiple_ranges() {
3669        use reth_net_banlist::IpFilter;
3670        use std::net::IpAddr;
3671
3672        // Create a filter that allows multiple ranges
3673        let ip_filter = IpFilter::from_cidr_string("192.168.0.0/16,10.0.0.0/8").unwrap();
3674        let config = PeersConfig::test().with_ip_filter(ip_filter);
3675        let mut peers = PeersManager::new(config);
3676
3677        // Try IPs from both allowed ranges
3678        let ip1: IpAddr = "192.168.1.1".parse().unwrap();
3679        let ip2: IpAddr = "10.5.10.20".parse().unwrap();
3680        assert!(peers.on_incoming_pending_session(ip1).is_ok());
3681        assert!(peers.on_incoming_pending_session(ip2).is_ok());
3682
3683        // Try IP from disallowed range
3684        let disallowed_ip: IpAddr = "172.16.0.1".parse().unwrap();
3685        assert!(peers.on_incoming_pending_session(disallowed_ip).is_err());
3686    }
3687
3688    #[tokio::test]
3689    async fn test_ip_filter_no_restriction() {
3690        use reth_net_banlist::IpFilter;
3691        use std::net::IpAddr;
3692
3693        // Create a filter with no restrictions (allow all)
3694        let ip_filter = IpFilter::allow_all();
3695        let config = PeersConfig::test().with_ip_filter(ip_filter);
3696        let mut peers = PeersManager::new(config);
3697
3698        // All IPs should be allowed
3699        let ip1: IpAddr = "192.168.1.1".parse().unwrap();
3700        let ip2: IpAddr = "10.0.0.1".parse().unwrap();
3701        let ip3: IpAddr = "8.8.8.8".parse().unwrap();
3702        assert!(peers.on_incoming_pending_session(ip1).is_ok());
3703        assert!(peers.on_incoming_pending_session(ip2).is_ok());
3704        assert!(peers.on_incoming_pending_session(ip3).is_ok());
3705    }
3706
3707    #[tokio::test]
3708    async fn test_best_unconnected_prefers_fork_id_as_tiebreaker() {
3709        let mut peers = PeersManager::default();
3710        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8008);
3711
3712        let fork_id = ForkId { hash: ForkHash([0xaa, 0xbb, 0xcc, 0xdd]), next: 0 };
3713
3714        // add two peers with equal reputation, only one has a fork_id
3715        let no_fork = PeerId::random();
3716        peers.add_peer(no_fork, PeerAddr::from_tcp(addr), None);
3717
3718        let with_fork = PeerId::random();
3719        peers.add_peer(with_fork, PeerAddr::from_tcp(addr), None);
3720        peers.peers.get_mut(&with_fork).unwrap().fork_id = Some(Box::new(fork_id));
3721
3722        let (best_id, _) = peers.best_unconnected(false).unwrap();
3723        assert_eq!(best_id, with_fork, "fork_id should break tie when reputation is equal");
3724    }
3725
3726    #[tokio::test]
3727    async fn test_add_trusted_peer_node_resolves_absent_peer() {
3728        let peer_id = PeerId::random();
3729        let trusted = TrustedPeer {
3730            host: url::Host::Domain("example.invalid".to_string()),
3731            tcp_port: 30303,
3732            udp_port: 30303,
3733            id: peer_id,
3734        };
3735
3736        let mut manager = PeersManager::default();
3737        manager.add_trusted_peer_node(trusted);
3738
3739        assert!(manager.trusted_peer_ids.contains(&peer_id));
3740        assert_eq!(manager.trusted_peers_resolver.trusted_peers.len(), 1);
3741        assert!(!manager.peers.contains_key(&peer_id));
3742
3743        let resolved = NodeRecord {
3744            address: "10.0.0.1".parse::<IpAddr>().unwrap(),
3745            tcp_port: 30303,
3746            udp_port: 30303,
3747            id: peer_id,
3748        };
3749        manager.on_resolved_peer(peer_id, resolved);
3750
3751        let peer = manager.peers.get(&peer_id).expect("peer should be added after resolution");
3752        assert!(peer.kind.is_trusted());
3753        assert_eq!(peer.addr.tcp().ip(), "10.0.0.1".parse::<IpAddr>().unwrap());
3754    }
3755
3756    #[tokio::test]
3757    async fn test_removed_trusted_peer_ignores_late_resolution() {
3758        let peer_id = PeerId::random();
3759        let trusted = TrustedPeer {
3760            host: url::Host::Domain("example.invalid".to_string()),
3761            tcp_port: 30303,
3762            udp_port: 30303,
3763            id: peer_id,
3764        };
3765
3766        let mut manager = PeersManager::default();
3767        manager.add_trusted_peer_node(trusted);
3768        manager.remove_peer_from_trusted_set(peer_id);
3769
3770        let resolved = NodeRecord {
3771            address: "10.0.0.1".parse::<IpAddr>().unwrap(),
3772            tcp_port: 30303,
3773            udp_port: 30303,
3774            id: peer_id,
3775        };
3776        manager.on_resolved_peer(peer_id, resolved);
3777
3778        assert!(!manager.peers.contains_key(&peer_id));
3779        assert!(!manager.trusted_peer_ids.contains(&peer_id));
3780    }
3781
3782    #[tokio::test]
3783    async fn test_rotation_disconnects_eligible_peer() {
3784        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
3785        let peer = PeerId::random();
3786        let mut peers = PeersManager::new(
3787            PeersConfig::test()
3788                .with_max_outbound(1)
3789                .with_peer_rotation_interval(Some(Duration::from_millis(50))),
3790        );
3791
3792        peers.add_peer(peer, PeerAddr::from_tcp(addr), None);
3793        match event!(peers) {
3794            PeerAction::PeerAdded(_) => {}
3795            _ => unreachable!(),
3796        }
3797        match event!(peers) {
3798            PeerAction::Connect { .. } => {}
3799            _ => unreachable!(),
3800        }
3801        peers.on_active_outgoing_established(peer);
3802
3803        set_connected_at(
3804            &mut peers,
3805            peer,
3806            std::time::Instant::now() - Duration::from_secs(11 * 60),
3807        );
3808
3809        tokio::time::sleep(Duration::from_millis(100)).await;
3810
3811        match event!(peers) {
3812            PeerAction::Disconnect { peer_id, reason } => {
3813                assert_eq!(peer_id, peer);
3814                assert_eq!(reason, Some(DisconnectReason::UselessPeer));
3815            }
3816            _ => unreachable!(),
3817        }
3818    }
3819
3820    #[tokio::test]
3821    async fn test_rotation_then_remove_uses_active_session_removal() {
3822        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 7)), 8008);
3823        let peer = PeerId::random();
3824        let mut peers = PeersManager::new(
3825            PeersConfig::test()
3826                .with_max_outbound(1)
3827                .with_peer_rotation_interval(Some(Duration::from_millis(50))),
3828        );
3829
3830        peers.add_peer(peer, PeerAddr::from_tcp(addr), None);
3831        match event!(peers) {
3832            PeerAction::PeerAdded(_) => {}
3833            _ => unreachable!(),
3834        }
3835        match event!(peers) {
3836            PeerAction::Connect { .. } => {}
3837            _ => unreachable!(),
3838        }
3839        peers.on_active_outgoing_established(peer);
3840        set_connected_at(
3841            &mut peers,
3842            peer,
3843            std::time::Instant::now() - Duration::from_secs(11 * 60),
3844        );
3845
3846        tokio::time::sleep(Duration::from_millis(100)).await;
3847
3848        match event!(peers) {
3849            PeerAction::Disconnect { peer_id, reason } => {
3850                assert_eq!(peer_id, peer);
3851                assert_eq!(reason, Some(DisconnectReason::UselessPeer));
3852            }
3853            _ => unreachable!(),
3854        }
3855        assert_eq!(peers.peers.get(&peer).unwrap().state, PeerConnectionState::Out);
3856        assert_eq!(peers.connection_info.num_outbound, 1);
3857
3858        peers.remove_peer(peer);
3859        match event!(peers) {
3860            PeerAction::PeerRemoved(peer_id) => assert_eq!(peer_id, peer),
3861            _ => unreachable!(),
3862        }
3863        match event!(peers) {
3864            PeerAction::Disconnect { peer_id, reason } => {
3865                assert_eq!(peer_id, peer);
3866                assert_eq!(reason, Some(DisconnectReason::DisconnectRequested));
3867            }
3868            _ => unreachable!(),
3869        }
3870
3871        let p = peers.peers.get(&peer).unwrap();
3872        assert_eq!(p.state, PeerConnectionState::DisconnectingOut);
3873        assert!(p.remove_after_disconnect);
3874        assert_eq!(peers.connection_info.num_outbound, 1);
3875
3876        peers.on_active_session_gracefully_closed(peer);
3877        assert_eq!(peers.connection_info.num_outbound, 0);
3878        assert!(!peers.peers.contains_key(&peer));
3879    }
3880
3881    #[tokio::test]
3882    async fn test_rotation_skips_trusted_peers() {
3883        let _addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 3)), 8008);
3884        let peer = PeerId::random();
3885        let trusted = TrustedPeer {
3886            host: Host::Ipv4(Ipv4Addr::new(127, 0, 1, 3)),
3887            tcp_port: 8008,
3888            udp_port: 8008,
3889            id: peer,
3890        };
3891        let mut peers = PeersManager::new(
3892            PeersConfig::test()
3893                .with_max_outbound(1)
3894                .with_trusted_nodes(vec![trusted])
3895                .with_peer_rotation_interval(Some(Duration::from_millis(50))),
3896        );
3897
3898        match event!(peers) {
3899            PeerAction::Connect { .. } => {}
3900            _ => unreachable!(),
3901        }
3902        peers.on_active_outgoing_established(peer);
3903        set_connected_at(
3904            &mut peers,
3905            peer,
3906            std::time::Instant::now() - Duration::from_secs(11 * 60),
3907        );
3908
3909        tokio::time::sleep(Duration::from_millis(100)).await;
3910
3911        poll_fn(|cx| {
3912            assert!(peers.poll(cx).is_pending(), "trusted peer must not be rotated");
3913            Poll::Ready(())
3914        })
3915        .await;
3916    }
3917
3918    #[tokio::test]
3919    async fn test_rotation_skips_recently_connected_peers() {
3920        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 4)), 8008);
3921        let peer = PeerId::random();
3922        let mut peers = PeersManager::new(
3923            PeersConfig::test()
3924                .with_max_outbound(1)
3925                .with_peer_rotation_interval(Some(Duration::from_millis(50))),
3926        );
3927
3928        peers.add_peer(peer, PeerAddr::from_tcp(addr), None);
3929        match event!(peers) {
3930            PeerAction::PeerAdded(_) => {}
3931            _ => unreachable!(),
3932        }
3933        match event!(peers) {
3934            PeerAction::Connect { .. } => {}
3935            _ => unreachable!(),
3936        }
3937        peers.on_active_outgoing_established(peer);
3938
3939        tokio::time::sleep(Duration::from_millis(100)).await;
3940
3941        poll_fn(|cx| {
3942            assert!(peers.poll(cx).is_pending(), "recently connected peer must not be rotated");
3943            Poll::Ready(())
3944        })
3945        .await;
3946    }
3947
3948    #[tokio::test]
3949    async fn test_rotation_skips_when_slots_available() {
3950        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 5)), 8008);
3951        let peer = PeerId::random();
3952        let mut peers = PeersManager::new(
3953            PeersConfig::test()
3954                .with_max_outbound(2)
3955                .with_peer_rotation_interval(Some(Duration::from_millis(50))),
3956        );
3957
3958        peers.add_peer(peer, PeerAddr::from_tcp(addr), None);
3959        match event!(peers) {
3960            PeerAction::PeerAdded(_) => {}
3961            _ => unreachable!(),
3962        }
3963        match event!(peers) {
3964            PeerAction::Connect { .. } => {}
3965            _ => unreachable!(),
3966        }
3967        peers.on_active_outgoing_established(peer);
3968        set_connected_at(
3969            &mut peers,
3970            peer,
3971            std::time::Instant::now() - Duration::from_secs(11 * 60),
3972        );
3973
3974        tokio::time::sleep(Duration::from_millis(100)).await;
3975
3976        poll_fn(|cx| {
3977            assert!(
3978                peers.poll(cx).is_pending(),
3979                "rotation must not fire when outbound slots are available"
3980            );
3981            Poll::Ready(())
3982        })
3983        .await;
3984    }
3985
3986    #[tokio::test]
3987    async fn test_rotation_disconnects_inbound_peer() {
3988        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 6)), 8008);
3989        let peer = PeerId::random();
3990        let mut peers = PeersManager::new(
3991            PeersConfig::test()
3992                .with_max_inbound(1)
3993                .with_peer_rotation_interval(Some(Duration::from_millis(50))),
3994        );
3995
3996        assert!(peers.on_incoming_pending_session(addr.ip()).is_ok());
3997        peers.on_incoming_session_established(peer, addr);
3998
3999        match event!(peers) {
4000            PeerAction::PeerAdded(_) => {}
4001            _ => unreachable!(),
4002        }
4003
4004        set_connected_at(
4005            &mut peers,
4006            peer,
4007            std::time::Instant::now() - Duration::from_secs(11 * 60),
4008        );
4009
4010        tokio::time::sleep(Duration::from_millis(100)).await;
4011
4012        match event!(peers) {
4013            PeerAction::Disconnect { peer_id, reason } => {
4014                assert_eq!(peer_id, peer);
4015                assert_eq!(reason, Some(DisconnectReason::UselessPeer));
4016            }
4017            _ => unreachable!(),
4018        }
4019    }
4020
4021    // Ensures explicitly added peers are dialed right away, while discovered peers wait for the
4022    // next refill tick.
4023    #[tokio::test]
4024    async fn test_requested_peer_is_dialed_before_refill_tick() {
4025        let mut peers = PeersManager::new(PeersConfig {
4026            refill_slots_interval: Duration::from_secs(60),
4027            ..PeersConfig::test()
4028        });
4029        // consume the initial refill tick
4030        assert!(poll_now(&mut peers).is_pending());
4031
4032        let discovered = PeerId::random();
4033        let discovered_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 1)), 8008);
4034        peers.add_peer(discovered, PeerAddr::from_tcp(discovered_addr), None);
4035        assert!(
4036            matches!(poll_now(&mut peers), Poll::Ready(PeerAction::PeerAdded(id)) if id == discovered)
4037        );
4038        assert!(poll_now(&mut peers).is_pending());
4039
4040        let requested = PeerId::random();
4041        let requested_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008);
4042        peers.handle().add_peer(requested, requested_addr);
4043        assert!(
4044            matches!(poll_now(&mut peers), Poll::Ready(PeerAction::PeerAdded(id)) if id == requested)
4045        );
4046
4047        // the requested peer fills all free outbound slots, including one for the discovered peer
4048        let mut dialed = Vec::new();
4049        while let Poll::Ready(PeerAction::Connect { peer_id, .. }) = poll_now(&mut peers) {
4050            dialed.push(peer_id);
4051        }
4052        dialed.sort();
4053        let mut expected = vec![discovered, requested];
4054        expected.sort();
4055        assert_eq!(dialed, expected);
4056    }
4057
4058    #[tokio::test]
4059    async fn test_revoke_trust_independent_of_peer_kind() {
4060        for kind in [None, Some(PeerKind::Basic), Some(PeerKind::Static), Some(PeerKind::Trusted)] {
4061            let mut peers = PeersManager::new(PeersConfig::test());
4062            let peer_id = PeerId::random();
4063            peers.add_trusted_peer_id(peer_id);
4064            if let Some(kind) = kind {
4065                peers.add_peer_kind(
4066                    peer_id,
4067                    Some(kind),
4068                    PeerAddr::from_tcp("127.0.0.1:8008".parse().unwrap()),
4069                    None,
4070                );
4071            }
4072            peers.remove_peer_from_trusted_set(peer_id);
4073            assert!(!peers.trusted_peer_ids.contains(&peer_id));
4074            assert_eq!(
4075                peers.peers.get(&peer_id).map(|peer| peer.kind),
4076                kind.map(|kind| if kind == PeerKind::Trusted { PeerKind::Basic } else { kind })
4077            );
4078            // Revocation also removes the administrative ban exemption.
4079            peers.ban_peer_by_admin(peer_id);
4080            assert!(peers.ban_list.is_banned_peer(&peer_id));
4081        }
4082    }
4083
4084    #[tokio::test]
4085    async fn test_persisted_trust_controls_inbound_admission() {
4086        for kind in [PeerKind::Basic, PeerKind::Static, PeerKind::Trusted] {
4087            for trusted_only in [false, true] {
4088                let peer_id = PeerId::random();
4089                let addr = "127.0.0.1:8008".parse::<SocketAddr>().unwrap();
4090                let mut config = PeersConfig::test().with_trusted_nodes_only(trusted_only);
4091                config.connection_info.max_inbound = 0;
4092                config.persisted_peers.push(PersistedPeerInfo {
4093                    record: NodeRecord::new(addr, peer_id),
4094                    kind,
4095                    fork_id: None,
4096                    reputation: 0,
4097                });
4098                let mut peers = PeersManager::new(config);
4099                assert_eq!(peers.trusted_peer_ids.contains(&peer_id), kind.is_trusted());
4100                peers.connection_info.inc_pending_in();
4101                peers.on_incoming_session_established(peer_id, addr);
4102                if kind.is_trusted() {
4103                    assert!(peers.queued_actions.is_empty());
4104                    assert_eq!(peers.connection_info.num_inbound, 1);
4105                } else if trusted_only {
4106                    assert!(matches!(peers.queued_actions.pop_front(),
4107                        Some(PeerAction::DisconnectUntrustedIncoming { peer_id: id }) if id == peer_id));
4108                } else {
4109                    assert!(matches!(peers.queued_actions.pop_front(),
4110                        Some(PeerAction::Disconnect { peer_id: id, reason: Some(DisconnectReason::TooManyPeers) }) if id == peer_id));
4111                }
4112            }
4113        }
4114    }
4115
4116    #[tokio::test]
4117    async fn test_filtered_persisted_peer_does_not_restore_trust() {
4118        let peer_id = PeerId::random();
4119        let mut config = PeersConfig::test();
4120        config.enforce_enr_fork_id = true;
4121        config.persisted_peers.push(PersistedPeerInfo {
4122            record: NodeRecord::new("127.0.0.1:8008".parse().unwrap(), peer_id),
4123            kind: PeerKind::Trusted,
4124            fork_id: None,
4125            reputation: 0,
4126        });
4127        let peers = PeersManager::new(config);
4128        assert!(!peers.trusted_peer_ids.contains(&peer_id));
4129        assert!(!peers.peers.contains_key(&peer_id));
4130    }
4131    #[tokio::test]
4132    async fn test_pending_dials_reserve_outbound_capacity() {
4133        for max_outbound in [0, 1, 3] {
4134            let mut peers = PeersManager::new(PeersConfig::test().with_max_outbound(max_outbound));
4135            let addr = PeerAddr::from_tcp("127.0.0.1:8008".parse().unwrap());
4136            for _ in 0..30 {
4137                peers.add_peer(PeerId::random(), addr, None);
4138            }
4139            peers.fill_outbound_slots();
4140            let dialed = peers
4141                .queued_actions
4142                .iter()
4143                .filter_map(|action| match action {
4144                    PeerAction::Connect { peer_id, .. } => Some(*peer_id),
4145                    _ => None,
4146                })
4147                .collect::<Vec<_>>();
4148            assert_eq!(dialed.len(), max_outbound);
4149            peers.queued_actions.clear();
4150            for peer in dialed {
4151                peers.on_active_outgoing_established(peer);
4152                peers.fill_outbound_slots();
4153                assert!(peers.queued_actions.is_empty());
4154            }
4155            assert_eq!(peers.connection_info.num_outbound, max_outbound);
4156            assert_eq!(peers.connection_info.num_pending_out, 0);
4157        }
4158    }
4159
4160    #[tokio::test]
4161    async fn test_surplus_outbound_completion() {
4162        for kind in [PeerKind::Basic, PeerKind::Trusted, PeerKind::Static] {
4163            let mut peers = PeersManager::new(PeersConfig::test().with_max_outbound(0));
4164            let peer = PeerId::random();
4165            let addr = PeerAddr::from_tcp("127.0.0.1:8008".parse().unwrap());
4166            peers.add_and_connect_kind(peer, kind, addr, None);
4167            peers.queued_actions.clear();
4168            peers.on_active_outgoing_established(peer);
4169            if kind == PeerKind::Basic {
4170                assert!(matches!(
4171                    peers.queued_actions.pop_front(),
4172                    Some(PeerAction::Disconnect {
4173                        peer_id,
4174                        reason: Some(DisconnectReason::TooManyPeers),
4175                    }) if peer_id == peer
4176                ));
4177            } else {
4178                assert!(peers.queued_actions.is_empty());
4179            }
4180            // The closing session releases the accounting reserved at establishment.
4181            peers.on_active_session_gracefully_closed(peer);
4182            assert_eq!(peers.connection_info.num_outbound, 0);
4183            assert_eq!(peers.connection_info.num_pending_out, 0);
4184        }
4185    }
4186
4187    #[tokio::test]
4188    async fn test_surplus_outbound_preserves_id_trusted_peers() {
4189        for hostname in [false, true] {
4190            let mut peers = PeersManager::new(PeersConfig::test().with_max_outbound(0));
4191            let peer_id = PeerId::random();
4192            let addr = PeerAddr::from_tcp("127.0.0.1:8008".parse().unwrap());
4193            if hostname {
4194                peers.add_peer(peer_id, addr, None);
4195                peers.add_trusted_peer_node(TrustedPeer {
4196                    host: Host::Domain("example.invalid".to_string()),
4197                    tcp_port: 8008,
4198                    udp_port: 8008,
4199                    id: peer_id,
4200                });
4201            } else {
4202                peers.add_trusted_peer_id(peer_id);
4203            }
4204            peers.add_and_connect_kind(peer_id, PeerKind::Basic, addr, None);
4205            assert_eq!(peers.peers[&peer_id].kind, PeerKind::Basic);
4206            peers.queued_actions.clear();
4207
4208            peers.on_active_outgoing_established(peer_id);
4209
4210            assert!(peers.queued_actions.is_empty());
4211            assert_eq!(peers.num_outbound_connections(), 1);
4212            peers.on_active_session_gracefully_closed(peer_id);
4213            assert_eq!(peers.num_outbound_connections(), 0);
4214            assert_eq!(peers.num_pending_outbound_connections(), 0);
4215        }
4216    }
4217
4218    #[tokio::test]
4219    async fn test_pending_outbound_cleanup_preserves_incoming_session() {
4220        let mut peers = PeersManager::new(PeersConfig::test());
4221        let peer_id = PeerId::random();
4222        let addr = "127.0.0.1:8008".parse::<SocketAddr>().unwrap();
4223        peers.add_and_connect_kind(peer_id, PeerKind::Basic, PeerAddr::from_tcp(addr), None);
4224        peers.on_incoming_pending_session(addr.ip()).unwrap();
4225        peers.on_incoming_session_established(peer_id, addr);
4226        let connected_at = peers.peers[&peer_id].connected_at;
4227
4228        peers.on_outgoing_pending_session_gracefully_closed(&peer_id);
4229
4230        assert_eq!(peers.peers[&peer_id].state, PeerConnectionState::In);
4231        assert_eq!(peers.peers[&peer_id].connected_at, connected_at);
4232        assert_eq!(peers.connection_info.num_inbound, 1);
4233        assert_eq!(peers.num_pending_outbound_connections(), 0);
4234    }
4235
4236    #[tokio::test]
4237    async fn test_inbound_capacity_preserves_pending_outbound_and_trusted_exemption() {
4238        for trusted in [false, true] {
4239            let mut peers = PeersManager::new(PeersConfig::test().with_max_inbound(0));
4240            let peer_id = PeerId::random();
4241            let addr = "127.0.0.1:8008".parse().unwrap();
4242            peers.add_and_connect_kind(peer_id, PeerKind::Basic, PeerAddr::from_tcp(addr), None);
4243            if trusted {
4244                peers.add_trusted_peer_id(peer_id);
4245            }
4246            assert_eq!(peers.has_inbound_capacity(peer_id), trusted);
4247            assert_eq!(peers.peers[&peer_id].state, PeerConnectionState::PendingOut);
4248            assert_eq!(peers.connection_info.num_pending_out, 1);
4249        }
4250    }
4251
4252    #[tokio::test]
4253    async fn test_unauthenticated_outbound_errors_do_not_ban_identity() {
4254        for error in [
4255            PendingSessionHandshakeError::Timeout,
4256            PendingSessionHandshakeError::Eth(EthStreamError::P2PStreamError(
4257                P2PStreamError::HandshakeError(P2PHandshakeError::Timeout),
4258            )),
4259            PendingSessionHandshakeError::Eth(EthStreamError::P2PStreamError(
4260                P2PStreamError::HandshakeError(P2PHandshakeError::NoResponse),
4261            )),
4262            PendingSessionHandshakeError::Ecies(ECIESErrorImpl::InvalidAckData.into()),
4263            PendingSessionHandshakeError::Ecies(ECIESErrorImpl::TagCheckDecryptFailed.into()),
4264            PendingSessionHandshakeError::Eth(EthStreamError::P2PStreamError(P2PStreamError::Io(
4265                io::Error::other("invalid frame MAC"),
4266            ))),
4267        ] {
4268            let mut peers = PeersManager::new(PeersConfig::test());
4269            let peer_id = PeerId::random();
4270            let addr = "8.8.8.8:30303".parse::<SocketAddr>().unwrap();
4271            peers.add_and_connect_kind(peer_id, PeerKind::Basic, PeerAddr::from_tcp(addr), None);
4272            peers.queued_actions.clear();
4273            let reputation = peers.peers[&peer_id].reputation;
4274            for _ in 0..=peers.max_backoff_count + 1 {
4275                peers.add_and_connect_kind(
4276                    peer_id,
4277                    PeerKind::Basic,
4278                    PeerAddr::from_tcp(addr),
4279                    None,
4280                );
4281                peers.queued_actions.clear();
4282                peers.on_outgoing_pending_session_dropped(&addr, &peer_id, &error);
4283                assert_eq!(peers.peers[&peer_id].reputation, reputation);
4284            }
4285            let peer = &peers.peers[&peer_id];
4286            assert_eq!(peer.state, PeerConnectionState::Idle);
4287            assert_eq!(peer.reputation, reputation);
4288            assert_eq!(peer.severe_backoff_counter, 0);
4289            assert!(peer.backed_off);
4290            assert!(!peers.ban_list.is_banned_peer(&peer_id));
4291            assert!(peers.queued_actions.is_empty());
4292            assert_eq!(peers.connection_info.num_pending_out, 0);
4293
4294            // Inbound errors remain attributable to the actual remote IP.
4295            if matches!(&error, PendingSessionHandshakeError::Ecies(_)) {
4296                peers.connection_info.inc_pending_in();
4297                peers.on_incoming_pending_session_dropped(addr, &error);
4298                assert!(peers.ban_list.is_banned_ip(&addr.ip()));
4299            }
4300        }
4301    }
4302
4303    #[tokio::test]
4304    async fn test_unattributable_backoff_respects_maximum() {
4305        let mut config = PeersConfig::test();
4306        config.backoff_durations.low = Duration::from_secs(60);
4307        config.backoff_durations.max = Duration::from_secs(1);
4308        let mut peers = PeersManager::new(config);
4309        let peer_id = PeerId::random();
4310        let addr = "8.8.8.8:30303".parse::<SocketAddr>().unwrap();
4311        peers.add_and_connect_kind(peer_id, PeerKind::Basic, PeerAddr::from_tcp(addr), None);
4312        peers.peers.get_mut(&peer_id).unwrap().severe_backoff_counter = 3;
4313        let before = std::time::Instant::now();
4314        peers.on_outgoing_pending_session_dropped(
4315            &addr,
4316            &peer_id,
4317            &PendingSessionHandshakeError::Timeout,
4318        );
4319        let after = std::time::Instant::now();
4320        let until = peers.backed_off_peers[&peer_id];
4321        assert!(until >= before + Duration::from_secs(1));
4322        assert!(until <= after + Duration::from_secs(1));
4323        assert_eq!(peers.peers[&peer_id].severe_backoff_counter, 3);
4324    }
4325
4326    #[tokio::test]
4327    async fn test_dns_trusted_ids_bypass_outbound_capacity() {
4328        for register_first in [false, true] {
4329            let mut peers = PeersManager::new(PeersConfig::test().with_max_outbound(0));
4330            let peer_id = PeerId::random();
4331            let addr = "127.0.0.1:30303".parse::<SocketAddr>().unwrap();
4332            let trusted =
4333                TrustedPeer::new(Host::Domain("trusted.invalid".into()), addr.port(), peer_id);
4334            if register_first {
4335                peers.add_trusted_peer_node(trusted.clone());
4336            }
4337            peers.add_peer(peer_id, PeerAddr::from_tcp(addr), None);
4338            if !register_first {
4339                peers.add_trusted_peer_node(trusted);
4340            }
4341            assert_eq!(peers.peers[&peer_id].kind, PeerKind::Basic);
4342            peers.queued_actions.clear();
4343            peers.fill_outbound_slots();
4344            assert!(
4345                matches!(peers.queued_actions.pop_front(), Some(PeerAction::Connect { peer_id: id, remote_addr }) if id == peer_id && remote_addr == addr)
4346            );
4347            assert!(peers.queued_actions.is_empty());
4348        }
4349    }
4350
4351    #[tokio::test]
4352    async fn test_trusted_bypass_respects_shared_dial_limit() {
4353        let mut peers = PeersManager::new(
4354            PeersConfig::test().with_max_outbound(1).with_max_concurrent_dials(1),
4355        );
4356        let addr = "127.0.0.1:30303".parse().unwrap();
4357        let basic = PeerId::random();
4358        peers.add_peer(basic, PeerAddr::from_tcp(addr), None);
4359        peers.fill_outbound_slots();
4360        assert_eq!(peers.connection_info.num_pending_out, 1);
4361        for _ in 0..2 {
4362            peers.add_trusted_peer(PeerId::random(), PeerAddr::from_tcp(addr));
4363        }
4364        peers.queued_actions.clear();
4365        peers.fill_outbound_slots();
4366        assert!(peers.queued_actions.is_empty());
4367        peers.on_active_outgoing_established(basic);
4368        peers.fill_outbound_slots();
4369        assert!(
4370            matches!(peers.queued_actions.pop_front(), Some(PeerAction::Connect { peer_id, .. }) if peers.peers[&peer_id].is_trusted())
4371        );
4372        assert!(peers.queued_actions.is_empty());
4373        assert_eq!(peers.connection_info.num_pending_out, 1);
4374        assert_eq!(peers.connection_info.num_outbound, 1);
4375    }
4376}