fuel_core/schema/
node_info.rs

1use super::scalars::{
2    U32,
3    U64,
4};
5use crate::{
6    database::database_description::IndexationKind,
7    fuel_core_graphql_api::{
8        Config as GraphQLConfig,
9        query_costs,
10    },
11    graphql_api::{
12        api_service::TxPool,
13        database::{
14            IndexationFlags,
15            ReadDatabase,
16        },
17    },
18};
19use async_graphql::{
20    Context,
21    Object,
22};
23use std::time::UNIX_EPOCH;
24
25pub struct NodeInfo {
26    utxo_validation: bool,
27    vm_backtrace: bool,
28    max_tx: U64,
29    max_gas: U64,
30    max_size: U64,
31    max_depth: U64,
32    node_version: String,
33    indexation: IndexationFlags,
34}
35
36#[Object]
37impl NodeInfo {
38    async fn utxo_validation(&self) -> bool {
39        self.utxo_validation
40    }
41
42    async fn vm_backtrace(&self) -> bool {
43        self.vm_backtrace
44    }
45
46    async fn max_tx(&self) -> U64 {
47        self.max_tx
48    }
49
50    async fn max_gas(&self) -> U64 {
51        self.max_gas
52    }
53
54    async fn max_size(&self) -> U64 {
55        self.max_size
56    }
57
58    async fn max_depth(&self) -> U64 {
59        self.max_depth
60    }
61
62    async fn node_version(&self) -> String {
63        self.node_version.to_owned()
64    }
65
66    async fn indexation(&self) -> &IndexationFlags {
67        &self.indexation
68    }
69
70    #[graphql(complexity = "query_costs().storage_read + child_complexity")]
71    async fn tx_pool_stats(
72        &self,
73        ctx: &Context<'_>,
74    ) -> async_graphql::Result<TxPoolStats> {
75        let tx_pool = ctx.data_unchecked::<TxPool>();
76        Ok(TxPoolStats(tx_pool.latest_pool_stats()))
77    }
78
79    #[graphql(complexity = "query_costs().get_peers + child_complexity")]
80    async fn peers(&self, _ctx: &Context<'_>) -> async_graphql::Result<Vec<PeerInfo>> {
81        #[cfg(feature = "p2p")]
82        {
83            let p2p: &crate::fuel_core_graphql_api::api_service::P2pService =
84                _ctx.data_unchecked();
85            let peer_info = p2p.all_peer_info().await?;
86            let peers = peer_info.into_iter().map(PeerInfo).collect();
87            Ok(peers)
88        }
89        #[cfg(not(feature = "p2p"))]
90        {
91            Err(async_graphql::Error::new(
92                "Peering is disabled in this build, try using the `p2p` feature flag.",
93            ))
94        }
95    }
96}
97
98#[derive(Default)]
99pub struct NodeQuery {}
100
101#[Object]
102impl NodeQuery {
103    #[graphql(complexity = "query_costs().storage_read + child_complexity")]
104    async fn node_info(&self, ctx: &Context<'_>) -> async_graphql::Result<NodeInfo> {
105        let config = ctx.data_unchecked::<GraphQLConfig>();
106
107        const VERSION: &str = env!("CARGO_PKG_VERSION");
108
109        let db = ctx.data_unchecked::<ReadDatabase>();
110        let read_view = db.view()?;
111        Ok(NodeInfo {
112            utxo_validation: config.utxo_validation,
113            vm_backtrace: false,
114            max_tx: (config.max_tx as u64).into(),
115            max_gas: config.max_gas.into(),
116            max_size: (config.max_size as u64).into(),
117            max_depth: (config.max_txpool_dependency_chain_length as u64).into(),
118            node_version: VERSION.to_owned(),
119            indexation: read_view.indexation_flags,
120        })
121    }
122}
123
124struct PeerInfo(fuel_core_types::services::p2p::PeerInfo);
125
126#[Object]
127impl PeerInfo {
128    /// The libp2p peer id
129    async fn id(&self) -> String {
130        self.0.id.to_string()
131    }
132
133    /// The advertised multi-addrs that can be used to connect to this peer
134    async fn addresses(&self) -> Vec<String> {
135        self.0.peer_addresses.iter().cloned().collect()
136    }
137
138    /// The self-reported version of the client the peer is using
139    async fn client_version(&self) -> Option<String> {
140        self.0.client_version.clone()
141    }
142
143    /// The last reported height of the peer
144    async fn block_height(&self) -> Option<U32> {
145        self.0
146            .heartbeat_data
147            .block_height
148            .map(|height| (*height).into())
149    }
150
151    /// The last heartbeat from this peer in unix epoch time ms
152    async fn last_heartbeat_ms(&self) -> U64 {
153        let time = self.0.heartbeat_data.last_heartbeat;
154        let time = time
155            .duration_since(UNIX_EPOCH)
156            .unwrap_or_default()
157            .as_millis();
158        U64(time.try_into().unwrap_or_default())
159    }
160
161    /// The internal fuel p2p reputation of this peer
162    async fn app_score(&self) -> f64 {
163        self.0.app_score
164    }
165}
166
167struct TxPoolStats(fuel_core_txpool::TxPoolStats);
168
169#[Object]
170impl TxPoolStats {
171    /// The number of transactions in the pool
172    async fn tx_count(&self) -> U64 {
173        self.0.tx_count.into()
174    }
175
176    /// The total size of the transactions in the pool
177    async fn total_size(&self) -> U64 {
178        self.0.total_size.into()
179    }
180
181    /// The total gas of the transactions in the pool
182    async fn total_gas(&self) -> U64 {
183        self.0.total_gas.into()
184    }
185}
186
187#[Object]
188impl IndexationFlags {
189    /// Is balances indexation enabled
190    async fn balances(&self) -> bool {
191        self.contains(&IndexationKind::Balances)
192    }
193
194    /// Is coins to spend indexation enabled
195    async fn coins_to_spend(&self) -> bool {
196        self.contains(&IndexationKind::CoinsToSpend)
197    }
198
199    /// Is asset metadata indexation enabled
200    async fn asset_metadata(&self) -> bool {
201        self.contains(&IndexationKind::AssetMetadata)
202    }
203}