use crate::ids::ShardId;
use crate::sync_codec::error::SyncError;
use crate::tree::Hash;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SyncDecision {
AlreadySynced,
WalkTrees,
}
impl SyncDecision {
#[must_use]
pub const fn requires_tree_walk(self) -> bool {
matches!(self, Self::WalkTrees)
}
pub(crate) const fn to_wire(self) -> u8 {
match self {
Self::AlreadySynced => 0,
Self::WalkTrees => 1,
}
}
pub(crate) const fn from_wire(value: u8) -> Result<Self, SyncError> {
match value {
0 => Ok(Self::AlreadySynced),
1 => Ok(Self::WalkTrees),
_ => Err(SyncError::InvalidMessage),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct SyncStats {
pub root_hashes_exchanged: usize,
pub tree_walks: usize,
pub source_nodes_read: usize,
pub target_nodes_checked: usize,
pub matching_subtrees_skipped: usize,
pub existing_subtrees_skipped: usize,
pub nodes_transferred: usize,
pub bytes_transferred: usize,
}
impl SyncStats {
pub(crate) const fn record_transfer_bytes(&mut self, byte_len: usize) {
self.nodes_transferred = self.nodes_transferred.saturating_add(1);
self.bytes_transferred = self.bytes_transferred.saturating_add(byte_len);
}
#[cfg(not(feature = "wasm"))]
#[must_use]
pub(crate) const fn without_transfer_counts(self) -> Self {
Self {
root_hashes_exchanged: self.root_hashes_exchanged,
tree_walks: self.tree_walks,
source_nodes_read: self.source_nodes_read,
target_nodes_checked: self.target_nodes_checked,
matching_subtrees_skipped: self.matching_subtrees_skipped,
existing_subtrees_skipped: self.existing_subtrees_skipped,
nodes_transferred: 0,
bytes_transferred: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RootExchange {
pub shard_id: ShardId,
pub source_root: Option<Hash>,
pub target_root: Option<Hash>,
pub decision: SyncDecision,
}
impl RootExchange {
#[must_use]
pub fn new(shard_id: ShardId, source_root: Option<Hash>, target_root: Option<Hash>) -> Self {
let decision = if source_root == target_root {
SyncDecision::AlreadySynced
} else {
SyncDecision::WalkTrees
};
Self {
shard_id,
source_root,
target_root,
decision,
}
}
#[must_use]
pub const fn requires_tree_walk(&self) -> bool {
self.decision.requires_tree_walk()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SyncPlan {
pub exchange: RootExchange,
pub stats: SyncStats,
}
impl SyncPlan {
#[must_use]
pub const fn requires_tree_walk(&self) -> bool {
self.exchange.requires_tree_walk()
}
}
#[must_use]
pub fn plan_sync(
shard_id: ShardId,
source_root: Option<Hash>,
target_root: Option<Hash>,
) -> SyncPlan {
let exchange = RootExchange::new(shard_id, source_root, target_root);
let mut stats = SyncStats {
root_hashes_exchanged: 1,
..SyncStats::default()
};
if exchange.requires_tree_walk() {
stats.tree_walks = 1;
}
SyncPlan { exchange, stats }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RootExchangeRequest {
pub shard_id: ShardId,
pub target_root: Option<Hash>,
}
impl RootExchangeRequest {
#[must_use]
pub const fn new(shard_id: ShardId, target_root: Option<Hash>) -> Self {
Self {
shard_id,
target_root,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RootExchangeResponse {
pub shard_id: ShardId,
pub source_root: Option<Hash>,
pub target_root: Option<Hash>,
pub decision: SyncDecision,
}
impl RootExchangeResponse {
#[must_use]
pub fn from_request(request: &RootExchangeRequest, source_root: Option<Hash>) -> Self {
let exchange = RootExchange::new(request.shard_id, source_root, request.target_root);
Self {
shard_id: request.shard_id,
source_root,
target_root: request.target_root,
decision: exchange.decision,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PullRequest {
pub shard_id: ShardId,
pub target_root: Option<Hash>,
}
impl PullRequest {
#[must_use]
pub const fn new(shard_id: ShardId, target_root: Option<Hash>) -> Self {
Self {
shard_id,
target_root,
}
}
#[must_use]
pub const fn root_exchange_request(self) -> RootExchangeRequest {
RootExchangeRequest::new(self.shard_id, self.target_root)
}
}