forest/chain_sync/
sync_status.rs1use crate::blocks::TipsetKey;
4use crate::lotus_json::lotus_json_with_self;
5use crate::networks::calculate_expected_epoch;
6use crate::shim::clock::ChainEpoch;
7use crate::state_manager::StateManager;
8use chrono::{DateTime, Utc};
9use fvm_ipld_blockstore::Blockstore;
10use parking_lot::RwLock;
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13use std::sync::Arc;
14use tracing::log;
15
16const SYNCED_EPOCH_THRESHOLD: u64 = 10;
18
19#[derive(
21 Serialize,
22 Deserialize,
23 Debug,
24 Clone,
25 Copy,
26 Default,
27 PartialEq,
28 Eq,
29 JsonSchema,
30 strum::Display,
31 strum::EnumString,
32)]
33pub enum NodeSyncStatus {
34 #[default]
36 #[strum(to_string = "Intializing")]
37 Initializing,
38 #[strum(to_string = "Syncing")]
40 Syncing,
41 #[strum(to_string = "Synced")]
43 Synced,
44 #[strum(to_string = "Error")]
46 Error,
47 #[strum(to_string = "Offline")]
49 Offline,
50}
51
52#[derive(
54 Serialize,
55 Deserialize,
56 Debug,
57 Clone,
58 PartialEq,
59 Eq,
60 JsonSchema,
61 strum::Display,
62 strum::EnumString,
63)]
64pub enum ForkSyncStage {
65 #[strum(to_string = "Fetching Headers")]
67 FetchingHeaders,
68 #[strum(to_string = "Validating Tipsets")]
70 ValidatingTipsets,
71 #[strum(to_string = "Complete")]
73 Complete,
74 #[strum(to_string = "Stalled")]
76 Stalled,
77 #[strum(to_string = "Error")]
79 Error,
80}
81
82#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
84pub struct ForkSyncInfo {
85 #[schemars(with = "crate::lotus_json::LotusJson<TipsetKey>")]
87 #[serde(with = "crate::lotus_json")]
88 pub(crate) target_tipset_key: TipsetKey,
89 pub(crate) target_epoch: ChainEpoch,
91 pub(crate) target_sync_epoch_start: ChainEpoch,
94 pub(crate) stage: ForkSyncStage,
96 pub(crate) validated_chain_head_epoch: ChainEpoch,
99 pub(crate) start_time: Option<DateTime<Utc>>,
101 pub(crate) last_updated: Option<DateTime<Utc>>,
103}
104
105pub type SyncStatus = Arc<RwLock<SyncStatusReport>>;
106
107#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema)]
109pub struct SyncStatusReport {
110 pub(crate) status: NodeSyncStatus,
112 pub(crate) current_head_epoch: ChainEpoch,
114 #[schemars(with = "crate::lotus_json::LotusJson<TipsetKey>")]
116 #[serde(with = "crate::lotus_json")]
117 pub(crate) current_head_key: Option<TipsetKey>,
118 pub(crate) network_head_epoch: ChainEpoch,
120 pub(crate) epochs_behind: i64,
123 pub(crate) active_forks: Vec<ForkSyncInfo>,
125 pub(crate) node_start_time: DateTime<Utc>,
127 pub(crate) last_updated: DateTime<Utc>,
129}
130
131lotus_json_with_self!(SyncStatusReport);
132
133impl SyncStatusReport {
134 pub(crate) fn init() -> Self {
135 Self {
136 node_start_time: Utc::now(),
137 ..Default::default()
138 }
139 }
140
141 pub(crate) fn update<DB: Blockstore + Sync + Send + 'static>(
144 &self,
145 state_manager: &StateManager<DB>,
146 active_forks: Vec<ForkSyncInfo>,
147 stateless_mode: bool,
148 ) -> Self {
149 let heaviest = state_manager.chain_store().heaviest_tipset();
150 let current_head_epoch = heaviest.epoch();
151 let current_head_key = Some(heaviest.key().clone());
152
153 let last_updated = Utc::now();
154 let last_updated_ts = last_updated.timestamp() as u64;
155 let seconds_per_epoch = state_manager.chain_config().block_delay_secs;
156 let network_head_epoch = calculate_expected_epoch(
157 last_updated_ts,
158 state_manager.chain_store().genesis_block_header().timestamp,
159 seconds_per_epoch,
160 );
161
162 let epochs_behind = network_head_epoch.saturating_sub(current_head_epoch);
163 log::trace!(
164 "Sync status report: current head epoch: {}, network head epoch: {}, epochs behind: {}",
165 current_head_epoch,
166 network_head_epoch,
167 epochs_behind
168 );
169
170 let time_diff = last_updated_ts.saturating_sub(heaviest.min_timestamp());
171 let status = match stateless_mode {
172 true => NodeSyncStatus::Offline,
173 false => {
174 if time_diff < seconds_per_epoch as u64 * SYNCED_EPOCH_THRESHOLD {
175 NodeSyncStatus::Synced
176 } else {
177 NodeSyncStatus::Syncing
178 }
179 }
180 };
181
182 Self {
183 node_start_time: self.node_start_time,
184 current_head_epoch,
185 current_head_key,
186 network_head_epoch,
187 epochs_behind,
188 status,
189 active_forks,
190 last_updated,
191 }
192 }
193
194 pub(crate) fn is_synced(&self) -> bool {
195 self.status == NodeSyncStatus::Synced
196 }
197
198 pub(crate) fn get_min_starting_block(&self) -> Option<ChainEpoch> {
199 self.active_forks
200 .iter()
201 .map(|fork_info| fork_info.target_sync_epoch_start)
202 .min()
203 }
204}