casper_node/types/
status_feed.rs

1use std::{
2    collections::BTreeMap,
3    net::{IpAddr, Ipv4Addr, SocketAddr},
4    time::Duration,
5};
6
7use once_cell::sync::Lazy;
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10
11use casper_binary_port::ConsensusStatus;
12use casper_types::{
13    ActivationPoint, AvailableBlockRange, Block, BlockHash, BlockSynchronizerStatus, Digest, EraId,
14    NextUpgrade, Peers, ProtocolVersion, PublicKey, TimeDiff, Timestamp,
15};
16
17use crate::{
18    components::rest_server::{DocExample, DOCS_EXAMPLE_PROTOCOL_VERSION},
19    reactor::main_reactor::ReactorState,
20    types::NodeId,
21};
22
23static CHAINSPEC_INFO: Lazy<ChainspecInfo> = Lazy::new(|| {
24    let next_upgrade = NextUpgrade::new(
25        ActivationPoint::EraId(EraId::from(42)),
26        ProtocolVersion::from_parts(2, 0, 1),
27    );
28    ChainspecInfo {
29        name: String::from("casper-example"),
30        next_upgrade: Some(next_upgrade),
31    }
32});
33
34static GET_STATUS_RESULT: Lazy<GetStatusResult> = Lazy::new(|| {
35    let node_id = NodeId::doc_example();
36    let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 54321);
37    let mut peers = BTreeMap::new();
38    peers.insert(*node_id, socket_addr.to_string());
39    let status_feed = StatusFeed {
40        last_added_block: Some(Block::example().clone()),
41        peers,
42        chainspec_info: ChainspecInfo::doc_example().clone(),
43        our_public_signing_key: Some(PublicKey::example().clone()),
44        round_length: Some(TimeDiff::from_millis(1 << 16)),
45        version: crate::VERSION_STRING.as_str(),
46        node_uptime: Duration::from_secs(13),
47        reactor_state: ReactorState::Initialize,
48        last_progress: Timestamp::from(0),
49        available_block_range: AvailableBlockRange::RANGE_0_0,
50        block_sync: BlockSynchronizerStatus::example().clone(),
51        starting_state_root_hash: Digest::default(),
52        latest_switch_block_hash: Some(BlockHash::default()),
53    };
54    GetStatusResult::new(status_feed, DOCS_EXAMPLE_PROTOCOL_VERSION)
55});
56
57/// Summary information from the chainspec.
58#[derive(Debug, Serialize, Deserialize, Clone)]
59pub struct ChainspecInfo {
60    /// Name of the network.
61    name: String,
62    next_upgrade: Option<NextUpgrade>,
63}
64
65impl DocExample for ChainspecInfo {
66    fn doc_example() -> &'static Self {
67        &CHAINSPEC_INFO
68    }
69}
70
71impl ChainspecInfo {
72    pub(crate) fn new(chainspec_network_name: String, next_upgrade: Option<NextUpgrade>) -> Self {
73        ChainspecInfo {
74            name: chainspec_network_name,
75            next_upgrade,
76        }
77    }
78}
79
80/// Data feed for client "info_get_status" endpoint.
81#[derive(Debug, Serialize)]
82pub struct StatusFeed {
83    /// The last block added to the chain.
84    pub last_added_block: Option<Block>,
85    /// The peer nodes which are connected to this node.
86    pub peers: BTreeMap<NodeId, String>,
87    /// The chainspec info for this node.
88    pub chainspec_info: ChainspecInfo,
89    /// Our public signing key.
90    pub our_public_signing_key: Option<PublicKey>,
91    /// The next round length if this node is a validator.
92    pub round_length: Option<TimeDiff>,
93    /// The compiled node version.
94    pub version: &'static str,
95    /// Time that passed since the node has started.
96    pub node_uptime: Duration,
97    /// The current state of node reactor.
98    pub reactor_state: ReactorState,
99    /// Timestamp of the last recorded progress in the reactor.
100    pub last_progress: Timestamp,
101    /// The available block range in storage.
102    pub available_block_range: AvailableBlockRange,
103    /// The status of the block synchronizer builders.
104    pub block_sync: BlockSynchronizerStatus,
105    /// The state root hash of the lowest block in the available block range.
106    pub starting_state_root_hash: Digest,
107    /// The hash of the latest switch block.
108    pub latest_switch_block_hash: Option<BlockHash>,
109}
110
111impl StatusFeed {
112    #[allow(clippy::too_many_arguments)]
113    pub(crate) fn new(
114        last_added_block: Option<Block>,
115        peers: BTreeMap<NodeId, String>,
116        chainspec_info: ChainspecInfo,
117        consensus_status: Option<ConsensusStatus>,
118        node_uptime: Duration,
119        reactor_state: ReactorState,
120        last_progress: Timestamp,
121        available_block_range: AvailableBlockRange,
122        block_sync: BlockSynchronizerStatus,
123        starting_state_root_hash: Digest,
124        latest_switch_block_hash: Option<BlockHash>,
125    ) -> Self {
126        let (our_public_signing_key, round_length) =
127            consensus_status.map_or((None, None), |consensus_status| {
128                (
129                    Some(consensus_status.validator_public_key().clone()),
130                    consensus_status.round_length(),
131                )
132            });
133        StatusFeed {
134            last_added_block,
135            peers,
136            chainspec_info,
137            our_public_signing_key,
138            round_length,
139            version: crate::VERSION_STRING.as_str(),
140            node_uptime,
141            reactor_state,
142            last_progress,
143            available_block_range,
144            block_sync,
145            starting_state_root_hash,
146            latest_switch_block_hash,
147        }
148    }
149}
150
151/// Minimal info of a `Block`.
152#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, JsonSchema)]
153#[serde(deny_unknown_fields)]
154pub struct MinimalBlockInfo {
155    hash: BlockHash,
156    timestamp: Timestamp,
157    era_id: EraId,
158    height: u64,
159    state_root_hash: Digest,
160    creator: PublicKey,
161}
162
163impl From<Block> for MinimalBlockInfo {
164    fn from(block: Block) -> Self {
165        let proposer = match &block {
166            Block::V1(v1) => v1.proposer().clone(),
167            Block::V2(v2) => v2.proposer().clone(),
168        };
169
170        MinimalBlockInfo {
171            hash: *block.hash(),
172            timestamp: block.timestamp(),
173            era_id: block.era_id(),
174            height: block.height(),
175            state_root_hash: *block.state_root_hash(),
176            creator: proposer,
177        }
178    }
179}
180
181/// Result for "info_get_status" RPC response.
182#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, JsonSchema)]
183#[serde(deny_unknown_fields)]
184pub struct GetStatusResult {
185    /// The node ID and network address of each connected peer.
186    pub peers: Peers,
187    /// The RPC API version.
188    #[schemars(with = "String")]
189    pub api_version: ProtocolVersion,
190    /// The compiled node version.
191    pub build_version: String,
192    /// The chainspec name.
193    pub chainspec_name: String,
194    /// The state root hash of the lowest block in the available block range.
195    pub starting_state_root_hash: Digest,
196    /// The minimal info of the last block from the linear chain.
197    pub last_added_block_info: Option<MinimalBlockInfo>,
198    /// Our public signing key.
199    pub our_public_signing_key: Option<PublicKey>,
200    /// The next round length if this node is a validator.
201    pub round_length: Option<TimeDiff>,
202    /// Information about the next scheduled upgrade.
203    pub next_upgrade: Option<NextUpgrade>,
204    /// Time that passed since the node has started.
205    pub uptime: TimeDiff,
206    /// The current state of node reactor.
207    pub reactor_state: ReactorState,
208    /// Timestamp of the last recorded progress in the reactor.
209    pub last_progress: Timestamp,
210    /// The available block range in storage.
211    pub available_block_range: AvailableBlockRange,
212    /// The status of the block synchronizer builders.
213    pub block_sync: BlockSynchronizerStatus,
214    /// The hash of the latest switch block.
215    pub latest_switch_block_hash: Option<BlockHash>,
216}
217
218impl GetStatusResult {
219    #[allow(deprecated)]
220    pub(crate) fn new(status_feed: StatusFeed, api_version: ProtocolVersion) -> Self {
221        GetStatusResult {
222            peers: Peers::from(status_feed.peers),
223            api_version,
224            chainspec_name: status_feed.chainspec_info.name,
225            starting_state_root_hash: status_feed.starting_state_root_hash,
226            last_added_block_info: status_feed.last_added_block.map(Into::into),
227            our_public_signing_key: status_feed.our_public_signing_key,
228            round_length: status_feed.round_length,
229            next_upgrade: status_feed.chainspec_info.next_upgrade,
230            uptime: status_feed.node_uptime.into(),
231            reactor_state: status_feed.reactor_state,
232            last_progress: status_feed.last_progress,
233            available_block_range: status_feed.available_block_range,
234            block_sync: status_feed.block_sync,
235            latest_switch_block_hash: status_feed.latest_switch_block_hash,
236            #[cfg(not(test))]
237            build_version: crate::VERSION_STRING.clone(),
238
239            //  Prevent these values from changing between test sessions
240            #[cfg(test)]
241            build_version: String::from("1.0.0-xxxxxxxxx@DEBUG"),
242        }
243    }
244}
245
246impl DocExample for GetStatusResult {
247    fn doc_example() -> &'static Self {
248        &GET_STATUS_RESULT
249    }
250}