Skip to main content

ckb_rpc/module/
stats.rs

1use async_trait::async_trait;
2use ckb_jsonrpc_types::{AlertMessage, ChainInfo, DeploymentInfo, DeploymentPos, DeploymentsInfo};
3use ckb_network_alert::notifier::Notifier as AlertNotifier;
4use ckb_shared::shared::Shared;
5use ckb_traits::HeaderFieldsProvider;
6use ckb_util::Mutex;
7use jsonrpc_core::Result;
8use jsonrpc_utils::rpc;
9use std::collections::BTreeMap;
10use std::sync::Arc;
11
12/// RPC Module Stats for getting various statistic data.
13#[rpc(openrpc)]
14#[async_trait]
15pub trait StatsRpc {
16    /// Returns statistics about the chain.
17    ///
18    /// ## Examples
19    ///
20    /// Request
21    ///
22    /// ```json
23    /// {
24    ///   "id": 42,
25    ///   "jsonrpc": "2.0",
26    ///   "method": "get_blockchain_info",
27    ///   "params": []
28    /// }
29    /// ```
30    ///
31    /// Response
32    ///
33    /// ```json
34    /// {
35    ///   "id": 42,
36    ///   "jsonrpc": "2.0",
37    ///   "result": {
38    ///     "alerts": [
39    ///       {
40    ///         "id": "0x2a",
41    ///         "message": "An example alert message!",
42    ///         "notice_until": "0x24bcca57c00",
43    ///         "priority": "0x1"
44    ///       }
45    ///     ],
46    ///     "chain": "ckb",
47    ///     "difficulty": "0x1f4003",
48    ///     "epoch": "0x7080018000001",
49    ///     "is_initial_block_download": true,
50    ///     "median_time": "0x5cd2b105"
51    ///   }
52    /// }
53    /// ```
54    #[rpc(name = "get_blockchain_info")]
55    fn get_blockchain_info(&self) -> Result<ChainInfo>;
56
57    /// Returns statistics about the chain.
58    ///
59    /// ## Examples
60    ///
61    /// Request
62    ///
63    /// ```json
64    /// {
65    ///   "id": 42,
66    ///   "jsonrpc": "2.0",
67    ///   "method": "get_deployments_info",
68    ///   "params": []
69    /// }
70    /// ```
71    ///
72    /// Response
73    ///
74    /// ```json
75    /// {
76    ///   "id": 42,
77    ///   "jsonrpc": "2.0",
78    ///   "result": {
79    ///     "epoch": "0x1",
80    ///     "hash": "0xa5f5c85987a15de25661e5a214f2c1449cd803f071acc7999820f25246471f40",
81    ///        "deployments": {
82    ///            "testdummy": {
83    ///                "bit": 1,
84    ///                "min_activation_epoch": "0x0",
85    ///                "period": "0xa",
86    ///                "since": "0x0",
87    ///                "start": "0x0",
88    ///                "state": "failed",
89    ///                "timeout": "0x0",
90    ///                "threshold": {
91    ///                     "numer": "0x3",
92    ///                     "denom": "0x4"
93    ///                 }
94    ///            }
95    ///        }
96    ///   }
97    /// }
98    /// ```
99    #[rpc(name = "get_deployments_info")]
100    fn get_deployments_info(&self) -> Result<DeploymentsInfo>;
101}
102
103#[derive(Clone)]
104pub(crate) struct StatsRpcImpl {
105    pub shared: Shared,
106    pub alert_notifier: Arc<Mutex<AlertNotifier>>,
107}
108
109#[async_trait]
110impl StatsRpc for StatsRpcImpl {
111    fn get_blockchain_info(&self) -> Result<ChainInfo> {
112        let chain = self.shared.consensus().id.clone();
113        let (tip_header, median_time) = {
114            let snapshot = self.shared.snapshot();
115            let tip_header = snapshot.tip_header().clone();
116            let median_time = snapshot.block_median_time(
117                &tip_header.hash(),
118                self.shared.consensus().median_time_block_count(),
119            );
120            (tip_header, median_time)
121        };
122        let epoch = if tip_header.is_genesis() {
123            self.shared
124                .consensus()
125                .genesis_epoch_ext()
126                .number_with_fraction(0)
127        } else {
128            tip_header.epoch()
129        };
130        let difficulty = tip_header.difficulty();
131        let is_initial_block_download = self.shared.is_initial_block_download();
132        let alerts: Vec<AlertMessage> = {
133            let now = ckb_systemtime::unix_time_as_millis();
134            let mut notifier = self.alert_notifier.lock();
135            notifier.clear_expired_alerts(now);
136            notifier
137                .noticed_alerts()
138                .into_iter()
139                .map(Into::into)
140                .collect()
141        };
142
143        Ok(ChainInfo {
144            chain,
145            median_time: median_time.into(),
146            epoch: epoch.into(),
147            difficulty,
148            is_initial_block_download,
149            alerts,
150        })
151    }
152
153    fn get_deployments_info(&self) -> Result<DeploymentsInfo> {
154        let snapshot = self.shared.snapshot();
155        let deployments: BTreeMap<DeploymentPos, DeploymentInfo> = self
156            .shared
157            .consensus()
158            .deployments
159            .clone()
160            .into_iter()
161            .filter_map(|(pos, deployment)| {
162                self.shared
163                    .consensus()
164                    .versionbits_state(pos, snapshot.tip_header(), snapshot.as_ref())
165                    .map(|state| {
166                        let mut info: DeploymentInfo = deployment.into();
167                        info.state = state.into();
168                        if let Some(since) = self.shared.consensus().versionbits_state_since_epoch(
169                            pos,
170                            snapshot.tip_header(),
171                            snapshot.as_ref(),
172                        ) {
173                            info.since = since.into();
174                        }
175                        (pos.into(), info)
176                    })
177            })
178            .collect();
179
180        Ok(DeploymentsInfo {
181            hash: snapshot.tip_hash().into(),
182            epoch: snapshot.tip_header().epoch().number().into(),
183            deployments,
184        })
185    }
186}