use borsh::{BorshDeserialize, BorshSerialize};
use super::hash_comparison::TreeLeafData;
pub const DEFAULT_SUBTREE_MAX_DEPTH: usize = 5;
pub const MAX_SUBTREE_DEPTH: usize = 64;
pub const MAX_SUBTREES_PER_REQUEST: usize = 100;
pub const MAX_ENTITIES_PER_SUBTREE: usize = 10_000;
pub const MAX_TOTAL_ENTITIES: usize = 100_000;
pub const DEEP_TREE_THRESHOLD: usize = 3;
pub const MAX_DIVERGENCE_RATIO: f64 = 0.20;
pub const MAX_CLUSTERED_SUBTREES: usize = 5;
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
pub struct SubtreePrefetchRequest {
pub subtree_roots: Vec<[u8; 32]>,
max_depth: Option<usize>,
}
impl SubtreePrefetchRequest {
#[must_use]
pub fn new(subtree_roots: Vec<[u8; 32]>) -> Self {
Self {
subtree_roots,
max_depth: Some(DEFAULT_SUBTREE_MAX_DEPTH),
}
}
#[must_use]
pub fn with_depth(subtree_roots: Vec<[u8; 32]>, max_depth: usize) -> Self {
Self {
subtree_roots,
max_depth: Some(max_depth.min(MAX_SUBTREE_DEPTH)),
}
}
#[must_use]
pub fn unlimited_depth(subtree_roots: Vec<[u8; 32]>) -> Self {
Self {
subtree_roots,
max_depth: None,
}
}
#[must_use]
pub fn depth(&self) -> usize {
self.max_depth
.map(|d| d.min(MAX_SUBTREE_DEPTH))
.unwrap_or(MAX_SUBTREE_DEPTH)
}
#[must_use]
pub fn is_unlimited(&self) -> bool {
self.max_depth.is_none()
}
#[must_use]
pub fn subtree_count(&self) -> usize {
self.subtree_roots.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.subtree_roots.is_empty()
}
#[must_use]
pub fn is_valid(&self) -> bool {
if self.subtree_roots.len() > MAX_SUBTREES_PER_REQUEST {
return false;
}
if let Some(depth) = self.max_depth {
if depth > MAX_SUBTREE_DEPTH {
return false;
}
}
true
}
}
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
pub struct SubtreePrefetchResponse {
pub subtrees: Vec<SubtreeData>,
pub not_found: Vec<[u8; 32]>,
}
impl SubtreePrefetchResponse {
#[must_use]
pub fn new(subtrees: Vec<SubtreeData>, not_found: Vec<[u8; 32]>) -> Self {
Self {
subtrees,
not_found,
}
}
#[must_use]
pub fn complete(subtrees: Vec<SubtreeData>) -> Self {
Self {
subtrees,
not_found: vec![],
}
}
#[must_use]
pub fn not_found(roots: Vec<[u8; 32]>) -> Self {
Self {
subtrees: vec![],
not_found: roots,
}
}
#[must_use]
pub fn is_complete(&self) -> bool {
self.not_found.is_empty()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.subtrees.is_empty() && self.not_found.is_empty()
}
#[must_use]
pub fn total_entity_count(&self) -> usize {
self.subtrees
.iter()
.fold(0usize, |acc, s| acc.saturating_add(s.entity_count()))
}
#[must_use]
pub fn subtree_count(&self) -> usize {
self.subtrees.len()
}
#[must_use]
pub fn is_valid(&self) -> bool {
if self.subtrees.len() > MAX_SUBTREES_PER_REQUEST {
return false;
}
if self.not_found.len() > MAX_SUBTREES_PER_REQUEST {
return false;
}
if self.total_entity_count() > MAX_TOTAL_ENTITIES {
return false;
}
self.subtrees.iter().all(SubtreeData::is_valid)
}
}
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
pub struct SubtreeData {
pub root_id: [u8; 32],
pub root_hash: [u8; 32],
pub entities: Vec<TreeLeafData>,
pub depth: usize,
pub truncated: bool,
}
impl SubtreeData {
#[must_use]
pub fn new(
root_id: [u8; 32],
root_hash: [u8; 32],
entities: Vec<TreeLeafData>,
depth: usize,
) -> Self {
Self {
root_id,
root_hash,
entities,
depth,
truncated: false,
}
}
#[must_use]
pub fn truncated(
root_id: [u8; 32],
root_hash: [u8; 32],
entities: Vec<TreeLeafData>,
depth: usize,
) -> Self {
Self {
root_id,
root_hash,
entities,
depth,
truncated: true,
}
}
#[must_use]
pub fn entity_count(&self) -> usize {
self.entities.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entities.is_empty()
}
#[must_use]
pub fn is_truncated(&self) -> bool {
self.truncated
}
#[must_use]
pub fn is_valid(&self) -> bool {
if self.entities.len() > MAX_ENTITIES_PER_SUBTREE {
return false;
}
if self.depth > MAX_SUBTREE_DEPTH {
return false;
}
self.entities.iter().all(TreeLeafData::is_valid)
}
}
#[must_use]
pub fn should_use_subtree_prefetch(
tree_depth: usize,
divergence_ratio: f64,
estimated_differing_subtrees: usize,
) -> bool {
let deep_tree = tree_depth > DEEP_TREE_THRESHOLD;
let moderate_divergence = divergence_ratio < MAX_DIVERGENCE_RATIO;
let clustered_changes = estimated_differing_subtrees <= MAX_CLUSTERED_SUBTREES;
deep_tree && moderate_divergence && clustered_changes
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sync::hash_comparison::{CrdtType, LeafMetadata, MAX_LEAF_VALUE_SIZE};
fn make_leaf(key: u8, value: Vec<u8>) -> TreeLeafData {
let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [key; 32]);
TreeLeafData::new([key; 32], value, metadata)
}
fn make_subtree(root_id: u8, entities: Vec<TreeLeafData>, depth: usize) -> SubtreeData {
SubtreeData::new([root_id; 32], [root_id + 100; 32], entities, depth)
}
#[test]
fn test_subtree_prefetch_request_new() {
let roots = vec![[1u8; 32], [2u8; 32]];
let request = SubtreePrefetchRequest::new(roots.clone());
assert_eq!(request.subtree_roots, roots);
assert_eq!(request.depth(), DEFAULT_SUBTREE_MAX_DEPTH);
assert!(!request.is_unlimited());
assert_eq!(request.subtree_count(), 2);
assert!(!request.is_empty());
assert!(request.is_valid());
}
#[test]
fn test_subtree_prefetch_request_empty() {
let request = SubtreePrefetchRequest::new(vec![]);
assert!(request.is_empty());
assert_eq!(request.subtree_count(), 0);
assert!(request.is_valid());
}
#[test]
fn test_subtree_prefetch_request_with_depth() {
let roots = vec![[1u8; 32]];
let request = SubtreePrefetchRequest::with_depth(roots, 10);
assert_eq!(request.depth(), 10);
assert!(!request.is_unlimited());
assert!(request.is_valid());
}
#[test]
fn test_subtree_prefetch_request_with_zero_depth() {
let roots = vec![[1u8; 32]];
let request = SubtreePrefetchRequest::with_depth(roots, 0);
assert_eq!(request.depth(), 0);
assert!(!request.is_unlimited());
assert!(request.is_valid());
}
#[test]
fn test_subtree_prefetch_request_depth_clamping() {
let request = SubtreePrefetchRequest::with_depth(vec![[1u8; 32]], MAX_SUBTREE_DEPTH);
assert_eq!(request.depth(), MAX_SUBTREE_DEPTH);
assert!(request.is_valid());
let excessive =
SubtreePrefetchRequest::with_depth(vec![[1u8; 32]], MAX_SUBTREE_DEPTH + 100);
assert_eq!(excessive.depth(), MAX_SUBTREE_DEPTH);
assert!(excessive.is_valid());
}
#[test]
fn test_subtree_prefetch_request_depth_accessor_always_bounded() {
let request = SubtreePrefetchRequest::new(vec![[1u8; 32]]);
assert_eq!(request.depth(), DEFAULT_SUBTREE_MAX_DEPTH);
let unlimited = SubtreePrefetchRequest::unlimited_depth(vec![[1u8; 32]]);
assert_eq!(unlimited.depth(), MAX_SUBTREE_DEPTH);
assert!(unlimited.is_unlimited());
}
#[test]
fn test_subtree_prefetch_request_unlimited() {
let roots = vec![[1u8; 32]];
let request = SubtreePrefetchRequest::unlimited_depth(roots);
assert!(request.is_unlimited());
assert_eq!(request.depth(), MAX_SUBTREE_DEPTH);
assert!(request.is_valid());
}
#[test]
fn test_subtree_prefetch_request_roundtrip() {
let request = SubtreePrefetchRequest::with_depth(vec![[1u8; 32], [2u8; 32]], 7);
let encoded = borsh::to_vec(&request).expect("serialize");
let decoded: SubtreePrefetchRequest = borsh::from_slice(&encoded).expect("deserialize");
assert_eq!(request, decoded);
}
#[test]
fn test_subtree_prefetch_request_validation() {
let roots: Vec<[u8; 32]> = (0..MAX_SUBTREES_PER_REQUEST)
.map(|i| [i as u8; 32])
.collect();
let at_limit = SubtreePrefetchRequest::new(roots);
assert!(at_limit.is_valid());
let roots: Vec<[u8; 32]> = (0..=MAX_SUBTREES_PER_REQUEST)
.map(|i| [i as u8; 32])
.collect();
let over_limit = SubtreePrefetchRequest::new(roots);
assert!(!over_limit.is_valid());
}
#[test]
fn test_subtree_data_new() {
let leaf = make_leaf(1, vec![1, 2, 3]);
let subtree = SubtreeData::new([10; 32], [11; 32], vec![leaf], 3);
assert_eq!(subtree.root_id, [10; 32]);
assert_eq!(subtree.root_hash, [11; 32]);
assert_eq!(subtree.entity_count(), 1);
assert_eq!(subtree.depth, 3);
assert!(!subtree.is_truncated());
assert!(!subtree.is_empty());
assert!(subtree.is_valid());
}
#[test]
fn test_subtree_data_truncated() {
let leaf = make_leaf(2, vec![4, 5, 6]);
let subtree = SubtreeData::truncated([20; 32], [21; 32], vec![leaf], 5);
assert!(subtree.is_truncated());
assert_eq!(subtree.depth, 5);
assert!(subtree.is_valid());
}
#[test]
fn test_subtree_data_empty() {
let subtree = SubtreeData::new([30; 32], [31; 32], vec![], 1);
assert!(subtree.is_empty());
assert_eq!(subtree.entity_count(), 0);
assert!(subtree.is_valid());
}
#[test]
fn test_subtree_data_zero_depth() {
let leaf = make_leaf(1, vec![1, 2, 3]);
let subtree = SubtreeData::new([10; 32], [11; 32], vec![leaf], 0);
assert_eq!(subtree.depth, 0);
assert!(subtree.is_valid());
}
#[test]
fn test_subtree_data_multiple_entities() {
let leaves = vec![
make_leaf(1, vec![1, 2, 3]),
make_leaf(2, vec![4, 5, 6]),
make_leaf(3, vec![7, 8, 9]),
];
let subtree = SubtreeData::new([10; 32], [11; 32], leaves, 3);
assert_eq!(subtree.entity_count(), 3);
assert!(!subtree.is_empty());
assert!(subtree.is_valid());
}
#[test]
fn test_subtree_data_roundtrip() {
let leaf = make_leaf(3, vec![7, 8, 9]);
let subtree = SubtreeData::truncated([40; 32], [41; 32], vec![leaf], 4);
let encoded = borsh::to_vec(&subtree).expect("serialize");
let decoded: SubtreeData = borsh::from_slice(&encoded).expect("deserialize");
assert_eq!(subtree, decoded);
}
#[test]
fn test_subtree_data_validation() {
let valid_leaf = make_leaf(1, vec![1, 2, 3]);
let valid = SubtreeData::new([1; 32], [2; 32], vec![valid_leaf], 2);
assert!(valid.is_valid());
let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [1; 32]);
let invalid_leaf = TreeLeafData::new([1; 32], vec![0u8; MAX_LEAF_VALUE_SIZE + 1], metadata);
let invalid = SubtreeData::new([1; 32], [2; 32], vec![invalid_leaf], 2);
assert!(!invalid.is_valid());
}
#[test]
fn test_subtree_prefetch_response_complete() {
let leaf = make_leaf(1, vec![1, 2, 3]);
let subtree = make_subtree(10, vec![leaf], 2);
let response = SubtreePrefetchResponse::complete(vec![subtree]);
assert!(response.is_complete());
assert!(!response.is_empty());
assert_eq!(response.subtree_count(), 1);
assert_eq!(response.total_entity_count(), 1);
assert!(response.is_valid());
}
#[test]
fn test_subtree_prefetch_response_not_found() {
let response = SubtreePrefetchResponse::not_found(vec![[1u8; 32], [2u8; 32]]);
assert!(!response.is_complete());
assert!(!response.is_empty());
assert_eq!(response.subtree_count(), 0);
assert_eq!(response.not_found.len(), 2);
assert!(response.is_valid());
}
#[test]
fn test_subtree_prefetch_response_empty() {
let response = SubtreePrefetchResponse::new(vec![], vec![]);
assert!(response.is_complete());
assert!(response.is_empty());
assert_eq!(response.subtree_count(), 0);
assert_eq!(response.total_entity_count(), 0);
assert!(response.is_valid());
}
#[test]
fn test_subtree_prefetch_response_partial() {
let leaf1 = make_leaf(1, vec![1, 2]);
let leaf2 = make_leaf(2, vec![3, 4]);
let subtree1 = make_subtree(10, vec![leaf1], 2);
let subtree2 = make_subtree(20, vec![leaf2], 3);
let response = SubtreePrefetchResponse::new(
vec![subtree1, subtree2],
vec![[30u8; 32]], );
assert!(!response.is_complete());
assert!(!response.is_empty());
assert_eq!(response.subtree_count(), 2);
assert_eq!(response.total_entity_count(), 2);
assert!(response.is_valid());
}
#[test]
fn test_subtree_prefetch_response_with_empty_subtrees() {
let leaf = make_leaf(1, vec![1, 2, 3]);
let populated = make_subtree(10, vec![leaf], 2);
let empty = make_subtree(20, vec![], 1);
let response = SubtreePrefetchResponse::complete(vec![populated, empty]);
assert!(response.is_complete());
assert_eq!(response.subtree_count(), 2);
assert_eq!(response.total_entity_count(), 1); assert!(response.is_valid());
}
#[test]
fn test_subtree_prefetch_response_total_entity_count_multiple() {
let subtree1 = make_subtree(1, vec![make_leaf(1, vec![1]), make_leaf(2, vec![2])], 2);
let subtree2 = make_subtree(
2,
vec![
make_leaf(3, vec![3]),
make_leaf(4, vec![4]),
make_leaf(5, vec![5]),
],
3,
);
let subtree3 = make_subtree(3, vec![], 1);
let response = SubtreePrefetchResponse::complete(vec![subtree1, subtree2, subtree3]);
assert_eq!(response.subtree_count(), 3);
assert_eq!(response.total_entity_count(), 5); }
#[test]
fn test_subtree_prefetch_response_roundtrip() {
let leaf = make_leaf(4, vec![10, 11, 12]);
let subtree = make_subtree(50, vec![leaf], 2);
let response = SubtreePrefetchResponse::new(vec![subtree], vec![[60u8; 32]]);
let encoded = borsh::to_vec(&response).expect("serialize");
let decoded: SubtreePrefetchResponse = borsh::from_slice(&encoded).expect("deserialize");
assert_eq!(response, decoded);
}
#[test]
fn test_subtree_prefetch_response_validation() {
let subtrees: Vec<SubtreeData> = (0..MAX_SUBTREES_PER_REQUEST)
.map(|i| make_subtree(i as u8, vec![], 1))
.collect();
let at_limit = SubtreePrefetchResponse::complete(subtrees);
assert!(at_limit.is_valid());
let subtrees: Vec<SubtreeData> = (0..=MAX_SUBTREES_PER_REQUEST)
.map(|i| make_subtree(i as u8, vec![], 1))
.collect();
let over_limit = SubtreePrefetchResponse::complete(subtrees);
assert!(!over_limit.is_valid());
let not_found: Vec<[u8; 32]> = (0..=MAX_SUBTREES_PER_REQUEST)
.map(|i| [i as u8; 32])
.collect();
let over_not_found = SubtreePrefetchResponse::not_found(not_found);
assert!(!over_not_found.is_valid());
let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [1; 32]);
let invalid_leaf = TreeLeafData::new([1; 32], vec![0u8; MAX_LEAF_VALUE_SIZE + 1], metadata);
let invalid_subtree = SubtreeData::new([1; 32], [2; 32], vec![invalid_leaf], 2);
let response_with_invalid = SubtreePrefetchResponse::complete(vec![invalid_subtree]);
assert!(!response_with_invalid.is_valid());
}
#[test]
fn test_heuristic_constants_are_sensible() {
assert!(DEEP_TREE_THRESHOLD > 0);
assert!(DEEP_TREE_THRESHOLD < MAX_SUBTREE_DEPTH);
assert!(MAX_DIVERGENCE_RATIO > 0.0);
assert!(MAX_DIVERGENCE_RATIO < 1.0);
assert!(MAX_CLUSTERED_SUBTREES > 0);
assert!(MAX_CLUSTERED_SUBTREES <= MAX_SUBTREES_PER_REQUEST);
}
#[test]
fn test_should_use_subtree_prefetch_basic() {
assert!(should_use_subtree_prefetch(5, 0.10, 3));
assert!(!should_use_subtree_prefetch(5, 0.30, 3));
assert!(!should_use_subtree_prefetch(2, 0.10, 3));
assert!(!should_use_subtree_prefetch(5, 0.10, 10));
}
#[test]
fn test_should_use_subtree_prefetch_boundary_conditions() {
assert!(!should_use_subtree_prefetch(DEEP_TREE_THRESHOLD, 0.10, 3));
assert!(should_use_subtree_prefetch(
DEEP_TREE_THRESHOLD + 1,
0.10,
3
));
assert!(!should_use_subtree_prefetch(5, MAX_DIVERGENCE_RATIO, 3));
assert!(should_use_subtree_prefetch(
5,
MAX_DIVERGENCE_RATIO - 0.01,
3
));
assert!(should_use_subtree_prefetch(
5,
MAX_DIVERGENCE_RATIO - 0.000001,
3
));
assert!(should_use_subtree_prefetch(5, 0.10, MAX_CLUSTERED_SUBTREES));
assert!(!should_use_subtree_prefetch(
5,
0.10,
MAX_CLUSTERED_SUBTREES + 1
));
}
#[test]
fn test_should_use_subtree_prefetch_edge_cases() {
assert!(!should_use_subtree_prefetch(0, 0.10, 3)); assert!(should_use_subtree_prefetch(5, 0.0, 3)); assert!(should_use_subtree_prefetch(5, 0.10, 0));
assert!(should_use_subtree_prefetch(1000, 0.10, 3)); assert!(!should_use_subtree_prefetch(5, 1.0, 3)); assert!(!should_use_subtree_prefetch(5, 10.0, 3)); assert!(!should_use_subtree_prefetch(5, 0.10, 1000));
assert!(!should_use_subtree_prefetch(2, 0.50, 10));
assert!(should_use_subtree_prefetch(100, 0.001, 1));
}
#[test]
fn test_should_use_subtree_prefetch_typical_scenarios() {
assert!(should_use_subtree_prefetch(10, 0.05, 2));
assert!(!should_use_subtree_prefetch(2, 0.05, 2));
assert!(!should_use_subtree_prefetch(10, 0.05, 20));
assert!(!should_use_subtree_prefetch(10, 0.60, 2));
assert!(should_use_subtree_prefetch(4, 0.15, 5));
}
#[test]
fn test_subtree_data_validation_entity_limit() {
let leaves: Vec<TreeLeafData> = (0..MAX_ENTITIES_PER_SUBTREE)
.map(|i| make_leaf(i as u8, vec![i as u8]))
.collect();
let at_limit = SubtreeData::new([1; 32], [2; 32], leaves, 5);
assert!(at_limit.is_valid());
let leaves: Vec<TreeLeafData> = (0..=MAX_ENTITIES_PER_SUBTREE)
.map(|i| make_leaf(i as u8, vec![i as u8]))
.collect();
let over_limit = SubtreeData::new([1; 32], [2; 32], leaves, 5);
assert!(!over_limit.is_valid());
}
#[test]
fn test_subtree_data_validation_depth_limit() {
let leaf = make_leaf(1, vec![1, 2, 3]);
let at_limit = SubtreeData::new([1; 32], [2; 32], vec![leaf.clone()], MAX_SUBTREE_DEPTH);
assert!(at_limit.is_valid());
let over_limit = SubtreeData::new([1; 32], [2; 32], vec![leaf], MAX_SUBTREE_DEPTH + 1);
assert!(!over_limit.is_valid());
}
#[test]
fn test_subtree_response_validation_total_entity_limit() {
use super::MAX_TOTAL_ENTITIES;
let entities_per_subtree = 1000;
let num_subtrees = (MAX_TOTAL_ENTITIES / entities_per_subtree) + 1;
let subtrees: Vec<SubtreeData> = (0..num_subtrees)
.map(|i| {
let leaves: Vec<TreeLeafData> = (0..entities_per_subtree)
.map(|j| make_leaf((i * 100 + j) as u8, vec![(i * 100 + j) as u8]))
.collect();
SubtreeData::new([i as u8; 32], [(i + 100) as u8; 32], leaves, 5)
})
.collect();
let response = SubtreePrefetchResponse::complete(subtrees);
assert!(!response.is_valid()); }
#[test]
fn test_subtree_request_memory_exhaustion_prevention() {
let roots: Vec<[u8; 32]> = (0..MAX_SUBTREES_PER_REQUEST)
.map(|i| [i as u8; 32])
.collect();
let valid = SubtreePrefetchRequest::new(roots);
assert!(valid.is_valid());
let roots: Vec<[u8; 32]> = (0..=MAX_SUBTREES_PER_REQUEST)
.map(|i| [i as u8; 32])
.collect();
let invalid = SubtreePrefetchRequest::new(roots);
assert!(!invalid.is_valid());
}
#[test]
fn test_subtree_depth_exhaustion_prevention() {
let request = SubtreePrefetchRequest::with_depth(vec![[1u8; 32]], usize::MAX);
assert_eq!(request.depth(), MAX_SUBTREE_DEPTH);
assert!(request.is_valid());
let unlimited = SubtreePrefetchRequest::unlimited_depth(vec![[1u8; 32]]);
assert_eq!(unlimited.depth(), MAX_SUBTREE_DEPTH);
assert!(unlimited.is_valid());
}
#[test]
fn test_subtree_request_max_depth_validation() {
let at_limit = SubtreePrefetchRequest::with_depth(vec![[1u8; 32]], MAX_SUBTREE_DEPTH);
assert!(at_limit.is_valid());
let valid = SubtreePrefetchRequest::with_depth(vec![[1u8; 32]], 10);
let mut bytes = borsh::to_vec(&valid).expect("serialize");
let depth_offset = 4 + 32 + 1; bytes[depth_offset..depth_offset + 8]
.copy_from_slice(&(MAX_SUBTREE_DEPTH as u64 + 100).to_le_bytes());
let corrupted: SubtreePrefetchRequest = borsh::from_slice(&bytes).expect("deserialize");
assert_eq!(corrupted.depth(), MAX_SUBTREE_DEPTH);
assert!(!corrupted.is_valid());
}
#[test]
fn test_subtree_total_entity_count_overflow_prevention() {
let leaf = make_leaf(1, vec![1, 2, 3]);
let subtree = SubtreeData::new([1; 32], [2; 32], vec![leaf], 2);
let response = SubtreePrefetchResponse::complete(vec![subtree]);
let count = response.total_entity_count();
assert_eq!(count, 1);
}
#[test]
fn test_subtree_cross_validation_consistency() {
let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [1; 32]);
let oversized_leaf =
TreeLeafData::new([1; 32], vec![0u8; MAX_LEAF_VALUE_SIZE + 1], metadata);
let invalid_subtree = SubtreeData::new([1; 32], [2; 32], vec![oversized_leaf], 2);
assert!(!invalid_subtree.is_valid());
let response = SubtreePrefetchResponse::complete(vec![invalid_subtree]);
assert!(!response.is_valid());
}
#[test]
fn test_subtree_special_values() {
let zeros_subtree = SubtreeData::new([0u8; 32], [0u8; 32], vec![], 0);
assert!(zeros_subtree.is_valid());
let ones_subtree = SubtreeData::new([0xFF; 32], [0xFF; 32], vec![], MAX_SUBTREE_DEPTH);
assert!(ones_subtree.is_valid());
let request = SubtreePrefetchRequest::new(vec![[0u8; 32]]);
assert!(request.is_valid());
let request = SubtreePrefetchRequest::new(vec![[0xFF; 32]]);
assert!(request.is_valid());
}
#[test]
fn test_subtree_serialization_roundtrip_with_edge_values() {
let leaf = make_leaf(0xFF, vec![0xFF; 100]);
let subtree = SubtreeData::truncated([0xFF; 32], [0u8; 32], vec![leaf], MAX_SUBTREE_DEPTH);
let encoded = borsh::to_vec(&subtree).expect("serialize");
let decoded: SubtreeData = borsh::from_slice(&encoded).expect("deserialize");
assert_eq!(subtree, decoded);
assert!(decoded.is_valid());
assert!(decoded.is_truncated());
assert_eq!(decoded.depth, MAX_SUBTREE_DEPTH);
}
#[test]
fn test_subtree_response_all_not_found() {
let not_found: Vec<[u8; 32]> = (0..50).map(|i| [i as u8; 32]).collect();
let response = SubtreePrefetchResponse::not_found(not_found);
assert!(!response.is_complete());
assert!(!response.is_empty());
assert_eq!(response.subtree_count(), 0);
assert_eq!(response.total_entity_count(), 0);
assert!(response.is_valid());
}
#[test]
fn test_subtree_empty_entities_is_valid() {
let empty_subtree = SubtreeData::new([1; 32], [2; 32], vec![], 5);
assert!(empty_subtree.is_valid());
assert!(empty_subtree.is_empty());
assert_eq!(empty_subtree.entity_count(), 0);
}
}