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 cursor_wiped: bool,
61 cursor_entry: Option<(Nibbles, BranchNodeCompact)>,
63 in_memory_cursor: ForwardInMemoryCursor<'a, Nibbles, Option<BranchNodeCompact>>,
65 last_key: Option<Nibbles>,
67 seeked: bool,
69 trie_updates: &'a TrieUpdatesSorted,
71}
72
73impl<'a, C: TrieCursor> InMemoryTrieCursor<'a, C> {
74 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 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 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 fn get_cursor_mut(&mut self) -> Option<&mut C> {
122 (!self.cursor_wiped).then_some(&mut self.cursor)
123 }
124
125 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 fn cursor_seek(&mut self, key: Nibbles) -> Result<(), DatabaseError> {
140 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 fn cursor_next(&mut self) -> Result<(), DatabaseError> {
157 debug_assert!(self.seeked);
158
159 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 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 self.in_memory_cursor.first_after(&mem_key);
183 }
184 (Some((mem_key, None)), Some((db_key, _))) if &mem_key == db_key => {
185 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 return Ok(Some((mem_key, node)))
196 }
197 _ => 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 let Some(last_key) = self.last_key else {
248 return Ok(None);
249 };
250
251 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 let mut db_nodes: Vec<(Nibbles, BranchNodeCompact)> = (0..10)
640 .map(|i| {
641 let key_bytes = vec![(i * 6) as u8, i as u8]; 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 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 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 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 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 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 itertools::EitherOrBoth::Left((key, node)) => Some((key, node)),
719 itertools::EitherOrBoth::Right((key, node_opt)) => {
721 node_opt.map(|node| (key, node))
722 }
723 itertools::EitherOrBoth::Both(_, (key, node_opt)) => {
725 node_opt.map(|node| (key, node))
726 }
727 })
728 .collect()
729 }
730
731 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 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 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 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 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 let expected_combined = merge_with_overlay(db_nodes.clone(), in_memory_nodes.clone());
811
812 let all_keys: Vec<Nibbles> = expected_combined.iter().map(|(k, _)| *k).collect();
814
815 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 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 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 control_first.is_none() && test_first.is_none() {
843 return Ok(());
844 }
845
846 let mut last_returned_key = control_first.as_ref().map(|(k, _)| *k);
848
849 for choice in op_choices {
851 let op_type = choice % 3;
852
853 match op_type {
854 0 => {
855 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 if control_result.is_none() && test_result.is_none() {
869 break;
870 }
871 }
872 1 => {
873 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 if control_result.is_none() && test_result.is_none() {
903 break;
904 }
905 }
906 _ => {
907 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 last_returned_key = control_result.as_ref().map(|(k, _)| *k);
935 }
936 }
937 }
938 }
939 }
940 }
941}