Skip to main content

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}