forest/networks/
metrics.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::sync::Arc;
5
6use educe::Educe;
7use prometheus_client::{
8    collector::Collector,
9    encoding::{DescriptorEncoder, EncodeMetric},
10    metrics::gauge::Gauge,
11};
12
13use super::calculate_expected_epoch;
14use crate::{networks::ChainConfig, shim::clock::ChainEpoch};
15
16#[derive(Educe)]
17#[educe(Debug)]
18pub struct NetworkHeightCollector<F>
19where
20    F: Fn() -> ChainEpoch,
21{
22    block_delay_secs: u32,
23    genesis_timestamp: u64,
24    #[educe(Debug(ignore))]
25    get_chain_head_height: Arc<F>,
26}
27
28impl<F> NetworkHeightCollector<F>
29where
30    F: Fn() -> ChainEpoch,
31{
32    pub fn new(
33        block_delay_secs: u32,
34        genesis_timestamp: u64,
35        get_chain_head_height: Arc<F>,
36    ) -> Self {
37        Self {
38            block_delay_secs,
39            genesis_timestamp,
40            get_chain_head_height,
41        }
42    }
43}
44
45impl<F> Collector for NetworkHeightCollector<F>
46where
47    F: Fn() -> ChainEpoch + Send + Sync + 'static,
48{
49    fn encode(
50        &self,
51        mut encoder: prometheus_client::encoding::DescriptorEncoder,
52    ) -> Result<(), std::fmt::Error> {
53        {
54            let network_height: Gauge = Default::default();
55            let epoch = (self.get_chain_head_height)();
56            network_height.set(epoch);
57            let metric_encoder = encoder.encode_descriptor(
58                "head_epoch",
59                "Latest epoch synchronized to the node",
60                None,
61                network_height.metric_type(),
62            )?;
63            network_height.encode(metric_encoder)?;
64        }
65        {
66            let expected_network_height: Gauge = Default::default();
67            let expected_epoch = calculate_expected_epoch(
68                chrono::Utc::now().timestamp() as u64,
69                self.genesis_timestamp,
70                self.block_delay_secs,
71            );
72            expected_network_height.set(expected_epoch);
73            let metric_encoder = encoder.encode_descriptor(
74                "expected_network_height",
75                "The expected network height based on the current time and the genesis block time",
76                None,
77                expected_network_height.metric_type(),
78            )?;
79            expected_network_height.encode(metric_encoder)?;
80        }
81        Ok(())
82    }
83}
84
85#[derive(Educe)]
86#[educe(Debug)]
87pub struct NetworkVersionCollector<F1, F2>
88where
89    F1: Fn() -> ChainEpoch,
90    F2: Fn() -> u64,
91{
92    chain_config: Arc<ChainConfig>,
93    #[educe(Debug(ignore))]
94    get_chain_head_height: Arc<F1>,
95    #[educe(Debug(ignore))]
96    get_chain_head_actor_version: Arc<F2>,
97}
98
99impl<F1, F2> NetworkVersionCollector<F1, F2>
100where
101    F1: Fn() -> ChainEpoch,
102    F2: Fn() -> u64,
103{
104    pub fn new(
105        chain_config: Arc<ChainConfig>,
106        get_chain_head_height: Arc<F1>,
107        get_chain_head_actor_version: Arc<F2>,
108    ) -> Self {
109        Self {
110            chain_config,
111            get_chain_head_height,
112            get_chain_head_actor_version,
113        }
114    }
115}
116
117impl<F1, F2> Collector for NetworkVersionCollector<F1, F2>
118where
119    F1: Fn() -> ChainEpoch + Send + Sync + 'static,
120    F2: Fn() -> u64 + Send + Sync + 'static,
121{
122    fn encode(&self, mut encoder: DescriptorEncoder) -> Result<(), std::fmt::Error> {
123        let epoch = (self.get_chain_head_height)();
124        {
125            let network_version = self.chain_config.network_version(epoch);
126            let nv_gauge: Gauge = Default::default();
127            nv_gauge.set(u32::from(network_version) as _);
128            let metric_encoder = encoder.encode_descriptor(
129                "network_version",
130                "Network version of the current chain head",
131                None,
132                nv_gauge.metric_type(),
133            )?;
134            nv_gauge.encode(metric_encoder)?;
135        }
136        {
137            let network_version_revision = self.chain_config.network_version_revision(epoch);
138            let nv_gauge: Gauge = Default::default();
139            nv_gauge.set(network_version_revision);
140            let metric_encoder = encoder.encode_descriptor(
141                "network_version_revision",
142                "Network version revision of the current chain head",
143                None,
144                nv_gauge.metric_type(),
145            )?;
146            nv_gauge.encode(metric_encoder)?;
147        }
148        {
149            let actor_version = (self.get_chain_head_actor_version)();
150            let av_gauge: Gauge = Default::default();
151            av_gauge.set(actor_version as _);
152            let metric_encoder = encoder.encode_descriptor(
153                "actor_version",
154                "Actor version of the current chain head",
155                None,
156                av_gauge.metric_type(),
157            )?;
158            av_gauge.encode(metric_encoder)?;
159        }
160        Ok(())
161    }
162}