reth_trie/trie_cursor/
in_memory.rs

1use super::{TrieCursor, TrieCursorFactory, TrieStorageCursor};
2use crate::{forward_cursor::ForwardInMemoryCursor, updates::TrieUpdatesSorted};
3use alloy_primitives::B256;
4use reth_storage_errors::db::DatabaseError;
5use reth_trie_common::{BranchNodeCompact, Nibbles};
6
7/// The trie cursor factory for the trie updates.
8#[derive(Debug, Clone)]
9pub struct InMemoryTrieCursorFactory<CF, T> {
10    /// Underlying trie cursor factory.
11    cursor_factory: CF,
12    /// Reference to sorted trie updates.
13    trie_updates: T,
14}
15
16impl<CF, T> InMemoryTrieCursorFactory<CF, T> {
17    /// Create a new trie cursor factory.
18    pub const fn new(cursor_factory: CF, trie_updates: T) -> Self {
19        Self { cursor_factory, trie_updates }
20    }
21}
22
23impl<'overlay, CF, T> TrieCursorFactory for InMemoryTrieCursorFactory<CF, &'overlay T>
24where
25    CF: TrieCursorFactory + 'overlay,
26    T: AsRef<TrieUpdatesSorted>,
27{
28    type AccountTrieCursor<'cursor>
29        = InMemoryTrieCursor<'overlay, CF::AccountTrieCursor<'cursor>>
30    where
31        Self: 'cursor;
32
33    type StorageTrieCursor<'cursor>
34        = InMemoryTrieCursor<'overlay, CF::StorageTrieCursor<'cursor>>
35    where
36        Self: 'cursor;
37
38    fn account_trie_cursor(&self) -> Result<Self::AccountTrieCursor<'_>, DatabaseError> {
39        let cursor = self.cursor_factory.account_trie_cursor()?;
40        Ok(InMemoryTrieCursor::new_account(cursor, self.trie_updates.as_ref()))
41    }
42
43    fn storage_trie_cursor(
44        &self,
45        hashed_address: B256,
46    ) -> Result<Self::StorageTrieCursor<'_>, DatabaseError> {
47        let trie_updates = self.trie_updates.as_ref();
48        let cursor = self.cursor_factory.storage_trie_cursor(hashed_address)?;
49        Ok(InMemoryTrieCursor::new_storage(cursor, trie_updates, hashed_address))
50    }
51}
52
53/// A cursor to iterate over trie updates and corresponding database entries.
54/// It will always give precedence to the data from the trie updates.
55#[derive(Debug)]
56pub struct InMemoryTrieCursor<'a, C> {
57    /// The underlying cursor.
58    cursor: C,
59    /// Whether the underlying cursor should be ignored (when storage trie was wiped).
60    cursor_wiped: bool,
61    /// Entry that `cursor` is currently pointing to.
62    cursor_entry: Option<(Nibbles, BranchNodeCompact)>,
63    /// Forward-only in-memory cursor over storage trie nodes.
64    in_memory_cursor: ForwardInMemoryCursor<'a, Nibbles, Option<BranchNodeCompact>>,
65    /// The key most recently returned from the Cursor.
66    last_key: Option<Nibbles>,
67    /// Whether an initial seek was called.
68    seeked: bool,
69    /// Reference to the full trie updates.
70    trie_updates: &'a TrieUpdatesSorted,
71}
72
73impl<'a, C: TrieCursor> InMemoryTrieCursor<'a, C> {
74    /// Create new account trie cursor which combines a DB cursor and the trie updates.
75    pub fn new_account(cursor: C, trie_updates: &'a TrieUpdatesSorted) -> Self {
76        let in_memory_cursor = ForwardInMemoryCursor::new(trie_updates.account_nodes_ref());
77        Self {
78            cursor,
79            cursor_wiped: false,
80            cursor_entry: None,
81            in_memory_cursor,
82            last_key: None,
83            seeked: false,
84            trie_updates,
85        }
86    }
87
88    /// Create new storage trie cursor with full trie updates reference.
89    /// This allows the cursor to switch between storage tries when `set_hashed_address` is called.
90    pub fn new_storage(
91        cursor: C,
92        trie_updates: &'a TrieUpdatesSorted,
93        hashed_address: B256,
94    ) -> Self {
95        let (in_memory_cursor, cursor_wiped) =
96            Self::get_storage_overlay(trie_updates, hashed_address);
97        Self {
98            cursor,
99            cursor_wiped,
100            cursor_entry: None,
101            in_memory_cursor,
102            last_key: None,
103            seeked: false,
104            trie_updates,
105        }
106    }
107
108    /// Returns the storage overlay for `hashed_address` and whether it was deleted.
109    fn get_storage_overlay(
110        trie_updates: &'a TrieUpdatesSorted,
111        hashed_address: B256,
112    ) -> (ForwardInMemoryCursor<'a, Nibbles, Option<BranchNodeCompact>>, bool) {
113        let storage_trie_updates = trie_updates.storage_tries_ref().get(&hashed_address);
114        let cursor_wiped = storage_trie_updates.is_some_and(|u| u.is_deleted());
115        let storage_nodes = storage_trie_updates.map(|u| u.storage_nodes_ref()).unwrap_or(&[]);
116
117        (ForwardInMemoryCursor::new(storage_nodes), cursor_wiped)
118    }
119
120    /// Returns a mutable reference to the underlying cursor if it's not wiped, None otherwise.
121    fn get_cursor_mut(&mut self) -> Option<&mut C> {
122        (!self.cursor_wiped).then_some(&mut self.cursor)
123    }
124
125    /// Asserts that the next entry to be returned from the cursor is not previous to the last entry
126    /// returned.
127    fn set_last_key(&mut self, next_entry: &Option<(Nibbles, BranchNodeCompact)>) {
128        let next_key = next_entry.as_ref().map(|e| e.0);
129        debug_assert!(
130            self.last_key.is_none_or(|last| next_key.is_none_or(|next| next >= last)),
131            "Cannot return entry {:?} previous to the last returned entry at {:?}",
132            next_key,
133            self.last_key,
134        );
135        self.last_key = next_key;
136    }
137
138    /// Seeks the `cursor_entry` field of the struct using the cursor.
139    fn cursor_seek(&mut self, key: Nibbles) -> Result<(), DatabaseError> {
140        // Only seek if:
141        // 1. We have a cursor entry and need to seek forward (entry.0 < key), OR
142        // 2. We have no cursor entry and haven't seeked yet (!self.seeked)
143        let should_seek = match self.cursor_entry.as_ref() {
144            Some(entry) => entry.0 < key,
145            None => !self.seeked,
146        };
147
148        if should_seek {
149            self.cursor_entry = self.get_cursor_mut().map(|c| c.seek(key)).transpose()?.flatten();
150        }
151
152        Ok(())
153    }
154
155    /// Seeks the `cursor_entry` field of the struct to the subsequent entry using the cursor.
156    fn cursor_next(&mut self) -> Result<(), DatabaseError> {
157        debug_assert!(self.seeked);
158
159        // If the previous entry is `None`, and we've done a seek previously, then the cursor is
160        // exhausted and we shouldn't call `next` again.
161        if self.cursor_entry.is_some() {
162            self.cursor_entry = self.get_cursor_mut().map(|c| c.next()).transpose()?.flatten();
163        }
164
165        Ok(())
166    }
167
168    /// Compares the current in-memory entry with the current entry of the cursor, and applies the
169    /// in-memory entry to the cursor entry as an overlay.
170    //
171    /// This may consume and move forward the current entries when the overlay indicates a removed
172    /// node.
173    fn choose_next_entry(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
174        loop {
175            match (self.in_memory_cursor.current().cloned(), &self.cursor_entry) {
176                (Some((mem_key, None)), _)
177                    if self.cursor_entry.as_ref().is_none_or(|(db_key, _)| &mem_key < db_key) =>
178                {
179                    // If overlay has a removed node but DB cursor is exhausted or ahead of the
180                    // in-memory cursor then move ahead in-memory, as there might be further
181                    // non-removed overlay nodes.
182                    self.in_memory_cursor.first_after(&mem_key);
183                }
184                (Some((mem_key, None)), Some((db_key, _))) if &mem_key == db_key => {
185                    // If overlay has a removed node which is returned from DB then move both
186                    // cursors ahead to the next key.
187                    self.in_memory_cursor.first_after(&mem_key);
188                    self.cursor_next()?;
189                }
190                (Some((mem_key, Some(node))), _)
191                    if self.cursor_entry.as_ref().is_none_or(|(db_key, _)| &mem_key <= db_key) =>
192                {
193                    // If overlay returns a node prior to the DB's node, or the DB is exhausted,
194                    // then we return the overlay's node.
195                    return Ok(Some((mem_key, node)))
196                }
197                // All other cases:
198                // - mem_key > db_key
199                // - overlay is exhausted
200                // Return the db_entry. If DB is also exhausted then this returns None.
201                _ => return Ok(self.cursor_entry.clone()),
202            }
203        }
204    }
205}
206
207impl<C: TrieCursor> TrieCursor for InMemoryTrieCursor<'_, C> {
208    fn seek_exact(
209        &mut self,
210        key: Nibbles,
211    ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
212        self.cursor_seek(key)?;
213        let mem_entry = self.in_memory_cursor.seek(&key);
214
215        self.seeked = true;
216
217        let entry = match (mem_entry, &self.cursor_entry) {
218            (Some((mem_key, entry_inner)), _) if mem_key == key => {
219                entry_inner.map(|node| (key, node))
220            }
221            (_, Some((db_key, node))) if db_key == &key => Some((key, node.clone())),
222            _ => None,
223        };
224
225        self.set_last_key(&entry);
226        Ok(entry)
227    }
228
229    fn seek(
230        &mut self,
231        key: Nibbles,
232    ) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
233        self.cursor_seek(key)?;
234        self.in_memory_cursor.seek(&key);
235
236        self.seeked = true;
237
238        let entry = self.choose_next_entry()?;
239        self.set_last_key(&entry);
240        Ok(entry)
241    }
242
243    fn next(&mut self) -> Result<Option<(Nibbles, BranchNodeCompact)>, DatabaseError> {
244        debug_assert!(self.seeked, "Cursor must be seek'd before next is called");
245
246        // A `last_key` of `None` indicates that the cursor is exhausted.
247        let Some(last_key) = self.last_key else {
248            return Ok(None);
249        };
250
251        // If either cursor is currently pointing to the last entry which was returned then consume
252        // that entry so that `choose_next_entry` is looking at the subsequent one.
253        if let Some((key, _)) = self.in_memory_cursor.current() &&
254            key == &last_key
255        {
256            self.in_memory_cursor.first_after(&last_key);
257        }
258
259        if let Some((key, _)) = &self.cursor_entry &&
260            key == &last_key
261        {
262            self.cursor_next()?;
263        }
264
265        let entry = self.choose_next_entry()?;
266        self.set_last_key(&entry);
267        Ok(entry)
268    }
269
270    fn current(&mut self) -> Result<Option<Nibbles>, DatabaseError> {
271        match &self.last_key {
272            Some(key) => Ok(Some(*key)),
273            None => Ok(self.get_cursor_mut().map(|c| c.current()).transpose()?.flatten()),
274        }
275    }
276
277    fn reset(&mut self) {
278        let Self {
279            cursor,
280            cursor_wiped,
281            cursor_entry,
282            in_memory_cursor,
283            last_key,
284            seeked,
285            trie_updates: _,
286        } = self;
287
288        cursor.reset();
289        in_memory_cursor.reset();
290
291        *cursor_wiped = false;
292        *cursor_entry = None;
293        *last_key = None;
294        *seeked = false;
295    }
296}
297
298impl<C: TrieStorageCursor> TrieStorageCursor for InMemoryTrieCursor<'_, C> {
299    fn set_hashed_address(&mut self, hashed_address: B256) {
300        self.reset();
301        self.cursor.set_hashed_address(hashed_address);
302        (self.in_memory_cursor, self.cursor_wiped) =
303            Self::get_storage_overlay(self.trie_updates, hashed_address);
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310    use crate::trie_cursor::mock::MockTrieCursor;
311    use parking_lot::Mutex;
312    use std::{collections::BTreeMap, sync::Arc};
313
314    #[derive(Debug)]
315    struct InMemoryTrieCursorTestCase {
316        db_nodes: Vec<(Nibbles, BranchNodeCompact)>,
317        in_memory_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
318        expected_results: Vec<(Nibbles, BranchNodeCompact)>,
319    }
320
321    fn execute_test(test_case: InMemoryTrieCursorTestCase) {
322        let db_nodes_map: BTreeMap<Nibbles, BranchNodeCompact> =
323            test_case.db_nodes.into_iter().collect();
324        let db_nodes_arc = Arc::new(db_nodes_map);
325        let visited_keys = Arc::new(Mutex::new(Vec::new()));
326        let mock_cursor = MockTrieCursor::new(db_nodes_arc, visited_keys);
327
328        let trie_updates = TrieUpdatesSorted::new(test_case.in_memory_nodes, Default::default());
329        let mut cursor = InMemoryTrieCursor::new_account(mock_cursor, &trie_updates);
330
331        let mut results = Vec::new();
332
333        if let Some(first_expected) = test_case.expected_results.first() &&
334            let Ok(Some(entry)) = cursor.seek(first_expected.0)
335        {
336            results.push(entry);
337        }
338
339        if !test_case.expected_results.is_empty() {
340            while let Ok(Some(entry)) = cursor.next() {
341                results.push(entry);
342            }
343        }
344
345        assert_eq!(
346            results, test_case.expected_results,
347            "Results mismatch.\nGot: {:?}\nExpected: {:?}",
348            results, test_case.expected_results
349        );
350    }
351
352    #[test]
353    fn test_empty_db_and_memory() {
354        let test_case = InMemoryTrieCursorTestCase {
355            db_nodes: vec![],
356            in_memory_nodes: vec![],
357            expected_results: vec![],
358        };
359        execute_test(test_case);
360    }
361
362    #[test]
363    fn test_only_db_nodes() {
364        let db_nodes = vec![
365            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(0b0011, 0b0001, 0, vec![], None)),
366            (Nibbles::from_nibbles([0x2]), BranchNodeCompact::new(0b0011, 0b0010, 0, vec![], None)),
367            (Nibbles::from_nibbles([0x3]), BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
368        ];
369
370        let test_case = InMemoryTrieCursorTestCase {
371            db_nodes: db_nodes.clone(),
372            in_memory_nodes: vec![],
373            expected_results: db_nodes,
374        };
375        execute_test(test_case);
376    }
377
378    #[test]
379    fn test_only_in_memory_nodes() {
380        let in_memory_nodes = vec![
381            (
382                Nibbles::from_nibbles([0x1]),
383                Some(BranchNodeCompact::new(0b0011, 0b0001, 0, vec![], None)),
384            ),
385            (
386                Nibbles::from_nibbles([0x2]),
387                Some(BranchNodeCompact::new(0b0011, 0b0010, 0, vec![], None)),
388            ),
389            (
390                Nibbles::from_nibbles([0x3]),
391                Some(BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
392            ),
393        ];
394
395        let expected_results: Vec<(Nibbles, BranchNodeCompact)> = in_memory_nodes
396            .iter()
397            .filter_map(|(k, v)| v.as_ref().map(|node| (*k, node.clone())))
398            .collect();
399
400        let test_case =
401            InMemoryTrieCursorTestCase { db_nodes: vec![], in_memory_nodes, expected_results };
402        execute_test(test_case);
403    }
404
405    #[test]
406    fn test_in_memory_overwrites_db() {
407        let db_nodes = vec![
408            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(0b0011, 0b0001, 0, vec![], None)),
409            (Nibbles::from_nibbles([0x2]), BranchNodeCompact::new(0b0011, 0b0010, 0, vec![], None)),
410        ];
411
412        let in_memory_nodes = vec![
413            (
414                Nibbles::from_nibbles([0x1]),
415                Some(BranchNodeCompact::new(0b1111, 0b1111, 0, vec![], None)),
416            ),
417            (
418                Nibbles::from_nibbles([0x3]),
419                Some(BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
420            ),
421        ];
422
423        let expected_results = vec![
424            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(0b1111, 0b1111, 0, vec![], None)),
425            (Nibbles::from_nibbles([0x2]), BranchNodeCompact::new(0b0011, 0b0010, 0, vec![], None)),
426            (Nibbles::from_nibbles([0x3]), BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
427        ];
428
429        let test_case = InMemoryTrieCursorTestCase { db_nodes, in_memory_nodes, expected_results };
430        execute_test(test_case);
431    }
432
433    #[test]
434    fn test_in_memory_deletes_db_nodes() {
435        let db_nodes = vec![
436            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(0b0011, 0b0001, 0, vec![], None)),
437            (Nibbles::from_nibbles([0x2]), BranchNodeCompact::new(0b0011, 0b0010, 0, vec![], None)),
438            (Nibbles::from_nibbles([0x3]), BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
439        ];
440
441        let in_memory_nodes = vec![(Nibbles::from_nibbles([0x2]), None)];
442
443        let expected_results = vec![
444            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(0b0011, 0b0001, 0, vec![], None)),
445            (Nibbles::from_nibbles([0x3]), BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
446        ];
447
448        let test_case = InMemoryTrieCursorTestCase { db_nodes, in_memory_nodes, expected_results };
449        execute_test(test_case);
450    }
451
452    #[test]
453    fn test_complex_interleaving() {
454        let db_nodes = vec![
455            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(0b0001, 0b0001, 0, vec![], None)),
456            (Nibbles::from_nibbles([0x3]), BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
457            (Nibbles::from_nibbles([0x5]), BranchNodeCompact::new(0b0101, 0b0101, 0, vec![], None)),
458            (Nibbles::from_nibbles([0x7]), BranchNodeCompact::new(0b0111, 0b0111, 0, vec![], None)),
459        ];
460
461        let in_memory_nodes = vec![
462            (
463                Nibbles::from_nibbles([0x2]),
464                Some(BranchNodeCompact::new(0b0010, 0b0010, 0, vec![], None)),
465            ),
466            (Nibbles::from_nibbles([0x3]), None),
467            (
468                Nibbles::from_nibbles([0x4]),
469                Some(BranchNodeCompact::new(0b0100, 0b0100, 0, vec![], None)),
470            ),
471            (
472                Nibbles::from_nibbles([0x6]),
473                Some(BranchNodeCompact::new(0b0110, 0b0110, 0, vec![], None)),
474            ),
475            (Nibbles::from_nibbles([0x7]), None),
476            (
477                Nibbles::from_nibbles([0x8]),
478                Some(BranchNodeCompact::new(0b1000, 0b1000, 0, vec![], None)),
479            ),
480        ];
481
482        let expected_results = vec![
483            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(0b0001, 0b0001, 0, vec![], None)),
484            (Nibbles::from_nibbles([0x2]), BranchNodeCompact::new(0b0010, 0b0010, 0, vec![], None)),
485            (Nibbles::from_nibbles([0x4]), BranchNodeCompact::new(0b0100, 0b0100, 0, vec![], None)),
486            (Nibbles::from_nibbles([0x5]), BranchNodeCompact::new(0b0101, 0b0101, 0, vec![], None)),
487            (Nibbles::from_nibbles([0x6]), BranchNodeCompact::new(0b0110, 0b0110, 0, vec![], None)),
488            (Nibbles::from_nibbles([0x8]), BranchNodeCompact::new(0b1000, 0b1000, 0, vec![], None)),
489        ];
490
491        let test_case = InMemoryTrieCursorTestCase { db_nodes, in_memory_nodes, expected_results };
492        execute_test(test_case);
493    }
494
495    #[test]
496    fn test_seek_exact() {
497        let db_nodes = vec![
498            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(0b0001, 0b0001, 0, vec![], None)),
499            (Nibbles::from_nibbles([0x3]), BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
500        ];
501
502        let in_memory_nodes = vec![(
503            Nibbles::from_nibbles([0x2]),
504            Some(BranchNodeCompact::new(0b0010, 0b0010, 0, vec![], None)),
505        )];
506
507        let db_nodes_map: BTreeMap<Nibbles, BranchNodeCompact> = db_nodes.into_iter().collect();
508        let db_nodes_arc = Arc::new(db_nodes_map);
509        let visited_keys = Arc::new(Mutex::new(Vec::new()));
510        let mock_cursor = MockTrieCursor::new(db_nodes_arc, visited_keys);
511
512        let trie_updates = TrieUpdatesSorted::new(in_memory_nodes, Default::default());
513        let mut cursor = InMemoryTrieCursor::new_account(mock_cursor, &trie_updates);
514
515        let result = cursor.seek_exact(Nibbles::from_nibbles([0x2])).unwrap();
516        assert_eq!(
517            result,
518            Some((
519                Nibbles::from_nibbles([0x2]),
520                BranchNodeCompact::new(0b0010, 0b0010, 0, vec![], None)
521            ))
522        );
523
524        let result = cursor.seek_exact(Nibbles::from_nibbles([0x3])).unwrap();
525        assert_eq!(
526            result,
527            Some((
528                Nibbles::from_nibbles([0x3]),
529                BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)
530            ))
531        );
532
533        let result = cursor.seek_exact(Nibbles::from_nibbles([0x4])).unwrap();
534        assert_eq!(result, None);
535    }
536
537    #[test]
538    fn test_multiple_consecutive_deletes() {
539        let db_nodes: Vec<(Nibbles, BranchNodeCompact)> = (1..=10)
540            .map(|i| {
541                (
542                    Nibbles::from_nibbles([i]),
543                    BranchNodeCompact::new(i as u16, i as u16, 0, vec![], None),
544                )
545            })
546            .collect();
547
548        let in_memory_nodes = vec![
549            (Nibbles::from_nibbles([0x3]), None),
550            (Nibbles::from_nibbles([0x4]), None),
551            (Nibbles::from_nibbles([0x5]), None),
552            (Nibbles::from_nibbles([0x6]), None),
553        ];
554
555        let expected_results = vec![
556            (Nibbles::from_nibbles([0x1]), BranchNodeCompact::new(1, 1, 0, vec![], None)),
557            (Nibbles::from_nibbles([0x2]), BranchNodeCompact::new(2, 2, 0, vec![], None)),
558            (Nibbles::from_nibbles([0x7]), BranchNodeCompact::new(7, 7, 0, vec![], None)),
559            (Nibbles::from_nibbles([0x8]), BranchNodeCompact::new(8, 8, 0, vec![], None)),
560            (Nibbles::from_nibbles([0x9]), BranchNodeCompact::new(9, 9, 0, vec![], None)),
561            (Nibbles::from_nibbles([0xa]), BranchNodeCompact::new(10, 10, 0, vec![], None)),
562        ];
563
564        let test_case = InMemoryTrieCursorTestCase { db_nodes, in_memory_nodes, expected_results };
565        execute_test(test_case);
566    }
567
568    #[test]
569    fn test_empty_db_with_in_memory_deletes() {
570        let in_memory_nodes = vec![
571            (Nibbles::from_nibbles([0x1]), None),
572            (
573                Nibbles::from_nibbles([0x2]),
574                Some(BranchNodeCompact::new(0b0010, 0b0010, 0, vec![], None)),
575            ),
576            (Nibbles::from_nibbles([0x3]), None),
577        ];
578
579        let expected_results = vec![(
580            Nibbles::from_nibbles([0x2]),
581            BranchNodeCompact::new(0b0010, 0b0010, 0, vec![], None),
582        )];
583
584        let test_case =
585            InMemoryTrieCursorTestCase { db_nodes: vec![], in_memory_nodes, expected_results };
586        execute_test(test_case);
587    }
588
589    #[test]
590    fn test_current_key_tracking() {
591        let db_nodes = vec![(
592            Nibbles::from_nibbles([0x2]),
593            BranchNodeCompact::new(0b0010, 0b0010, 0, vec![], None),
594        )];
595
596        let in_memory_nodes = vec![
597            (
598                Nibbles::from_nibbles([0x1]),
599                Some(BranchNodeCompact::new(0b0001, 0b0001, 0, vec![], None)),
600            ),
601            (
602                Nibbles::from_nibbles([0x3]),
603                Some(BranchNodeCompact::new(0b0011, 0b0011, 0, vec![], None)),
604            ),
605        ];
606
607        let db_nodes_map: BTreeMap<Nibbles, BranchNodeCompact> = db_nodes.into_iter().collect();
608        let db_nodes_arc = Arc::new(db_nodes_map);
609        let visited_keys = Arc::new(Mutex::new(Vec::new()));
610        let mock_cursor = MockTrieCursor::new(db_nodes_arc, visited_keys);
611
612        let trie_updates = TrieUpdatesSorted::new(in_memory_nodes, Default::default());
613        let mut cursor = InMemoryTrieCursor::new_account(mock_cursor, &trie_updates);
614
615        assert_eq!(cursor.current().unwrap(), None);
616
617        cursor.seek(Nibbles::from_nibbles([0x1])).unwrap();
618        assert_eq!(cursor.current().unwrap(), Some(Nibbles::from_nibbles([0x1])));
619
620        cursor.next().unwrap();
621        assert_eq!(cursor.current().unwrap(), Some(Nibbles::from_nibbles([0x2])));
622
623        cursor.next().unwrap();
624        assert_eq!(cursor.current().unwrap(), Some(Nibbles::from_nibbles([0x3])));
625    }
626
627    #[test]
628    fn test_all_storage_slots_deleted_not_wiped_exact_keys() {
629        use tracing::debug;
630        reth_tracing::init_test_tracing();
631
632        // This test reproduces an edge case where:
633        // - cursor is not None (not wiped)
634        // - All in-memory entries are deletions (None values)
635        // - Database has corresponding entries
636        // - Expected: NO leaves should be returned (all deleted)
637
638        // Generate 42 trie node entries with keys distributed across the keyspace
639        let mut db_nodes: Vec<(Nibbles, BranchNodeCompact)> = (0..10)
640            .map(|i| {
641                let key_bytes = vec![(i * 6) as u8, i as u8]; // Spread keys across keyspace
642                let nibbles = Nibbles::unpack(key_bytes);
643                (nibbles, BranchNodeCompact::new(i as u16, i as u16, 0, vec![], None))
644            })
645            .collect();
646
647        db_nodes.sort_by_key(|(key, _)| *key);
648        db_nodes.dedup_by_key(|(key, _)| *key);
649
650        for (key, _) in &db_nodes {
651            debug!("node at {key:?}");
652        }
653
654        // Create in-memory entries with same keys but all None values (deletions)
655        let in_memory_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)> =
656            db_nodes.iter().map(|(key, _)| (*key, None)).collect();
657
658        let db_nodes_map: BTreeMap<Nibbles, BranchNodeCompact> = db_nodes.into_iter().collect();
659        let db_nodes_arc = Arc::new(db_nodes_map);
660        let visited_keys = Arc::new(Mutex::new(Vec::new()));
661        let mock_cursor = MockTrieCursor::new(db_nodes_arc, visited_keys);
662
663        let trie_updates = TrieUpdatesSorted::new(in_memory_nodes, Default::default());
664        let mut cursor = InMemoryTrieCursor::new_account(mock_cursor, &trie_updates);
665
666        // Seek to beginning should return None (all nodes are deleted)
667        tracing::debug!("seeking to 0x");
668        let result = cursor.seek(Nibbles::default()).unwrap();
669        assert_eq!(
670            result, None,
671            "Expected no entries when all nodes are deleted, but got {:?}",
672            result
673        );
674
675        // Test seek operations at various positions - all should return None
676        let seek_keys = vec![
677            Nibbles::unpack([0x00]),
678            Nibbles::unpack([0x5d]),
679            Nibbles::unpack([0x5e]),
680            Nibbles::unpack([0x5f]),
681            Nibbles::unpack([0xc2]),
682            Nibbles::unpack([0xc5]),
683            Nibbles::unpack([0xc9]),
684            Nibbles::unpack([0xf0]),
685        ];
686
687        for seek_key in seek_keys {
688            tracing::debug!("seeking to {seek_key:?}");
689            let result = cursor.seek(seek_key).unwrap();
690            assert_eq!(
691                result, None,
692                "Expected None when seeking to {:?} but got {:?}",
693                seek_key, result
694            );
695        }
696
697        // next() should also always return None
698        let result = cursor.next().unwrap();
699        assert_eq!(result, None, "Expected None from next() but got {:?}", result);
700    }
701
702    mod proptest_tests {
703        use super::*;
704        use itertools::Itertools;
705        use proptest::prelude::*;
706
707        /// Merge `db_nodes` with `in_memory_nodes`, applying the in-memory overlay.
708        /// This properly handles deletions (None values in `in_memory_nodes`).
709        fn merge_with_overlay(
710            db_nodes: Vec<(Nibbles, BranchNodeCompact)>,
711            in_memory_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
712        ) -> Vec<(Nibbles, BranchNodeCompact)> {
713            db_nodes
714                .into_iter()
715                .merge_join_by(in_memory_nodes, |db_entry, mem_entry| db_entry.0.cmp(&mem_entry.0))
716                .filter_map(|entry| match entry {
717                    // Only in db: keep it
718                    itertools::EitherOrBoth::Left((key, node)) => Some((key, node)),
719                    // Only in memory: keep if not a deletion
720                    itertools::EitherOrBoth::Right((key, node_opt)) => {
721                        node_opt.map(|node| (key, node))
722                    }
723                    // In both: memory takes precedence (keep if not a deletion)
724                    itertools::EitherOrBoth::Both(_, (key, node_opt)) => {
725                        node_opt.map(|node| (key, node))
726                    }
727                })
728                .collect()
729        }
730
731        /// Generate a strategy for a `BranchNodeCompact` with simplified parameters.
732        /// The constraints are:
733        /// - `tree_mask` must be a subset of `state_mask`
734        /// - `hash_mask` must be a subset of `state_mask`
735        /// - `hash_mask.count_ones()` must equal `hashes.len()`
736        ///
737        /// To keep it simple, we use an empty hashes vec and `hash_mask` of 0.
738        fn branch_node_strategy() -> impl Strategy<Value = BranchNodeCompact> {
739            any::<u16>()
740                .prop_flat_map(|state_mask| {
741                    let tree_mask_strategy = any::<u16>().prop_map(move |tree| tree & state_mask);
742                    (Just(state_mask), tree_mask_strategy)
743                })
744                .prop_map(|(state_mask, tree_mask)| {
745                    BranchNodeCompact::new(state_mask, tree_mask, 0, vec![], None)
746                })
747        }
748
749        /// Generate a sorted vector of (Nibbles, `BranchNodeCompact`) entries
750        fn sorted_db_nodes_strategy() -> impl Strategy<Value = Vec<(Nibbles, BranchNodeCompact)>> {
751            prop::collection::vec(
752                (prop::collection::vec(any::<u8>(), 0..2), branch_node_strategy()),
753                0..20,
754            )
755            .prop_map(|entries| {
756                // Convert Vec<u8> to Nibbles and sort
757                let mut result: Vec<(Nibbles, BranchNodeCompact)> = entries
758                    .into_iter()
759                    .map(|(bytes, node)| (Nibbles::from_nibbles_unchecked(bytes), node))
760                    .collect();
761                result.sort_by(|a, b| a.0.cmp(&b.0));
762                result.dedup_by(|a, b| a.0 == b.0);
763                result
764            })
765        }
766
767        /// Generate a sorted vector of (Nibbles, Option<BranchNodeCompact>) entries
768        fn sorted_in_memory_nodes_strategy(
769        ) -> impl Strategy<Value = Vec<(Nibbles, Option<BranchNodeCompact>)>> {
770            prop::collection::vec(
771                (
772                    prop::collection::vec(any::<u8>(), 0..2),
773                    prop::option::of(branch_node_strategy()),
774                ),
775                0..20,
776            )
777            .prop_map(|entries| {
778                // Convert Vec<u8> to Nibbles and sort
779                let mut result: Vec<(Nibbles, Option<BranchNodeCompact>)> = entries
780                    .into_iter()
781                    .map(|(bytes, node)| (Nibbles::from_nibbles_unchecked(bytes), node))
782                    .collect();
783                result.sort_by(|a, b| a.0.cmp(&b.0));
784                result.dedup_by(|a, b| a.0 == b.0);
785                result
786            })
787        }
788
789        proptest! {
790            #![proptest_config(ProptestConfig::with_cases(10000))]
791
792            #[test]
793            fn proptest_in_memory_trie_cursor(
794                db_nodes in sorted_db_nodes_strategy(),
795                in_memory_nodes in sorted_in_memory_nodes_strategy(),
796                op_choices in prop::collection::vec(any::<u8>(), 10..500),
797            ) {
798                reth_tracing::init_test_tracing();
799                use tracing::debug;
800
801                debug!(
802                    db_paths=?db_nodes.iter().map(|(k, _)| k).collect::<Vec<_>>(),
803                    in_mem_nodes=?in_memory_nodes.iter().map(|(k, v)| (k, v.is_some())).collect::<Vec<_>>(),
804                    num_op_choices=?op_choices.len(),
805                    "Starting proptest!",
806                );
807
808                // Create the expected results by merging the two sorted vectors,
809                // properly handling deletions (None values in in_memory_nodes)
810                let expected_combined = merge_with_overlay(db_nodes.clone(), in_memory_nodes.clone());
811
812                // Collect all keys for operation generation
813                let all_keys: Vec<Nibbles> = expected_combined.iter().map(|(k, _)| *k).collect();
814
815                // Create a control cursor using the combined result with a mock cursor
816                let control_db_map: BTreeMap<Nibbles, BranchNodeCompact> =
817                    expected_combined.into_iter().collect();
818                let control_db_arc = Arc::new(control_db_map);
819                let control_visited_keys = Arc::new(Mutex::new(Vec::new()));
820                let mut control_cursor = MockTrieCursor::new(control_db_arc, control_visited_keys);
821
822                // Create the InMemoryTrieCursor being tested
823                let db_nodes_map: BTreeMap<Nibbles, BranchNodeCompact> =
824                    db_nodes.into_iter().collect();
825                let db_nodes_arc = Arc::new(db_nodes_map);
826                let visited_keys = Arc::new(Mutex::new(Vec::new()));
827                let mock_cursor = MockTrieCursor::new(db_nodes_arc, visited_keys);
828                let trie_updates = TrieUpdatesSorted::new(in_memory_nodes, Default::default());
829                let mut test_cursor = InMemoryTrieCursor::new_account(mock_cursor, &trie_updates);
830
831                // Test: seek to the beginning first
832                let control_first = control_cursor.seek(Nibbles::default()).unwrap();
833                let test_first = test_cursor.seek(Nibbles::default()).unwrap();
834                debug!(
835                    control=?control_first.as_ref().map(|(k, _)| k),
836                    test=?test_first.as_ref().map(|(k, _)| k),
837                    "Initial seek returned",
838                );
839                assert_eq!(control_first, test_first, "Initial seek mismatch");
840
841                // If both cursors returned None, nothing to test
842                if control_first.is_none() && test_first.is_none() {
843                    return Ok(());
844                }
845
846                // Track the last key returned from the cursor
847                let mut last_returned_key = control_first.as_ref().map(|(k, _)| *k);
848
849                // Execute a sequence of random operations
850                for choice in op_choices {
851                    let op_type = choice % 3;
852
853                    match op_type {
854                        0 => {
855                            // Next operation
856                            let control_result = control_cursor.next().unwrap();
857                            let test_result = test_cursor.next().unwrap();
858                            debug!(
859                                control=?control_result.as_ref().map(|(k, _)| k),
860                                test=?test_result.as_ref().map(|(k, _)| k),
861                                "Next returned",
862                            );
863                            assert_eq!(control_result, test_result, "Next operation mismatch");
864
865                            last_returned_key = control_result.as_ref().map(|(k, _)| *k);
866
867                            // Stop if both cursors are exhausted
868                            if control_result.is_none() && test_result.is_none() {
869                                break;
870                            }
871                        }
872                        1 => {
873                            // Seek operation - choose a key >= last_returned_key
874                            if all_keys.is_empty() {
875                                continue;
876                            }
877
878                            let valid_keys: Vec<_> = all_keys
879                                .iter()
880                                .filter(|k| last_returned_key.is_none_or(|last| **k >= last))
881                                .collect();
882
883                            if valid_keys.is_empty() {
884                                continue;
885                            }
886
887                            let key = *valid_keys[choice as usize % valid_keys.len()];
888
889                            let control_result = control_cursor.seek(key).unwrap();
890                            let test_result = test_cursor.seek(key).unwrap();
891                            debug!(
892                                control=?control_result.as_ref().map(|(k, _)| k),
893                                test=?test_result.as_ref().map(|(k, _)| k),
894                                ?key,
895                                "Seek returned",
896                            );
897                            assert_eq!(control_result, test_result, "Seek operation mismatch for key {:?}", key);
898
899                            last_returned_key = control_result.as_ref().map(|(k, _)| *k);
900
901                            // Stop if both cursors are exhausted
902                            if control_result.is_none() && test_result.is_none() {
903                                break;
904                            }
905                        }
906                        _ => {
907                            // SeekExact operation - choose a key >= last_returned_key
908                            if all_keys.is_empty() {
909                                continue;
910                            }
911
912                            let valid_keys: Vec<_> = all_keys
913                                .iter()
914                                .filter(|k| last_returned_key.is_none_or(|last| **k >= last))
915                                .collect();
916
917                            if valid_keys.is_empty() {
918                                continue;
919                            }
920
921                            let key = *valid_keys[choice as usize  % valid_keys.len()];
922
923                            let control_result = control_cursor.seek_exact(key).unwrap();
924                            let test_result = test_cursor.seek_exact(key).unwrap();
925                            debug!(
926                                control=?control_result.as_ref().map(|(k, _)| k),
927                                test=?test_result.as_ref().map(|(k, _)| k),
928                                ?key,
929                                "SeekExact returned",
930                            );
931                            assert_eq!(control_result, test_result, "SeekExact operation mismatch for key {:?}", key);
932
933                            // seek_exact updates the last_key internally but only if it found something
934                            last_returned_key = control_result.as_ref().map(|(k, _)| *k);
935                        }
936                    }
937                }
938            }
939        }
940    }
941}