Skip to main content

forest/rpc/methods/
node.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::time::{Duration, SystemTime, UNIX_EPOCH};
5
6use crate::{
7    lotus_json::lotus_json_with_self,
8    rpc::{ApiPaths, Ctx, Permission, RpcMethod, ServerError},
9};
10use enumflags2::BitFlags;
11use fvm_ipld_blockstore::Blockstore;
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14
15pub enum NodeStatus {}
16impl RpcMethod<0> for NodeStatus {
17    const NAME: &'static str = "Filecoin.NodeStatus";
18    const PARAM_NAMES: [&'static str; 0] = [];
19    const API_PATHS: BitFlags<ApiPaths> = ApiPaths::all();
20    const PERMISSION: Permission = Permission::Read;
21
22    type Params = ();
23    type Ok = NodeStatusResult;
24
25    async fn handle(
26        ctx: Ctx<impl Blockstore>,
27        (): Self::Params,
28        _: &http::Extensions,
29    ) -> Result<Self::Ok, ServerError> {
30        let mut node_status = NodeStatusResult::default();
31
32        let head = ctx.chain_store().heaviest_tipset();
33        let cur_duration: Duration = SystemTime::now().duration_since(UNIX_EPOCH)?;
34
35        let ts = head.min_timestamp();
36        let cur_duration_secs = cur_duration.as_secs();
37        let behind = if ts <= cur_duration_secs + 1 {
38            cur_duration_secs.saturating_sub(ts)
39        } else {
40            return Err(anyhow::anyhow!(
41                "System time should not be behind tipset timestamp, please sync the system clock."
42            )
43            .into());
44        };
45
46        let chain_finality = ctx.chain_config().policy.chain_finality;
47
48        node_status.sync_status.epoch = head.epoch() as u64;
49        node_status.sync_status.behind = behind;
50
51        if head.epoch() > chain_finality {
52            let mut block_count = 0;
53            let mut ts = head;
54
55            for _ in 0..100 {
56                block_count += ts.block_headers().len();
57                let tsk = ts.parents();
58                ts = ctx.chain_index().load_required_tipset(tsk)?;
59            }
60
61            node_status.chain_status.blocks_per_tipset_last_100 = block_count as f64 / 100.;
62
63            for _ in 100..chain_finality {
64                block_count += ts.block_headers().len();
65                let tsk = ts.parents();
66                ts = ctx.chain_index().load_required_tipset(tsk)?;
67            }
68
69            node_status.chain_status.blocks_per_tipset_last_finality =
70                block_count as f64 / chain_finality as f64;
71        }
72
73        Ok(node_status)
74    }
75}
76
77#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, Clone, JsonSchema)]
78pub struct NodeSyncStatus {
79    pub epoch: u64,
80    pub behind: u64,
81}
82lotus_json_with_self!(NodeSyncStatus);
83
84#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default, Clone, JsonSchema)]
85pub struct NodePeerStatus {
86    pub peers_to_publish_msgs: u32,
87    pub peers_to_publish_blocks: u32,
88}
89lotus_json_with_self!(NodePeerStatus);
90
91#[derive(Debug, PartialEq, Serialize, Deserialize, Default, Clone, JsonSchema)]
92pub struct NodeChainStatus {
93    pub blocks_per_tipset_last_100: f64,
94    pub blocks_per_tipset_last_finality: f64,
95}
96lotus_json_with_self!(NodeChainStatus);
97
98#[derive(Debug, Deserialize, Default, Serialize, Clone, JsonSchema, PartialEq)]
99pub struct NodeStatusResult {
100    pub sync_status: NodeSyncStatus,
101    pub peer_status: NodePeerStatus,
102    pub chain_status: NodeChainStatus,
103}
104lotus_json_with_self!(NodeStatusResult);