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}