1use super::{HashedCursor, HashedCursorFactory, HashedStorageCursor};
2use crate::forward_cursor::ForwardInMemoryCursor;
3use alloy_primitives::{B256, U256};
4use reth_primitives_traits::Account;
5use reth_storage_errors::db::DatabaseError;
6use reth_trie_common::HashedPostStateSorted;
7
8#[derive(Clone, Debug)]
10pub struct HashedPostStateCursorFactory<CF, T> {
11 cursor_factory: CF,
12 post_state: T,
13}
14
15impl<CF, T> HashedPostStateCursorFactory<CF, T> {
16 pub const fn new(cursor_factory: CF, post_state: T) -> Self {
18 Self { cursor_factory, post_state }
19 }
20}
21
22impl<'overlay, CF, T> HashedCursorFactory for HashedPostStateCursorFactory<CF, &'overlay T>
23where
24 CF: HashedCursorFactory,
25 T: AsRef<HashedPostStateSorted>,
26{
27 type AccountCursor<'cursor>
28 = HashedPostStateCursor<'overlay, CF::AccountCursor<'cursor>, Option<Account>>
29 where
30 Self: 'cursor;
31 type StorageCursor<'cursor>
32 = HashedPostStateCursor<'overlay, CF::StorageCursor<'cursor>, U256>
33 where
34 Self: 'cursor;
35
36 fn hashed_account_cursor(&self) -> Result<Self::AccountCursor<'_>, DatabaseError> {
37 let cursor = self.cursor_factory.hashed_account_cursor()?;
38 Ok(HashedPostStateCursor::new_account(cursor, self.post_state.as_ref()))
39 }
40
41 fn hashed_storage_cursor(
42 &self,
43 hashed_address: B256,
44 ) -> Result<Self::StorageCursor<'_>, DatabaseError> {
45 let post_state = self.post_state.as_ref();
46 let cursor = self.cursor_factory.hashed_storage_cursor(hashed_address)?;
47 Ok(HashedPostStateCursor::new_storage(cursor, post_state, hashed_address))
48 }
49}
50
51pub trait HashedPostStateCursorValue: Clone {
61 type NonZero: Clone + std::fmt::Debug;
65
66 fn into_option(self) -> Option<Self::NonZero>;
68}
69
70impl HashedPostStateCursorValue for Option<Account> {
71 type NonZero = Account;
72
73 fn into_option(self) -> Option<Self::NonZero> {
74 self
75 }
76}
77
78impl HashedPostStateCursorValue for U256 {
79 type NonZero = Self;
80
81 fn into_option(self) -> Option<Self::NonZero> {
82 (!self.is_zero()).then_some(self)
83 }
84}
85
86#[derive(Debug)]
89pub struct HashedPostStateCursor<'a, C, V>
90where
91 V: HashedPostStateCursorValue,
92{
93 cursor: C,
95 db_cursor_state: DbCursorState<V::NonZero>,
97 post_state_cursor: ForwardInMemoryCursor<'a, B256, V>,
99 last_key: Option<B256>,
102 #[cfg(debug_assertions)]
103 seeked: bool,
105 post_state: &'a HashedPostStateSorted,
107}
108
109#[derive(Debug)]
110enum DbCursorState<V> {
111 NeedsPosition,
112 Positioned((B256, V)),
113 Exhausted,
114}
115
116impl<V> DbCursorState<V> {
117 const fn entry(&self) -> Option<&(B256, V)> {
118 match self {
119 Self::Positioned(entry) => Some(entry),
120 Self::NeedsPosition | Self::Exhausted => None,
121 }
122 }
123
124 fn set_entry(&mut self, entry: Option<(B256, V)>) {
125 *self = match entry {
126 Some(entry) => Self::Positioned(entry),
127 None => Self::Exhausted,
128 };
129 }
130}
131
132impl<'a, C> HashedPostStateCursor<'a, C, Option<Account>>
133where
134 C: HashedCursor<Value = Account>,
135{
136 pub fn new_account(cursor: C, post_state: &'a HashedPostStateSorted) -> Self {
138 let post_state_cursor = ForwardInMemoryCursor::new(&post_state.accounts);
139 Self {
140 cursor,
141 db_cursor_state: DbCursorState::NeedsPosition,
142 post_state_cursor,
143 last_key: None,
144 #[cfg(debug_assertions)]
145 seeked: false,
146 post_state,
147 }
148 }
149}
150
151impl<'a, C> HashedPostStateCursor<'a, C, U256>
152where
153 C: HashedStorageCursor<Value = U256>,
154{
155 pub fn new_storage(
158 cursor: C,
159 post_state: &'a HashedPostStateSorted,
160 hashed_address: B256,
161 ) -> Self {
162 let post_state_cursor = Self::get_storage_overlay(post_state, hashed_address);
163 Self {
164 cursor,
165 db_cursor_state: DbCursorState::NeedsPosition,
166 post_state_cursor,
167 last_key: None,
168 #[cfg(debug_assertions)]
169 seeked: false,
170 post_state,
171 }
172 }
173
174 fn get_storage_overlay(
176 post_state: &'a HashedPostStateSorted,
177 hashed_address: B256,
178 ) -> ForwardInMemoryCursor<'a, B256, U256> {
179 let post_state_storage = post_state.storages.get(&hashed_address);
180 let storage_slots = post_state_storage.map(|u| u.storage_slots_ref()).unwrap_or(&[]);
181
182 ForwardInMemoryCursor::new(storage_slots)
183 }
184}
185
186impl<'a, C, V> HashedPostStateCursor<'a, C, V>
187where
188 C: HashedCursor<Value = V::NonZero>,
189 V: HashedPostStateCursorValue,
190{
191 const fn get_cursor_mut(&mut self) -> &mut C {
192 &mut self.cursor
193 }
194
195 fn set_last_key(&mut self, next_entry: &Option<(B256, V::NonZero)>) {
198 let next_key = next_entry.as_ref().map(|e| e.0);
199 debug_assert!(
200 self.last_key.is_none_or(|last| next_key.is_none_or(|next| next >= last)),
201 "Cannot return entry {:?} previous to the last returned entry at {:?}",
202 next_key,
203 self.last_key,
204 );
205 self.last_key = next_key;
206 }
207
208 fn cursor_seek(&mut self, key: B256) -> Result<(), DatabaseError> {
210 let should_seek = match &self.db_cursor_state {
214 DbCursorState::NeedsPosition => true,
215 DbCursorState::Positioned((entry_key, _)) => entry_key < &key,
216 DbCursorState::Exhausted => false,
217 };
218
219 if should_seek {
220 let entry = self.get_cursor_mut().seek(key)?;
221 self.db_cursor_state.set_entry(entry);
222 }
223
224 Ok(())
225 }
226
227 fn cursor_next(&mut self) -> Result<(), DatabaseError> {
229 #[cfg(debug_assertions)]
230 {
231 debug_assert!(self.seeked);
232 }
233
234 if matches!(self.db_cursor_state, DbCursorState::Positioned(_)) {
236 let entry = self.get_cursor_mut().next()?;
237 self.db_cursor_state.set_entry(entry);
238 }
239
240 Ok(())
241 }
242
243 fn choose_next_entry(&mut self) -> Result<Option<(B256, V::NonZero)>, DatabaseError> {
249 loop {
250 let post_state_current =
251 self.post_state_cursor.current().cloned().map(|(k, v)| (k, v.into_option()));
252 let db_entry = self.db_cursor_state.entry();
253
254 match (post_state_current, db_entry) {
255 (Some((mem_key, None)), _)
256 if db_entry.is_none_or(|(db_key, _)| &mem_key < db_key) =>
257 {
258 self.post_state_cursor.first_after(&mem_key);
262 }
263 (Some((mem_key, None)), Some((db_key, _))) if &mem_key == db_key => {
264 self.post_state_cursor.first_after(&mem_key);
267 self.cursor_next()?;
268 }
269 (Some((mem_key, Some(value))), _)
270 if db_entry.is_none_or(|(db_key, _)| &mem_key <= db_key) =>
271 {
272 return Ok(Some((mem_key, value)))
275 }
276 _ => return Ok(db_entry.cloned()),
281 }
282 }
283 }
284}
285
286impl<C, V> HashedCursor for HashedPostStateCursor<'_, C, V>
287where
288 C: HashedCursor<Value = V::NonZero>,
289 V: HashedPostStateCursorValue,
290{
291 type Value = V::NonZero;
292
293 fn seek(&mut self, key: B256) -> Result<Option<(B256, Self::Value)>, DatabaseError> {
302 if self.last_key.is_none_or(|last| key < last) {
304 self.reset();
305 }
306 let post_state_entry =
307 self.post_state_cursor.seek(&key).cloned().map(|(k, v)| (k, v.into_option()));
308
309 if let Some((mem_key, Some(value))) = post_state_entry &&
310 mem_key == key
311 {
312 #[cfg(debug_assertions)]
313 {
314 self.seeked = true;
315 }
316
317 if matches!(&self.db_cursor_state, DbCursorState::Positioned((db_key, _)) if db_key < &key)
320 {
321 self.db_cursor_state = DbCursorState::NeedsPosition;
322 }
323
324 let entry = Some((key, value));
325 self.set_last_key(&entry);
326 return Ok(entry)
327 }
328
329 self.cursor_seek(key)?;
330
331 #[cfg(debug_assertions)]
332 {
333 self.seeked = true;
334 }
335
336 let entry = self.choose_next_entry()?;
337 self.set_last_key(&entry);
338 Ok(entry)
339 }
340
341 fn next(&mut self) -> Result<Option<(B256, Self::Value)>, DatabaseError> {
348 #[cfg(debug_assertions)]
349 {
350 debug_assert!(self.seeked, "Cursor must be seek'd before next is called");
351 }
352
353 let Some(last_key) = self.last_key else {
355 return Ok(None);
356 };
357
358 if let Some((key, _)) = self.post_state_cursor.current() &&
361 key == &last_key
362 {
363 self.post_state_cursor.first_after(&last_key);
364 }
365
366 if matches!(self.db_cursor_state, DbCursorState::NeedsPosition) {
367 self.cursor_seek(last_key)?;
368 }
369
370 if let Some((key, _)) = self.db_cursor_state.entry() &&
371 key == &last_key
372 {
373 self.cursor_next()?;
374 }
375
376 let entry = self.choose_next_entry()?;
377 self.set_last_key(&entry);
378 Ok(entry)
379 }
380
381 fn reset(&mut self) {
382 let Self { cursor, db_cursor_state, post_state_cursor, last_key, .. } = self;
383
384 cursor.reset();
385 post_state_cursor.reset();
386
387 *db_cursor_state = DbCursorState::NeedsPosition;
388 *last_key = None;
389 #[cfg(debug_assertions)]
390 {
391 self.seeked = false;
392 }
393 }
394}
395
396impl<C> HashedStorageCursor for HashedPostStateCursor<'_, C, U256>
399where
400 C: HashedStorageCursor<Value = U256>,
401{
402 fn is_storage_empty(&mut self) -> Result<bool, DatabaseError> {
407 let is_empty = self.seek(B256::ZERO)?.is_none();
408 self.reset();
409 Ok(is_empty)
410 }
411
412 fn set_hashed_address(&mut self, hashed_address: B256) {
413 self.reset();
414 self.cursor.set_hashed_address(hashed_address);
415 let post_state_cursor =
416 HashedPostStateCursor::<C, U256>::get_storage_overlay(self.post_state, hashed_address);
417 self.post_state_cursor = post_state_cursor;
418 self.db_cursor_state = DbCursorState::NeedsPosition;
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425 use crate::hashed_cursor::mock::MockHashedCursor;
426 use parking_lot::Mutex;
427 use std::{collections::BTreeMap, sync::Arc};
428
429 fn key(byte: u8) -> B256 {
430 B256::repeat_byte(byte)
431 }
432
433 fn storage_post_state(storage_slots: Vec<(B256, U256)>) -> HashedPostStateSorted {
434 let storage_sorted = reth_trie_common::HashedStorageSorted { storage_slots };
435 let mut storages = alloy_primitives::map::B256Map::default();
436 storages.insert(B256::ZERO, storage_sorted);
437 HashedPostStateSorted::new(Vec::new(), storages)
438 }
439
440 #[test]
441 fn test_seek_overlay_exact_hit_does_not_touch_db_until_next() {
442 let db_nodes = vec![(key(0x02), U256::from(2)), (key(0x03), U256::from(3))];
443 let post_state_nodes = vec![(key(0x02), U256::from(42))];
444
445 let db_nodes_map: BTreeMap<B256, U256> = db_nodes.into_iter().collect();
446 let db_nodes_arc = Arc::new(db_nodes_map);
447 let visited_keys = Arc::new(Mutex::new(Vec::new()));
448 let mock_cursor = MockHashedCursor::new(db_nodes_arc, visited_keys.clone());
449
450 let post_state = storage_post_state(post_state_nodes);
451 let mut cursor = HashedPostStateCursor::new_storage(mock_cursor, &post_state, B256::ZERO);
452
453 let result = cursor.seek(key(0x02)).unwrap();
454 assert_eq!(result, Some((key(0x02), U256::from(42))));
455 assert!(visited_keys.lock().is_empty(), "exact overlay hit should not touch the DB cursor");
456
457 let result = cursor.next().unwrap();
458 assert_eq!(result, Some((key(0x03), U256::from(3))));
459 assert!(!visited_keys.lock().is_empty(), "next should lazily position the DB cursor");
460 }
461
462 #[test]
463 fn test_seek_overlay_exact_hit_repositions_stale_db_on_next() {
464 let db_nodes = vec![(key(0x01), U256::ONE), (key(0x03), U256::from(3))];
465 let post_state_nodes = vec![(key(0x02), U256::from(2))];
466
467 let db_nodes_map: BTreeMap<B256, U256> = db_nodes.into_iter().collect();
468 let db_nodes_arc = Arc::new(db_nodes_map);
469 let visited_keys = Arc::new(Mutex::new(Vec::new()));
470 let mock_cursor = MockHashedCursor::new(db_nodes_arc, visited_keys.clone());
471
472 let post_state = storage_post_state(post_state_nodes);
473 let mut cursor = HashedPostStateCursor::new_storage(mock_cursor, &post_state, B256::ZERO);
474
475 let result = cursor.seek(key(0x01)).unwrap();
476 assert_eq!(result, Some((key(0x01), U256::ONE)));
477 assert_eq!(visited_keys.lock().len(), 1);
478
479 let result = cursor.seek(key(0x02)).unwrap();
480 assert_eq!(result, Some((key(0x02), U256::from(2))));
481 assert_eq!(visited_keys.lock().len(), 1, "exact overlay hit should not seek the DB");
482
483 let result = cursor.next().unwrap();
484 assert_eq!(result, Some((key(0x03), U256::from(3))));
485 }
486
487 #[test]
488 fn test_seek_overlay_exact_deletion_still_seeks_db() {
489 let db_nodes = vec![(key(0x02), U256::from(2)), (key(0x03), U256::from(3))];
490 let post_state_nodes = vec![(key(0x02), U256::ZERO)];
491
492 let db_nodes_map: BTreeMap<B256, U256> = db_nodes.into_iter().collect();
493 let db_nodes_arc = Arc::new(db_nodes_map);
494 let visited_keys = Arc::new(Mutex::new(Vec::new()));
495 let mock_cursor = MockHashedCursor::new(db_nodes_arc, visited_keys.clone());
496
497 let post_state = storage_post_state(post_state_nodes);
498 let mut cursor = HashedPostStateCursor::new_storage(mock_cursor, &post_state, B256::ZERO);
499
500 let result = cursor.seek(key(0x02)).unwrap();
501 assert_eq!(result, Some((key(0x03), U256::from(3))));
502 assert!(!visited_keys.lock().is_empty(), "exact overlay deletion should consult the DB");
503 }
504
505 mod proptest_tests {
506 use super::*;
507 use itertools::Itertools;
508 use proptest::prelude::*;
509
510 fn merge_with_overlay<V>(
513 db_nodes: Vec<(B256, V::NonZero)>,
514 post_state_nodes: Vec<(B256, V)>,
515 ) -> Vec<(B256, V::NonZero)>
516 where
517 V: HashedPostStateCursorValue,
518 V::NonZero: Clone,
519 {
520 db_nodes
521 .into_iter()
522 .merge_join_by(post_state_nodes, |db_entry, mem_entry| db_entry.0.cmp(&mem_entry.0))
523 .filter_map(|entry| match entry {
524 itertools::EitherOrBoth::Left((key, node)) => Some((key, node)),
526 itertools::EitherOrBoth::Right((key, wrapped)) => {
528 wrapped.into_option().map(|val| (key, val))
529 }
530 itertools::EitherOrBoth::Both(_, (key, wrapped)) => {
532 wrapped.into_option().map(|val| (key, val))
533 }
534 })
535 .collect()
536 }
537
538 fn u256_strategy() -> impl Strategy<Value = U256> {
540 any::<u64>().prop_map(U256::from)
541 }
542
543 fn sorted_db_nodes_strategy() -> impl Strategy<Value = Vec<(B256, U256)>> {
545 prop::collection::vec((any::<u8>(), u256_strategy()), 0..20).prop_map(|entries| {
546 let mut result: Vec<(B256, U256)> = entries
547 .into_iter()
548 .map(|(byte, value)| (B256::repeat_byte(byte), value))
549 .collect();
550 result.sort_by_key(|a| a.0);
551 result.dedup_by(|a, b| a.0 == b.0);
552 result
553 })
554 }
555
556 fn sorted_post_state_nodes_strategy() -> impl Strategy<Value = Vec<(B256, U256)>> {
558 prop::collection::vec((any::<u8>(), u256_strategy(), any::<bool>()), 0..20).prop_map(
560 |entries| {
561 let mut result: Vec<(B256, U256)> = entries
562 .into_iter()
563 .map(|(byte, value, is_deletion)| {
564 let effective_value = if is_deletion { U256::ZERO } else { value };
565 (B256::repeat_byte(byte), effective_value)
566 })
567 .collect();
568 result.sort_by_key(|a| a.0);
569 result.dedup_by(|a, b| a.0 == b.0);
570 result
571 },
572 )
573 }
574
575 proptest! {
576 #![proptest_config(ProptestConfig::with_cases(1000))]
577 #[test]
587 fn proptest_hashed_post_state_cursor(
588 db_nodes in sorted_db_nodes_strategy(),
589 post_state_nodes in sorted_post_state_nodes_strategy(),
590 op_choices in prop::collection::vec(any::<u8>(), 10..500),
591 ) {
592 reth_tracing::init_test_tracing();
593 use tracing::debug;
594
595 debug!("Starting proptest!");
596
597 let expected_combined = merge_with_overlay(db_nodes.clone(), post_state_nodes.clone());
600
601 let all_keys: Vec<B256> = expected_combined.iter().map(|(k, _)| *k).collect();
603
604 let control_db_map: BTreeMap<B256, U256> = expected_combined.into_iter().collect();
606 let control_db_arc = Arc::new(control_db_map);
607 let control_visited_keys = Arc::new(Mutex::new(Vec::new()));
608 let mut control_cursor = MockHashedCursor::new(control_db_arc, control_visited_keys);
609
610 let db_nodes_map: BTreeMap<B256, U256> = db_nodes.into_iter().collect();
612 let db_nodes_arc = Arc::new(db_nodes_map);
613 let visited_keys = Arc::new(Mutex::new(Vec::new()));
614 let mock_cursor = MockHashedCursor::new(db_nodes_arc, visited_keys);
615
616 let hashed_address = B256::ZERO;
618 let storage_sorted = reth_trie_common::HashedStorageSorted {
619 storage_slots: post_state_nodes,
620 };
621 let mut storages = alloy_primitives::map::B256Map::default();
622 storages.insert(hashed_address, storage_sorted);
623 let post_state = HashedPostStateSorted::new(Vec::new(), storages);
624
625 let mut test_cursor = HashedPostStateCursor::new_storage(mock_cursor, &post_state, hashed_address);
626
627 let control_first = control_cursor.seek(B256::ZERO).unwrap();
629 let test_first = test_cursor.seek(B256::ZERO).unwrap();
630 debug!(
631 control=?control_first.as_ref().map(|(k, _)| k),
632 test=?test_first.as_ref().map(|(k, _)| k),
633 "Initial seek returned",
634 );
635 assert_eq!(control_first, test_first, "Initial seek mismatch");
636
637 if control_first.is_none() && test_first.is_none() {
639 return Ok(());
640 }
641
642 let mut last_returned_key = control_first.as_ref().map(|(k, _)| *k);
644
645 for choice in op_choices {
647 let op_type = choice % 2; match op_type {
650 0 => {
651 let control_result = control_cursor.next().unwrap();
653 let test_result = test_cursor.next().unwrap();
654 debug!(
655 control=?control_result.as_ref().map(|(k, _)| k),
656 test=?test_result.as_ref().map(|(k, _)| k),
657 "Next returned",
658 );
659 assert_eq!(control_result, test_result, "Next operation mismatch");
660
661 last_returned_key = control_result.as_ref().map(|(k, _)| *k);
662
663 if control_result.is_none() && test_result.is_none() {
665 break;
666 }
667 }
668 _ => {
669 if all_keys.is_empty() {
671 continue;
672 }
673
674 let valid_keys: Vec<_> = all_keys
675 .iter()
676 .filter(|k| last_returned_key.is_none_or(|last| **k >= last))
677 .collect();
678
679 if valid_keys.is_empty() {
680 continue;
681 }
682
683 let key = *valid_keys[(choice as usize / 2) % valid_keys.len()];
684
685 let control_result = control_cursor.seek(key).unwrap();
686 let test_result = test_cursor.seek(key).unwrap();
687 debug!(
688 control=?control_result.as_ref().map(|(k, _)| k),
689 test=?test_result.as_ref().map(|(k, _)| k),
690 ?key,
691 "Seek returned",
692 );
693 assert_eq!(control_result, test_result, "Seek operation mismatch for key {:?}", key);
694
695 last_returned_key = control_result.as_ref().map(|(k, _)| *k);
696
697 if control_result.is_none() && test_result.is_none() {
699 break;
700 }
701 }
702 }
703 }
704 }
705 }
706 }
707}