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#[derive(Debug, Clone)]
9pub struct InMemoryTrieCursorFactory<CF, T> {
10 cursor_factory: CF,
12 trie_updates: T,
14}
15
16impl<CF, T> InMemoryTrieCursorFactory<CF, T> {
17 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#[derive(Debug)]
56pub struct InMemoryTrieCursor<'a, C> {
57 cursor: C,
59 db_cursor_state: DbCursorState,
61 in_memory_cursor: ForwardInMemoryCursor<'a, Nibbles, Option<BranchNodeCompact>>,
63 last_key: Option<Nibbles>,
65 #[cfg(debug_assertions)]
66 seeked: bool,
68 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 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 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 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 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 fn cursor_seek(&mut self, key: Nibbles) -> Result<(), DatabaseError> {
159 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 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 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 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 self.in_memory_cursor.first_after(&mem_key);
212 }
213 (Some((mem_key, None)), Some((db_key, _))) if &mem_key == db_key => {
214 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 return Ok(Some((mem_key, node)))
225 }
226 _ => 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 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 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 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 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 let Some(last_key) = self.last_key else {
334 return Ok(None);
335 };
336
337 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 let mut db_nodes: Vec<(Nibbles, BranchNodeCompact)> = (0..10)
815 .map(|i| {
816 let key_bytes = vec![(i * 6) as u8, i as u8]; 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 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 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 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 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 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 itertools::EitherOrBoth::Left((key, node)) => Some((key, node)),
894 itertools::EitherOrBoth::Right((key, node_opt)) => {
896 node_opt.map(|node| (key, node))
897 }
898 itertools::EitherOrBoth::Both(_, (key, node_opt)) => {
900 node_opt.map(|node| (key, node))
901 }
902 })
903 .collect()
904 }
905
906 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 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 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 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 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 let expected_combined = merge_with_overlay(db_nodes.clone(), in_memory_nodes.clone());
986
987 let all_keys: Vec<Nibbles> = expected_combined.iter().map(|(k, _)| *k).collect();
989
990 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 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 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 control_first.is_none() && test_first.is_none() {
1018 return Ok(());
1019 }
1020
1021 let mut last_returned_key = control_first.as_ref().map(|(k, _)| *k);
1023
1024 for choice in op_choices {
1026 let op_type = choice % 3;
1027
1028 match op_type {
1029 0 => {
1030 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 if control_result.is_none() && test_result.is_none() {
1044 break;
1045 }
1046 }
1047 1 => {
1048 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 if control_result.is_none() && test_result.is_none() {
1078 break;
1079 }
1080 }
1081 _ => {
1082 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 last_returned_key = control_result.as_ref().map(|(k, _)| *k);
1110 }
1111 }
1112 }
1113 }
1114 }
1115 }
1116}