reth_engine_tree/tree/payload_processor/preserved_sparse_trie.rs
1//! Preserved sparse trie for reuse across payload validations.
2
3use alloy_primitives::B256;
4use parking_lot::Mutex;
5use reth_trie_sparse::SparseStateTrie;
6use std::sync::Arc;
7use tracing::debug;
8
9/// Type alias for the sparse trie type used in preservation.
10pub(super) type SparseTrie = SparseStateTrie;
11
12/// Shared handle to a preserved sparse trie that can be reused across payload validations.
13///
14/// This is stored in [`PayloadProcessor`](super::PayloadProcessor) and cloned to pass to
15/// [`SparseTrieTask`](super::sparse_trie::SparseTrieTask) for trie reuse.
16#[derive(Debug, Default, Clone)]
17pub(super) struct SharedPreservedSparseTrie(Arc<Mutex<Option<PreservedSparseTrie>>>);
18
19impl SharedPreservedSparseTrie {
20 /// Takes the preserved trie if present, leaving `None` in its place.
21 pub(super) fn take(&self) -> Option<PreservedSparseTrie> {
22 self.0.lock().take()
23 }
24
25 /// Acquires a guard that blocks `take()` until dropped.
26 /// Use this before sending the state root result to ensure the next block
27 /// waits for the trie to be stored.
28 pub(super) fn lock(&self) -> PreservedTrieGuard<'_> {
29 PreservedTrieGuard(self.0.lock())
30 }
31}
32
33/// Guard that holds the lock on the preserved trie.
34/// While held, `take()` will block. Call `store()` to save the trie before dropping.
35pub(super) struct PreservedTrieGuard<'a>(parking_lot::MutexGuard<'a, Option<PreservedSparseTrie>>);
36
37impl PreservedTrieGuard<'_> {
38 /// Stores a preserved trie for later reuse.
39 pub(super) fn store(&mut self, trie: PreservedSparseTrie) {
40 self.0.replace(trie);
41 }
42}
43
44/// A preserved sparse trie that can be reused across payload validations.
45///
46/// The trie exists in one of two states:
47/// - **Anchored**: Has a computed state root and can be reused for payloads whose parent state root
48/// matches the anchor.
49/// - **Cleared**: Trie data has been cleared but allocations are preserved for reuse.
50#[derive(Debug)]
51pub(super) enum PreservedSparseTrie {
52 /// Trie with a computed state root that can be reused for continuation payloads.
53 Anchored {
54 /// The sparse state trie (pruned after root computation).
55 trie: SparseTrie,
56 /// The state root this trie represents (computed from the previous block).
57 /// Used to verify continuity: new payload's `parent_state_root` must match this.
58 state_root: B256,
59 },
60 /// Cleared trie with preserved allocations, ready for fresh use.
61 Cleared {
62 /// The sparse state trie with cleared data but preserved allocations.
63 trie: SparseTrie,
64 },
65}
66
67impl PreservedSparseTrie {
68 /// Creates a new anchored preserved trie.
69 ///
70 /// The `state_root` is the computed state root from the trie, which becomes the
71 /// anchor for determining if subsequent payloads can reuse this trie.
72 pub(super) const fn anchored(trie: SparseTrie, state_root: B256) -> Self {
73 Self::Anchored { trie, state_root }
74 }
75
76 /// Creates a cleared preserved trie (allocations preserved, data cleared).
77 pub(super) const fn cleared(trie: SparseTrie) -> Self {
78 Self::Cleared { trie }
79 }
80
81 /// Consumes self and returns the trie for reuse.
82 ///
83 /// If the preserved trie is anchored and the parent state root matches, the pruned
84 /// trie structure is reused directly. Otherwise, the trie is cleared but allocations
85 /// are preserved to reduce memory overhead.
86 pub(super) fn into_trie_for(self, parent_state_root: B256) -> SparseTrie {
87 match self {
88 Self::Anchored { trie, state_root } if state_root == parent_state_root => {
89 debug!(
90 target: "engine::tree::payload_processor",
91 %state_root,
92 "Reusing anchored sparse trie for continuation payload"
93 );
94 trie
95 }
96 Self::Anchored { mut trie, state_root } => {
97 debug!(
98 target: "engine::tree::payload_processor",
99 anchor_root = %state_root,
100 %parent_state_root,
101 "Clearing anchored sparse trie - parent state root mismatch"
102 );
103 trie.clear();
104 trie
105 }
106 Self::Cleared { trie } => {
107 debug!(
108 target: "engine::tree::payload_processor",
109 %parent_state_root,
110 "Using cleared sparse trie with preserved allocations"
111 );
112 trie
113 }
114 }
115 }
116}