@@ -421,18 +421,9 @@ fn compute_omitted_markers<'a>(
421421 . flatten ( )
422422}
423423
424- /// Task for iterative post-order DFS over the implicit merkle tree.
425- enum DfsTask {
426- /// Descend into a subtree covering the range [start..start+len).
427- Descend { start : usize , len : usize } ,
428- /// Combine two child hashes after both subtrees have been processed.
429- Combine ,
430- }
431-
432- /// Build merkle tree iteratively (DFS, left-to-right) and collect missing_hashes.
424+ /// Build merkle tree recursively (DFS, left-to-right) and collect missing_hashes.
433425///
434426/// Per the spec, missing_hashes are in depth-first left-to-right order.
435- /// Uses an explicit stack to simulate post-order DFS without recursion.
436427///
437428/// Note: a level-by-level approach (as used by `root_hash()`) cannot produce
438429/// DFS-ordered missing_hashes because it processes all subtrees at each depth
@@ -443,43 +434,31 @@ fn build_tree_with_disclosure(
443434 debug_assert ! ( !tlv_data. is_empty( ) , "TLV stream must contain at least one record" ) ;
444435
445436 let mut missing_hashes = Vec :: new ( ) ;
446- // Each entry: (hash, has_included_leaf_in_subtree)
447- let mut hash_stack: Vec < ( sha256:: Hash , bool ) > = Vec :: new ( ) ;
448- let mut task_stack: Vec < DfsTask > = vec ! [ DfsTask :: Descend { start: 0 , len: tlv_data. len( ) } ] ;
449-
450- while let Some ( task) = task_stack. pop ( ) {
451- match task {
452- DfsTask :: Descend { start, len } => {
453- if len == 1 {
454- hash_stack. push ( ( tlv_data[ start] . per_tlv_hash , tlv_data[ start] . is_included ) ) ;
455- } else {
456- let mid = len. next_power_of_two ( ) / 2 ;
457- // Push combine first (processed after both children).
458- task_stack. push ( DfsTask :: Combine ) ;
459- // Push right then left so left is processed first (LIFO).
460- task_stack. push ( DfsTask :: Descend { start : start + mid, len : len - mid } ) ;
461- task_stack. push ( DfsTask :: Descend { start, len : mid } ) ;
462- }
463- } ,
464- DfsTask :: Combine => {
465- let ( right_hash, right_incl) = hash_stack. pop ( ) . unwrap ( ) ;
466- let ( left_hash, left_incl) = hash_stack. pop ( ) . unwrap ( ) ;
467-
468- if left_incl && !right_incl {
469- missing_hashes. push ( right_hash) ;
470- } else if !left_incl && right_incl {
471- missing_hashes. push ( left_hash) ;
472- }
473-
474- let combined =
475- tagged_branch_hash_from_engine ( branch_tag. clone ( ) , left_hash, right_hash) ;
476- hash_stack. push ( ( combined, left_incl || right_incl) ) ;
477- } ,
478- }
437+ let ( root, _) = build_tree_dfs ( tlv_data, branch_tag, & mut missing_hashes) ;
438+ ( root, missing_hashes)
439+ }
440+
441+ fn build_tree_dfs (
442+ tlv_data : & [ TlvMerkleData ] , branch_tag : & sha256:: HashEngine ,
443+ missing_hashes : & mut Vec < sha256:: Hash > ,
444+ ) -> ( sha256:: Hash , bool ) {
445+ if tlv_data. len ( ) == 1 {
446+ return ( tlv_data[ 0 ] . per_tlv_hash , tlv_data[ 0 ] . is_included ) ;
479447 }
480448
481- let ( root, _) = hash_stack. pop ( ) . unwrap ( ) ;
482- ( root, missing_hashes)
449+ let mid = tlv_data. len ( ) . next_power_of_two ( ) / 2 ;
450+ let ( left_data, right_data) = tlv_data. split_at ( mid) ;
451+ let ( left_hash, left_incl) = build_tree_dfs ( left_data, branch_tag, missing_hashes) ;
452+ let ( right_hash, right_incl) = build_tree_dfs ( right_data, branch_tag, missing_hashes) ;
453+
454+ if left_incl && !right_incl {
455+ missing_hashes. push ( right_hash) ;
456+ } else if !left_incl && right_incl {
457+ missing_hashes. push ( left_hash) ;
458+ }
459+
460+ let combined = tagged_branch_hash_from_engine ( branch_tag. clone ( ) , left_hash, right_hash) ;
461+ ( combined, left_incl || right_incl)
483462}
484463
485464/// Reconstruct merkle root from selective disclosure data.
@@ -546,71 +525,49 @@ pub(super) fn reconstruct_merkle_root(
546525 }
547526 }
548527
549- // Iterative DFS reconstruction: consume missing_hashes in DFS order.
550- // result_stack holds Option<hash>: Some = subtree has included leaves, None = all omitted.
551- let mut result_stack: Vec < Option < sha256:: Hash > > = Vec :: new ( ) ;
552- let mut task_stack: Vec < DfsTask > = vec ! [ DfsTask :: Descend { start: 0 , len: num_nodes } ] ;
553528 let mut missing_idx: usize = 0 ;
554-
555- while let Some ( task) = task_stack. pop ( ) {
556- match task {
557- DfsTask :: Descend { start, len } => {
558- if len == 1 {
559- result_stack. push ( hashes[ start] ) ;
560- } else {
561- let mid = len. next_power_of_two ( ) / 2 ;
562- task_stack. push ( DfsTask :: Combine ) ;
563- task_stack. push ( DfsTask :: Descend { start : start + mid, len : len - mid } ) ;
564- task_stack. push ( DfsTask :: Descend { start, len : mid } ) ;
565- }
566- } ,
567- DfsTask :: Combine => {
568- let right = result_stack. pop ( ) . unwrap ( ) ;
569- let left = result_stack. pop ( ) . unwrap ( ) ;
570-
571- match ( left, right) {
572- ( None , None ) => result_stack. push ( None ) ,
573- ( Some ( l) , None ) => {
574- if missing_idx >= missing_hashes. len ( ) {
575- return Err ( SelectiveDisclosureError :: InsufficientMissingHashes ) ;
576- }
577- let r = missing_hashes[ missing_idx] ;
578- missing_idx += 1 ;
579- result_stack. push ( Some ( tagged_branch_hash_from_engine (
580- branch_tag. clone ( ) ,
581- l,
582- r,
583- ) ) ) ;
584- } ,
585- ( None , Some ( r) ) => {
586- if missing_idx >= missing_hashes. len ( ) {
587- return Err ( SelectiveDisclosureError :: InsufficientMissingHashes ) ;
588- }
589- let l = missing_hashes[ missing_idx] ;
590- missing_idx += 1 ;
591- result_stack. push ( Some ( tagged_branch_hash_from_engine (
592- branch_tag. clone ( ) ,
593- l,
594- r,
595- ) ) ) ;
596- } ,
597- ( Some ( l) , Some ( r) ) => {
598- result_stack. push ( Some ( tagged_branch_hash_from_engine (
599- branch_tag. clone ( ) ,
600- l,
601- r,
602- ) ) ) ;
603- } ,
604- }
605- } ,
606- }
607- }
529+ let root = reconstruct_merkle_root_dfs ( & hashes, & branch_tag, missing_hashes, & mut missing_idx) ?;
608530
609531 if missing_idx != missing_hashes. len ( ) {
610532 return Err ( SelectiveDisclosureError :: InsufficientMissingHashes ) ;
611533 }
612534
613- result_stack. pop ( ) . unwrap ( ) . ok_or ( SelectiveDisclosureError :: InsufficientMissingHashes )
535+ root. ok_or ( SelectiveDisclosureError :: InsufficientMissingHashes )
536+ }
537+
538+ fn reconstruct_merkle_root_dfs (
539+ hashes : & [ Option < sha256:: Hash > ] , branch_tag : & sha256:: HashEngine ,
540+ missing_hashes : & [ sha256:: Hash ] , missing_idx : & mut usize ,
541+ ) -> Result < Option < sha256:: Hash > , SelectiveDisclosureError > {
542+ if hashes. len ( ) == 1 {
543+ return Ok ( hashes[ 0 ] ) ;
544+ }
545+
546+ let mid = hashes. len ( ) . next_power_of_two ( ) / 2 ;
547+ let ( left_hashes, right_hashes) = hashes. split_at ( mid) ;
548+ let left = reconstruct_merkle_root_dfs ( left_hashes, branch_tag, missing_hashes, missing_idx) ?;
549+ let right = reconstruct_merkle_root_dfs ( right_hashes, branch_tag, missing_hashes, missing_idx) ?;
550+
551+ match ( left, right) {
552+ ( None , None ) => Ok ( None ) ,
553+ ( Some ( l) , None ) => {
554+ if * missing_idx >= missing_hashes. len ( ) {
555+ return Err ( SelectiveDisclosureError :: InsufficientMissingHashes ) ;
556+ }
557+ let r = missing_hashes[ * missing_idx] ;
558+ * missing_idx += 1 ;
559+ Ok ( Some ( tagged_branch_hash_from_engine ( branch_tag. clone ( ) , l, r) ) )
560+ } ,
561+ ( None , Some ( r) ) => {
562+ if * missing_idx >= missing_hashes. len ( ) {
563+ return Err ( SelectiveDisclosureError :: InsufficientMissingHashes ) ;
564+ }
565+ let l = missing_hashes[ * missing_idx] ;
566+ * missing_idx += 1 ;
567+ Ok ( Some ( tagged_branch_hash_from_engine ( branch_tag. clone ( ) , l, r) ) )
568+ } ,
569+ ( Some ( l) , Some ( r) ) => Ok ( Some ( tagged_branch_hash_from_engine ( branch_tag. clone ( ) , l, r) ) ) ,
570+ }
614571}
615572
616573fn validate_omitted_markers ( markers : & [ u64 ] ) -> Result < ( ) , SelectiveDisclosureError > {
0 commit comments