1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use std::time::{Duration, SystemTime, UNIX_EPOCH};

use crate::{
    lotus_json::lotus_json_with_self,
    rpc::{ApiPaths, Ctx, Permission, RpcMethod, ServerError},
};
use fvm_ipld_blockstore::Blockstore;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

pub enum NodeStatus {}
impl RpcMethod<0> for NodeStatus {
    const NAME: &'static str = "Filecoin.NodeStatus";
    const PARAM_NAMES: [&'static str; 0] = [];
    const API_PATHS: ApiPaths = ApiPaths::V1;
    const PERMISSION: Permission = Permission::Read;

    type Params = ();
    type Ok = NodeStatusResult;

    async fn handle(ctx: Ctx<impl Blockstore>, (): Self::Params) -> Result<Self::Ok, ServerError> {
        let mut node_status = NodeStatusResult::default();

        let head = ctx.chain_store().heaviest_tipset();
        let cur_duration: Duration = SystemTime::now().duration_since(UNIX_EPOCH)?;

        let ts = head.min_timestamp();
        let cur_duration_secs = cur_duration.as_secs();
        let behind = if ts <= cur_duration_secs + 1 {
            cur_duration_secs.saturating_sub(ts)
        } else {
            return Err(anyhow::anyhow!(
                "System time should not be behind tipset timestamp, please sync the system clock."
            )
            .into());
        };

        let chain_finality = ctx.chain_config().policy.chain_finality;

        node_status.sync_status.epoch = head.epoch() as u64;
        node_status.sync_status.behind = behind;

        if head.epoch() > chain_finality {
            let mut block_count = 0;
            let mut ts = head;

            for _ in 0..100 {
                block_count += ts.block_headers().len();
                let tsk = ts.parents();
                ts = ctx.chain_index().load_required_tipset(tsk)?;
            }

            node_status.chain_status.blocks_per_tipset_last_100 = block_count as f64 / 100.;

            for _ in 100..chain_finality {
                block_count += ts.block_headers().len();
                let tsk = ts.parents();
                ts = ctx.chain_index().load_required_tipset(tsk)?;
            }

            node_status.chain_status.blocks_per_tipset_last_finality =
                block_count as f64 / chain_finality as f64;
        }

        Ok(node_status)
    }
}

#[derive(Debug, Serialize, Deserialize, Default, Clone, JsonSchema)]
pub struct NodeSyncStatus {
    pub epoch: u64,
    pub behind: u64,
}
lotus_json_with_self!(NodeSyncStatus);

#[derive(Debug, Serialize, Deserialize, Default, Clone, JsonSchema)]
pub struct NodePeerStatus {
    pub peers_to_publish_msgs: u32,
    pub peers_to_publish_blocks: u32,
}
lotus_json_with_self!(NodePeerStatus);

#[derive(Debug, Serialize, Deserialize, Default, Clone, JsonSchema)]
pub struct NodeChainStatus {
    pub blocks_per_tipset_last_100: f64,
    pub blocks_per_tipset_last_finality: f64,
}
lotus_json_with_self!(NodeChainStatus);

#[derive(Debug, Deserialize, Default, Serialize, Clone, JsonSchema)]
pub struct NodeStatusResult {
    pub sync_status: NodeSyncStatus,
    pub peer_status: NodePeerStatus,
    pub chain_status: NodeChainStatus,
}
lotus_json_with_self!(NodeStatusResult);