1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(not(feature = "std"), allow(clippy::arc_with_non_send_sync))]
11
12#[macro_use]
13extern crate alloc;
14
15pub mod db;
16pub mod error;
17#[cfg(feature = "std")]
19pub mod logger;
20mod nibbles;
21pub mod node;
22mod node_hash;
23pub mod rkyv_utils;
24mod rlp;
25#[cfg(test)]
26mod test_utils;
27#[cfg(feature = "std")]
29pub mod threadpool;
30mod trie_iter;
31#[cfg(feature = "std")]
32pub mod trie_sorted;
33mod verify_range;
34
35use alloc::collections::BTreeMap;
36use alloc::sync::Arc;
37#[cfg(not(feature = "std"))]
38use alloc::{boxed::Box, vec::Vec};
39use ethereum_types::H256;
40use ethrex_crypto::{Crypto, NativeCrypto};
41use ethrex_rlp::constants::RLP_NULL;
42use ethrex_rlp::encode::RLPEncode;
43
44#[cfg(feature = "std")]
45use rustc_hash::FxHashSet;
46#[cfg(feature = "std")]
50pub use rustc_hash::FxHashMap;
51#[cfg(not(feature = "std"))]
53pub type FxHashMap<K, V> = hashbrown::HashMap<K, V, rustc_hash::FxBuildHasher>;
54#[cfg(not(feature = "std"))]
55type FxHashSet<K> = hashbrown::HashSet<K, rustc_hash::FxBuildHasher>;
56
57pub use self::db::{InMemoryTrieDB, TrieDB};
58#[cfg(feature = "std")]
59pub use self::logger::{TrieLogger, TrieWitness};
60pub use self::nibbles::Nibbles;
61#[cfg(feature = "std")]
62pub use self::threadpool::ThreadPool;
63pub use self::verify_range::verify_range;
64pub use self::{
65 node::{Node, NodeRef, OnceLock},
66 node_hash::NodeHash,
67};
68
69pub use self::error::{ExtensionNodeErrorData, InconsistentTreeError, TrieError};
70use self::{node::LeafNode, trie_iter::TrieIterator};
71
72use ethrex_rlp::decode::RLPDecode;
73
74pub const EMPTY_TRIE_HASH: H256 = H256([
79 0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e,
80 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21,
81]);
82
83pub type PathRLP = Vec<u8>;
85pub type ValueRLP = Vec<u8>;
87pub type NodeRLP = Vec<u8>;
89pub type TrieNode = (Nibbles, NodeRLP);
91
92pub struct Trie {
94 db: Box<dyn TrieDB>,
95 pub root: NodeRef,
96 pending_removal: FxHashSet<Nibbles>,
97 dirty: FxHashSet<Nibbles>,
98}
99
100impl Default for Trie {
102 fn default() -> Self {
103 Self::new_temp()
104 }
105}
106
107impl Trie {
108 pub fn new(db: Box<dyn TrieDB>) -> Self {
110 Self {
111 db,
112 root: NodeRef::default(),
113 pending_removal: Default::default(),
114 dirty: Default::default(),
115 }
116 }
117
118 pub fn open(db: Box<dyn TrieDB>, root: H256) -> Self {
120 Self {
121 db,
122 root: if root != EMPTY_TRIE_HASH {
123 NodeHash::from(root).into()
124 } else {
125 Default::default()
126 },
127 pending_removal: Default::default(),
128 dirty: Default::default(),
129 }
130 }
131
132 pub fn db(&self) -> &dyn TrieDB {
137 self.db.as_ref()
138 }
139
140 pub fn get(&self, pathrlp: &[u8]) -> Result<Option<ValueRLP>, TrieError> {
142 let path = Nibbles::from_bytes(pathrlp);
143
144 if !self.dirty.contains(&path) && self.db().flatkeyvalue_computed(path.clone()) {
145 let Some(value_rlp) = self.db.get(path)? else {
146 return Ok(None);
147 };
148 if value_rlp.is_empty() {
149 return Ok(None);
150 }
151 return Ok(Some(value_rlp));
152 }
153
154 Ok(match self.root {
155 NodeRef::Node(ref node, _) => node.get(self.db.as_ref(), path)?,
156 NodeRef::Hash(hash) if hash.is_valid() => {
157 Node::decode(&self.db.get(Nibbles::default())?.ok_or_else(|| {
158 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFound(
159 hash.finalize(&NativeCrypto),
160 )))
161 })?)
162 .map_err(TrieError::RLPDecode)?
163 .get(self.db.as_ref(), path)?
164 }
165 _ => None,
166 })
167 }
168
169 pub fn insert(&mut self, path: PathRLP, value: ValueRLP) -> Result<(), TrieError> {
171 let path = Nibbles::from_bytes(&path);
172 self.pending_removal.remove(&path);
173 self.dirty.insert(path.clone());
174
175 if self.root.is_valid() {
176 self.root
178 .get_node_mut(self.db.as_ref(), Nibbles::default())?
179 .ok_or_else(|| {
180 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash))
181 })?
182 .insert(self.db.as_ref(), path, value)?
183 } else {
184 self.root = Node::from(LeafNode::new(path, value)).into()
186 };
187 self.root.clear_hash();
188
189 Ok(())
190 }
191
192 pub fn prefetch_sorted(&mut self, sorted_paths: &[Nibbles]) -> Result<(), TrieError> {
211 if sorted_paths.is_empty() {
212 return Ok(());
213 }
214 if !matches!(self.root, NodeRef::Hash(NodeHash::Hashed(_))) {
215 return Ok(());
216 }
217
218 if self
221 .root
222 .get_node_mut(self.db.as_ref(), Nibbles::default())?
223 .is_none()
224 {
225 return Ok(());
229 }
230
231 let all_indices: Vec<usize> = (0..sorted_paths.len()).collect();
232 const MAX_LEVELS: usize = 68;
235
236 for _ in 0..MAX_LEVELS {
237 let root_node = match &self.root {
242 NodeRef::Node(node, _) => node.clone(),
243 _ => break,
244 };
245
246 let mut to_fetch: Vec<Nibbles> = Vec::new();
247 collect_prefetch_targets(
248 &root_node,
249 &Nibbles::default(),
250 &all_indices,
251 sorted_paths,
252 &mut to_fetch,
253 );
254 drop(root_node);
258 if to_fetch.is_empty() {
259 break;
260 }
261
262 let mut results = self.db.multi_get(&to_fetch).into_iter();
263
264 let NodeRef::Node(root_arc, _) = &mut self.root else {
265 break;
266 };
267 let root_mut = Arc::make_mut(root_arc);
268 install_prefetch_targets(
269 root_mut,
270 &Nibbles::default(),
271 &all_indices,
272 sorted_paths,
273 &mut results,
274 )?;
275 }
276
277 Ok(())
278 }
279
280 pub fn remove(&mut self, path: &[u8]) -> Result<Option<ValueRLP>, TrieError> {
283 self.dirty.insert(Nibbles::from_bytes(path));
284 if !self.root.is_valid() {
285 return Ok(None);
286 }
287 self.pending_removal.insert(Nibbles::from_bytes(path));
288
289 let (is_trie_empty, value) = self
291 .root
292 .get_node_mut(self.db.as_ref(), Nibbles::default())?
293 .ok_or_else(|| {
294 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash))
295 })?
296 .remove(self.db.as_ref(), Nibbles::from_bytes(path))?;
297 if is_trie_empty {
298 self.root = NodeRef::default();
299 } else {
300 self.root.clear_hash();
301 }
302
303 Ok(value)
304 }
305
306 pub fn hash(&mut self, crypto: &dyn Crypto) -> Result<H256, TrieError> {
310 self.commit(crypto)?;
311 Ok(self.hash_no_commit(crypto))
312 }
313
314 pub fn hash_no_commit(&self, crypto: &dyn Crypto) -> H256 {
317 if self.root.is_valid() {
318 let mut buf = Vec::with_capacity(512);
320 self.root
321 .compute_hash_no_alloc(&mut buf, crypto)
322 .finalize(crypto)
323 } else {
324 EMPTY_TRIE_HASH
325 }
326 }
327
328 pub fn get_root_node(&self, path: Nibbles) -> Result<Arc<Node>, TrieError> {
329 self.root
330 .get_node_checked(self.db.as_ref(), path)?
331 .ok_or_else(|| {
332 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFound(
333 self.root
334 .compute_hash(&NativeCrypto)
335 .finalize(&NativeCrypto),
336 )))
337 })
338 }
339
340 pub fn collect_changes_since_last_hash(
346 &mut self,
347 crypto: &dyn Crypto,
348 ) -> (H256, Vec<TrieNode>) {
349 let updates = self.commit_without_storing(crypto);
350 let ret_hash = self.hash_no_commit(crypto);
351 (ret_hash, updates)
352 }
353
354 pub fn commit(&mut self, crypto: &dyn Crypto) -> Result<(), TrieError> {
359 let acc = self.commit_without_storing(crypto);
360 self.db.put_batch(acc)?;
361
362 self.db.commit()?;
364
365 Ok(())
366 }
367
368 pub fn commit_without_storing(&mut self, crypto: &dyn Crypto) -> Vec<TrieNode> {
371 let mut acc = Vec::new();
372 if self.root.is_valid() {
373 self.root.commit(Nibbles::default(), &mut acc, crypto);
374 }
375 if self.root.compute_hash(crypto) == NodeHash::Hashed(EMPTY_TRIE_HASH) {
376 acc.push((Nibbles::default(), vec![RLP_NULL]))
377 }
378 acc.extend(self.pending_removal.drain().map(|nib| (nib, vec![])));
379
380 acc
381 }
382
383 pub fn get_proof(&self, path: &[u8]) -> Result<Vec<NodeRLP>, TrieError> {
391 if self.root.is_valid() {
392 let hash = self.root.compute_hash(&NativeCrypto);
393
394 let mut node_path = Vec::new();
395 if let NodeHash::Inline((data, len)) = hash {
396 node_path.push(data[..len as usize].to_vec());
397 }
398
399 let root = match self
400 .root
401 .get_node_checked(self.db.as_ref(), Nibbles::default())?
402 {
403 Some(x) => x,
404 None => return Ok(Vec::new()),
405 };
406 root.get_path(self.db.as_ref(), Nibbles::from_bytes(path), &mut node_path)?;
407
408 Ok(node_path)
409 } else {
410 Ok(Vec::new())
411 }
412 }
413
414 pub fn get_proofs(
418 &self,
419 paths: &[PathRLP],
420 ) -> Result<(Option<NodeRLP>, Vec<NodeRLP>), TrieError> {
421 if self.root.is_valid() {
422 let encoded_root = self.get_root_node(Nibbles::default())?.encode_to_vec();
423
424 let mut node_path: FxHashSet<_> = Default::default();
425 for path in paths {
426 let mut nodes = self.get_proof(path)?;
427 nodes.swap_remove(0);
428 node_path.extend(nodes);
429 }
430
431 Ok((Some(encoded_root), node_path.into_iter().collect()))
432 } else {
433 Ok((None, Vec::new()))
434 }
435 }
436
437 #[cfg(feature = "std")]
438 pub fn empty_in_memory() -> Self {
439 Self::new(Box::new(InMemoryTrieDB::new(Arc::new(
440 std::sync::Mutex::new(BTreeMap::new()),
441 ))))
442 }
443
444 pub fn get_embedded_root(
446 all_nodes: &BTreeMap<H256, Node>,
447 root_hash: H256,
448 ) -> Result<NodeRef, TrieError> {
449 if root_hash == EMPTY_TRIE_HASH {
451 return Ok(NodeRef::default());
452 }
453
454 let root_rlp = all_nodes.get(&root_hash).ok_or_else(|| {
455 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFound(root_hash)))
456 })?;
457
458 fn get_embedded_node(
459 all_nodes: &BTreeMap<H256, Node>,
460 cur_node: &Node,
461 ) -> Result<Node, TrieError> {
462 Ok(match cur_node.clone() {
463 Node::Branch(mut node) => {
464 for choice in &mut node.choices {
465 let NodeRef::Hash(hash) = *choice else {
466 continue;
467 };
468
469 if hash.is_valid() {
470 *choice = match all_nodes.get(&hash.finalize(&NativeCrypto)) {
471 Some(node) => get_embedded_node(all_nodes, node)?.into(),
472 None => hash.into(),
473 };
474 }
475 }
476
477 (*node).into()
478 }
479 Node::Extension(mut node) => {
480 let NodeRef::Hash(hash) = node.child else {
481 return Ok(node.into());
482 };
483
484 node.child = match all_nodes.get(&hash.finalize(&NativeCrypto)) {
485 Some(node) => get_embedded_node(all_nodes, node)?.into(),
486 None => hash.into(),
487 };
488
489 node.into()
490 }
491 Node::Leaf(node) => node.into(),
492 })
493 }
494
495 let root = get_embedded_node(all_nodes, root_rlp)?;
496 Ok(root.into())
497 }
498
499 pub fn get_embedded_root_committed(
503 all_nodes: &FxHashMap<H256, Node>,
504 root_hash: H256,
505 crypto: &dyn Crypto,
506 ) -> Result<NodeRef, TrieError> {
507 if root_hash == EMPTY_TRIE_HASH {
509 return Ok(NodeRef::default());
510 }
511
512 let root_rlp = all_nodes.get(&root_hash).ok_or_else(|| {
513 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFound(root_hash)))
514 })?;
515
516 fn node_with_hash(node: Node, hash: NodeHash) -> NodeRef {
523 NodeRef::Node(Arc::new(node), OnceLock::from(hash))
524 }
525
526 fn get_embedded_node_committed(
527 all_nodes: &FxHashMap<H256, Node>,
528 cur_node: &Node,
529 crypto: &dyn Crypto,
530 ) -> Result<Node, TrieError> {
531 Ok(match cur_node.clone() {
532 Node::Branch(mut node) => {
533 for choice in &mut node.choices {
534 let NodeRef::Hash(hash) = *choice else {
535 continue;
536 };
537
538 if hash.is_valid() {
539 *choice = match all_nodes.get(&hash.finalize(crypto)) {
540 Some(node) => node_with_hash(
541 get_embedded_node_committed(all_nodes, node, crypto)?,
542 hash,
543 ),
544 None => hash.into(),
545 };
546 }
547 }
548
549 (*node).into()
550 }
551 Node::Extension(mut node) => {
552 let NodeRef::Hash(hash) = node.child else {
553 return Ok(node.into());
554 };
555
556 node.child = match all_nodes.get(&hash.finalize(crypto)) {
557 Some(node) => node_with_hash(
558 get_embedded_node_committed(all_nodes, node, crypto)?,
559 hash,
560 ),
561 None => hash.into(),
562 };
563
564 node.into()
565 }
566 Node::Leaf(node) => node.into(),
567 })
568 }
569
570 let root = get_embedded_node_committed(all_nodes, root_rlp, crypto)?;
571 Ok(node_with_hash(root, NodeHash::Hashed(root_hash)))
572 }
573
574 #[cfg(feature = "std")]
582 pub fn from_nodes(
583 root_hash: H256,
584 state_nodes: &BTreeMap<H256, Node>,
585 ) -> Result<Self, TrieError> {
586 let mut trie = Trie::new(Box::new(InMemoryTrieDB::default()));
587 let root = Self::get_embedded_root(state_nodes, root_hash)?;
588 trie.root = root;
589
590 Ok(trie)
591 }
592
593 pub fn compute_hash_from_unsorted_iter(
595 iter: impl Iterator<Item = (PathRLP, ValueRLP)>,
596 crypto: &dyn Crypto,
597 ) -> H256 {
598 let mut trie = Trie::stateless();
599 for (path, value) in iter {
600 trie.insert(path, value).unwrap();
602 }
603
604 trie.hash_no_commit(crypto)
605 }
606
607 pub(crate) fn stateless() -> Trie {
610 struct NullTrieDB;
612
613 impl TrieDB for NullTrieDB {
614 fn get(&self, _key: Nibbles) -> Result<Option<Vec<u8>>, TrieError> {
615 Ok(None)
616 }
617
618 fn put_batch(&self, _key_values: Vec<TrieNode>) -> Result<(), TrieError> {
619 Ok(())
620 }
621 }
622
623 Trie::new(Box::new(NullTrieDB))
624 }
625
626 pub fn get_node(&self, partial_path: &PathRLP) -> Result<Vec<u8>, TrieError> {
629 let partial_path = match partial_path.len() {
631 n if n < 32 => Nibbles::decode_compact(partial_path),
633 32 => Nibbles::from_bytes(partial_path),
635 _ => return Ok(vec![]),
637 };
638
639 fn get_node_inner(
640 db: &dyn TrieDB,
641 current_path: Nibbles,
642 node: &Node,
643 mut partial_path: Nibbles,
644 ) -> Result<Vec<u8>, TrieError> {
645 if partial_path.is_empty() {
647 return Ok(node.encode_to_vec());
648 }
649 match node {
650 Node::Branch(branch_node) => match partial_path.next_choice() {
651 Some(idx) => {
652 let child_ref = &branch_node.choices[idx];
653 if child_ref.is_valid() {
654 let child_path = current_path.append_new(idx as u8);
655 let child_node = child_ref
656 .get_node_checked(db, child_path.clone())?
657 .ok_or_else(|| {
658 TrieError::InconsistentTree(Box::new(
659 InconsistentTreeError::NodeNotFoundOnBranchNode(
660 child_ref
661 .compute_hash(&NativeCrypto)
662 .finalize(&NativeCrypto),
663 branch_node
664 .compute_hash(&NativeCrypto)
665 .finalize(&NativeCrypto),
666 child_path.clone(),
667 ),
668 ))
669 })?;
670 get_node_inner(db, child_path, &child_node, partial_path)
671 } else {
672 Ok(vec![])
673 }
674 }
675 _ => Ok(vec![]),
676 },
677 Node::Extension(extension_node) => {
678 if partial_path.skip_prefix(&extension_node.prefix)
679 && extension_node.child.is_valid()
680 {
681 let child_path = partial_path.concat(&extension_node.prefix);
682 let child_node = extension_node
683 .child
684 .get_node_checked(db, child_path.clone())?
685 .ok_or_else(|| {
686 TrieError::InconsistentTree(Box::new(
687 InconsistentTreeError::ExtensionNodeChildNotFound(
688 ExtensionNodeErrorData {
689 node_hash: extension_node
690 .child
691 .compute_hash(&NativeCrypto)
692 .finalize(&NativeCrypto),
693 extension_node_hash: extension_node
694 .compute_hash(&NativeCrypto)
695 .finalize(&NativeCrypto),
696 extension_node_prefix: extension_node.prefix.clone(),
697 node_path: child_path.clone(),
698 },
699 ),
700 ))
701 })?;
702 get_node_inner(db, child_path, &child_node, partial_path)
703 } else {
704 Ok(vec![])
705 }
706 }
707 Node::Leaf(_) => Ok(vec![]),
708 }
709 }
710
711 if self.root.is_valid() {
713 let root_node = self.get_root_node(Default::default())?;
714 get_node_inner(
715 self.db.as_ref(),
716 Default::default(),
717 &root_node,
718 partial_path,
719 )
720 } else {
721 Ok(Vec::new())
722 }
723 }
724
725 pub fn root_node(&self) -> Result<Option<Arc<Node>>, TrieError> {
726 if self.root.is_valid() {
727 self.root.get_node(self.db.as_ref(), Nibbles::default())
728 } else {
729 Ok(None)
730 }
731 }
732
733 pub fn new_temp() -> Self {
735 let db = InMemoryTrieDB::new(Default::default());
736 Trie::new(Box::new(db))
737 }
738
739 pub fn new_temp_with_root(root: NodeRef) -> Self {
743 let db = InMemoryTrieDB::new(Default::default());
744 let mut trie = Trie::new(Box::new(db));
745 trie.root = root;
746 trie
747 }
748
749 pub fn validate(self) -> Result<(), TrieError> {
756 let mut expected_count = if self.root.is_valid() { 1 } else { 0 };
757 for (_, node) in self.into_iter() {
758 expected_count -= 1;
759 match node {
760 Node::Branch(branch_node) => {
761 expected_count += branch_node
762 .choices
763 .iter()
764 .filter(|child| child.is_valid())
765 .count();
766 }
767 Node::Extension(_) => {
768 expected_count += 1;
769 }
770 Node::Leaf(_) => {}
771 }
772 }
773 if expected_count != 0 {
774 return Err(TrieError::Verify(format!(
775 "Node count mismatch, expected {expected_count} more"
776 )));
777 }
778 Ok(())
779 }
780
781 #[cfg(feature = "std")]
784 pub fn validate_parallel(self) -> Result<(), TrieError> {
785 use rayon::prelude::*;
786
787 if !self.root.is_valid() {
788 return Ok(());
789 }
790
791 let db = &*self.db;
792 let root_node = self
793 .root
794 .get_node_checked(db, Nibbles::default())?
795 .ok_or_else(|| TrieError::Verify("Root node not found".to_string()))?;
796
797 match &*root_node {
798 Node::Branch(branch_node) => {
799 let children: Vec<(Nibbles, NodeRef)> = branch_node
800 .choices
801 .iter()
802 .enumerate()
803 .filter(|(_, child)| child.is_valid())
804 .map(|(i, child)| {
805 let path = Nibbles::default().append_new(i as u8);
806 (path, child.clone())
807 })
808 .collect();
809
810 children.par_iter().try_for_each(|(start_path, start_ref)| {
811 validate_subtree(db, start_path.clone(), start_ref.clone())
812 })
813 }
814 _ => {
815 validate_subtree(db, Nibbles::default(), self.root.clone())
817 }
818 }
819 }
820}
821
822fn collect_prefetch_targets(
830 node: &Node,
831 path: &Nibbles,
832 key_indices: &[usize],
833 sorted_paths: &[Nibbles],
834 out: &mut Vec<Nibbles>,
835) {
836 match node {
837 Node::Branch(branch) => {
838 let depth = path.len();
839 let mut buckets: [Vec<usize>; 16] = core::array::from_fn(|_| Vec::new());
840 for &i in key_indices {
841 let p = sorted_paths[i].as_ref();
842 if depth >= p.len() {
843 continue;
844 }
845 let nibble = p[depth];
846 if nibble < 16 {
847 buckets[nibble as usize].push(i);
848 }
849 }
850 for (nibble, indices) in buckets.iter().enumerate() {
851 if indices.is_empty() {
852 continue;
853 }
854 let child_path = path.append_new(nibble as u8);
855 match &branch.choices[nibble] {
856 NodeRef::Hash(NodeHash::Hashed(_)) => out.push(child_path),
857 NodeRef::Node(child, _) => {
858 collect_prefetch_targets(child, &child_path, indices, sorted_paths, out)
859 }
860 _ => {}
862 }
863 }
864 }
865 Node::Extension(ext) => {
866 let depth = path.len();
867 let prefix = ext.prefix.as_ref();
868 let matched: Vec<usize> = key_indices
869 .iter()
870 .copied()
871 .filter(|&i| {
872 let p = sorted_paths[i].as_ref();
873 depth + prefix.len() <= p.len() && &p[depth..depth + prefix.len()] == prefix
874 })
875 .collect();
876 if matched.is_empty() {
877 return;
881 }
882 let child_path = path.concat(&ext.prefix);
883 match &ext.child {
884 NodeRef::Hash(NodeHash::Hashed(_)) => out.push(child_path),
885 NodeRef::Node(child, _) => {
886 collect_prefetch_targets(child, &child_path, &matched, sorted_paths, out)
887 }
888 _ => {}
889 }
890 }
891 Node::Leaf(_) => {}
893 }
894}
895
896fn install_prefetch_targets(
904 node: &mut Node,
905 path: &Nibbles,
906 key_indices: &[usize],
907 sorted_paths: &[Nibbles],
908 results: &mut alloc::vec::IntoIter<Result<Option<Vec<u8>>, TrieError>>,
909) -> Result<(), TrieError> {
910 match node {
911 Node::Branch(branch) => {
912 let depth = path.len();
913 let mut buckets: [Vec<usize>; 16] = core::array::from_fn(|_| Vec::new());
914 for &i in key_indices {
915 let p = sorted_paths[i].as_ref();
916 if depth >= p.len() {
917 continue;
918 }
919 let nibble = p[depth];
920 if nibble < 16 {
921 buckets[nibble as usize].push(i);
922 }
923 }
924 for (nibble, indices) in buckets.iter().enumerate() {
925 if indices.is_empty() {
926 continue;
927 }
928 let child_path = path.append_new(nibble as u8);
929 let slot = &mut branch.choices[nibble];
930 match slot {
931 NodeRef::Hash(hash @ NodeHash::Hashed(_)) => {
932 let hash = *hash;
933 let bytes = results.next().ok_or_else(|| {
934 TrieError::DbError(anyhow::anyhow!(
935 "prefetch_sorted: multi_get returned fewer results than requested"
936 ))
937 })??;
938 if let Some(bytes) = bytes.filter(|b| !b.is_empty()) {
939 let decoded = Node::decode(&bytes).map_err(TrieError::RLPDecode)?;
940 *slot = NodeRef::Node(Arc::new(decoded), OnceLock::from(hash));
941 }
942 }
943 NodeRef::Node(child, _) => {
944 let child_mut = Arc::make_mut(child);
945 install_prefetch_targets(
946 child_mut,
947 &child_path,
948 indices,
949 sorted_paths,
950 results,
951 )?;
952 }
953 _ => {}
954 }
955 }
956 }
957 Node::Extension(ext) => {
958 let depth = path.len();
959 let prefix = ext.prefix.as_ref();
960 let matched: Vec<usize> = key_indices
961 .iter()
962 .copied()
963 .filter(|&i| {
964 let p = sorted_paths[i].as_ref();
965 depth + prefix.len() <= p.len() && &p[depth..depth + prefix.len()] == prefix
966 })
967 .collect();
968 if matched.is_empty() {
969 return Ok(());
970 }
971 let child_path = path.concat(&ext.prefix);
972 let slot = &mut ext.child;
973 match slot {
974 NodeRef::Hash(hash @ NodeHash::Hashed(_)) => {
975 let hash = *hash;
976 let bytes = results.next().ok_or_else(|| {
977 TrieError::DbError(anyhow::anyhow!(
978 "prefetch_sorted: multi_get returned fewer results than requested"
979 ))
980 })??;
981 if let Some(bytes) = bytes.filter(|b| !b.is_empty()) {
982 let decoded = Node::decode(&bytes).map_err(TrieError::RLPDecode)?;
983 *slot = NodeRef::Node(Arc::new(decoded), OnceLock::from(hash));
984 }
985 }
986 NodeRef::Node(child, _) => {
987 let child_mut = Arc::make_mut(child);
988 install_prefetch_targets(
989 child_mut,
990 &child_path,
991 &matched,
992 sorted_paths,
993 results,
994 )?;
995 }
996 _ => {}
997 }
998 }
999 Node::Leaf(_) => {}
1000 }
1001 Ok(())
1002}
1003
1004#[cfg(feature = "std")]
1007fn validate_subtree(
1008 db: &dyn TrieDB,
1009 start_path: Nibbles,
1010 start_ref: NodeRef,
1011) -> Result<(), TrieError> {
1012 let mut expected_count: isize = 1;
1013 let mut stack = vec![(start_path, start_ref)];
1014
1015 while let Some((path, node_ref)) = stack.pop() {
1016 let node = node_ref
1017 .get_node_checked(db, path.clone())?
1018 .ok_or_else(|| TrieError::Verify(format!("Missing node at path {path:?}")))?;
1019
1020 expected_count -= 1;
1021 match &*node {
1022 Node::Branch(branch) => {
1023 for (choice, child) in branch.choices.iter().enumerate().rev() {
1024 if child.is_valid() {
1025 expected_count += 1;
1026 stack.push((path.append_new(choice as u8), child.clone()));
1027 }
1028 }
1029 }
1030 Node::Extension(ext) => {
1031 expected_count += 1;
1032 stack.push((path.concat(&ext.prefix), ext.child.clone()));
1033 }
1034 Node::Leaf(_) => {}
1035 }
1036 }
1037
1038 if expected_count != 0 {
1039 return Err(TrieError::Verify(format!(
1040 "Node count mismatch in subtree, expected {expected_count} more"
1041 )));
1042 }
1043 Ok(())
1044}
1045
1046impl IntoIterator for Trie {
1047 type Item = (Nibbles, Node);
1048
1049 type IntoIter = TrieIterator;
1050
1051 fn into_iter(self) -> Self::IntoIter {
1052 TrieIterator::new(self)
1053 }
1054}
1055
1056pub struct ProofTrie(Trie);
1057
1058impl ProofTrie {
1059 pub fn insert(
1060 &mut self,
1061 partial_path: Nibbles,
1062 external_ref: NodeHash,
1063 ) -> Result<(), TrieError> {
1064 if self.0.root.is_valid() {
1065 self.0
1067 .root
1068 .get_node_mut(self.0.db.as_ref(), Nibbles::default())?
1069 .ok_or_else(|| {
1070 TrieError::InconsistentTree(Box::new(InconsistentTreeError::RootNotFoundNoHash))
1071 })?
1072 .insert(self.0.db.as_ref(), partial_path, external_ref)?;
1073 self.0.root.clear_hash();
1074 } else {
1075 self.0.root = external_ref.into();
1076 };
1077
1078 Ok(())
1079 }
1080
1081 pub fn hash(&self, crypto: &dyn Crypto) -> H256 {
1082 self.0.hash_no_commit(crypto)
1083 }
1084}
1085
1086impl From<Trie> for ProofTrie {
1087 fn from(value: Trie) -> Self {
1088 Self(value)
1089 }
1090}
1091
1092#[cfg(test)]
1093mod tests {
1094 use super::*;
1095 use ethrex_crypto::keccak::keccak_hash;
1096
1097 #[test]
1100 fn empty_trie_hash_matches_keccak() {
1101 assert_eq!(EMPTY_TRIE_HASH, H256(keccak_hash([RLP_NULL])));
1102 }
1103}