mina_node_native/graphql/
constants.rs1use juniper::GraphQLObject;
2use mina_core::{
3 consensus::{ConsensusConstants, ConsensusTime},
4 constants::ConstraintConstants,
5};
6use node::{
7 rpc::{
8 ConsensusTimeQuery, PeerConnectionStatus, RpcConsensusTimeGetResponse,
9 RpcNodeStatusNetworkInfo, RpcPeerInfo, RpcRequest,
10 },
11 BuildEnv,
12};
13
14use super::{Context, ConversionError, Error};
15
16#[derive(Clone, Debug, Copy)]
17pub(crate) struct GraphQLDaemonStatus;
18
19#[juniper::graphql_object(context = Context)]
20impl GraphQLDaemonStatus {
21 async fn consensus_configuration(
22 &self,
23 context: &Context,
24 ) -> juniper::FieldResult<GraphQLConsensusConfiguration> {
25 let consensus_constants: ConsensusConstants = context
26 .rpc_sender
27 .oneshot_request(RpcRequest::ConsensusConstantsGet)
28 .await
29 .ok_or(Error::StateMachineEmptyResponse)?;
30 Ok(GraphQLConsensusConfiguration::from(consensus_constants))
31 }
32
33 async fn peers(&self, context: &Context) -> juniper::FieldResult<Vec<GraphQLRpcPeerInfo>> {
34 let peers: Vec<RpcPeerInfo> = context
35 .rpc_sender
36 .oneshot_request(RpcRequest::PeersGet)
37 .await
38 .ok_or(Error::StateMachineEmptyResponse)?;
39
40 let connected_peers = peers
41 .iter()
42 .filter(|peer| matches!(peer.connection_status, PeerConnectionStatus::Connected))
43 .map(GraphQLRpcPeerInfo::from)
44 .collect();
45
46 Ok(connected_peers)
47 }
48
49 async fn consensus_time_now(
50 &self,
51 context: &Context,
52 ) -> juniper::FieldResult<GraphQLConsensusTime> {
53 let consensus_time: RpcConsensusTimeGetResponse = context
54 .rpc_sender
55 .oneshot_request(RpcRequest::ConsensusTimeGet(ConsensusTimeQuery::Now))
56 .await
57 .ok_or(Error::StateMachineEmptyResponse)?;
58
59 match consensus_time {
60 Some(consensus_time) => Ok(GraphQLConsensusTime::from(consensus_time)),
61 None => Err(juniper::FieldError::new(
62 "No consensus time found",
63 juniper::Value::Null,
64 )),
65 }
66 }
67
68 async fn consensus_time_best_tip(
69 &self,
70 context: &Context,
71 ) -> juniper::FieldResult<GraphQLConsensusTime> {
72 let consensus_time_res: RpcConsensusTimeGetResponse = context
73 .rpc_sender
74 .oneshot_request(RpcRequest::ConsensusTimeGet(ConsensusTimeQuery::BestTip))
75 .await
76 .ok_or(Error::StateMachineEmptyResponse)?;
77
78 match consensus_time_res {
79 Some(consensus_time) => Ok(GraphQLConsensusTime::from(consensus_time)),
80 None => Err(juniper::FieldError::new(
81 "No consensus time found",
82 juniper::Value::Null,
83 )),
84 }
85 }
86
87 async fn consensus_mechanism(&self, _context: &Context) -> juniper::FieldResult<String> {
88 Ok("proof_of_stake".to_string())
89 }
90
91 async fn blockchain_length(&self, context: &Context) -> juniper::FieldResult<Option<i32>> {
92 let status = context.get_or_fetch_status().await;
93
94 Ok(status.and_then(|status| {
95 status
96 .transition_frontier
97 .best_tip
98 .map(|block_summary| block_summary.height as i32)
99 }))
100 }
101
102 async fn chain_id(&self, context: &Context) -> juniper::FieldResult<Option<String>> {
103 let status = context.get_or_fetch_status().await;
104
105 Ok(status.and_then(|status| status.chain_id))
106 }
107
108 async fn commit_id(&self, _context: &Context) -> juniper::FieldResult<String> {
109 Ok(BuildEnv::get().git.commit_hash.to_string())
110 }
111
112 async fn global_slot_since_genesis_best_tip(
113 &self,
114 context: &Context,
115 ) -> juniper::FieldResult<Option<i32>> {
116 let best_tip = context.get_or_fetch_best_tip().await;
117 Ok(best_tip.and_then(|best_tip| {
118 println!("best_tip OK");
119 best_tip.global_slot_since_genesis().try_into().ok()
120 }))
121 }
122
123 async fn ledger_merkle_root(&self, context: &Context) -> juniper::FieldResult<Option<String>> {
124 let best_tip = context.get_or_fetch_best_tip().await;
125
126 Ok(best_tip.map(|best_tip| best_tip.merkle_root_hash().to_string()))
127 }
128
129 async fn state_hash(&self, context: &Context) -> juniper::FieldResult<Option<String>> {
130 let best_tip = context.get_or_fetch_best_tip().await;
131 Ok(best_tip.map(|best_tip| best_tip.hash().to_string()))
132 }
133
134 async fn num_accounts(&self, context: &Context) -> juniper::FieldResult<Option<i32>> {
135 let best_tip = context.get_or_fetch_best_tip().await;
136
137 match best_tip {
138 Some(best_tip) => {
139 let ledger_status = context
140 .get_or_fetch_ledger_status(best_tip.merkle_root_hash())
141 .await;
142 Ok(ledger_status.map(|ledger_status| ledger_status.num_accounts as i32))
143 }
144 None => Ok(None),
145 }
146 }
147
148 async fn highest_unvalidated_block_length_received(
149 &self,
150 context: &Context,
151 ) -> juniper::FieldResult<Option<i32>> {
152 let status = context.get_or_fetch_status().await;
153 Ok(status.and_then(|status| {
154 status
155 .transition_frontier
156 .best_tip
157 .map(|best_tip| best_tip.height as i32)
158 .or_else(|| {
159 status
160 .transition_frontier
161 .sync
162 .target
163 .map(|target| target.height as i32)
164 })
165 }))
166 }
167
168 async fn highest_block_length_received(
169 &self,
170 context: &Context,
171 ) -> juniper::FieldResult<Option<i32>> {
172 let status = context.get_or_fetch_status().await;
173 Ok(status.and_then(|status| {
174 status
175 .transition_frontier
176 .best_tip
177 .map(|best_tip| best_tip.height as i32)
178 .or_else(|| {
179 status
180 .transition_frontier
181 .sync
182 .target
183 .map(|target| target.height as i32)
184 })
185 }))
186 }
187
188 async fn addrs_and_ports(
189 &self,
190 context: &Context,
191 ) -> juniper::FieldResult<GraphQLAddrsAndPorts> {
192 let status = context.get_or_fetch_status().await;
193
194 match status {
195 Some(status) => Ok(GraphQLAddrsAndPorts::from(&status.network_info)),
196 None => Ok(Default::default()),
197 }
198 }
199
200 async fn block_production_keys(&self, context: &Context) -> juniper::FieldResult<Vec<String>> {
201 let status = context.get_or_fetch_status().await;
202 Ok(status.map_or(vec![], |status| {
203 status
204 .block_producer
205 .map_or(vec![], |key| vec![key.to_string()])
206 }))
207 }
208
209 async fn coinbase_receiver(&self, context: &Context) -> juniper::FieldResult<Option<String>> {
210 let status = context.get_or_fetch_status().await;
211 Ok(status.and_then(|status| status.coinbase_receiver.map(|key| key.to_string())))
212 }
213}
214
215#[derive(GraphQLObject, Clone, Debug)]
216pub struct GraphQLAddrsAndPorts {
217 pub bind_ip: String,
218 pub external_ip: Option<String>,
219 pub client_port: Option<i32>,
220 pub libp2p_port: Option<i32>,
221}
222
223impl Default for GraphQLAddrsAndPorts {
224 fn default() -> Self {
225 Self {
226 bind_ip: "0.0.0.0".to_string(),
227 external_ip: None,
228 client_port: None,
229 libp2p_port: None,
230 }
231 }
232}
233
234impl From<&RpcNodeStatusNetworkInfo> for GraphQLAddrsAndPorts {
235 fn from(network_info: &RpcNodeStatusNetworkInfo) -> Self {
236 Self {
237 bind_ip: network_info.bind_ip.clone(),
238 external_ip: network_info.external_ip.clone(),
239 client_port: network_info.client_port.map(|port| port.into()),
240 libp2p_port: network_info.libp2p_port.map(|port| port.into()),
241 }
242 }
243}
244
245#[derive(GraphQLObject, Clone, Debug)]
246pub struct GraphQLRpcPeerInfo {
247 pub peer_id: String,
248 pub best_tip: Option<String>,
249 pub best_tip_height: Option<String>,
250 pub best_tip_global_slot: Option<String>,
251 pub best_tip_timestamp: Option<String>,
252 pub connection_status: String,
253 pub connecting_details: Option<String>,
254 pub address: Option<String>,
255 pub incoming: bool,
256 pub is_libp2p: bool,
257 pub time: String,
258}
259
260impl From<&RpcPeerInfo> for GraphQLRpcPeerInfo {
261 fn from(peer: &RpcPeerInfo) -> Self {
262 Self {
263 peer_id: peer.peer_id.to_string(),
264 best_tip: peer.best_tip.as_ref().map(|hash| hash.to_string()),
265 best_tip_height: peer.best_tip_height.map(|height| height.to_string()),
266 best_tip_global_slot: peer.best_tip_global_slot.map(|slot| slot.to_string()),
267 best_tip_timestamp: peer
268 .best_tip_timestamp
269 .map(|timestamp| timestamp.to_string()),
270 connection_status: peer.connection_status.to_string(),
271 connecting_details: peer.connecting_details.clone(),
272 address: peer.address.clone(),
273 incoming: peer.incoming,
274 is_libp2p: peer.is_libp2p,
275 time: peer.time.to_string(),
276 }
277 }
278}
279
280#[derive(GraphQLObject, Debug)]
281pub struct GraphQLGenesisConstants {
282 pub genesis_timestamp: String,
283 pub coinbase: String,
284 pub account_creation_fee: String,
285}
286
287impl GraphQLGenesisConstants {
288 pub fn try_new(
289 constrain_constants: ConstraintConstants,
290 consensus_constants: ConsensusConstants,
291 ) -> Result<Self, ConversionError> {
292 Ok(GraphQLGenesisConstants {
293 genesis_timestamp: consensus_constants
294 .human_readable_genesis_timestamp()
295 .map_err(|e| ConversionError::Custom(e.to_string()))?,
296 coinbase: constrain_constants.coinbase_amount.to_string(),
297 account_creation_fee: constrain_constants.account_creation_fee.to_string(),
298 })
299 }
300}
301
302#[derive(GraphQLObject, Debug)]
303pub struct GraphQLConsensusConfiguration {
304 pub epoch_duration: i32,
305 pub k: i32,
306 pub slot_duration: i32,
307 pub slots_per_epoch: i32,
308}
309
310impl From<ConsensusConstants> for GraphQLConsensusConfiguration {
311 fn from(consensus_constants: ConsensusConstants) -> Self {
312 GraphQLConsensusConfiguration {
313 epoch_duration: consensus_constants.epoch_duration as i32,
314 k: consensus_constants.k as i32,
315 slot_duration: consensus_constants.slot_duration_ms as i32,
316 slots_per_epoch: consensus_constants.slots_per_epoch as i32,
317 }
318 }
319}
320
321#[derive(GraphQLObject, Debug)]
322pub struct GraphQLConsensusTime {
323 pub start_time: String,
324 pub end_time: String,
325 pub epoch: String,
326 pub global_slot: String,
327 pub slot: String,
328}
329
330impl From<ConsensusTime> for GraphQLConsensusTime {
331 fn from(consensus_time: ConsensusTime) -> Self {
332 let start_time: u64 = consensus_time.start_time.into();
333 let end_time: u64 = consensus_time.end_time.into();
334
335 let start_time_ms = start_time.checked_div(1_000_000).expect("division by zero");
336 let end_time_ms = end_time.checked_div(1_000_000).expect("division by zero");
337
338 GraphQLConsensusTime {
339 start_time: start_time_ms.to_string(),
340 end_time: end_time_ms.to_string(),
341 epoch: consensus_time.epoch.to_string(),
342 global_slot: consensus_time.global_slot.to_string(),
343 slot: consensus_time.slot.to_string(),
344 }
345 }
346}