reth_trie_common/
lazy.rs

1//! Lazy initialization wrapper for trie data.
2//!
3//! Provides a no-std compatible [`LazyTrieData`] type for lazily initialized
4//! trie-related data containing sorted hashed state and trie updates.
5
6use crate::{updates::TrieUpdatesSorted, HashedPostStateSorted};
7use alloc::sync::Arc;
8use core::fmt;
9use reth_primitives_traits::sync::OnceLock;
10
11/// Container for sorted trie data: hashed state and trie updates.
12///
13/// This bundles both [`HashedPostStateSorted`] and [`TrieUpdatesSorted`] together
14/// for convenient passing and storage.
15#[derive(Clone, Debug, Default, PartialEq, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct SortedTrieData {
18    /// Sorted hashed post-state produced by execution.
19    pub hashed_state: Arc<HashedPostStateSorted>,
20    /// Sorted trie updates produced by state root computation.
21    pub trie_updates: Arc<TrieUpdatesSorted>,
22}
23
24impl SortedTrieData {
25    /// Creates a new [`SortedTrieData`] with the given values.
26    pub const fn new(
27        hashed_state: Arc<HashedPostStateSorted>,
28        trie_updates: Arc<TrieUpdatesSorted>,
29    ) -> Self {
30        Self { hashed_state, trie_updates }
31    }
32}
33
34/// Lazily initialized trie data containing sorted hashed state and trie updates.
35///
36/// This is a no-std compatible wrapper that supports two modes:
37/// 1. **Ready mode**: Data is available immediately (created via `ready()`)
38/// 2. **Deferred mode**: Data is computed on first access (created via `deferred()`)
39///
40/// In deferred mode, the computation runs on the first call to `get()`, `hashed_state()`,
41/// or `trie_updates()`, and results are cached for subsequent calls.
42///
43/// Cloning is cheap (Arc clone) and clones share the cached state.
44pub struct LazyTrieData {
45    /// Cached sorted trie data, computed on first access.
46    data: Arc<OnceLock<SortedTrieData>>,
47    /// Optional deferred computation function.
48    compute: Option<Arc<dyn Fn() -> SortedTrieData + Send + Sync>>,
49}
50
51impl Clone for LazyTrieData {
52    fn clone(&self) -> Self {
53        Self { data: Arc::clone(&self.data), compute: self.compute.clone() }
54    }
55}
56
57impl fmt::Debug for LazyTrieData {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        f.debug_struct("LazyTrieData")
60            .field("data", &if self.data.get().is_some() { "initialized" } else { "pending" })
61            .finish()
62    }
63}
64
65impl PartialEq for LazyTrieData {
66    fn eq(&self, other: &Self) -> bool {
67        self.get() == other.get()
68    }
69}
70
71impl Eq for LazyTrieData {}
72
73impl LazyTrieData {
74    /// Creates a new [`LazyTrieData`] that is already initialized with the given values.
75    pub fn ready(
76        hashed_state: Arc<HashedPostStateSorted>,
77        trie_updates: Arc<TrieUpdatesSorted>,
78    ) -> Self {
79        let data = OnceLock::new();
80        let _ = data.set(SortedTrieData::new(hashed_state, trie_updates));
81        Self { data: Arc::new(data), compute: None }
82    }
83
84    /// Creates a new [`LazyTrieData`] from pre-computed [`SortedTrieData`].
85    pub fn from_sorted(sorted: SortedTrieData) -> Self {
86        let data = OnceLock::new();
87        let _ = data.set(sorted);
88        Self { data: Arc::new(data), compute: None }
89    }
90
91    /// Creates a new [`LazyTrieData`] with a deferred computation function.
92    ///
93    /// The computation will run on the first call to `get()`, `hashed_state()`,
94    /// or `trie_updates()`. Results are cached for subsequent calls.
95    pub fn deferred(compute: impl Fn() -> SortedTrieData + Send + Sync + 'static) -> Self {
96        Self { data: Arc::new(OnceLock::new()), compute: Some(Arc::new(compute)) }
97    }
98
99    /// Returns a reference to the sorted trie data, computing if necessary.
100    ///
101    /// # Panics
102    ///
103    /// Panics if created via `deferred()` and the computation function was not provided.
104    pub fn get(&self) -> &SortedTrieData {
105        self.data.get_or_init(|| {
106            self.compute.as_ref().expect("LazyTrieData::get called before initialization")()
107        })
108    }
109
110    /// Returns a clone of the hashed state Arc.
111    ///
112    /// If not initialized, computes from the deferred source or panics.
113    pub fn hashed_state(&self) -> Arc<HashedPostStateSorted> {
114        Arc::clone(&self.get().hashed_state)
115    }
116
117    /// Returns a clone of the trie updates Arc.
118    ///
119    /// If not initialized, computes from the deferred source or panics.
120    pub fn trie_updates(&self) -> Arc<TrieUpdatesSorted> {
121        Arc::clone(&self.get().trie_updates)
122    }
123
124    /// Returns a clone of the [`SortedTrieData`].
125    ///
126    /// If not initialized, computes from the deferred source or panics.
127    pub fn sorted_trie_data(&self) -> SortedTrieData {
128        self.get().clone()
129    }
130}
131
132#[cfg(feature = "serde")]
133impl serde::Serialize for LazyTrieData {
134    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135    where
136        S: serde::Serializer,
137    {
138        self.get().serialize(serializer)
139    }
140}
141
142#[cfg(feature = "serde")]
143impl<'de> serde::Deserialize<'de> for LazyTrieData {
144    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
145    where
146        D: serde::Deserializer<'de>,
147    {
148        let data = SortedTrieData::deserialize(deserializer)?;
149        Ok(Self::from_sorted(data))
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_lazy_ready_is_initialized() {
159        let lazy = LazyTrieData::ready(
160            Arc::new(HashedPostStateSorted::default()),
161            Arc::new(TrieUpdatesSorted::default()),
162        );
163        let _ = lazy.hashed_state();
164        let _ = lazy.trie_updates();
165    }
166
167    #[test]
168    fn test_lazy_clone_shares_state() {
169        let lazy1 = LazyTrieData::ready(
170            Arc::new(HashedPostStateSorted::default()),
171            Arc::new(TrieUpdatesSorted::default()),
172        );
173        let lazy2 = lazy1.clone();
174
175        // Both point to the same data
176        assert!(Arc::ptr_eq(&lazy1.hashed_state(), &lazy2.hashed_state()));
177        assert!(Arc::ptr_eq(&lazy1.trie_updates(), &lazy2.trie_updates()));
178    }
179
180    #[test]
181    fn test_lazy_deferred() {
182        let lazy = LazyTrieData::deferred(SortedTrieData::default);
183        assert!(lazy.hashed_state().is_empty());
184        assert!(lazy.trie_updates().is_empty());
185    }
186
187    #[test]
188    fn test_lazy_from_sorted() {
189        let sorted = SortedTrieData::default();
190        let lazy = LazyTrieData::from_sorted(sorted);
191        assert!(lazy.hashed_state().is_empty());
192        assert!(lazy.trie_updates().is_empty());
193    }
194}