reth_trie/
walker.rs

1use crate::{
2    prefix_set::PrefixSet,
3    trie_cursor::{subnode::SubNodePosition, CursorSubNode, TrieCursor},
4    BranchNodeCompact, Nibbles,
5};
6use alloy_primitives::{map::HashSet, B256};
7use reth_storage_errors::db::DatabaseError;
8use tracing::{instrument, trace};
9
10#[cfg(feature = "metrics")]
11use crate::metrics::WalkerMetrics;
12
13/// `TrieWalker` is a structure that enables traversal of a Merkle trie.
14/// It allows moving through the trie in a depth-first manner, skipping certain branches
15/// if they have not changed.
16#[derive(Debug)]
17pub struct TrieWalker<C> {
18    /// A mutable reference to a trie cursor instance used for navigating the trie.
19    pub cursor: C,
20    /// A vector containing the trie nodes that have been visited.
21    pub stack: Vec<CursorSubNode>,
22    /// A flag indicating whether the current node can be skipped when traversing the trie. This
23    /// is determined by whether the current key's prefix is included in the prefix set and if the
24    /// hash flag is set.
25    pub can_skip_current_node: bool,
26    /// A `PrefixSet` representing the changes to be applied to the trie.
27    pub changes: PrefixSet,
28    /// The retained trie node keys that need to be removed.
29    removed_keys: Option<HashSet<Nibbles>>,
30    #[cfg(feature = "metrics")]
31    /// Walker metrics.
32    metrics: WalkerMetrics,
33}
34
35impl<C> TrieWalker<C> {
36    /// Constructs a new `TrieWalker` for the state trie from existing stack and a cursor.
37    pub fn state_trie_from_stack(cursor: C, stack: Vec<CursorSubNode>, changes: PrefixSet) -> Self {
38        Self::from_stack(
39            cursor,
40            stack,
41            changes,
42            #[cfg(feature = "metrics")]
43            crate::TrieType::State,
44        )
45    }
46
47    /// Constructs a new `TrieWalker` for the storage trie from existing stack and a cursor.
48    pub fn storage_trie_from_stack(
49        cursor: C,
50        stack: Vec<CursorSubNode>,
51        changes: PrefixSet,
52    ) -> Self {
53        Self::from_stack(
54            cursor,
55            stack,
56            changes,
57            #[cfg(feature = "metrics")]
58            crate::TrieType::Storage,
59        )
60    }
61
62    /// Constructs a new `TrieWalker` from existing stack and a cursor.
63    fn from_stack(
64        cursor: C,
65        stack: Vec<CursorSubNode>,
66        changes: PrefixSet,
67        #[cfg(feature = "metrics")] trie_type: crate::TrieType,
68    ) -> Self {
69        let mut this = Self {
70            cursor,
71            changes,
72            stack,
73            can_skip_current_node: false,
74            removed_keys: None,
75            #[cfg(feature = "metrics")]
76            metrics: WalkerMetrics::new(trie_type),
77        };
78        this.update_skip_node();
79        this
80    }
81
82    /// Sets the flag whether the trie updates should be stored.
83    pub fn with_deletions_retained(mut self, retained: bool) -> Self {
84        if retained {
85            self.removed_keys = Some(HashSet::default());
86        }
87        self
88    }
89
90    /// Split the walker into stack and trie updates.
91    pub fn split(mut self) -> (Vec<CursorSubNode>, HashSet<Nibbles>) {
92        let keys = self.take_removed_keys();
93        (self.stack, keys)
94    }
95
96    /// Take removed keys from the walker.
97    pub fn take_removed_keys(&mut self) -> HashSet<Nibbles> {
98        self.removed_keys.take().unwrap_or_default()
99    }
100
101    /// Prints the current stack of trie nodes.
102    pub fn print_stack(&self) {
103        println!("====================== STACK ======================");
104        for node in &self.stack {
105            println!("{node:?}");
106        }
107        println!("====================== END STACK ======================\n");
108    }
109
110    /// The current length of the removed keys.
111    pub fn removed_keys_len(&self) -> usize {
112        self.removed_keys.as_ref().map_or(0, |u| u.len())
113    }
114
115    /// Returns the current key in the trie.
116    pub fn key(&self) -> Option<&Nibbles> {
117        self.stack.last().map(|n| n.full_key())
118    }
119
120    /// Returns the current hash in the trie, if any.
121    pub fn hash(&self) -> Option<B256> {
122        self.stack.last().and_then(|n| n.hash())
123    }
124
125    /// Returns the current hash in the trie, if any.
126    ///
127    /// Differs from [`Self::hash`] in that it returns `None` if the subnode is positioned at the
128    /// child without a hash mask bit set. [`Self::hash`] panics in that case.
129    pub fn maybe_hash(&self) -> Option<B256> {
130        self.stack.last().and_then(|n| n.maybe_hash())
131    }
132
133    /// Indicates whether the children of the current node are present in the trie.
134    pub fn children_are_in_trie(&self) -> bool {
135        self.stack.last().is_some_and(|n| n.tree_flag())
136    }
137
138    /// Returns the next unprocessed key in the trie along with its raw [`Nibbles`] representation.
139    #[instrument(level = "trace", skip(self), ret)]
140    pub fn next_unprocessed_key(&self) -> Option<(B256, Nibbles)> {
141        self.key()
142            .and_then(
143                |key| if self.can_skip_current_node { key.increment() } else { Some(key.clone()) },
144            )
145            .map(|key| {
146                let mut packed = key.pack();
147                packed.resize(32, 0);
148                (B256::from_slice(packed.as_slice()), key)
149            })
150    }
151
152    /// Updates the skip node flag based on the walker's current state.
153    fn update_skip_node(&mut self) {
154        let old = self.can_skip_current_node;
155        self.can_skip_current_node = self
156            .stack
157            .last()
158            .is_some_and(|node| !self.changes.contains(node.full_key()) && node.hash_flag());
159        trace!(
160            target: "trie::walker",
161            old,
162            new = self.can_skip_current_node,
163            last = ?self.stack.last(),
164            "updated skip node flag"
165        );
166    }
167}
168
169impl<C: TrieCursor> TrieWalker<C> {
170    /// Constructs a new [`TrieWalker`] for the state trie.
171    pub fn state_trie(cursor: C, changes: PrefixSet) -> Self {
172        Self::new(
173            cursor,
174            changes,
175            #[cfg(feature = "metrics")]
176            crate::TrieType::State,
177        )
178    }
179
180    /// Constructs a new [`TrieWalker`] for the storage trie.
181    pub fn storage_trie(cursor: C, changes: PrefixSet) -> Self {
182        Self::new(
183            cursor,
184            changes,
185            #[cfg(feature = "metrics")]
186            crate::TrieType::Storage,
187        )
188    }
189
190    /// Constructs a new `TrieWalker`, setting up the initial state of the stack and cursor.
191    fn new(
192        cursor: C,
193        changes: PrefixSet,
194        #[cfg(feature = "metrics")] trie_type: crate::TrieType,
195    ) -> Self {
196        // Initialize the walker with a single empty stack element.
197        let mut this = Self {
198            cursor,
199            changes,
200            stack: vec![CursorSubNode::default()],
201            can_skip_current_node: false,
202            removed_keys: None,
203            #[cfg(feature = "metrics")]
204            metrics: WalkerMetrics::new(trie_type),
205        };
206
207        // Set up the root node of the trie in the stack, if it exists.
208        if let Some((key, value)) = this.node(true).unwrap() {
209            this.stack[0] = CursorSubNode::new(key, Some(value));
210        }
211
212        // Update the skip state for the root node.
213        this.update_skip_node();
214        this
215    }
216
217    /// Advances the walker to the next trie node and updates the skip node flag.
218    /// The new key can then be obtained via `key()`.
219    ///
220    /// # Returns
221    ///
222    /// * `Result<(), Error>` - Unit on success or an error.
223    pub fn advance(&mut self) -> Result<(), DatabaseError> {
224        if let Some(last) = self.stack.last() {
225            if !self.can_skip_current_node && self.children_are_in_trie() {
226                trace!(
227                    target: "trie::walker",
228                    position = ?last.position(),
229                    "cannot skip current node and children are in the trie"
230                );
231                // If we can't skip the current node and the children are in the trie,
232                // either consume the next node or move to the next sibling.
233                match last.position() {
234                    SubNodePosition::ParentBranch => self.move_to_next_sibling(true)?,
235                    SubNodePosition::Child(_) => self.consume_node()?,
236                }
237            } else {
238                trace!(target: "trie::walker", "can skip current node");
239                // If we can skip the current node, move to the next sibling.
240                self.move_to_next_sibling(false)?;
241            }
242
243            // Update the skip node flag based on the new position in the trie.
244            self.update_skip_node();
245        }
246
247        Ok(())
248    }
249
250    /// Retrieves the current root node from the DB, seeking either the exact node or the next one.
251    fn node(&mut self, exact: bool) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
252        let key = self.key().expect("key must exist").clone();
253        let entry = if exact { self.cursor.seek_exact(key)? } else { self.cursor.seek(key)? };
254        #[cfg(feature = "metrics")]
255        self.metrics.inc_branch_nodes_seeked();
256
257        if let Some((_, node)) = &entry {
258            assert!(!node.state_mask.is_empty());
259        }
260
261        Ok(entry)
262    }
263
264    /// Consumes the next node in the trie, updating the stack.
265    #[instrument(level = "trace", skip(self), ret)]
266    fn consume_node(&mut self) -> Result<(), DatabaseError> {
267        let Some((key, node)) = self.node(false)? else {
268            // If no next node is found, clear the stack.
269            self.stack.clear();
270            return Ok(())
271        };
272
273        // Overwrite the root node's first nibble
274        // We need to sync the stack with the trie structure when consuming a new node. This is
275        // necessary for proper traversal and accurately representing the trie in the stack.
276        if !key.is_empty() && !self.stack.is_empty() {
277            self.stack[0].set_nibble(key[0]);
278        }
279
280        // The current tree mask might have been set incorrectly.
281        // Sanity check that the newly retrieved trie node key is the child of the last item
282        // on the stack. If not, advance to the next sibling instead of adding the node to the
283        // stack.
284        if let Some(subnode) = self.stack.last() {
285            if !key.starts_with(subnode.full_key()) {
286                #[cfg(feature = "metrics")]
287                self.metrics.inc_out_of_order_subnode(1);
288                self.move_to_next_sibling(false)?;
289                return Ok(())
290            }
291        }
292
293        // Create a new CursorSubNode and push it to the stack.
294        let subnode = CursorSubNode::new(key, Some(node));
295        let position = subnode.position();
296        self.stack.push(subnode);
297        self.update_skip_node();
298
299        // Delete the current node if it's included in the prefix set or it doesn't contain the root
300        // hash.
301        if !self.can_skip_current_node || position.is_child() {
302            if let Some((keys, key)) = self.removed_keys.as_mut().zip(self.cursor.current()?) {
303                keys.insert(key);
304            }
305        }
306
307        Ok(())
308    }
309
310    /// Moves to the next sibling node in the trie, updating the stack.
311    #[instrument(level = "trace", skip(self), ret)]
312    fn move_to_next_sibling(
313        &mut self,
314        allow_root_to_child_nibble: bool,
315    ) -> Result<(), DatabaseError> {
316        let Some(subnode) = self.stack.last_mut() else { return Ok(()) };
317
318        // Check if the walker needs to backtrack to the previous level in the trie during its
319        // traversal.
320        if subnode.position().is_last_child() ||
321            (subnode.position().is_parent() && !allow_root_to_child_nibble)
322        {
323            self.stack.pop();
324            self.move_to_next_sibling(false)?;
325            return Ok(())
326        }
327
328        subnode.inc_nibble();
329
330        if subnode.node.is_none() {
331            return self.consume_node()
332        }
333
334        // Find the next sibling with state.
335        loop {
336            let position = subnode.position();
337            if subnode.state_flag() {
338                trace!(target: "trie::walker", ?position, "found next sibling with state");
339                return Ok(())
340            }
341            if position.is_last_child() {
342                trace!(target: "trie::walker", ?position, "checked all siblings");
343                break
344            }
345            subnode.inc_nibble();
346        }
347
348        // Pop the current node and move to the next sibling.
349        self.stack.pop();
350        self.move_to_next_sibling(false)?;
351
352        Ok(())
353    }
354}