···12121313pub use error::{Result, StarError};
1414pub use parser::StarParser;
1515-pub use ser::{StarEncoder, StarSerializer};
1515+pub use ser::{StarEncoder, StarSerializer, StarValidator};
1616pub use types::{RepoMstEntry, RepoMstNode, StarCommit, StarItem, StarMstEntry, StarMstNode};
1717-pub use validation::StarValidator;
18171918#[cfg(feature = "blocking")]
2019pub use blocking::StarIterator;
+42-152
src/parser.rs
···11use crate::error::{Result, StarError};
22use crate::types::{StarCommit, StarItem, StarMstNode};
33+use crate::validation::validate_node_structure;
34use cid::Cid;
45use sha2::{Digest, Sha256};
5667#[derive(Debug)]
78enum State {
89 Header,
99- Body {
1010+ Body {
1011 stack: Vec<StackItem>,
1112 current_len: Option<usize>,
1213 },
···1718enum StackItem {
1819 Node {
1920 expected: Option<Cid>,
2020- expected_height: Option<u32>, // The node at this position must have height exactly == this value (if set)
2121+ expected_height: Option<u32>,
2122 },
2223 Record {
2324 key: Vec<u8>,
···45464647 pub fn parse(&mut self, buf: &[u8]) -> Result<(usize, Option<StarItem>)> {
4748 let mut consumed = 0;
4848-4949+4950 loop {
5051 let is_body_done = if let State::Body { stack, .. } = &self.state {
5152 stack.is_empty()
···7980 Some((n, len)) => (n, len),
8081 None => return Ok((consumed, None)),
8182 };
8282-8383+8384 let body_buf = ¤t_buf[len_consumed..];
8485 if body_buf.len() < len {
8586 consumed += len_consumed;
···88898990 let block_bytes = &body_buf[..len];
9091 *current_len = None;
9191-9292+9293 let item = stack.pop().unwrap();
9394 let result_item = match item {
9494- StackItem::Node {
9595- expected,
9696- expected_height,
9797- } => Self::process_node(block_bytes, expected, expected_height, stack)?,
9898- StackItem::Record {
9999- key,
100100- expected,
101101- implicit_index,
102102- } => {
9595+ StackItem::Node { expected, expected_height } => {
9696+ Self::process_node(block_bytes, expected, expected_height, stack)?
9797+ },
9898+ StackItem::Record { key, expected, implicit_index } => {
10399 Self::process_record(block_bytes, key, expected, implicit_index, stack)?
104104- }
100100+ },
105101 _ => return Err(StarError::InvalidState("Unexpected stack item".into())),
106102 };
107103···112108 }
113109 }
114110115115- fn calculate_height(key: &[u8]) -> u32 {
116116- let digest = Sha256::digest(key);
117117- let mut zeros = 0;
118118- for &byte in digest.iter() {
119119- if byte == 0 {
120120- zeros += 8;
121121- } else {
122122- zeros += byte.leading_zeros();
123123- break;
124124- }
125125- }
126126- zeros / 2
127127- }
128128-129111 fn parse_header(&mut self, buf: &[u8]) -> Result<Option<(usize, StarItem)>> {
130112 if buf.len() < 1 {
131113 return Ok(None);
···135117 }
136118137119 let slice = &buf[1..];
138138-120120+139121 let (ver, remaining1) = match unsigned_varint::decode::usize(slice) {
140122 Ok(res) => res,
141123 Err(unsigned_varint::decode::Error::Insufficient) => return Ok(None),
142124 Err(e) => return Err(StarError::InvalidState(format!("Varint error: {}", e))),
143125 };
144144-126126+145127 let (len, remaining2) = match unsigned_varint::decode::usize(remaining1) {
146128 Ok(res) => res,
147129 Err(unsigned_varint::decode::Error::Insufficient) => return Ok(None),
148130 Err(e) => return Err(StarError::InvalidState(format!("Varint error: {}", e))),
149131 };
150132151151- let header_varints_len = buf.len() - 1 - remaining2.len();
152152- let total_header_len = 1 + header_varints_len;
133133+ let header_varints_len = buf.len() - 1 - remaining2.len();
134134+ let total_header_len = 1 + header_varints_len;
153135 let total_len = total_header_len + len;
154136155137 if buf.len() < total_len {
···159141 let commit_bytes = &buf[total_header_len..total_len];
160142 let commit: StarCommit = serde_ipld_dagcbor::from_slice(commit_bytes)
161143 .map_err(|e| StarError::Cbor(e.to_string()))?;
162162-163163- let _ = ver;
144144+145145+ let _ = ver;
164146165147 let mut stack = Vec::new();
166148 if let Some(root_cid) = commit.data {
···170152 });
171153 }
172154173173- self.state = State::Body {
155155+ self.state = State::Body {
174156 stack,
175175- current_len: None,
157157+ current_len: None
176158 };
177159 Ok(Some((total_len, StarItem::Commit(commit))))
178160 }
···223205 let consumed = buf.len() - remaining.len();
224206 *current_len = Some(l);
225207 Ok(Some((consumed, l)))
226226- }
208208+ },
227209 Err(unsigned_varint::decode::Error::Insufficient) => Ok(None),
228210 Err(e) => Err(StarError::InvalidState(format!("Varint error: {}", e))),
229211 }
230212 }
231213232232- fn process_node(
233233- block_bytes: &[u8],
234234- expected: Option<Cid>,
235235- expected_height: Option<u32>,
236236- stack: &mut Vec<StackItem>,
237237- ) -> Result<Option<StarItem>> {
214214+ fn process_node(block_bytes: &[u8], expected: Option<Cid>, expected_height: Option<u32>, stack: &mut Vec<StackItem>) -> Result<Option<StarItem>> {
238215 let node: StarMstNode = serde_ipld_dagcbor::from_slice(block_bytes)
239216 .map_err(|e| StarError::Cbor(e.to_string()))?;
240217241241- // We run the loop to reconstruct keys and validate their internal consistency first.
242242- let mut prev_key_bytes = Vec::new();
243243- let mut entry_keys = Vec::new();
244244- let mut node_height = None;
245245-246246- for e in &node.e {
247247- let mut key = if e.p as usize <= prev_key_bytes.len() {
248248- prev_key_bytes[..e.p as usize].to_vec()
249249- } else {
250250- prev_key_bytes.clone()
251251- };
252252- key.extend_from_slice(&e.k);
253253-254254- let h = Self::calculate_height(&key);
255255-256256- if let Some(existing_h) = node_height {
257257- if h != existing_h {
258258- return Err(StarError::InvalidState(format!(
259259- "Inconsistent key height in node: {} vs {}",
260260- h, existing_h
261261- )));
262262- }
263263- } else {
264264- node_height = Some(h);
265265- }
266266-267267- entry_keys.push(key.clone());
268268- prev_key_bytes = key;
269269- }
270270-271271- // 2. Validate Height against Expectation
272272- let height = match (node_height, expected_height) {
273273- (Some(h), Some(exp)) => {
274274- if h != exp {
275275- return Err(StarError::InvalidState(format!(
276276- "Invalid MST height: found {}, expected {}",
277277- h, exp
278278- )));
279279- }
280280- h
281281- }
282282- (Some(h), None) => {
283283- // Root node with keys.
284284- h
285285- }
286286- (None, Some(exp)) => {
287287- // Empty node (intermediate). Must match expectation.
288288- // Constraint: Height 0 cannot be empty.
289289- if exp == 0 {
290290- return Err(StarError::InvalidState(
291291- "MST nodes at depth=0 cannot be empty".into(),
292292- ));
293293- }
294294- exp
295295- }
296296- (None, None) => {
297297- // Root node empty.
298298- return Err(StarError::InvalidState(
299299- "Root node must contain entries".into(),
300300- ));
301301- }
302302- };
218218+ // Use shared validation logic
219219+ let (height, entry_keys) = validate_node_structure(&node, expected_height)?;
303220304304- // 3. Validate Structure based on Height
221221+ // Check for implicit records (needed for VerifyLayer0 logic)
222222+ let mut has_implicit = false;
223223+ // Optimization: validate_node_structure already ensures consistency of v based on height.
224224+ // If height == 0, v is None (Implicit). If height > 0, v is Some (Explicit).
305225 if height == 0 {
306306- // Must have no children
307307- if node.l.is_some() || node.l_archived.is_some() {
308308- return Err(StarError::InvalidState(
309309- "Height 0 node cannot have left child".into(),
310310- ));
311311- }
312226 for e in &node.e {
313313- if e.t.is_some() || e.t_archived.is_some() {
314314- return Err(StarError::InvalidState(
315315- "Height 0 entries cannot have subtrees".into(),
316316- ));
227227+ if e.v_archived == Some(true) {
228228+ has_implicit = true;
229229+ break;
317230 }
318231 }
319319- } else {
320320- // Height > 0
321321- if node.e.is_empty() && node.l.is_none() {
322322- return Err(StarError::InvalidState(
323323- "Empty intermediate node must have left child".into(),
324324- ));
325325- }
326326- }
327327-328328- // Check for implicit records
329329- let mut has_implicit = false;
330330- for e in &node.e {
331331- if e.v_archived == Some(true) && e.v.is_none() {
332332- has_implicit = true;
333333- break;
334334- }
335232 }
336233337234 if !has_implicit {
···340237 .map_err(|e| StarError::Cbor(e.to_string()))?;
341238342239 let hash = Sha256::digest(&bytes);
343343- let cid = Cid::new_v1(0x71, cid::multihash::Multihash::wrap(0x12, &hash)?);
240240+ let cid = Cid::new_v1(
241241+ 0x71,
242242+ cid::multihash::Multihash::wrap(0x12, &hash)?,
243243+ );
344244 if let Some(exp) = expected {
345245 if cid != exp {
346246 return Err(StarError::VerificationFailed {
···359259 }
360260361261 // Push children in reverse
362362- // Expected height for children is strictly height - 1
363262 let child_expected_height = height.checked_sub(1);
364364-365365- // If height is 0, we already verified no children exist, so child_expected_height is None/invalid but unused.
366366- // Actually height - 1 would underflow if height 0. But logic ensures no children pushed if height 0.
367367-263263+368264 if height > 0 {
369265 let next_h = child_expected_height.unwrap();
370370-266266+371267 for i in (0..node.e.len()).rev() {
372268 let e = &node.e[i];
373269 let key = entry_keys[i].clone();
374270375271 if e.t_archived == Some(true) {
376376- stack.push(StackItem::Node {
272272+ stack.push(StackItem::Node {
377273 expected: e.t,
378378- expected_height: Some(next_h),
274274+ expected_height: Some(next_h),
379275 });
380276 }
381277···390286 }
391287392288 if node.l_archived == Some(true) {
393393- stack.push(StackItem::Node {
289289+ stack.push(StackItem::Node {
394290 expected: node.l,
395291 expected_height: Some(next_h),
396292 });
397293 }
398294 } else {
399295 // Height 0: Push records only
400400- for i in (0..node.e.len()).rev() {
296296+ for i in (0..node.e.len()).rev() {
401297 let e = &node.e[i];
402298 let key = entry_keys[i].clone();
403403-299299+404300 if e.v_archived == Some(true) {
405301 let implicit_index = if e.v.is_none() { Some(i) } else { None };
406302 stack.push(StackItem::Record {
···415311 Ok(Some(StarItem::Node(node)))
416312 }
417313418418- fn process_record(
419419- block_bytes: &[u8],
420420- key: Vec<u8>,
421421- expected: Option<Cid>,
422422- implicit_index: Option<usize>,
423423- stack: &mut Vec<StackItem>,
424424- ) -> Result<Option<StarItem>> {
314314+ fn process_record(block_bytes: &[u8], key: Vec<u8>, expected: Option<Cid>, implicit_index: Option<usize>, stack: &mut Vec<StackItem>) -> Result<Option<StarItem>> {
425315 let hash = Sha256::digest(block_bytes);
426316 let cid = Cid::new_v1(0x71, cid::multihash::Multihash::wrap(0x12, &hash)?);
427317