use super::handshake::SyncHandshake;
pub trait LocalSyncState {
fn root_hash(&self) -> [u8; 32];
fn entity_count(&self) -> u64;
fn max_depth(&self) -> u32;
fn dag_heads(&self) -> Vec<[u8; 32]>;
fn has_state(&self) -> bool {
self.root_hash() != [0; 32]
}
}
#[must_use]
pub fn build_handshake<T: LocalSyncState>(state: &T) -> SyncHandshake {
SyncHandshake::new(
state.root_hash(),
state.entity_count(),
state.max_depth(),
state.dag_heads(),
)
}
#[must_use]
pub fn build_handshake_from_raw(
root_hash: [u8; 32],
entity_count: u64,
max_depth: u32,
dag_heads: Vec<[u8; 32]>,
) -> SyncHandshake {
SyncHandshake::new(root_hash, entity_count, max_depth, dag_heads)
}
#[must_use]
pub fn estimate_entity_count(root_hash: [u8; 32], dag_heads_len: usize) -> u64 {
if root_hash == [0; 32] {
0
} else if dag_heads_len == 0 {
1 } else {
dag_heads_len as u64
}
}
#[must_use]
pub fn estimate_max_depth(entity_count: u64) -> u32 {
if entity_count == 0 {
0
} else {
let log2_approx = 64u32.saturating_sub(entity_count.leading_zeros());
log2_approx.max(1).min(32)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestNode {
root_hash: [u8; 32],
entity_count: u64,
max_depth: u32,
dag_heads: Vec<[u8; 32]>,
}
impl LocalSyncState for TestNode {
fn root_hash(&self) -> [u8; 32] {
self.root_hash
}
fn entity_count(&self) -> u64 {
self.entity_count
}
fn max_depth(&self) -> u32 {
self.max_depth
}
fn dag_heads(&self) -> Vec<[u8; 32]> {
self.dag_heads.clone()
}
}
#[test]
fn test_build_handshake_fresh_node() {
let node = TestNode {
root_hash: [0; 32],
entity_count: 0,
max_depth: 0,
dag_heads: vec![],
};
let hs = build_handshake(&node);
assert_eq!(hs.root_hash, [0; 32]);
assert_eq!(hs.entity_count, 0);
assert_eq!(hs.max_depth, 0);
assert!(hs.dag_heads.is_empty());
assert!(!hs.has_state);
}
#[test]
fn test_build_handshake_initialized_node() {
let node = TestNode {
root_hash: [42; 32],
entity_count: 100,
max_depth: 5,
dag_heads: vec![[1; 32], [2; 32]],
};
let hs = build_handshake(&node);
assert_eq!(hs.root_hash, [42; 32]);
assert_eq!(hs.entity_count, 100);
assert_eq!(hs.max_depth, 5);
assert_eq!(hs.dag_heads.len(), 2);
assert!(hs.has_state);
}
#[test]
fn test_estimate_entity_count() {
assert_eq!(estimate_entity_count([0; 32], 0), 0);
assert_eq!(estimate_entity_count([1; 32], 0), 1);
assert_eq!(estimate_entity_count([1; 32], 5), 5);
}
#[test]
fn test_estimate_max_depth() {
assert_eq!(estimate_max_depth(0), 0);
assert_eq!(estimate_max_depth(1), 1);
assert_eq!(estimate_max_depth(2), 2);
assert_eq!(estimate_max_depth(16), 5); assert_eq!(estimate_max_depth(256), 9); }
#[test]
fn test_has_state_default_implementation() {
let fresh = TestNode {
root_hash: [0; 32],
entity_count: 0,
max_depth: 0,
dag_heads: vec![],
};
assert!(!fresh.has_state());
let initialized = TestNode {
root_hash: [1; 32],
entity_count: 1,
max_depth: 1,
dag_heads: vec![],
};
assert!(initialized.has_state());
}
}