Skip to main content

reth_network_types/peers/
mod.rs

1pub mod addr;
2pub mod config;
3pub mod kind;
4pub mod reputation;
5pub mod state;
6
7pub use config::{ConnectionsConfig, PeersConfig};
8pub use reputation::{Reputation, ReputationChange, ReputationChangeKind, ReputationChangeWeights};
9
10use alloy_eip2124::ForkId;
11use reth_network_peers::{NodeRecord, PeerId};
12use tracing::trace;
13
14use crate::{
15    is_banned_reputation, PeerAddr, PeerConnectionState, PeerKind, ReputationChangeOutcome,
16    DEFAULT_REPUTATION,
17};
18
19/// Tracks info about a single peer.
20#[derive(Debug, Clone)]
21pub struct Peer {
22    /// Where to reach the peer.
23    pub addr: PeerAddr,
24    /// Reputation of the peer.
25    pub reputation: i32,
26    /// The state of the connection, if any.
27    pub state: PeerConnectionState,
28    /// The [`ForkId`] that the peer announced via discovery.
29    pub fork_id: Option<Box<ForkId>>,
30    /// Whether the entry should be removed after an existing session was terminated.
31    pub remove_after_disconnect: bool,
32    /// The kind of peer
33    pub kind: PeerKind,
34    /// Whether the peer is currently backed off.
35    pub backed_off: bool,
36    /// Counts number of times the peer was backed off due to a severe
37    /// [`BackoffKind`](crate::BackoffKind).
38    pub severe_backoff_counter: u8,
39}
40
41// === impl Peer ===
42
43impl Peer {
44    /// Returns a new peer for given [`PeerAddr`].
45    pub fn new(addr: PeerAddr) -> Self {
46        Self::with_state(addr, Default::default())
47    }
48
49    /// Returns a new trusted peer for given [`PeerAddr`].
50    pub fn trusted(addr: PeerAddr) -> Self {
51        Self { kind: PeerKind::Trusted, ..Self::new(addr) }
52    }
53
54    /// Returns the reputation of the peer
55    pub const fn reputation(&self) -> i32 {
56        self.reputation
57    }
58
59    /// Returns a new peer for given [`PeerAddr`] and [`PeerConnectionState`].
60    pub fn with_state(addr: PeerAddr, state: PeerConnectionState) -> Self {
61        Self {
62            addr,
63            state,
64            reputation: DEFAULT_REPUTATION,
65            fork_id: None,
66            remove_after_disconnect: false,
67            kind: Default::default(),
68            backed_off: false,
69            severe_backoff_counter: 0,
70        }
71    }
72
73    /// Returns a new peer for given [`PeerAddr`] and [`PeerKind`].
74    pub fn with_kind(addr: PeerAddr, kind: PeerKind) -> Self {
75        Self { kind, ..Self::new(addr) }
76    }
77
78    /// Resets the reputation of the peer to the default value. This always returns
79    /// [`ReputationChangeOutcome::None`].
80    pub const fn reset_reputation(&mut self) -> ReputationChangeOutcome {
81        self.reputation = DEFAULT_REPUTATION;
82
83        ReputationChangeOutcome::None
84    }
85
86    /// Applies a reputation change to the peer and returns what action should be taken.
87    pub fn apply_reputation(
88        &mut self,
89        reputation: i32,
90        kind: ReputationChangeKind,
91    ) -> ReputationChangeOutcome {
92        let previous = self.reputation;
93        // we add reputation since negative reputation change decrease total reputation
94        self.reputation = previous.saturating_add(reputation);
95
96        trace!(target: "net::peers", reputation=%self.reputation, banned=%self.is_banned(), ?kind, "applied reputation change");
97
98        if self.state.is_connected() && self.is_banned() {
99            self.state.disconnect();
100            return ReputationChangeOutcome::DisconnectAndBan
101        }
102
103        if self.is_banned() && !is_banned_reputation(previous) {
104            return ReputationChangeOutcome::Ban
105        }
106
107        if !self.is_banned() && is_banned_reputation(previous) {
108            return ReputationChangeOutcome::Unban
109        }
110
111        ReputationChangeOutcome::None
112    }
113
114    /// Returns true if the peer's reputation is below the banned threshold.
115    #[inline]
116    pub const fn is_banned(&self) -> bool {
117        is_banned_reputation(self.reputation)
118    }
119
120    /// Returns `true` if peer is banned.
121    #[inline]
122    pub const fn is_backed_off(&self) -> bool {
123        self.backed_off
124    }
125
126    /// Unbans the peer by resetting its reputation
127    #[inline]
128    pub const fn unban(&mut self) {
129        self.reputation = DEFAULT_REPUTATION
130    }
131
132    /// Returns whether this peer is trusted
133    #[inline]
134    pub const fn is_trusted(&self) -> bool {
135        matches!(self.kind, PeerKind::Trusted)
136    }
137
138    /// Returns whether this peer is static
139    #[inline]
140    pub const fn is_static(&self) -> bool {
141        matches!(self.kind, PeerKind::Static)
142    }
143}
144
145/// Peer info persisted to disk.
146///
147/// Contains richer metadata than a plain [`NodeRecord`], preserving the peer's kind, fork ID,
148/// and reputation across restarts.
149#[derive(Debug, Clone, PartialEq, Eq)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
151pub struct PersistedPeerInfo {
152    /// The node record (id, address, ports).
153    pub record: NodeRecord,
154    /// The kind of peer.
155    pub kind: PeerKind,
156    /// The [`ForkId`] that the peer announced via discovery.
157    #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
158    pub fork_id: Option<ForkId>,
159    /// The peer's reputation at the time of persisting.
160    pub reputation: i32,
161}
162
163impl PersistedPeerInfo {
164    /// Returns the peer id.
165    pub const fn peer_id(&self) -> PeerId {
166        self.record.id
167    }
168
169    /// Converts a legacy [`NodeRecord`] into a [`PersistedPeerInfo`] with default metadata.
170    pub const fn from_node_record(record: NodeRecord) -> Self {
171        Self { record, kind: PeerKind::Basic, fork_id: None, reputation: DEFAULT_REPUTATION }
172    }
173}