use crate::error::{Result, StarError}; use crate::types::{StarMstNode, calculate_height}; /// Validates the structure of a STAR MST node. /// /// Checks: /// - Key height consistency (all keys must have same height) /// - Height matching against expectation /// - Structural rules (Height 0 vs >0 constraints) /// - Implicit/Explicit V rules /// /// Returns (height, reconstructed_keys) pub fn validate_node_structure( node: &StarMstNode, expected: Option, ) -> Result<(u32, Vec>)> { let mut node_height = None; let mut prev_key_bytes = Vec::new(); let mut entry_keys = Vec::new(); for e in &node.e { let mut key = if e.p as usize <= prev_key_bytes.len() { prev_key_bytes[..e.p as usize].to_vec() } else { prev_key_bytes.clone() }; key.extend_from_slice(&e.k); let h = calculate_height(&key); if let Some(existing) = node_height { if h != existing { return Err(StarError::InvalidStructure(format!( "Inconsistent key height in node: {} vs {}", h, existing ))); } } else { node_height = Some(h); } entry_keys.push(key.clone()); prev_key_bytes = key; } let height = match (node_height, expected) { (Some(h), Some(exp)) => { if h != exp { return Err(StarError::HeightMismatch { found: h, expected: exp, }); } h } (Some(h), None) => h, (None, Some(exp)) => { if exp == 0 { return Err(StarError::InvalidStructure( "Height 0 cannot be empty".into(), )); } exp } (None, None) => return Err(StarError::InvalidStructure("Root cannot be empty".into())), }; if height == 0 { if node.l.is_some() || node.l_archived.is_some() { return Err(StarError::InvalidStructure( "Height 0 node cannot have left child".into(), )); } for e in &node.e { if e.t.is_some() || e.t_archived.is_some() { return Err(StarError::InvalidStructure( "Height 0 entries cannot have subtrees".into(), )); } if e.v.is_some() { return Err(StarError::InvalidStructure( "Height 0 node must omit record CIDs".into(), )); } } } else { if node.e.is_empty() && node.l.is_none() { return Err(StarError::InvalidStructure( "Empty intermediate node must have left child".into(), )); } for e in &node.e { if e.v.is_none() { return Err(StarError::InvalidStructure( "Intermediate node must include record CIDs".into(), )); } } } Ok((height, entry_keys)) }