Skip to main content

reth_network/
metrics.rs

1use metrics::Histogram;
2use reth_eth_wire::DisconnectReason;
3use reth_ethereum_primitives::TxType;
4use reth_metrics::{
5    metrics::{Counter, Gauge},
6    Metrics,
7};
8
9/// Scope for monitoring transactions sent from the manager to the tx manager
10pub(crate) const NETWORK_POOL_TRANSACTIONS_SCOPE: &str = "network.pool.transactions";
11
12/// Metrics for the entire network, handled by `NetworkManager`
13#[derive(Metrics)]
14#[metrics(scope = "network")]
15pub struct NetworkMetrics {
16    /// Number of currently connected peers
17    pub(crate) connected_peers: Gauge,
18
19    /// Number of currently backed off peers
20    pub(crate) backed_off_peers: Gauge,
21
22    /// Number of peers known to the node
23    pub(crate) tracked_peers: Gauge,
24
25    /// Number of active incoming connections
26    pub(crate) incoming_connections: Gauge,
27
28    /// Number of active outgoing connections
29    pub(crate) outgoing_connections: Gauge,
30
31    /// Number of currently pending outgoing connections
32    pub(crate) pending_outgoing_connections: Gauge,
33
34    /// Total number of pending connections, incoming and outgoing.
35    pub(crate) total_pending_connections: Gauge,
36
37    /// Total Number of incoming connections handled
38    pub(crate) total_incoming_connections: Counter,
39
40    /// Total Number of outgoing connections established
41    pub(crate) total_outgoing_connections: Counter,
42
43    /// Number of invalid/malformed messages received from peers
44    pub(crate) invalid_messages_received: Counter,
45
46    /// Number of Eth Requests dropped due to channel being at full capacity
47    pub(crate) total_dropped_eth_requests_at_full_capacity: Counter,
48
49    /* ================ POLL DURATION ================ */
50
51    /* -- Total poll duration of `NetworksManager` future -- */
52    /// Duration in seconds of call to
53    /// [`NetworkManager`](crate::NetworkManager)'s poll function.
54    ///
55    /// True duration of this call, should be sum of the accumulated durations of calling nested
56    // items.
57    pub(crate) duration_poll_network_manager: Gauge,
58
59    /* -- Poll duration of items nested in `NetworkManager` future -- */
60    /// Time spent streaming messages sent over the [`NetworkHandle`](crate::NetworkHandle), which
61    /// can be cloned and shared via [`NetworkManager::handle`](crate::NetworkManager::handle), in
62    /// one call to poll the [`NetworkManager`](crate::NetworkManager) future. At least
63    /// [`TransactionsManager`](crate::transactions::TransactionsManager) holds this handle.
64    ///
65    /// Duration in seconds.
66    pub(crate) acc_duration_poll_network_handle: Gauge,
67    /// Time spent polling [`Swarm`](crate::swarm::Swarm), in one call to poll the
68    /// [`NetworkManager`](crate::NetworkManager) future.
69    ///
70    /// Duration in seconds.
71    pub(crate) acc_duration_poll_swarm: Gauge,
72}
73
74/// Metrics for closed sessions, split by direction.
75#[derive(Debug)]
76pub struct ClosedSessionsMetrics {
77    /// Sessions closed from active (established) connections.
78    pub active: Counter,
79    /// Sessions closed from incoming pending connections.
80    pub incoming_pending: Counter,
81    /// Sessions closed from outgoing pending connections.
82    pub outgoing_pending: Counter,
83}
84
85impl Default for ClosedSessionsMetrics {
86    fn default() -> Self {
87        Self {
88            active: metrics::counter!("network_closed_sessions", "direction" => "active"),
89            incoming_pending: metrics::counter!("network_closed_sessions", "direction" => "incoming_pending"),
90            outgoing_pending: metrics::counter!("network_closed_sessions", "direction" => "outgoing_pending"),
91        }
92    }
93}
94
95/// Metrics for pending session failures, split by direction.
96#[derive(Debug)]
97pub struct PendingSessionFailureMetrics {
98    /// Failures on incoming pending sessions.
99    pub inbound: Counter,
100    /// Failures on outgoing pending sessions.
101    pub outbound: Counter,
102}
103
104impl Default for PendingSessionFailureMetrics {
105    fn default() -> Self {
106        Self {
107            inbound: metrics::counter!("network_pending_session_failures", "direction" => "inbound"),
108            outbound: metrics::counter!("network_pending_session_failures", "direction" => "outbound"),
109        }
110    }
111}
112
113/// Metrics for backed off peers, split by reason.
114#[derive(Metrics)]
115#[metrics(scope = "network.backed_off_peers")]
116pub struct BackedOffPeersMetrics {
117    /// Peers backed off because they reported too many peers.
118    pub too_many_peers: Counter,
119    /// Peers backed off after a graceful session close.
120    pub graceful_close: Counter,
121    /// Peers backed off due to connection or protocol errors.
122    pub connection_error: Counter,
123}
124
125impl BackedOffPeersMetrics {
126    /// Increments the counter for the given backoff reason.
127    pub fn increment_for_reason(&self, reason: crate::peers::BackoffReason) {
128        match reason {
129            crate::peers::BackoffReason::TooManyPeers => self.too_many_peers.increment(1),
130            crate::peers::BackoffReason::GracefulClose => self.graceful_close.increment(1),
131            crate::peers::BackoffReason::ConnectionError => self.connection_error.increment(1),
132        }
133    }
134}
135
136/// Metrics for `SessionManager`
137#[derive(Metrics)]
138#[metrics(scope = "network")]
139pub struct SessionManagerMetrics {
140    /// Number of successful outgoing dial attempts.
141    pub(crate) total_dial_successes: Counter,
142    /// Number of dropped outgoing peer messages.
143    pub(crate) total_outgoing_peer_messages_dropped: Counter,
144    /// Number of queued outgoing messages
145    pub(crate) queued_outgoing_messages: Gauge,
146}
147
148/// Metrics for the [`TransactionsManager`](crate::transactions::TransactionsManager).
149#[derive(Metrics)]
150#[metrics(scope = "network")]
151pub struct TransactionsManagerMetrics {
152    /* ================ BROADCAST ================ */
153    /// Total number of propagated transactions
154    pub(crate) propagated_transactions: Counter,
155    /// Total number of reported bad transactions
156    pub(crate) reported_bad_transactions: Counter,
157
158    /* -- Freq txns already marked as seen by peer -- */
159    /// Total number of messages from a peer, announcing transactions that have already been
160    /// marked as seen by that peer.
161    pub(crate) messages_with_hashes_already_seen_by_peer: Counter,
162    /// Total number of messages from a peer, with transaction that have already been marked as
163    /// seen by that peer.
164    pub(crate) messages_with_transactions_already_seen_by_peer: Counter,
165    /// Total number of occurrences, of a peer announcing a transaction that has already been
166    /// marked as seen by that peer.
167    pub(crate) occurrences_hash_already_seen_by_peer: Counter,
168    /// Total number of times a transaction is seen from a peer, that has already been marked as
169    /// seen by that peer.
170    pub(crate) occurrences_of_transaction_already_seen_by_peer: Counter,
171
172    /* -- Freq txns already in pool -- */
173    /// Total number of times a hash is announced that is already in the local pool.
174    pub(crate) occurrences_hashes_already_in_pool: Counter,
175    /// Total number of times a transaction is sent that is already in the local pool.
176    pub(crate) occurrences_transactions_already_in_pool: Counter,
177
178    /* ================ POOL IMPORTS ================ */
179    /// Number of transactions about to be imported into the pool.
180    pub(crate) pending_pool_imports: Gauge,
181    /// Total number of bad imports, imports that fail because the transaction is badly formed
182    /// (i.e. have no chance of passing validation, unlike imports that fail due to e.g. nonce
183    /// gaps).
184    pub(crate) bad_imports: Counter,
185    /// Number of inflight requests at which the
186    /// [`TransactionPool`](reth_transaction_pool::TransactionPool) is considered to be at
187    /// capacity. Note, this is not a limit to the number of inflight requests, but a health
188    /// measure.
189    pub(crate) capacity_pending_pool_imports: Counter,
190    /// Total number of transactions ignored because pending pool imports are at capacity.
191    pub(crate) skipped_transactions_pending_pool_imports_at_capacity: Counter,
192    /// The time it took to prepare transactions for import. This is mostly sender recovery.
193    pub(crate) pool_import_prepare_duration: Histogram,
194
195    /* ================ POLL DURATION ================ */
196
197    /* -- Total poll duration of `TransactionsManager` future -- */
198    /// Duration in seconds of call to
199    /// [`TransactionsManager`](crate::transactions::TransactionsManager)'s poll function.
200    ///
201    /// Updating metrics could take time, so the true duration of this call could
202    /// be longer than the sum of the accumulated durations of polling nested items.
203    pub(crate) duration_poll_tx_manager: Gauge,
204
205    /* -- Poll duration of items nested in `TransactionsManager` future -- */
206    /// Accumulated time spent streaming session updates and updating peers accordingly, in
207    /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
208    /// future.
209    ///
210    /// Duration in seconds.
211    pub(crate) acc_duration_poll_network_events: Gauge,
212    /// Accumulated time spent flushing the queue of batched pending pool imports into pool, in
213    /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
214    /// future.
215    ///
216    /// Duration in seconds.
217    pub(crate) acc_duration_poll_pending_pool_imports: Gauge,
218    /// Accumulated time spent streaming transaction and announcement broadcast, queueing for
219    /// pool import or requesting respectively, in one call to poll the
220    /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
221    ///
222    /// Duration in seconds.
223    pub(crate) acc_duration_poll_transaction_events: Gauge,
224    /// Accumulated time spent streaming fetch events, queueing for pool import on successful
225    /// fetch, in one call to poll the
226    /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
227    ///
228    /// Duration in seconds.
229    pub(crate) acc_duration_poll_fetch_events: Gauge,
230    /// Accumulated time spent streaming and propagating transactions that were successfully
231    /// imported into the pool, in one call to poll the
232    /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
233    ///
234    /// Duration in seconds.
235    pub(crate) acc_duration_poll_imported_transactions: Gauge,
236    /// Accumulated time spent assembling and sending requests for hashes fetching pending, in
237    /// one call to poll the [`TransactionsManager`](crate::transactions::TransactionsManager)
238    /// future.
239    ///
240    /// Duration in seconds.
241    pub(crate) acc_duration_fetch_pending_hashes: Gauge,
242    /// Accumulated time spent streaming commands and propagating, fetching and serving
243    /// transactions accordingly, in one call to poll the
244    /// [`TransactionsManager`](crate::transactions::TransactionsManager) future.
245    ///
246    /// Duration in seconds.
247    pub(crate) acc_duration_poll_commands: Gauge,
248}
249
250/// Metrics for the [`TransactionsManager`](crate::transactions::TransactionsManager).
251#[derive(Metrics)]
252#[metrics(scope = "network")]
253pub struct TransactionFetcherMetrics {
254    /// Currently active outgoing [`GetPooledTransactions`](reth_eth_wire::GetPooledTransactions)
255    /// requests.
256    pub(crate) inflight_transaction_requests: Gauge,
257    /// Number of inflight requests at which the
258    /// [`TransactionFetcher`](crate::transactions::TransactionFetcher) is considered to be at
259    /// capacity. Note, this is not a limit to the number of inflight requests, but a health
260    /// measure.
261    pub(crate) capacity_inflight_requests: Counter,
262    /// Hashes in currently active outgoing
263    /// [`GetPooledTransactions`](reth_eth_wire::GetPooledTransactions) requests.
264    pub(crate) hashes_inflight_transaction_requests: Gauge,
265    /// How often we failed to send a request to the peer because the channel was full.
266    pub(crate) egress_peer_channel_full: Counter,
267    /// Total number of hashes pending fetch.
268    pub(crate) hashes_pending_fetch: Gauge,
269    /// Total number of fetched transactions.
270    pub(crate) fetched_transactions: Counter,
271    /// Total number of transactions that were received in
272    /// [`PooledTransactions`](reth_eth_wire::PooledTransactions) responses, that weren't
273    /// requested.
274    pub(crate) unsolicited_transactions: Counter,
275    /* ================ SEARCH DURATION ================ */
276    /// Time spent searching for an idle peer in call to
277    /// [`TransactionFetcher::find_any_idle_fallback_peer_for_any_pending_hash`](crate::transactions::TransactionFetcher::find_any_idle_fallback_peer_for_any_pending_hash).
278    ///
279    /// Duration in seconds.
280    pub(crate) duration_find_idle_fallback_peer_for_any_pending_hash: Gauge,
281
282    /// Time spent searching for hashes pending fetch, announced by a given peer in
283    /// [`TransactionFetcher::fill_request_from_hashes_pending_fetch`](crate::transactions::TransactionFetcher::fill_request_from_hashes_pending_fetch).
284    ///
285    /// Duration in seconds.
286    pub(crate) duration_fill_request_from_hashes_pending_fetch: Gauge,
287}
288
289/// Measures the duration of executing the given code block. The duration is added to the given
290/// accumulator value passed as a mutable reference.
291#[macro_export]
292macro_rules! duration_metered_exec {
293    ($code:expr, $acc:expr) => {{
294        let start = std::time::Instant::now();
295
296        let res = $code;
297
298        $acc += start.elapsed();
299
300        res
301    }};
302}
303
304/// Direction-aware wrapper for disconnect metrics.
305///
306/// Tracks disconnect reasons for inbound and outbound connections separately, in addition to
307/// the combined (legacy) counters.
308#[derive(Debug, Default)]
309pub(crate) struct DirectionalDisconnectMetrics {
310    /// Combined disconnect metrics (all directions).
311    pub(crate) total: DisconnectMetrics,
312    /// Disconnect metrics for inbound connections only.
313    pub(crate) inbound: InboundDisconnectMetrics,
314    /// Disconnect metrics for outbound connections only.
315    pub(crate) outbound: OutboundDisconnectMetrics,
316}
317
318impl DirectionalDisconnectMetrics {
319    /// Increments disconnect counters for an inbound connection.
320    pub(crate) fn increment_inbound(&self, reason: DisconnectReason) {
321        self.total.increment(reason);
322        self.inbound.increment(reason);
323    }
324
325    /// Increments disconnect counters for an outbound connection.
326    pub(crate) fn increment_outbound(&self, reason: DisconnectReason) {
327        self.total.increment(reason);
328        self.outbound.increment(reason);
329    }
330}
331
332/// Metrics for Disconnection types
333///
334/// These are just counters, and ideally we would implement these metrics on a peer-by-peer basis,
335/// in that we do not double-count peers for `TooManyPeers` if we make an outgoing connection and
336/// get disconnected twice
337#[derive(Metrics)]
338#[metrics(scope = "network")]
339pub struct DisconnectMetrics {
340    /// Number of peer disconnects due to `DisconnectRequested` (0x00)
341    pub(crate) disconnect_requested: Counter,
342
343    /// Number of peer disconnects due to `TcpSubsystemError` (0x01)
344    pub(crate) tcp_subsystem_error: Counter,
345
346    /// Number of peer disconnects due to `ProtocolBreach` (0x02)
347    pub(crate) protocol_breach: Counter,
348
349    /// Number of peer disconnects due to `UselessPeer` (0x03)
350    pub(crate) useless_peer: Counter,
351
352    /// Number of peer disconnects due to `TooManyPeers` (0x04)
353    pub(crate) too_many_peers: Counter,
354
355    /// Number of peer disconnects due to `AlreadyConnected` (0x05)
356    pub(crate) already_connected: Counter,
357
358    /// Number of peer disconnects due to `IncompatibleP2PProtocolVersion` (0x06)
359    pub(crate) incompatible: Counter,
360
361    /// Number of peer disconnects due to `NullNodeIdentity` (0x07)
362    pub(crate) null_node_identity: Counter,
363
364    /// Number of peer disconnects due to `ClientQuitting` (0x08)
365    pub(crate) client_quitting: Counter,
366
367    /// Number of peer disconnects due to `UnexpectedHandshakeIdentity` (0x09)
368    pub(crate) unexpected_identity: Counter,
369
370    /// Number of peer disconnects due to `ConnectedToSelf` (0x0a)
371    pub(crate) connected_to_self: Counter,
372
373    /// Number of peer disconnects due to `PingTimeout` (0x0b)
374    pub(crate) ping_timeout: Counter,
375
376    /// Number of peer disconnects due to `SubprotocolSpecific` (0x10)
377    pub(crate) subprotocol_specific: Counter,
378}
379
380impl DisconnectMetrics {
381    /// Increments the proper counter for the given disconnect reason
382    pub(crate) fn increment(&self, reason: DisconnectReason) {
383        match reason {
384            DisconnectReason::DisconnectRequested => self.disconnect_requested.increment(1),
385            DisconnectReason::TcpSubsystemError => self.tcp_subsystem_error.increment(1),
386            DisconnectReason::ProtocolBreach => self.protocol_breach.increment(1),
387            DisconnectReason::UselessPeer => self.useless_peer.increment(1),
388            DisconnectReason::TooManyPeers => self.too_many_peers.increment(1),
389            DisconnectReason::AlreadyConnected => self.already_connected.increment(1),
390            DisconnectReason::IncompatibleP2PProtocolVersion => self.incompatible.increment(1),
391            DisconnectReason::NullNodeIdentity => self.null_node_identity.increment(1),
392            DisconnectReason::ClientQuitting => self.client_quitting.increment(1),
393            DisconnectReason::UnexpectedHandshakeIdentity => self.unexpected_identity.increment(1),
394            DisconnectReason::ConnectedToSelf => self.connected_to_self.increment(1),
395            DisconnectReason::PingTimeout => self.ping_timeout.increment(1),
396            DisconnectReason::SubprotocolSpecific => self.subprotocol_specific.increment(1),
397        }
398    }
399}
400
401/// Disconnect metrics scoped to inbound connections only.
402///
403/// These counters track disconnect reasons exclusively for sessions that were initiated by
404/// remote peers connecting to this node. This helps operators distinguish between being rejected
405/// by remote peers (outbound) vs rejecting incoming peers (inbound).
406#[derive(Metrics)]
407#[metrics(scope = "network.inbound")]
408pub struct InboundDisconnectMetrics {
409    /// Number of inbound peer disconnects due to `DisconnectRequested` (0x00)
410    pub(crate) disconnect_requested: Counter,
411
412    /// Number of inbound peer disconnects due to `TcpSubsystemError` (0x01)
413    pub(crate) tcp_subsystem_error: Counter,
414
415    /// Number of inbound peer disconnects due to `ProtocolBreach` (0x02)
416    pub(crate) protocol_breach: Counter,
417
418    /// Number of inbound peer disconnects due to `UselessPeer` (0x03)
419    pub(crate) useless_peer: Counter,
420
421    /// Number of inbound peer disconnects due to `TooManyPeers` (0x04)
422    pub(crate) too_many_peers: Counter,
423
424    /// Number of inbound peer disconnects due to `AlreadyConnected` (0x05)
425    pub(crate) already_connected: Counter,
426
427    /// Number of inbound peer disconnects due to `IncompatibleP2PProtocolVersion` (0x06)
428    pub(crate) incompatible: Counter,
429
430    /// Number of inbound peer disconnects due to `NullNodeIdentity` (0x07)
431    pub(crate) null_node_identity: Counter,
432
433    /// Number of inbound peer disconnects due to `ClientQuitting` (0x08)
434    pub(crate) client_quitting: Counter,
435
436    /// Number of inbound peer disconnects due to `UnexpectedHandshakeIdentity` (0x09)
437    pub(crate) unexpected_identity: Counter,
438
439    /// Number of inbound peer disconnects due to `ConnectedToSelf` (0x0a)
440    pub(crate) connected_to_self: Counter,
441
442    /// Number of inbound peer disconnects due to `PingTimeout` (0x0b)
443    pub(crate) ping_timeout: Counter,
444
445    /// Number of inbound peer disconnects due to `SubprotocolSpecific` (0x10)
446    pub(crate) subprotocol_specific: Counter,
447}
448
449impl InboundDisconnectMetrics {
450    /// Increments the proper counter for the given disconnect reason
451    pub(crate) fn increment(&self, reason: DisconnectReason) {
452        match reason {
453            DisconnectReason::DisconnectRequested => self.disconnect_requested.increment(1),
454            DisconnectReason::TcpSubsystemError => self.tcp_subsystem_error.increment(1),
455            DisconnectReason::ProtocolBreach => self.protocol_breach.increment(1),
456            DisconnectReason::UselessPeer => self.useless_peer.increment(1),
457            DisconnectReason::TooManyPeers => self.too_many_peers.increment(1),
458            DisconnectReason::AlreadyConnected => self.already_connected.increment(1),
459            DisconnectReason::IncompatibleP2PProtocolVersion => self.incompatible.increment(1),
460            DisconnectReason::NullNodeIdentity => self.null_node_identity.increment(1),
461            DisconnectReason::ClientQuitting => self.client_quitting.increment(1),
462            DisconnectReason::UnexpectedHandshakeIdentity => self.unexpected_identity.increment(1),
463            DisconnectReason::ConnectedToSelf => self.connected_to_self.increment(1),
464            DisconnectReason::PingTimeout => self.ping_timeout.increment(1),
465            DisconnectReason::SubprotocolSpecific => self.subprotocol_specific.increment(1),
466        }
467    }
468}
469
470/// Disconnect metrics scoped to outbound connections only.
471///
472/// These counters track disconnect reasons exclusively for sessions that this node initiated
473/// by dialing out to remote peers. A high `too_many_peers` count here indicates remote peers
474/// are rejecting our connection attempts because they are full.
475#[derive(Metrics)]
476#[metrics(scope = "network.outbound")]
477pub struct OutboundDisconnectMetrics {
478    /// Number of outbound peer disconnects due to `DisconnectRequested` (0x00)
479    pub(crate) disconnect_requested: Counter,
480
481    /// Number of outbound peer disconnects due to `TcpSubsystemError` (0x01)
482    pub(crate) tcp_subsystem_error: Counter,
483
484    /// Number of outbound peer disconnects due to `ProtocolBreach` (0x02)
485    pub(crate) protocol_breach: Counter,
486
487    /// Number of outbound peer disconnects due to `UselessPeer` (0x03)
488    pub(crate) useless_peer: Counter,
489
490    /// Number of outbound peer disconnects due to `TooManyPeers` (0x04)
491    pub(crate) too_many_peers: Counter,
492
493    /// Number of outbound peer disconnects due to `AlreadyConnected` (0x05)
494    pub(crate) already_connected: Counter,
495
496    /// Number of outbound peer disconnects due to `IncompatibleP2PProtocolVersion` (0x06)
497    pub(crate) incompatible: Counter,
498
499    /// Number of outbound peer disconnects due to `NullNodeIdentity` (0x07)
500    pub(crate) null_node_identity: Counter,
501
502    /// Number of outbound peer disconnects due to `ClientQuitting` (0x08)
503    pub(crate) client_quitting: Counter,
504
505    /// Number of outbound peer disconnects due to `UnexpectedHandshakeIdentity` (0x09)
506    pub(crate) unexpected_identity: Counter,
507
508    /// Number of outbound peer disconnects due to `ConnectedToSelf` (0x0a)
509    pub(crate) connected_to_self: Counter,
510
511    /// Number of outbound peer disconnects due to `PingTimeout` (0x0b)
512    pub(crate) ping_timeout: Counter,
513
514    /// Number of outbound peer disconnects due to `SubprotocolSpecific` (0x10)
515    pub(crate) subprotocol_specific: Counter,
516}
517
518impl OutboundDisconnectMetrics {
519    /// Increments the proper counter for the given disconnect reason
520    pub(crate) fn increment(&self, reason: DisconnectReason) {
521        match reason {
522            DisconnectReason::DisconnectRequested => self.disconnect_requested.increment(1),
523            DisconnectReason::TcpSubsystemError => self.tcp_subsystem_error.increment(1),
524            DisconnectReason::ProtocolBreach => self.protocol_breach.increment(1),
525            DisconnectReason::UselessPeer => self.useless_peer.increment(1),
526            DisconnectReason::TooManyPeers => self.too_many_peers.increment(1),
527            DisconnectReason::AlreadyConnected => self.already_connected.increment(1),
528            DisconnectReason::IncompatibleP2PProtocolVersion => self.incompatible.increment(1),
529            DisconnectReason::NullNodeIdentity => self.null_node_identity.increment(1),
530            DisconnectReason::ClientQuitting => self.client_quitting.increment(1),
531            DisconnectReason::UnexpectedHandshakeIdentity => self.unexpected_identity.increment(1),
532            DisconnectReason::ConnectedToSelf => self.connected_to_self.increment(1),
533            DisconnectReason::PingTimeout => self.ping_timeout.increment(1),
534            DisconnectReason::SubprotocolSpecific => self.subprotocol_specific.increment(1),
535        }
536    }
537}
538
539/// Metrics for the `EthRequestHandler`
540#[derive(Metrics)]
541#[metrics(scope = "network")]
542pub struct EthRequestHandlerMetrics {
543    /// Number of `GetBlockHeaders` requests received
544    pub(crate) eth_headers_requests_received_total: Counter,
545
546    /// Number of `GetReceipts` requests received
547    pub(crate) eth_receipts_requests_received_total: Counter,
548
549    /// Number of `GetBlockBodies` requests received
550    pub(crate) eth_bodies_requests_received_total: Counter,
551
552    /// Number of `GetNodeData` requests received
553    pub(crate) eth_node_data_requests_received_total: Counter,
554
555    /// Duration in seconds of call to poll
556    /// [`EthRequestHandler`](crate::eth_requests::EthRequestHandler).
557    pub(crate) acc_duration_poll_eth_req_handler: Gauge,
558}
559
560/// Eth67 announcement metrics, track entries by `TxType`
561#[derive(Metrics)]
562#[metrics(scope = "network.transaction_fetcher")]
563pub struct AnnouncedTxTypesMetrics {
564    /// Histogram for tracking frequency of legacy transaction type
565    pub(crate) legacy: Histogram,
566
567    /// Histogram for tracking frequency of EIP-2930 transaction type
568    pub(crate) eip2930: Histogram,
569
570    /// Histogram for tracking frequency of EIP-1559 transaction type
571    pub(crate) eip1559: Histogram,
572
573    /// Histogram for tracking frequency of EIP-4844 transaction type
574    pub(crate) eip4844: Histogram,
575
576    /// Histogram for tracking frequency of EIP-7702 transaction type
577    pub(crate) eip7702: Histogram,
578}
579
580/// Counts the number of transactions by their type in a block or collection.
581///
582/// This struct keeps track of the count of different transaction types
583/// as defined by various Ethereum Improvement Proposals (EIPs).
584#[derive(Debug, Default)]
585pub struct TxTypesCounter {
586    /// Count of legacy transactions (pre-EIP-2718).
587    pub(crate) legacy: usize,
588
589    /// Count of transactions conforming to EIP-2930 (Optional access lists).
590    pub(crate) eip2930: usize,
591
592    /// Count of transactions conforming to EIP-1559 (Fee market change).
593    pub(crate) eip1559: usize,
594
595    /// Count of transactions conforming to EIP-4844 (Shard Blob Transactions).
596    pub(crate) eip4844: usize,
597
598    /// Count of transactions conforming to EIP-7702 (Restricted Storage Windows).
599    pub(crate) eip7702: usize,
600}
601
602impl TxTypesCounter {
603    pub(crate) const fn increase_by_tx_type(&mut self, tx_type: TxType) {
604        match tx_type {
605            TxType::Legacy => {
606                self.legacy += 1;
607            }
608            TxType::Eip2930 => {
609                self.eip2930 += 1;
610            }
611            TxType::Eip1559 => {
612                self.eip1559 += 1;
613            }
614            TxType::Eip4844 => {
615                self.eip4844 += 1;
616            }
617            TxType::Eip7702 => {
618                self.eip7702 += 1;
619            }
620        }
621    }
622}
623
624impl AnnouncedTxTypesMetrics {
625    /// Update metrics during announcement validation, by examining each announcement entry based on
626    /// `TxType`
627    pub(crate) fn update_eth68_announcement_metrics(&self, tx_types_counter: TxTypesCounter) {
628        self.legacy.record(tx_types_counter.legacy as f64);
629        self.eip2930.record(tx_types_counter.eip2930 as f64);
630        self.eip1559.record(tx_types_counter.eip1559 as f64);
631        self.eip4844.record(tx_types_counter.eip4844 as f64);
632        self.eip7702.record(tx_types_counter.eip7702 as f64);
633    }
634}