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#[derive(Debug)]
17pub struct TrieWalker<C> {
18 pub cursor: C,
20 pub stack: Vec<CursorSubNode>,
22 pub can_skip_current_node: bool,
26 pub changes: PrefixSet,
28 removed_keys: Option<HashSet<Nibbles>>,
30 #[cfg(feature = "metrics")]
31 metrics: WalkerMetrics,
33}
34
35impl<C> TrieWalker<C> {
36 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 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 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 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 pub fn split(mut self) -> (Vec<CursorSubNode>, HashSet<Nibbles>) {
92 let keys = self.take_removed_keys();
93 (self.stack, keys)
94 }
95
96 pub fn take_removed_keys(&mut self) -> HashSet<Nibbles> {
98 self.removed_keys.take().unwrap_or_default()
99 }
100
101 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 pub fn removed_keys_len(&self) -> usize {
112 self.removed_keys.as_ref().map_or(0, |u| u.len())
113 }
114
115 pub fn key(&self) -> Option<&Nibbles> {
117 self.stack.last().map(|n| n.full_key())
118 }
119
120 pub fn hash(&self) -> Option<B256> {
122 self.stack.last().and_then(|n| n.hash())
123 }
124
125 pub fn maybe_hash(&self) -> Option<B256> {
130 self.stack.last().and_then(|n| n.maybe_hash())
131 }
132
133 pub fn children_are_in_trie(&self) -> bool {
135 self.stack.last().is_some_and(|n| n.tree_flag())
136 }
137
138 #[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 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 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 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 fn new(
192 cursor: C,
193 changes: PrefixSet,
194 #[cfg(feature = "metrics")] trie_type: crate::TrieType,
195 ) -> Self {
196 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 if let Some((key, value)) = this.node(true).unwrap() {
209 this.stack[0] = CursorSubNode::new(key, Some(value));
210 }
211
212 this.update_skip_node();
214 this
215 }
216
217 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 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 self.move_to_next_sibling(false)?;
241 }
242
243 self.update_skip_node();
245 }
246
247 Ok(())
248 }
249
250 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 #[instrument(level = "trace", skip(self), ret)]
266 fn consume_node(&mut self) -> Result<(), DatabaseError> {
267 let Some((key, node)) = self.node(false)? else {
268 self.stack.clear();
270 return Ok(())
271 };
272
273 if !key.is_empty() && !self.stack.is_empty() {
277 self.stack[0].set_nibble(key[0]);
278 }
279
280 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 let subnode = CursorSubNode::new(key, Some(node));
295 let position = subnode.position();
296 self.stack.push(subnode);
297 self.update_skip_node();
298
299 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 #[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 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 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 self.stack.pop();
350 self.move_to_next_sibling(false)?;
351
352 Ok(())
353 }
354}