reth_network_types/peers/reputation.rs
1//! Peer reputation management
2
3/// The default reputation of a peer
4pub const DEFAULT_REPUTATION: Reputation = 0;
5
6/// The minimal unit we're measuring reputation
7const REPUTATION_UNIT: i32 = -1024;
8
9/// The reputation value below which new connection from/to peers are rejected.
10pub const BANNED_REPUTATION: i32 = 50 * REPUTATION_UNIT;
11
12/// The reputation change to apply to a peer that dropped the connection.
13const REMOTE_DISCONNECT_REPUTATION_CHANGE: i32 = 4 * REPUTATION_UNIT;
14
15/// The reputation change to apply to a peer that we failed to connect to.
16const FAILED_TO_CONNECT_REPUTATION_CHANGE: i32 = 25 * REPUTATION_UNIT;
17
18/// The reputation change to apply to a peer that failed to respond in time.
19const TIMEOUT_REPUTATION_CHANGE: i32 = 4 * REPUTATION_UNIT;
20
21/// The reputation change to apply to a peer that sent a bad message.
22const BAD_MESSAGE_REPUTATION_CHANGE: i32 = 16 * REPUTATION_UNIT;
23
24/// The reputation change applies to a peer that has sent a transaction (full or hash) that we
25/// already know about and have already previously received from that peer.
26///
27/// Note: this appears to be quite common in practice, so by default this is 0, which doesn't
28/// apply any changes to the peer's reputation, effectively ignoring it.
29const ALREADY_SEEN_TRANSACTION_REPUTATION_CHANGE: i32 = 0;
30
31/// The reputation change to apply to a peer which violates protocol rules: minimal reputation
32const BAD_PROTOCOL_REPUTATION_CHANGE: i32 = i32::MIN;
33
34/// The reputation change to apply to a peer that sent a bad announcement.
35// todo: current value is a hint, needs to be set properly
36const BAD_ANNOUNCEMENT_REPUTATION_CHANGE: i32 = REPUTATION_UNIT;
37
38/// The maximum reputation change that can be applied to a trusted peer.
39/// This is used to prevent a single bad message from a trusted peer to cause a significant change.
40/// This gives a trusted peer more leeway when interacting with the node, which is useful for in
41/// custom setups. By not setting this to `0` we still allow trusted peer penalization but less than
42/// untrusted peers.
43pub const MAX_TRUSTED_PEER_REPUTATION_CHANGE: Reputation = 2 * REPUTATION_UNIT;
44
45/// Returns `true` if the given reputation is below the [`BANNED_REPUTATION`] threshold
46#[inline]
47pub const fn is_banned_reputation(reputation: i32) -> bool {
48 reputation < BANNED_REPUTATION
49}
50
51/// The type that tracks the reputation score.
52pub type Reputation = i32;
53
54/// Various kinds of reputation changes.
55#[derive(Debug, Copy, Clone, PartialEq, Eq)]
56pub enum ReputationChangeKind {
57 /// Received an unspecific bad message from the peer
58 BadMessage,
59 /// Peer sent a bad block.
60 ///
61 /// Note: this will we only used in pre-merge, pow consensus, since after no more block announcements are sent via devp2p: [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p)
62 BadBlock,
63 /// Peer sent a bad transaction message. E.g. Transactions which weren't recoverable.
64 BadTransactions,
65 /// Peer sent a bad announcement message, e.g. invalid transaction type for the configured
66 /// network.
67 BadAnnouncement,
68 /// Peer sent a message that included a hash or transaction that we already received from the
69 /// peer.
70 ///
71 /// According to the [Eth spec](https://github.com/ethereum/devp2p/blob/master/caps/eth.md):
72 ///
73 /// > A node should never send a transaction back to a peer that it can determine already knows
74 /// > of it (either because it was previously sent or because it was informed from this peer
75 /// > originally). This is usually achieved by remembering a set of transaction hashes recently
76 /// > relayed by the peer.
77 AlreadySeenTransaction,
78 /// Peer failed to respond in time.
79 Timeout,
80 /// Peer does not adhere to network protocol rules.
81 BadProtocol,
82 /// Failed to establish a connection to the peer.
83 FailedToConnect,
84 /// Connection dropped by peer.
85 Dropped,
86 /// Reset the reputation to the default value.
87 Reset,
88 /// Apply a reputation change by value
89 Other(Reputation),
90}
91
92impl ReputationChangeKind {
93 /// Returns true if the reputation change is a [`ReputationChangeKind::Reset`].
94 pub const fn is_reset(&self) -> bool {
95 matches!(self, Self::Reset)
96 }
97
98 /// Returns true if the reputation change is [`ReputationChangeKind::Dropped`].
99 pub const fn is_dropped(&self) -> bool {
100 matches!(self, Self::Dropped)
101 }
102}
103
104/// How the [`ReputationChangeKind`] are weighted.
105#[derive(Debug, Clone, PartialEq, Eq)]
106#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
107#[cfg_attr(feature = "serde", serde(default))]
108pub struct ReputationChangeWeights {
109 /// Weight for [`ReputationChangeKind::BadMessage`]
110 pub bad_message: Reputation,
111 /// Weight for [`ReputationChangeKind::BadBlock`]
112 pub bad_block: Reputation,
113 /// Weight for [`ReputationChangeKind::BadTransactions`]
114 pub bad_transactions: Reputation,
115 /// Weight for [`ReputationChangeKind::AlreadySeenTransaction`]
116 pub already_seen_transactions: Reputation,
117 /// Weight for [`ReputationChangeKind::Timeout`]
118 pub timeout: Reputation,
119 /// Weight for [`ReputationChangeKind::BadProtocol`]
120 pub bad_protocol: Reputation,
121 /// Weight for [`ReputationChangeKind::FailedToConnect`]
122 pub failed_to_connect: Reputation,
123 /// Weight for [`ReputationChangeKind::Dropped`]
124 pub dropped: Reputation,
125 /// Weight for [`ReputationChangeKind::BadAnnouncement`]
126 pub bad_announcement: Reputation,
127}
128
129// === impl ReputationChangeWeights ===
130
131impl ReputationChangeWeights {
132 /// Creates a new instance that doesn't penalize any kind of reputation change.
133 pub const fn zero() -> Self {
134 Self {
135 bad_block: 0,
136 bad_transactions: 0,
137 already_seen_transactions: 0,
138 bad_message: 0,
139 timeout: 0,
140 bad_protocol: 0,
141 failed_to_connect: 0,
142 dropped: 0,
143 bad_announcement: 0,
144 }
145 }
146
147 /// Returns the quantifiable [`ReputationChange`] for the given [`ReputationChangeKind`] using
148 /// the configured weights
149 pub fn change(&self, kind: ReputationChangeKind) -> ReputationChange {
150 match kind {
151 ReputationChangeKind::BadMessage => self.bad_message.into(),
152 ReputationChangeKind::BadBlock => self.bad_block.into(),
153 ReputationChangeKind::BadTransactions => self.bad_transactions.into(),
154 ReputationChangeKind::AlreadySeenTransaction => self.already_seen_transactions.into(),
155 ReputationChangeKind::Timeout => self.timeout.into(),
156 ReputationChangeKind::BadProtocol => self.bad_protocol.into(),
157 ReputationChangeKind::FailedToConnect => self.failed_to_connect.into(),
158 ReputationChangeKind::Dropped => self.dropped.into(),
159 ReputationChangeKind::Reset => DEFAULT_REPUTATION.into(),
160 ReputationChangeKind::Other(val) => val.into(),
161 ReputationChangeKind::BadAnnouncement => self.bad_announcement.into(),
162 }
163 }
164}
165
166impl Default for ReputationChangeWeights {
167 fn default() -> Self {
168 Self {
169 bad_block: BAD_MESSAGE_REPUTATION_CHANGE,
170 bad_transactions: BAD_MESSAGE_REPUTATION_CHANGE,
171 already_seen_transactions: ALREADY_SEEN_TRANSACTION_REPUTATION_CHANGE,
172 bad_message: BAD_MESSAGE_REPUTATION_CHANGE,
173 timeout: TIMEOUT_REPUTATION_CHANGE,
174 bad_protocol: BAD_PROTOCOL_REPUTATION_CHANGE,
175 failed_to_connect: FAILED_TO_CONNECT_REPUTATION_CHANGE,
176 dropped: REMOTE_DISCONNECT_REPUTATION_CHANGE,
177 bad_announcement: BAD_ANNOUNCEMENT_REPUTATION_CHANGE,
178 }
179 }
180}
181
182/// Represents a change in a peer's reputation.
183#[derive(Debug, Copy, Clone, Default)]
184pub struct ReputationChange(Reputation);
185
186// === impl ReputationChange ===
187
188impl ReputationChange {
189 /// Helper type for easier conversion
190 #[inline]
191 pub const fn as_i32(self) -> Reputation {
192 self.0
193 }
194}
195
196impl From<ReputationChange> for Reputation {
197 fn from(value: ReputationChange) -> Self {
198 value.0
199 }
200}
201
202impl From<Reputation> for ReputationChange {
203 fn from(value: Reputation) -> Self {
204 Self(value)
205 }
206}
207
208/// Outcomes when a reputation change is applied to a peer
209#[derive(Debug, Clone, Copy)]
210pub enum ReputationChangeOutcome {
211 /// Nothing to do.
212 None,
213 /// Ban the peer.
214 Ban,
215 /// Ban and disconnect
216 DisconnectAndBan,
217 /// Unban the peer
218 Unban,
219}