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