reth_network/transactions/
config.rs1use core::fmt;
2use std::{fmt::Debug, str::FromStr};
3
4use super::{
5 PeerMetadata, DEFAULT_MAX_COUNT_TRANSACTIONS_SEEN_BY_PEER,
6 DEFAULT_SOFT_LIMIT_BYTE_SIZE_POOLED_TRANSACTIONS_RESP_ON_PACK_GET_POOLED_TRANSACTIONS_REQ,
7 SOFT_LIMIT_BYTE_SIZE_POOLED_TRANSACTIONS_RESPONSE,
8};
9use crate::transactions::constants::{
10 tx_fetcher::{
11 DEFAULT_MAX_CAPACITY_CACHE_PENDING_FETCH, DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS,
12 DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS_PER_PEER,
13 },
14 tx_manager::{
15 DEFAULT_MAX_COUNT_PENDING_POOL_IMPORTS, DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES,
16 },
17};
18use alloy_eips::eip2718::IsTyped2718;
19use alloy_primitives::B256;
20use derive_more::{Constructor, Display};
21use reth_eth_wire::NetworkPrimitives;
22use reth_network_types::peers::kind::PeerKind;
23
24#[derive(Debug, Clone)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct TransactionsManagerConfig {
28 pub transaction_fetcher_config: TransactionFetcherConfig,
30 pub max_transactions_seen_by_peer_history: u32,
32 #[cfg_attr(feature = "serde", serde(default = "default_max_pending_pool_imports"))]
34 pub max_pending_pool_imports: usize,
35 #[cfg_attr(feature = "serde", serde(default))]
37 pub propagation_mode: TransactionPropagationMode,
38 #[cfg_attr(feature = "serde", serde(default))]
40 pub ingress_policy: TransactionIngressPolicy,
41 #[cfg_attr(feature = "serde", serde(default = "default_tx_channel_memory_limit_bytes"))]
46 pub tx_channel_memory_limit_bytes: usize,
47}
48
49#[cfg(feature = "serde")]
50const fn default_max_pending_pool_imports() -> usize {
51 DEFAULT_MAX_COUNT_PENDING_POOL_IMPORTS
52}
53
54#[cfg(feature = "serde")]
55const fn default_tx_channel_memory_limit_bytes() -> usize {
56 DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES
57}
58
59impl Default for TransactionsManagerConfig {
60 fn default() -> Self {
61 Self {
62 transaction_fetcher_config: TransactionFetcherConfig::default(),
63 max_transactions_seen_by_peer_history: DEFAULT_MAX_COUNT_TRANSACTIONS_SEEN_BY_PEER,
64 max_pending_pool_imports: DEFAULT_MAX_COUNT_PENDING_POOL_IMPORTS,
65 propagation_mode: TransactionPropagationMode::default(),
66 ingress_policy: TransactionIngressPolicy::default(),
67 tx_channel_memory_limit_bytes: DEFAULT_TX_MANAGER_CHANNEL_MEMORY_LIMIT_BYTES,
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75pub enum TransactionPropagationMode {
76 #[default]
78 Sqrt,
79 All,
81 Max(usize),
83}
84
85impl TransactionPropagationMode {
86 pub(crate) fn full_peer_count(&self, peer_count: usize) -> usize {
88 match self {
89 Self::Sqrt => (peer_count as f64).sqrt().round() as usize,
90 Self::All => peer_count,
91 Self::Max(max) => peer_count.min(*max),
92 }
93 }
94}
95impl std::fmt::Display for TransactionPropagationMode {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 Self::Sqrt => write!(f, "sqrt"),
99 Self::All => write!(f, "all"),
100 Self::Max(max) => write!(f, "max:{max}"),
101 }
102 }
103}
104
105impl FromStr for TransactionPropagationMode {
106 type Err = String;
107
108 fn from_str(s: &str) -> Result<Self, Self::Err> {
109 let s = s.to_lowercase();
110 match s.as_str() {
111 "sqrt" => Ok(Self::Sqrt),
112 "all" => Ok(Self::All),
113 s => {
114 if let Some(num) = s.strip_prefix("max:") {
115 num.parse::<usize>()
116 .map(TransactionPropagationMode::Max)
117 .map_err(|_| format!("Invalid number for Max variant: {num}"))
118 } else {
119 Err(format!("Invalid transaction propagation mode: {s}"))
120 }
121 }
122 }
123 }
124}
125
126#[derive(Debug, Constructor, Clone)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
129pub struct TransactionFetcherConfig {
130 pub max_inflight_requests: u32,
132 pub max_inflight_requests_per_peer: u8,
135 pub soft_limit_byte_size_pooled_transactions_response: usize,
140 pub soft_limit_byte_size_pooled_transactions_response_on_pack_request: usize,
144 pub max_capacity_cache_txns_pending_fetch: u32,
149}
150
151impl Default for TransactionFetcherConfig {
152 fn default() -> Self {
153 Self {
154 max_inflight_requests: DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS,
155 max_inflight_requests_per_peer: DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS_PER_PEER,
156 soft_limit_byte_size_pooled_transactions_response:
157 SOFT_LIMIT_BYTE_SIZE_POOLED_TRANSACTIONS_RESPONSE,
158 soft_limit_byte_size_pooled_transactions_response_on_pack_request:
159 DEFAULT_SOFT_LIMIT_BYTE_SIZE_POOLED_TRANSACTIONS_RESP_ON_PACK_GET_POOLED_TRANSACTIONS_REQ,
160 max_capacity_cache_txns_pending_fetch: DEFAULT_MAX_CAPACITY_CACHE_PENDING_FETCH,
161 }
162 }
163}
164
165pub trait TransactionPropagationPolicy<N: NetworkPrimitives>:
167 Send + Sync + Unpin + fmt::Debug + 'static
168{
169 fn can_propagate(&self, peer: &mut PeerMetadata<N>) -> bool;
173
174 fn on_session_established(&mut self, peer: &mut PeerMetadata<N>);
176
177 fn on_session_closed(&mut self, peer: &mut PeerMetadata<N>);
179}
180
181#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Display)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
184pub enum TransactionPropagationKind {
185 #[default]
189 All,
190 Trusted,
192 None,
194}
195
196impl<N: NetworkPrimitives> TransactionPropagationPolicy<N> for TransactionPropagationKind {
197 fn can_propagate(&self, peer: &mut PeerMetadata<N>) -> bool {
198 match self {
199 Self::All => true,
200 Self::Trusted => peer.peer_kind.is_trusted(),
201 Self::None => false,
202 }
203 }
204
205 fn on_session_established(&mut self, _peer: &mut PeerMetadata<N>) {}
206
207 fn on_session_closed(&mut self, _peer: &mut PeerMetadata<N>) {}
208}
209
210impl FromStr for TransactionPropagationKind {
211 type Err = String;
212
213 fn from_str(s: &str) -> Result<Self, Self::Err> {
214 match s {
215 "All" | "all" => Ok(Self::All),
216 "Trusted" | "trusted" => Ok(Self::Trusted),
217 "None" | "none" => Ok(Self::None),
218 _ => Err(format!("Invalid transaction propagation policy: {s}")),
219 }
220 }
221}
222
223#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Display)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226pub enum TransactionIngressPolicy {
227 #[default]
229 All,
230 Trusted,
232 None,
234}
235
236impl TransactionIngressPolicy {
237 pub const fn allows(&self, peer_kind: PeerKind) -> bool {
239 match self {
240 Self::All => true,
241 Self::Trusted => peer_kind.is_trusted(),
242 Self::None => false,
243 }
244 }
245
246 pub const fn allows_all(&self) -> bool {
248 matches!(self, Self::All)
249 }
250}
251
252impl FromStr for TransactionIngressPolicy {
253 type Err = String;
254
255 fn from_str(s: &str) -> Result<Self, Self::Err> {
256 match s {
257 "All" | "all" => Ok(Self::All),
258 "Trusted" | "trusted" => Ok(Self::Trusted),
259 "None" | "none" => Ok(Self::None),
260 _ => Err(format!("Invalid transaction ingress policy: {s}")),
261 }
262 }
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269pub enum AnnouncementAcceptance {
270 Accept,
272 Ignore,
274 Reject {
276 penalize_peer: bool,
278 },
279}
280
281pub trait AnnouncementFilteringPolicy<N: NetworkPrimitives>:
284 Send + Sync + Unpin + fmt::Debug + 'static
285{
286 fn decide_on_announcement(&self, ty: u8, hash: &B256, size: usize) -> AnnouncementAcceptance;
288}
289
290#[derive(Debug, Clone, Default)]
293#[non_exhaustive]
294pub struct TypedStrictFilter;
295
296impl<N: NetworkPrimitives> AnnouncementFilteringPolicy<N> for TypedStrictFilter {
297 fn decide_on_announcement(&self, ty: u8, hash: &B256, size: usize) -> AnnouncementAcceptance {
298 if N::PooledTransaction::is_type(ty) {
299 AnnouncementAcceptance::Accept
300 } else {
301 tracing::trace!(target: "net::tx::policy::strict_typed",
302 %ty,
303 %size,
304 %hash,
305 "Invalid or unrecognized transaction type byte. Rejecting entry and recommending peer penalization."
306 );
307 AnnouncementAcceptance::Reject { penalize_peer: true }
308 }
309 }
310}
311
312pub type StrictEthAnnouncementFilter = TypedStrictFilter;
314
315#[derive(Debug, Clone, Default)]
320#[non_exhaustive]
321pub struct TypedRelaxedFilter;
322
323impl<N: NetworkPrimitives> AnnouncementFilteringPolicy<N> for TypedRelaxedFilter {
324 fn decide_on_announcement(&self, ty: u8, hash: &B256, size: usize) -> AnnouncementAcceptance {
325 if N::PooledTransaction::is_type(ty) {
326 AnnouncementAcceptance::Accept
327 } else {
328 tracing::trace!(target: "net::tx::policy::relaxed_typed",
329 %ty,
330 %size,
331 %hash,
332 "Unknown transaction type byte. Ignoring entry."
333 );
334 AnnouncementAcceptance::Ignore
335 }
336 }
337}
338
339pub type RelaxedEthAnnouncementFilter = TypedRelaxedFilter;
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_transaction_propagation_mode_from_str() {
349 assert_eq!(
351 TransactionPropagationMode::from_str("sqrt").unwrap(),
352 TransactionPropagationMode::Sqrt
353 );
354 assert_eq!(
355 TransactionPropagationMode::from_str("SQRT").unwrap(),
356 TransactionPropagationMode::Sqrt
357 );
358 assert_eq!(
359 TransactionPropagationMode::from_str("Sqrt").unwrap(),
360 TransactionPropagationMode::Sqrt
361 );
362
363 assert_eq!(
365 TransactionPropagationMode::from_str("all").unwrap(),
366 TransactionPropagationMode::All
367 );
368 assert_eq!(
369 TransactionPropagationMode::from_str("ALL").unwrap(),
370 TransactionPropagationMode::All
371 );
372 assert_eq!(
373 TransactionPropagationMode::from_str("All").unwrap(),
374 TransactionPropagationMode::All
375 );
376
377 assert_eq!(
379 TransactionPropagationMode::from_str("max:10").unwrap(),
380 TransactionPropagationMode::Max(10)
381 );
382 assert_eq!(
383 TransactionPropagationMode::from_str("MAX:42").unwrap(),
384 TransactionPropagationMode::Max(42)
385 );
386 assert_eq!(
387 TransactionPropagationMode::from_str("Max:100").unwrap(),
388 TransactionPropagationMode::Max(100)
389 );
390
391 assert!(TransactionPropagationMode::from_str("invalid").is_err());
393 assert!(TransactionPropagationMode::from_str("max:not_a_number").is_err());
394 assert!(TransactionPropagationMode::from_str("max:").is_err());
395 assert!(TransactionPropagationMode::from_str("max").is_err());
396 assert!(TransactionPropagationMode::from_str("").is_err());
397 }
398}