use exocore_core::{cell::NodeId, time::Clock};
use super::{BlockMetadata, ChainSyncConfig};
use crate::{
block::{Block, BlockHeight},
chain::ChainStore,
engine::{error::EngineError, request_tracker::RequestTracker},
};
pub struct NodeSyncInfo {
pub config: ChainSyncConfig,
pub node_id: NodeId,
pub(super) last_common_block: Option<BlockMetadata>,
pub last_common_is_known: bool,
pub(super) last_known_block: Option<BlockMetadata>,
pub request_tracker: RequestTracker,
}
impl NodeSyncInfo {
pub fn new(node_id: NodeId, config: ChainSyncConfig, clock: Clock) -> NodeSyncInfo {
let request_tracker_config = config.request_tracker;
NodeSyncInfo {
config,
node_id,
last_common_block: None,
last_common_is_known: false,
last_known_block: None,
request_tracker: RequestTracker::new_with_clock(clock, request_tracker_config),
}
}
pub fn check_status(&mut self) -> NodeStatus {
let response_failures_count = self.request_tracker.response_failure_count();
let is_failed = response_failures_count >= self.config.meta_sync_max_failures;
if self.last_common_is_known && !is_failed {
NodeStatus::Synchronized
} else {
if self.last_common_is_known {
debug!("Lost node {} synchronization status", self.node_id);
self.last_common_is_known = false;
}
NodeStatus::Unknown
}
}
pub fn status(&self) -> NodeStatus {
if self.last_common_is_known {
NodeStatus::Synchronized
} else {
NodeStatus::Unknown
}
}
pub fn chain_fully_downloaded(&self) -> bool {
let last_known_offset = self.last_known_block.as_ref().map(|b| b.offset);
let last_common_offset = self.last_common_block.as_ref().map(|b| b.offset);
self.last_known_block.is_some() && last_known_offset == last_common_offset
}
pub fn common_blocks_height_delta(&self) -> Option<BlockHeight> {
match (&self.last_common_block, &self.last_known_block) {
(Some(common), Some(known)) => Some(known.height - common.height),
_ => None,
}
}
pub fn is_divergent<CS: ChainStore>(&self, local_store: &CS) -> Result<bool, EngineError> {
if let Some(last_common_block) = &self.last_common_block {
let last_known_block = if let Some(last_known_block) = self.last_known_block.as_ref() {
last_known_block
} else {
return Ok(false);
};
let last_local_block = local_store.get_last_block()?.ok_or_else(|| {
anyhow!("Expected a common block to be in stored since it had previously been")
})?;
let last_local_height = last_local_block.get_height()?;
Ok(last_local_height > last_common_block.height
&& last_known_block.height > last_common_block.height)
} else {
let last_local_block = local_store.get_last_block()?;
Ok(last_local_block.is_some() && self.last_known_block.is_some())
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum NodeStatus {
Unknown,
Synchronized,
}