1use crate::{HashBuilder, Nibbles, RlpNode, TrieNode};
9use alloc::vec::Vec;
10use alloy_primitives::{keccak256, map::B256Map, Bytes, B256};
11use alloy_rlp::Decodable;
12
13const KEY_NIBBLES: usize = B256::len_bytes() * 2;
14
15struct RangeProofVerifier<'a> {
17 range: ProofRange,
19 nodes: ProofNodes<'a>,
21 frontier: ProofFrontier,
23 next: Option<Nibbles>,
25}
26
27impl<'a> RangeProofVerifier<'a> {
28 fn new(left: B256, right: B256, proof: &'a [Bytes], frontier: ProofFrontier) -> Self {
30 Self {
31 range: ProofRange::new(left, right),
32 nodes: ProofNodes::new(proof),
33 frontier,
34 next: None,
35 }
36 }
37
38 fn verify(mut self, root: B256) -> Result<Option<B256>, RangeProofErrorKind> {
41 self.visit_reference(Nibbles::new(), &RlpNode::word_rlp(&root))?;
42
43 let got = self.frontier.root()?;
44 if got != root {
45 return Err(RangeProofErrorKind::RootMismatch { expected: root, got })
46 }
47 Ok(self.next.as_ref().map(TriePath::lowest_key))
48 }
49
50 fn visit_reference(
53 &mut self,
54 prefix: Nibbles,
55 reference: &RlpNode,
56 ) -> Result<(), RangeProofErrorKind> {
57 match self.range.subtree_relation(&prefix)? {
58 SubtreeRelation::OutsideLeft => self.add_outside_reference(prefix, reference),
59 SubtreeRelation::OutsideRight => {
60 self.note_next(prefix);
61 self.add_outside_reference(prefix, reference)
62 }
63 SubtreeRelation::Inside => Ok(()),
64 SubtreeRelation::Boundary => {
65 let node = self.nodes.resolve(prefix, reference)?;
66 self.visit_node(node, prefix)
67 }
68 }
69 }
70
71 fn visit_node(&mut self, node: TrieNode, prefix: Nibbles) -> Result<(), RangeProofErrorKind> {
73 match node {
74 TrieNode::EmptyRoot => Ok(()),
75 TrieNode::Leaf(leaf) => {
76 let path = prefix.descend_leaf(&leaf.key)?;
77 match self.range.key_relation(&path) {
78 KeyRelation::Before => self.frontier.push_leaf(path, leaf.value),
79 KeyRelation::Inside => {}
80 KeyRelation::After => {
81 self.note_next(path);
82 self.frontier.push_leaf(path, leaf.value);
83 }
84 }
85 Ok(())
86 }
87 TrieNode::Extension(extension) => {
88 self.visit_reference(prefix.descend_extension(&extension.key)?, &extension.child)
89 }
90 TrieNode::Branch(branch) => {
91 for (nibble, child) in branch
92 .as_ref()
93 .children()
94 .filter_map(|(nibble, child)| child.map(|child| (nibble, child)))
95 {
96 self.visit_reference(prefix.descend_child(nibble)?, child)?;
97 }
98 Ok(())
99 }
100 }
101 }
102
103 fn add_outside_reference(
105 &mut self,
106 prefix: Nibbles,
107 reference: &RlpNode,
108 ) -> Result<(), RangeProofErrorKind> {
109 if let Some(hash) = reference.as_hash() {
110 self.frontier.push_subtree(prefix, hash);
111 return Ok(())
112 }
113 self.add_outside_node(TrieNode::decode(&mut reference.as_slice())?, prefix)
114 }
115
116 fn add_outside_node(
118 &mut self,
119 node: TrieNode,
120 prefix: Nibbles,
121 ) -> Result<(), RangeProofErrorKind> {
122 match node {
123 TrieNode::EmptyRoot => Ok(()),
124 TrieNode::Leaf(leaf) => {
125 let path = prefix.descend_leaf(&leaf.key)?;
126 self.frontier.push_leaf(path, leaf.value);
127 Ok(())
128 }
129 TrieNode::Extension(extension) => self
130 .add_outside_reference(prefix.descend_extension(&extension.key)?, &extension.child),
131 TrieNode::Branch(branch) => {
132 for (nibble, child) in branch
133 .as_ref()
134 .children()
135 .filter_map(|(nibble, child)| child.map(|child| (nibble, child)))
136 {
137 self.add_outside_reference(prefix.descend_child(nibble)?, child)?;
138 }
139 Ok(())
140 }
141 }
142 }
143
144 fn note_next(&mut self, path: Nibbles) {
146 if self.next.is_none_or(|next| path < next) {
147 self.next = Some(path);
148 }
149 }
150}
151
152struct ProofRange {
154 left: Nibbles,
156 right: Nibbles,
158}
159
160impl ProofRange {
161 fn new(left: B256, right: B256) -> Self {
163 Self { left: Nibbles::unpack(left), right: Nibbles::unpack(right) }
164 }
165
166 fn subtree_relation(&self, prefix: &Nibbles) -> Result<SubtreeRelation, RangeProofErrorKind> {
168 if prefix.len() > KEY_NIBBLES {
169 return Err(RangeProofErrorKind::PathTooLong { path: *prefix })
170 }
171 let left = self.left.slice(..prefix.len());
172 let right = self.right.slice(..prefix.len());
173
174 Ok(if *prefix < left {
175 SubtreeRelation::OutsideLeft
176 } else if *prefix > right {
177 SubtreeRelation::OutsideRight
178 } else if *prefix > left && *prefix < right {
179 SubtreeRelation::Inside
180 } else {
181 SubtreeRelation::Boundary
182 })
183 }
184
185 fn key_relation(&self, path: &Nibbles) -> KeyRelation {
187 if path < &self.left {
188 KeyRelation::Before
189 } else if path > &self.right {
190 KeyRelation::After
191 } else {
192 KeyRelation::Inside
193 }
194 }
195}
196
197#[derive(Clone, Copy, Debug, PartialEq, Eq)]
200enum SubtreeRelation {
201 OutsideLeft,
203 OutsideRight,
205 Boundary,
207 Inside,
209}
210
211#[derive(Clone, Copy, Debug, PartialEq, Eq)]
213enum KeyRelation {
214 Before,
216 Inside,
218 After,
220}
221
222struct ProofNodes<'a>(B256Map<&'a [u8]>);
224
225impl<'a> ProofNodes<'a> {
226 fn new(proof: &'a [Bytes]) -> Self {
228 Self(proof.iter().map(|node| (keccak256(node), node.as_ref())).collect())
229 }
230
231 fn resolve(&self, path: Nibbles, reference: &RlpNode) -> Result<TrieNode, RangeProofErrorKind> {
233 let Some(hash) = reference.as_hash() else {
234 return Ok(TrieNode::decode(&mut reference.as_slice())?)
235 };
236 let node = self.0.get(&hash).ok_or(RangeProofErrorKind::MissingProofNode { path })?;
237 Ok(TrieNode::decode(&mut &node[..])?)
238 }
239}
240
241#[derive(Default)]
243struct ProofFrontier(Vec<FrontierEntry>);
244
245impl ProofFrontier {
246 fn from_leaves<I, V>(
248 origin: B256,
249 leaves: I,
250 ) -> Result<(Self, Option<B256>), RangeProofErrorKind>
251 where
252 I: IntoIterator<Item = (B256, V)>,
253 V: Into<Vec<u8>>,
254 {
255 let mut frontier = Self::default();
256 let mut previous = None;
257
258 for (key, value) in leaves {
259 let value = value.into();
260 if key < origin {
261 return Err(RangeProofErrorKind::LeafBeforeOrigin { key, origin })
262 }
263 if previous.is_some_and(|previous| key <= previous) {
264 return Err(RangeProofErrorKind::NonMonotonicLeaves)
265 }
266 if value.is_empty() {
267 return Err(RangeProofErrorKind::EmptyLeafValue { key })
268 }
269 previous = Some(key);
270 frontier.push_leaf(Nibbles::unpack(key), value);
271 }
272
273 Ok((frontier, previous))
274 }
275
276 fn push_leaf(&mut self, path: Nibbles, value: Vec<u8>) {
278 debug_assert_eq!(path.len(), KEY_NIBBLES);
279 self.0.push(FrontierEntry::Leaf { path, value });
280 }
281
282 fn push_subtree(&mut self, path: Nibbles, hash: B256) {
284 debug_assert!(path.len() <= KEY_NIBBLES);
285 self.0.push(FrontierEntry::Subtree { path, hash });
286 }
287
288 fn root(mut self) -> Result<B256, RangeProofErrorKind> {
290 self.0.sort_unstable_by_key(FrontierEntry::path);
293 let mut builder = HashBuilder::default();
294 let mut previous = None;
295
296 for entry in self.0 {
297 let path = entry.path();
298 if previous.is_some_and(|previous| path <= previous) {
299 return Err(RangeProofErrorKind::DuplicateFrontierPath { path })
300 }
301 previous = Some(path);
302 match entry {
303 FrontierEntry::Leaf { path, value } => builder.add_leaf(path, &value),
304 FrontierEntry::Subtree { path, hash } => builder.add_branch(path, hash, false),
305 }
306 }
307 Ok(builder.root())
308 }
309}
310
311#[derive(Clone, Debug)]
314enum FrontierEntry {
315 Leaf { path: Nibbles, value: Vec<u8> },
317 Subtree { path: Nibbles, hash: B256 },
319}
320
321impl FrontierEntry {
322 const fn path(&self) -> Nibbles {
324 match self {
325 Self::Leaf { path, .. } | Self::Subtree { path, .. } => *path,
326 }
327 }
328}
329
330#[derive(Debug, thiserror::Error)]
334#[error(transparent)]
335pub struct RangeProofError(#[from] RangeProofErrorKind);
336
337#[derive(Debug, thiserror::Error, PartialEq, Eq)]
338enum RangeProofErrorKind {
339 #[error("range leaves are not strictly increasing")]
341 NonMonotonicLeaves,
342 #[error("range leaf {key} precedes origin {origin}")]
344 LeafBeforeOrigin {
345 key: B256,
347 origin: B256,
349 },
350 #[error("range leaf {key} has an empty value")]
352 EmptyLeafValue {
353 key: B256,
355 },
356 #[error("missing proof node at path {path:?}")]
358 MissingProofNode {
359 path: Nibbles,
361 },
362 #[error("extension node at path {path:?} has an empty key")]
364 EmptyExtensionKey {
365 path: Nibbles,
367 },
368 #[error("proof path {path:?} exceeds hashed key length")]
370 PathTooLong {
371 path: Nibbles,
373 },
374 #[error("leaf path {path:?} does not resolve to a hashed key")]
376 InvalidLeafPath {
377 path: Nibbles,
379 },
380 #[error("duplicate range-proof frontier path {path:?}")]
382 DuplicateFrontierPath {
383 path: Nibbles,
385 },
386 #[error("range proof root mismatch: expected {expected}, got {got}")]
388 RootMismatch {
389 expected: B256,
391 got: B256,
393 },
394 #[error(transparent)]
396 Rlp(#[from] alloy_rlp::Error),
397}
398
399pub fn verify_range_proof<I, V>(
409 root: B256,
410 origin: B256,
411 limit: B256,
412 leaves: I,
413 proof: &[Bytes],
414) -> Result<Option<B256>, RangeProofError>
415where
416 I: IntoIterator<Item = (B256, V)>,
417 V: Into<Vec<u8>>,
418{
419 let (frontier, last_key) = ProofFrontier::from_leaves(origin, leaves)?;
420
421 if proof.is_empty() {
423 let got = frontier.root()?;
424 if got != root {
425 return Err(RangeProofErrorKind::RootMismatch { expected: root, got }.into())
426 }
427 return Ok(None)
428 }
429
430 RangeProofVerifier::new(origin, last_key.unwrap_or(limit), proof, frontier)
431 .verify(root)
432 .map_err(Into::into)
433}
434
435trait TriePath: Sized {
438 fn lowest_key(&self) -> B256;
440
441 fn descend_extension(self, key: &Nibbles) -> Result<Self, RangeProofErrorKind>;
443
444 fn descend_child(self, nibble: u8) -> Result<Self, RangeProofErrorKind>;
446
447 fn descend_leaf(self, key: &Nibbles) -> Result<Self, RangeProofErrorKind>;
449
450 fn join_checked(self, key: &Nibbles) -> Result<Self, RangeProofErrorKind>;
452}
453
454impl TriePath for Nibbles {
455 fn lowest_key(&self) -> B256 {
457 B256::right_padding_from(&self.pack())
458 }
459
460 fn descend_extension(self, key: &Nibbles) -> Result<Self, RangeProofErrorKind> {
462 if key.is_empty() {
463 return Err(RangeProofErrorKind::EmptyExtensionKey { path: self })
464 }
465 self.join_checked(key)
466 }
467
468 fn descend_child(self, nibble: u8) -> Result<Self, RangeProofErrorKind> {
470 if self.len() >= KEY_NIBBLES {
471 return Err(RangeProofErrorKind::PathTooLong { path: self })
472 }
473 let mut path = self;
474 path.push(nibble);
475 Ok(path)
476 }
477
478 fn descend_leaf(self, key: &Nibbles) -> Result<Self, RangeProofErrorKind> {
480 let path = self.join_checked(key)?;
481 if path.len() != KEY_NIBBLES {
482 return Err(RangeProofErrorKind::InvalidLeafPath { path })
483 }
484 Ok(path)
485 }
486
487 fn join_checked(self, key: &Nibbles) -> Result<Self, RangeProofErrorKind> {
489 if self.len() + key.len() > KEY_NIBBLES {
490 return Err(RangeProofErrorKind::PathTooLong { path: self })
491 }
492 Ok(self.join(key))
493 }
494}
495
496#[cfg(test)]
497mod tests {
498 use super::*;
499 use crate::{proof::ProofRetainer, BranchNode, ExtensionNode, TrieMask, EMPTY_ROOT_HASH};
500 use alloc::{vec, vec::Vec};
501
502 const MAX_HASH: B256 = B256::repeat_byte(0xff);
503
504 fn verify_range_proof<I, V>(
505 root: B256,
506 origin: B256,
507 leaves: I,
508 proof: &[Bytes],
509 ) -> Result<Option<B256>, RangeProofErrorKind>
510 where
511 I: IntoIterator<Item = (B256, V)>,
512 V: Into<Vec<u8>>,
513 {
514 super::verify_range_proof(root, origin, MAX_HASH, leaves, proof).map_err(|error| error.0)
515 }
516
517 fn key(value: u64) -> B256 {
518 B256::left_padding_from(&value.to_be_bytes())
519 }
520
521 fn encode_node(node: &TrieNode) -> Bytes {
522 alloy_rlp::encode(node).into()
523 }
524
525 fn no_leaves() -> Vec<(B256, Vec<u8>)> {
526 Vec::new()
527 }
528
529 fn value(byte: u8) -> Vec<u8> {
530 vec![byte; 64]
531 }
532
533 fn build_proof(leaves: &[(B256, Vec<u8>)], targets: &[B256]) -> (B256, Vec<Bytes>) {
534 let targets = targets.iter().copied().map(Nibbles::unpack).collect();
535 let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(targets));
536 for (key, value) in leaves {
537 builder.add_leaf(Nibbles::unpack(*key), value);
538 }
539 let root = builder.root();
540 let proof = builder
541 .take_proof_nodes()
542 .into_nodes_sorted()
543 .into_iter()
544 .map(|(_, node)| node)
545 .collect();
546 (root, proof)
547 }
548
549 #[test]
550 fn partial_range_authenticates_and_reports_the_next_key() {
551 let leaves =
552 vec![(key(1), value(1)), (key(2), value(2)), (key(3), value(3)), (key(4), value(4))];
553 let (root, proof) = build_proof(&leaves, &[key(2), key(3)]);
554
555 assert_eq!(
556 verify_range_proof(root, key(2), leaves[1..3].to_vec(), &proof).unwrap(),
557 Some(key(4))
558 );
559 }
560
561 #[test]
563 fn unexpanded_right_subtree_reports_a_prefix_bound() {
564 let right = |tail: u8| {
565 let mut key = B256::ZERO;
566 key.0[0] = 0x40;
567 key.0[31] = tail;
568 key
569 };
570 let leaves = vec![
571 (key(1), value(1)),
572 (key(2), value(2)),
573 (right(1), value(3)),
574 (right(2), value(4)),
575 ];
576 let (root, proof) = build_proof(&leaves, &[key(1), key(2)]);
577
578 assert_eq!(
579 verify_range_proof(root, key(1), leaves[..2].to_vec(), &proof).unwrap(),
580 Some(B256::right_padding_from(&[0x40]))
581 );
582 }
583
584 #[test]
585 fn inline_outside_nodes_are_reconstructed() {
586 let leaves =
587 vec![(key(1), vec![1]), (key(2), vec![2]), (key(3), vec![3]), (key(4), vec![4])];
588 let (root, proof) = build_proof(&leaves, &[key(2), key(3)]);
589
590 assert_eq!(
591 verify_range_proof(root, key(2), leaves[1..3].to_vec(), &proof).unwrap(),
592 Some(key(4))
593 );
594 }
595
596 #[test]
597 fn unused_proof_nodes_are_accepted() {
598 let leaves =
599 vec![(key(1), value(1)), (key(2), value(2)), (key(3), value(3)), (key(4), value(4))];
600 let (root, mut proof) = build_proof(&leaves, &[key(2), key(3)]);
601 proof.push(Bytes::from_static(&[alloy_rlp::EMPTY_STRING_CODE]));
602
603 assert_eq!(
604 verify_range_proof(root, key(2), leaves[1..3].to_vec(), &proof).unwrap(),
605 Some(key(4))
606 );
607 }
608
609 #[test]
611 fn node_paths_are_bounded_before_they_overflow_a_key() {
612 let dangling = RlpNode::word_rlp(&B256::repeat_byte(0xaa));
613
614 let overlong = encode_node(&TrieNode::Extension(ExtensionNode::new(
616 Nibbles::unpack(key(1)),
617 dangling.clone(),
618 )));
619 let branch = encode_node(&TrieNode::Branch(BranchNode::new(
620 vec![RlpNode::word_rlp(&keccak256(&overlong))],
621 TrieMask::new(1),
622 )));
623 let proof = vec![branch.clone(), overlong];
624
625 assert!(matches!(
626 verify_range_proof(keccak256(&branch), key(1), no_leaves(), &proof),
627 Err(RangeProofErrorKind::PathTooLong { .. })
628 ));
629
630 let deep_branch =
632 encode_node(&TrieNode::Branch(BranchNode::new(vec![dangling], TrieMask::new(1))));
633 let reach = encode_node(&TrieNode::Extension(ExtensionNode::new(
634 Nibbles::unpack(key(1)),
635 RlpNode::word_rlp(&keccak256(&deep_branch)),
636 )));
637 let proof = vec![reach.clone(), deep_branch];
638
639 assert!(matches!(
640 verify_range_proof(keccak256(&reach), key(1), no_leaves(), &proof),
641 Err(RangeProofErrorKind::PathTooLong { .. })
642 ));
643 }
644
645 #[test]
647 fn empty_extension_keys_are_rejected() {
648 let empty_key = Nibbles::new();
649 let mut proof = Vec::new();
650 let mut child = RlpNode::word_rlp(&B256::repeat_byte(0xaa));
651
652 for _ in 0..64 {
653 let node = encode_node(&TrieNode::Extension(ExtensionNode::new(empty_key, child)));
654 child = RlpNode::word_rlp(&keccak256(&node));
655 proof.push(node);
656 }
657 let root = keccak256(proof.last().unwrap());
658
659 assert!(matches!(
660 verify_range_proof(root, key(1), no_leaves(), &proof),
661 Err(RangeProofErrorKind::EmptyExtensionKey { .. })
662 ));
663 }
664
665 #[test]
666 fn missing_boundary_node_is_rejected() {
667 let leaves =
668 vec![(key(1), value(1)), (key(2), value(2)), (key(3), value(3)), (key(4), value(4))];
669 let (root, _) = build_proof(&leaves, &[key(2), key(3)]);
670 let unrelated = [Bytes::from_static(&[alloy_rlp::EMPTY_STRING_CODE])];
671
672 assert!(matches!(
673 verify_range_proof(root, key(2), leaves[1..3].to_vec(), &unrelated),
674 Err(RangeProofErrorKind::MissingProofNode { .. })
675 ));
676 }
677
678 #[test]
679 fn boundary_proof_can_authenticate_an_exhausted_range() {
680 let leaves =
681 vec![(key(1), value(1)), (key(2), value(2)), (key(3), value(3)), (key(4), value(4))];
682 let (root, proof) = build_proof(&leaves, &[key(2), key(4)]);
683
684 assert_eq!(verify_range_proof(root, key(2), leaves[1..].to_vec(), &proof).unwrap(), None);
685 }
686
687 #[test]
688 fn proof_free_full_trie_is_exhausted() {
689 let leaves = vec![(key(1), value(1)), (key(2), value(2)), (key(3), value(3))];
690 let (root, _) = build_proof(&leaves, &[]);
691
692 assert_eq!(verify_range_proof(root, B256::ZERO, leaves, &[]).unwrap(), None);
693 }
694
695 #[test]
696 fn omitted_interior_leaf_changes_root() {
697 let leaves =
698 vec![(key(1), value(1)), (key(2), value(2)), (key(3), value(3)), (key(4), value(4))];
699 let (root, proof) = build_proof(&leaves, &[key(2), key(4)]);
700 let returned = vec![(key(2), value(2)), (key(4), value(4))];
701
702 assert!(matches!(
703 verify_range_proof(root, key(2), returned, &proof),
704 Err(RangeProofErrorKind::RootMismatch { .. })
705 ));
706 }
707
708 #[test]
709 fn mutated_leaf_changes_root() {
710 let leaves = vec![(key(1), value(1)), (key(2), value(2)), (key(3), value(3))];
711 let (root, proof) = build_proof(&leaves, &[key(2), key(3)]);
712 let returned = vec![(key(2), value(9)), (key(3), value(3))];
713
714 assert!(matches!(
715 verify_range_proof(root, key(2), returned, &proof),
716 Err(RangeProofErrorKind::RootMismatch { .. })
717 ));
718 }
719
720 #[test]
721 fn empty_tail_is_authenticated() {
722 let leaves = vec![(key(1), value(1)), (key(2), value(2))];
723 let (root, proof) = build_proof(&leaves, &[key(3)]);
724
725 assert_eq!(
726 verify_range_proof(root, key(3), core::iter::empty::<(B256, Vec<u8>)>(), &proof)
727 .unwrap(),
728 None
729 );
730 }
731
732 #[test]
733 fn empty_range_cannot_hide_a_right_leaf() {
734 let leaves = vec![(key(1), value(1)), (key(3), value(3))];
735 let (root, proof) = build_proof(&leaves, &[key(2)]);
736
737 assert!(matches!(
738 verify_range_proof(root, key(2), core::iter::empty::<(B256, Vec<u8>)>(), &proof,),
739 Err(RangeProofErrorKind::RootMismatch { .. })
740 ));
741 }
742
743 #[test]
744 fn empty_interval_is_authenticated_through_its_limit() {
745 let leaves = vec![(key(1), value(1)), (key(3), value(3))];
746 let (root, proof) = build_proof(&leaves, &[key(2)]);
747
748 assert_eq!(
749 super::verify_range_proof(root, key(2), key(2), no_leaves(), &proof).unwrap(),
750 Some(key(3))
751 );
752 }
753
754 #[test]
755 fn rejects_non_monotonic_leaves_and_leaves_before_origin() {
756 let leaves = vec![(key(2), value(2)), (key(1), value(1))];
757
758 assert_eq!(
759 verify_range_proof(B256::ZERO, B256::ZERO, leaves, &[]),
760 Err(RangeProofErrorKind::NonMonotonicLeaves)
761 );
762 assert!(matches!(
763 verify_range_proof(B256::ZERO, key(2), [(key(1), value(1))], &[],),
764 Err(RangeProofErrorKind::LeafBeforeOrigin { .. })
765 ));
766 assert_eq!(
767 verify_range_proof(B256::ZERO, B256::ZERO, [(key(1), Vec::new())], &[]),
768 Err(RangeProofErrorKind::EmptyLeafValue { key: key(1) })
769 );
770 }
771
772 #[test]
773 fn empty_root_accepts_only_an_empty_range() {
774 assert_eq!(
775 verify_range_proof(
776 EMPTY_ROOT_HASH,
777 B256::ZERO,
778 core::iter::empty::<(B256, Vec<u8>)>(),
779 &[]
780 )
781 .unwrap(),
782 None
783 );
784 assert!(matches!(
785 verify_range_proof(EMPTY_ROOT_HASH, B256::ZERO, [(key(1), value(1))], &[],),
786 Err(RangeProofErrorKind::RootMismatch { .. })
787 ));
788 assert!(
789 verify_range_proof(EMPTY_ROOT_HASH, B256::ZERO, no_leaves(), &[Bytes::new()]).is_err()
790 );
791 }
792}