44#[ cfg( test) ]  
55mod  tests; 
66
7- use  crate :: proof:: { Proof ,  ProofCollection ,  ProofError ,  ProofNode } ; 
7+ use  crate :: proof:: { Proof ,  ProofCollection ,  ProofError ,  ProofNode ,  verify_opt_value_digest } ; 
88use  crate :: range_proof:: RangeProof ; 
99use  crate :: stream:: { MerkleKeyValueStream ,  PathIterator } ; 
1010use  crate :: v2:: api:: { self ,  FrozenProof ,  FrozenRangeProof ,  KeyType ,  ValueType } ; 
1111use  firewood_storage:: { 
12-     BranchNode ,  Child ,  FileIoError ,  HashType ,  HashedNodeReader ,   ImmutableProposal ,   IntoHashType , 
13-     LeafNode ,  MaybePersistedNode ,  MutableProposal ,  NibblesIterator ,  Node ,   NodeStore ,   Parentable , 
14-     Path ,  ReadableStorage ,  SharedNode ,  TrieHash ,  TrieReader ,  ValueDigest , 
12+     BranchNode ,  Child ,  FileIoError ,  HashType ,  Hashable ,   HashedNodeReader ,   ImmutableProposal , 
13+     IntoHashType ,   LeafNode ,  MaybePersistedNode ,  MemStore ,   MutableProposal ,  NibblesIterator ,  Node , 
14+     NodeStore ,   Parentable ,   Path ,  ReadableStorage ,  SharedNode ,  TrieHash ,  TrieReader ,  ValueDigest , 
1515} ; 
1616use  futures:: { StreamExt ,  TryStreamExt } ; 
1717use  metrics:: counter; 
18+ use  smallvec:: SmallVec ; 
1819use  std:: collections:: HashSet ; 
1920use  std:: fmt:: Debug ; 
2021use  std:: future:: ready; 
@@ -133,7 +134,6 @@ impl<T: TrieReader> Merkle<T> {
133134        self . nodestore . root_node ( ) 
134135    } 
135136
136-     #[ cfg( test) ]  
137137    pub ( crate )  const  fn  nodestore ( & self )  -> & T  { 
138138        & self . nodestore 
139139    } 
@@ -160,7 +160,6 @@ impl<T: TrieReader> Merkle<T> {
160160        if  proof. is_empty ( )  { 
161161            // No nodes, even the root, are before `key`. 
162162            // The root alone proves the non-existence of `key`. 
163-             // TODO reduce duplicate code with ProofNode::from<PathIterItem> 
164163            let  child_hashes = if  let  Some ( branch)  = root. as_branch ( )  { 
165164                branch. children_hashes ( ) 
166165            }  else  { 
@@ -257,12 +256,284 @@ impl<T: TrieReader> Merkle<T> {
257256///   incremental range proof verification 
258257pub  fn  verify_range_proof ( 
259258        & self , 
260-         _first_key :  Option < impl  KeyType > , 
261-         _last_key :  Option < impl  KeyType > , 
262-         _root_hash :  & TrieHash , 
263-         _proof :  & RangeProof < impl  KeyType ,  impl  ValueType ,  impl  ProofCollection > , 
259+         first_key :  Option < impl  KeyType > , 
260+         last_key :  Option < impl  KeyType > , 
261+         root_hash :  & TrieHash , 
262+         proof :  & RangeProof < impl  KeyType ,  impl  ValueType ,  impl  ProofCollection > , 
264263    )  -> Result < ( ) ,  api:: Error >  { 
265-         todo ! ( ) 
264+         let  first_key = first_key. map ( Path :: from) . unwrap_or_default ( ) ; 
265+         let  last_key = last_key. map ( Path :: from) . unwrap_or_default ( ) ; 
266+ 
267+         // 1. Validate proof structure (similar to validateChangeProof in Go) 
268+         self . validate_range_proof_structure ( & first_key,  & last_key,  proof) ?; 
269+ 
270+         // 2. Verify start proof nodes. 
271+         self . verify_proof_nodes_values ( 
272+             proof. start_proof ( ) , 
273+             & first_key, 
274+             & last_key, 
275+             // validate_range_proof_structure will have verified that the keys are 
276+             // in order, allowing us to use binary search on lookup 
277+             proof. key_values ( ) , 
278+         ) ?; 
279+ 
280+         // 3. Verify end proof nodes 
281+         self . verify_proof_nodes_values ( 
282+             proof. end_proof ( ) , 
283+             & first_key, 
284+             & last_key, 
285+             // validate_range_proof_structure will have verified that the keys are 
286+             // in order, allowing us to use binary search on lookup 
287+             proof. key_values ( ) , 
288+         ) ?; 
289+ 
290+         // 4. Reconstruct trie and verify root 
291+         self . verify_reconstructed_trie_root ( proof,  root_hash) ?; 
292+ 
293+         Ok ( ( ) ) 
294+     } 
295+ 
296+     /// Verify that the range proof is structurally valid and that we can use it 
297+ /// to verify the trie root once reconstructed. 
298+ fn  validate_range_proof_structure ( 
299+         & self , 
300+         first_key :  & Path , 
301+         last_key :  & Path , 
302+         proof :  & RangeProof < impl  KeyType ,  impl  ValueType ,  impl  ProofCollection > , 
303+     )  -> Result < ( ) ,  ProofError >  { 
304+         // 1. Basic validation 
305+         if  proof. is_empty ( )  { 
306+             return  Err ( ProofError :: Empty ) ; 
307+         } 
308+ 
309+         // 2. Range validation 
310+         if  !first_key. is_empty ( )  && !last_key. is_empty ( )  && first_key > last_key { 
311+             return  Err ( ProofError :: InvalidRange ) ; 
312+         } 
313+ 
314+         // 3. Proof structure validation 
315+         match  ( 
316+             first_key. is_empty ( ) , 
317+             last_key. is_empty ( ) , 
318+             proof. key_values ( ) . is_empty ( ) , 
319+         )  { 
320+             ( _,  true ,  true )  if  !proof. end_proof ( ) . is_empty ( )  => { 
321+                 return  Err ( ProofError :: UnexpectedEndProof ) ; 
322+             } 
323+             ( true ,  _,  _)  if  !proof. start_proof ( ) . is_empty ( )  => { 
324+                 return  Err ( ProofError :: UnexpectedStartProof ) ; 
325+             } 
326+             ( _,  false ,  _)  | ( _,  _,  false )  if  proof. end_proof ( ) . is_empty ( )  => { 
327+                 return  Err ( ProofError :: ExpectedEndProof ) ; 
328+             } 
329+             _ => { }  // Valid combination 
330+         } 
331+ 
332+         let  last_key = if  proof. key_values ( ) . is_empty ( )  { 
333+             // no key-values, `last_key` remains the `last_key` provided by the caller 
334+             last_key
335+         }  else  { 
336+             // two re-usable buffers to expand the keys into nibbles. re-using 
337+             // these buffers avoids multiple allocations and deallocations in the 
338+             // loop below. 
339+             // 
340+             // Uses a smallvec so we can convert directly to a Path without 
341+             // an extra copy step. 
342+             let  mut  this_key_buf = SmallVec :: < [ u8 ;  64 ] > :: new ( ) ; 
343+             let  mut  last_key_buf = SmallVec :: < [ u8 ;  64 ] > :: new ( ) ; 
344+ 
345+             // 4. verify key-values are in strict order by key (no duplicates either) 
346+             for  ( key,  _)  in  proof. key_values ( )  { 
347+                 this_key_buf. clear ( ) ; 
348+                 this_key_buf. extend ( NibblesIterator :: new ( key. as_ref ( ) ) ) ; 
349+                 debug_assert ! ( !this_key_buf. is_empty( ) ,  "key must not be empty" ) ; 
350+                 debug_assert ! ( this_key_buf. len( )  % 2  == 0 ,  "key must be even length" ) ; 
351+ 
352+                 // verify that the first key is not larger than the first key 
353+                 // in the range. Only check the first key as all remaining keys 
354+                 // are implicitly larger if other checks hold. 
355+                 if  last_key_buf. is_empty ( )  && !first_key. is_empty ( )  && * this_key_buf < * * first_key { 
356+                     return  Err ( ProofError :: StateFromOutsideOfRange ) ; 
357+                 } 
358+ 
359+                 // For every key, check that it is less than or equal to the last 
360+                 // key in the range. 
361+                 if  !last_key. is_empty ( )  && * this_key_buf <= * * last_key { 
362+                     return  Err ( ProofError :: StateFromOutsideOfRange ) ; 
363+                 } 
364+ 
365+                 if  !last_key_buf. is_empty ( )  && this_key_buf < last_key_buf { 
366+                     // we have a last key but it is greater than the current key 
367+                     // therefore, the list is not sorted or has duplicates 
368+                     return  Err ( ProofError :: NonIncreasingValues ) ; 
369+                 } 
370+ 
371+                 // swap the buffers so that `last_key_buf` contains the key we 
372+                 // processed in this iteration. 
373+                 std:: mem:: swap ( & mut  last_key_buf,  & mut  this_key_buf) ; 
374+             } 
375+ 
376+             // at this point, `last_key_buf` is filled with the nibbles of the last 
377+             // and largest key in the key-values. We can re-use it for the 
378+             // verification below. It overrides the `last_key` provided by the 
379+             // caller in order to verify the end proof. 
380+ 
381+             & Path ( last_key_buf) 
382+         } ; 
383+ 
384+         // 5. Validate proof paths (structural only, not root verification) 
385+         if  !proof. start_proof ( ) . is_empty ( )  { 
386+             proof. start_proof ( ) . verify_proof_path_structure ( first_key) ?; 
387+         } 
388+ 
389+         if  !proof. end_proof ( ) . is_empty ( )  { 
390+             proof. end_proof ( ) . verify_proof_path_structure ( last_key) ?; 
391+         } 
392+ 
393+         Ok ( ( ) ) 
394+     } 
395+ 
396+     fn  verify_proof_nodes_values ( 
397+         & self , 
398+         proof :  & Proof < impl  ProofCollection > , 
399+         first_key :  & Path , 
400+         last_key :  & Path , 
401+         key_values_sorted_by_key :  & [ ( impl  KeyType ,  impl  ValueType ) ] , 
402+     )  -> Result < ( ) ,  ProofError >  { 
403+         // cache the root node to avoid multiple lookups 
404+         let  root = self . root ( ) ; 
405+         let  root = root. as_deref ( ) ; 
406+ 
407+         let  mut  node_key = Path :: default ( ) ; 
408+         for  node in  proof. as_ref ( )  { 
409+             node_key. 0 . clear ( ) ; 
410+             node_key. 0 . extend ( node. key ( ) ) ; 
411+ 
412+             // skip partial paths as they cannot have values 
413+             #[ cfg( not( feature = "branch_factor_256" ) ) ]  
414+             if  node_key. len ( )  % 2  != 0  { 
415+                 continue ; 
416+             } 
417+ 
418+             if  !first_key. is_empty ( )  && * node_key < * * first_key
419+                 || !last_key. is_empty ( )  && * node_key > * * last_key
420+             { 
421+                 // node not in range, ignore it 
422+                 continue ; 
423+             } 
424+ 
425+             verify_opt_value_digest ( 
426+                 // must be inline with the call to verify_opt_value_digest 
427+                 // in order for the lifetime of the temp storage to be valid, 
428+                 // which ends before the semicolon 
429+                 self . get_node_value_for_proof ( 
430+                     key_values_sorted_by_key, 
431+                     & node_key, 
432+                     root, 
433+                     // temp storage for the fetched node so we don't need to 
434+                     // copy the value off the node 
435+                     & mut  None , 
436+                 ) ?, 
437+                 node. value_digest ( ) , 
438+             ) ?;  // temp storage is dropped here 
439+         } 
440+ 
441+         Ok ( ( ) ) 
442+     } 
443+ 
444+     fn  verify_reconstructed_trie_root ( 
445+         & self , 
446+         proof :  & RangeProof < impl  KeyType ,  impl  ValueType ,  impl  ProofCollection > , 
447+         root_hash :  & TrieHash , 
448+     )  -> Result < ( ) ,  api:: Error >  { 
449+         // Create in-memory trie for reconstruction 
450+         let  memstore = MemStore :: new ( vec ! [ ] ) ; 
451+         let  nodestore = NodeStore :: new_empty_proposal ( memstore. into ( ) ) ; 
452+         let  mut  merkle = Merkle  {  nodestore } ; 
453+ 
454+         // Insert all key-value pairs from the range proof 
455+         for  ( key,  value)  in  proof. key_values ( )  { 
456+             merkle
457+                 . insert ( key. as_ref ( ) ,  value. as_ref ( ) . into ( ) ) 
458+                 . map_err ( ProofError :: IO ) ?; 
459+         } 
460+ 
461+         // Hash the trie and get root 
462+         let  merkle:  Merkle < NodeStore < Arc < ImmutableProposal > ,  _ > >  = merkle. try_into ( ) ?; 
463+         let  computed_root = merkle. nodestore ( ) . root_hash ( ) . ok_or ( ProofError :: Empty ) ?; 
464+ 
465+         // Compare with expected root 
466+         if  computed_root == * root_hash { 
467+             Ok ( ( ) ) 
468+         }  else  { 
469+             Err ( api:: Error :: IncorrectRootHash  { 
470+                 provided :  root_hash. clone ( ) , 
471+                 current :  computed_root, 
472+             } ) 
473+         } 
474+     } 
475+ 
476+     /// Get the value for the given key from the proof key-values. If not found, 
477+ /// it will look for the value in the trie. 
478+ /// 
479+ /// This lifetime means `'out` is dependent on `'this`, `'kvs`, and `'store`. 
480+ /// 
481+ /// This allows us to return a reference to the value in the node without 
482+ /// copying it to a new buffer. 
483+ fn  get_node_value_for_proof < ' this :  ' out ,  ' kvs :  ' out ,  ' store :  ' out ,  ' out > ( 
484+         & ' this  self , 
485+         key_values_sorted_by_key :  & ' kvs  [ ( impl  KeyType ,  impl  ValueType ) ] , 
486+         path :  & Path , 
487+         root :  Option < & Node > , 
488+         fetched_node :  & ' store  mut  Option < SharedNode > , 
489+     )  -> Result < Option < & ' out  [ u8 ] > ,  FileIoError >  { 
490+         use  std:: cmp:: Ordering :: { self ,  Equal ,  Greater ,  Less } ; 
491+ 
492+         fn  cmp_path_with_key ( path :  & Path ,  key :  & [ u8 ] )  -> Ordering  { 
493+             let  mut  path = path. iter ( ) ; 
494+             let  mut  key = NibblesIterator :: new ( key) ; 
495+             loop  { 
496+                 break  match  ( path. next ( ) ,  key. next ( ) )  { 
497+                     ( Some ( path) ,  Some ( key) )  => match  path. cmp ( & key)  { 
498+                         Equal  => continue ,  // all other branches break 
499+                         ord => ord, 
500+                     } , 
501+                     ( Some ( _) ,  None )  => Greater ,  // path is longer than key 
502+                     ( None ,  Some ( _) )  => Less ,     // key is longer than path 
503+                     ( None ,  None )  => Equal ,       // both are empty 
504+                 } ; 
505+             } 
506+         } 
507+ 
508+         // use binary search to find the value for the key in `key_values_sorted_by_key` 
509+         // (if it exists) 
510+         if  let  Ok ( found)  = key_values_sorted_by_key
511+             . binary_search_by ( |( key,  _) | cmp_path_with_key ( path,  key. as_ref ( ) ) ) 
512+             . map ( 
513+                 #[ expect(  
514+                     clippy:: indexing_slicing,  
515+                     reason = "binary_search guarantees the index is in bounds"  
516+                 ) ]  
517+                 |index| key_values_sorted_by_key[ index] . 1 . as_ref ( ) , 
518+             ) 
519+         { 
520+             return  Ok ( Some ( found) ) ; 
521+         } 
522+ 
523+         // otherwise, look for it in the trie 
524+         let  Some ( root)  = root else  { 
525+             // no root, so no value 
526+             return  Ok ( None ) ; 
527+         } ; 
528+ 
529+         let  Some ( node)  = get_helper ( & self . nodestore ,  root,  path. as_ref ( ) ) ? else  { 
530+             // node was not found, so no value 
531+             return  Ok ( None ) ; 
532+         } ; 
533+ 
534+         let  node = & * fetched_node. insert ( node) ; 
535+ 
536+         Ok ( node. value ( ) ) 
266537    } 
267538
268539    pub ( crate )  fn  path_iter < ' a > ( 
0 commit comments