reth_trie/node_iter.rs
1use crate::{hashed_cursor::HashedCursor, trie_cursor::TrieCursor, walker::TrieWalker, Nibbles};
2use alloy_primitives::B256;
3use reth_storage_errors::db::DatabaseError;
4
5/// Represents a branch node in the trie.
6#[derive(Debug)]
7pub struct TrieBranchNode {
8 /// The key associated with the node.
9 pub key: Nibbles,
10 /// The value associated with the node.
11 pub value: B256,
12 /// Indicates whether children are in the trie.
13 pub children_are_in_trie: bool,
14}
15
16impl TrieBranchNode {
17 /// Creates a new `TrieBranchNode`.
18 pub const fn new(key: Nibbles, value: B256, children_are_in_trie: bool) -> Self {
19 Self { key, value, children_are_in_trie }
20 }
21}
22
23/// Represents variants of trie nodes returned by the iteration.
24#[derive(Debug)]
25pub enum TrieElement<Value> {
26 /// Branch node.
27 Branch(TrieBranchNode),
28 /// Leaf node.
29 Leaf(B256, Value),
30}
31
32/// An iterator over existing intermediate branch nodes and updated leaf nodes.
33#[derive(Debug)]
34pub struct TrieNodeIter<C, H: HashedCursor> {
35 /// The walker over intermediate nodes.
36 pub walker: TrieWalker<C>,
37 /// The cursor for the hashed entries.
38 pub hashed_cursor: H,
39 /// The previous hashed key. If the iteration was previously interrupted, this value can be
40 /// used to resume iterating from the last returned leaf node.
41 previous_hashed_key: Option<B256>,
42
43 /// Current hashed entry.
44 current_hashed_entry: Option<(B256, <H as HashedCursor>::Value)>,
45 /// Flag indicating whether we should check the current walker key.
46 current_walker_key_checked: bool,
47}
48
49impl<C, H: HashedCursor> TrieNodeIter<C, H> {
50 /// Creates a new [`TrieNodeIter`].
51 pub const fn new(walker: TrieWalker<C>, hashed_cursor: H) -> Self {
52 Self {
53 walker,
54 hashed_cursor,
55 previous_hashed_key: None,
56 current_hashed_entry: None,
57 current_walker_key_checked: false,
58 }
59 }
60
61 /// Sets the last iterated hashed key and returns the modified [`TrieNodeIter`].
62 /// This is used to resume iteration from the last checkpoint.
63 pub const fn with_last_hashed_key(mut self, previous_hashed_key: B256) -> Self {
64 self.previous_hashed_key = Some(previous_hashed_key);
65 self
66 }
67}
68
69impl<C, H> TrieNodeIter<C, H>
70where
71 C: TrieCursor,
72 H: HashedCursor,
73{
74 /// Return the next trie node to be added to the hash builder.
75 ///
76 /// Returns the nodes using this algorithm:
77 /// 1. Return the current intermediate branch node if it hasn't been updated.
78 /// 2. Advance the trie walker to the next intermediate branch node and retrieve next
79 /// unprocessed key.
80 /// 3. Reposition the hashed cursor on the next unprocessed key.
81 /// 4. Return every hashed entry up to the key of the current intermediate branch node.
82 /// 5. Repeat.
83 ///
84 /// NOTE: The iteration will start from the key of the previous hashed entry if it was supplied.
85 pub fn try_next(
86 &mut self,
87 ) -> Result<Option<TrieElement<<H as HashedCursor>::Value>>, DatabaseError> {
88 loop {
89 // If the walker has a key...
90 if let Some(key) = self.walker.key() {
91 // Check if the current walker key is unchecked and there's no previous hashed key
92 if !self.current_walker_key_checked && self.previous_hashed_key.is_none() {
93 self.current_walker_key_checked = true;
94 // If it's possible to skip the current node in the walker, return a branch node
95 if self.walker.can_skip_current_node {
96 return Ok(Some(TrieElement::Branch(TrieBranchNode::new(
97 key.clone(),
98 self.walker.hash().unwrap(),
99 self.walker.children_are_in_trie(),
100 ))))
101 }
102 }
103 }
104
105 // If there's a hashed entry...
106 if let Some((hashed_key, value)) = self.current_hashed_entry.take() {
107 // If the walker's key is less than the unpacked hashed key,
108 // reset the checked status and continue
109 if self.walker.key().is_some_and(|key| key < &Nibbles::unpack(hashed_key)) {
110 self.current_walker_key_checked = false;
111 continue
112 }
113
114 // Set the next hashed entry as a leaf node and return
115 self.current_hashed_entry = self.hashed_cursor.next()?;
116 return Ok(Some(TrieElement::Leaf(hashed_key, value)))
117 }
118
119 // Handle seeking and advancing based on the previous hashed key
120 match self.previous_hashed_key.take() {
121 Some(hashed_key) => {
122 // Seek to the previous hashed key and get the next hashed entry
123 self.hashed_cursor.seek(hashed_key)?;
124 self.current_hashed_entry = self.hashed_cursor.next()?;
125 }
126 None => {
127 // Get the seek key and set the current hashed entry based on walker's next
128 // unprocessed key
129 let seek_key = match self.walker.next_unprocessed_key() {
130 Some(key) => key,
131 None => break, // no more keys
132 };
133 self.current_hashed_entry = self.hashed_cursor.seek(seek_key)?;
134 self.walker.advance()?;
135 }
136 }
137 }
138
139 Ok(None)
140 }
141}