Skip to main content

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