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