1use crate::{
2prefix_set::PrefixSet,
3 trie_cursor::{CursorSubNode, TrieCursor},
4BranchNodeCompact, Nibbles,
5};
6use alloy_primitives::{map::HashSet, B256};
7use reth_storage_errors::db::DatabaseError;
89#[cfg(feature = "metrics")]
10use crate::metrics::WalkerMetrics;
1112/// `TrieWalker` is a structure that enables traversal of a Merkle trie.
13/// It allows moving through the trie in a depth-first manner, skipping certain branches
14/// if they have not changed.
15#[derive(Debug)]
16pub struct TrieWalker<C> {
17/// A mutable reference to a trie cursor instance used for navigating the trie.
18pub cursor: C,
19/// A vector containing the trie nodes that have been visited.
20pub stack: Vec<CursorSubNode>,
21/// A flag indicating whether the current node can be skipped when traversing the trie. This
22 /// is determined by whether the current key's prefix is included in the prefix set and if the
23 /// hash flag is set.
24pub can_skip_current_node: bool,
25/// A `PrefixSet` representing the changes to be applied to the trie.
26pub changes: PrefixSet,
27/// The retained trie node keys that need to be removed.
28removed_keys: Option<HashSet<Nibbles>>,
29#[cfg(feature = "metrics")]
30/// Walker metrics.
31metrics: WalkerMetrics,
32}
3334impl<C> TrieWalker<C> {
35/// Constructs a new `TrieWalker` from existing stack and a cursor.
36pub fn from_stack(cursor: C, stack: Vec<CursorSubNode>, changes: PrefixSet) -> Self {
37let mut this = Self {
38cursor,
39changes,
40stack,
41 can_skip_current_node: false,
42 removed_keys: None,
43#[cfg(feature = "metrics")]
44metrics: WalkerMetrics::default(),
45 };
46this.update_skip_node();
47this48 }
4950/// Sets the flag whether the trie updates should be stored.
51pub fn with_deletions_retained(mut self, retained: bool) -> Self {
52if retained {
53self.removed_keys = Some(HashSet::default());
54 }
55self56}
5758/// Split the walker into stack and trie updates.
59pub fn split(mut self) -> (Vec<CursorSubNode>, HashSet<Nibbles>) {
60let keys = self.take_removed_keys();
61 (self.stack, keys)
62 }
6364/// Take removed keys from the walker.
65pub fn take_removed_keys(&mut self) -> HashSet<Nibbles> {
66self.removed_keys.take().unwrap_or_default()
67 }
6869/// Prints the current stack of trie nodes.
70pub fn print_stack(&self) {
71println!("====================== STACK ======================");
72for node in &self.stack {
73println!("{node:?}");
74 }
75println!("====================== END STACK ======================\n");
76 }
7778/// The current length of the removed keys.
79pub fn removed_keys_len(&self) -> usize {
80self.removed_keys.as_ref().map_or(0, |u| u.len())
81 }
8283/// Returns the current key in the trie.
84pub fn key(&self) -> Option<&Nibbles> {
85self.stack.last().map(|n| n.full_key())
86 }
8788/// Returns the current hash in the trie if any.
89pub fn hash(&self) -> Option<B256> {
90self.stack.last().and_then(|n| n.hash())
91 }
9293/// Indicates whether the children of the current node are present in the trie.
94pub fn children_are_in_trie(&self) -> bool {
95self.stack.last().is_some_and(|n| n.tree_flag())
96 }
9798/// Returns the next unprocessed key in the trie.
99pub fn next_unprocessed_key(&self) -> Option<B256> {
100self.key()
101 .and_then(|key| {
102if self.can_skip_current_node {
103key.increment().map(|inc| inc.pack())
104 } else {
105Some(key.pack())
106 }
107 })
108 .map(|mut key| {
109key.resize(32, 0);
110 B256::from_slice(key.as_slice())
111 })
112 }
113114/// Updates the skip node flag based on the walker's current state.
115fn update_skip_node(&mut self) {
116self.can_skip_current_node = self117.stack
118 .last()
119 .is_some_and(|node| !self.changes.contains(node.full_key()) && node.hash_flag());
120 }
121}
122123impl<C: TrieCursor> TrieWalker<C> {
124/// Constructs a new `TrieWalker`, setting up the initial state of the stack and cursor.
125pub fn new(cursor: C, changes: PrefixSet) -> Self {
126// Initialize the walker with a single empty stack element.
127let mut this = Self {
128cursor,
129changes,
130 stack: vec![CursorSubNode::default()],
131 can_skip_current_node: false,
132 removed_keys: None,
133#[cfg(feature = "metrics")]
134metrics: WalkerMetrics::default(),
135 };
136137// Set up the root node of the trie in the stack, if it exists.
138if let Some((key, value)) = this.node(true).unwrap() {
139this.stack[0] = CursorSubNode::new(key, Some(value));
140 }
141142// Update the skip state for the root node.
143this.update_skip_node();
144this145 }
146147/// Advances the walker to the next trie node and updates the skip node flag.
148 /// The new key can then be obtained via `key()`.
149 ///
150 /// # Returns
151 ///
152 /// * `Result<(), Error>` - Unit on success or an error.
153pub fn advance(&mut self) -> Result<(), DatabaseError> {
154if let Some(last) = self.stack.last() {
155if !self.can_skip_current_node && self.children_are_in_trie() {
156// If we can't skip the current node and the children are in the trie,
157 // either consume the next node or move to the next sibling.
158match last.nibble() {
159 -1 => self.move_to_next_sibling(true)?,
160_ => self.consume_node()?,
161 }
162 } else {
163// If we can skip the current node, move to the next sibling.
164self.move_to_next_sibling(false)?;
165 }
166167// Update the skip node flag based on the new position in the trie.
168self.update_skip_node();
169 }
170171Ok(())
172 }
173174/// Retrieves the current root node from the DB, seeking either the exact node or the next one.
175fn node(&mut self, exact: bool) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
176let key = self.key().expect("key must exist").clone();
177let entry = if exact { self.cursor.seek_exact(key)? } else { self.cursor.seek(key)? };
178179if let Some((_, node)) = &entry {
180assert!(!node.state_mask.is_empty());
181 }
182183Ok(entry)
184 }
185186/// Consumes the next node in the trie, updating the stack.
187fn consume_node(&mut self) -> Result<(), DatabaseError> {
188let Some((key, node)) = self.node(false)? else {
189// If no next node is found, clear the stack.
190self.stack.clear();
191return Ok(())
192 };
193194// Overwrite the root node's first nibble
195 // We need to sync the stack with the trie structure when consuming a new node. This is
196 // necessary for proper traversal and accurately representing the trie in the stack.
197if !key.is_empty() && !self.stack.is_empty() {
198self.stack[0].set_nibble(key[0] as i8);
199 }
200201// The current tree mask might have been set incorrectly.
202 // Sanity check that the newly retrieved trie node key is the child of the last item
203 // on the stack. If not, advance to the next sibling instead of adding the node to the
204 // stack.
205if let Some(subnode) = self.stack.last() {
206if !key.starts_with(subnode.full_key()) {
207#[cfg(feature = "metrics")]
208self.metrics.inc_out_of_order_subnode(1);
209self.move_to_next_sibling(false)?;
210return Ok(())
211 }
212 }
213214// Create a new CursorSubNode and push it to the stack.
215let subnode = CursorSubNode::new(key, Some(node));
216let nibble = subnode.nibble();
217self.stack.push(subnode);
218self.update_skip_node();
219220// Delete the current node if it's included in the prefix set or it doesn't contain the root
221 // hash.
222if !self.can_skip_current_node || nibble != -1 {
223if let Some((keys, key)) = self.removed_keys.as_mut().zip(self.cursor.current()?) {
224keys.insert(key);
225 }
226 }
227228Ok(())
229 }
230231/// Moves to the next sibling node in the trie, updating the stack.
232fn move_to_next_sibling(
233&mut self,
234 allow_root_to_child_nibble: bool,
235 ) -> Result<(), DatabaseError> {
236let Some(subnode) = self.stack.last_mut() else { return Ok(()) };
237238// Check if the walker needs to backtrack to the previous level in the trie during its
239 // traversal.
240if subnode.nibble() >= 0xf || (subnode.nibble() < 0 && !allow_root_to_child_nibble) {
241self.stack.pop();
242self.move_to_next_sibling(false)?;
243return Ok(())
244 }
245246subnode.inc_nibble();
247248if subnode.node.is_none() {
249return self.consume_node()
250 }
251252// Find the next sibling with state.
253loop {
254if subnode.state_flag() {
255return Ok(())
256 }
257if subnode.nibble() == 0xf {
258break
259}
260subnode.inc_nibble();
261 }
262263// Pop the current node and move to the next sibling.
264self.stack.pop();
265self.move_to_next_sibling(false)?;
266267Ok(())
268 }
269}