1use crate::{
7 updates::{TrieUpdates, TrieUpdatesSorted},
8 HashedPostState, HashedPostStateSorted,
9};
10use alloc::sync::Arc;
11use core::fmt;
12use reth_primitives_traits::sync::OnceLock;
13
14#[derive(Clone, Debug, Default, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct SortedTrieData {
21 pub hashed_state: Arc<HashedPostStateSorted>,
23 pub trie_updates: Arc<TrieUpdatesSorted>,
25}
26
27impl SortedTrieData {
28 pub const fn new(
30 hashed_state: Arc<HashedPostStateSorted>,
31 trie_updates: Arc<TrieUpdatesSorted>,
32 ) -> Self {
33 Self { hashed_state, trie_updates }
34 }
35}
36
37pub struct LazyTrieData {
50 data: Arc<OnceLock<SortedTrieData>>,
52 mode: LazyTrieDataMode,
56}
57
58impl Clone for LazyTrieData {
59 fn clone(&self) -> Self {
60 Self { data: Arc::clone(&self.data), mode: self.mode.clone() }
61 }
62}
63
64impl fmt::Debug for LazyTrieData {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.debug_struct("LazyTrieData")
67 .field("data", &if self.data.get().is_some() { "initialized" } else { "pending" })
68 .finish()
69 }
70}
71
72impl PartialEq for LazyTrieData {
73 fn eq(&self, other: &Self) -> bool {
74 self.get() == other.get()
75 }
76}
77
78impl Eq for LazyTrieData {}
79
80impl LazyTrieData {
81 pub fn ready(sorted: SortedTrieData) -> Self {
83 Self { data: Arc::new(OnceLock::from(sorted)), mode: LazyTrieDataMode::Ready }
84 }
85
86 pub fn deferred(compute: impl Fn() -> SortedTrieData + Send + Sync + 'static) -> Self {
91 Self {
92 data: Arc::new(OnceLock::new()),
93 mode: LazyTrieDataMode::Deferred(Arc::new(compute)),
94 }
95 }
96
97 #[cfg(feature = "std")]
103 pub fn pending(
104 hashed_state: Arc<HashedPostState>,
105 trie_updates: Arc<TrieUpdates>,
106 ) -> (Self, LazyTrieDataProducer) {
107 let value = Arc::new(OnceLock::new());
108 (
109 Self { data: Arc::clone(&value), mode: LazyTrieDataMode::Pending },
110 LazyTrieDataProducer { value, inputs: PendingInputs { hashed_state, trie_updates } },
111 )
112 }
113
114 pub fn get(&self) -> &SortedTrieData {
123 match &self.mode {
124 LazyTrieDataMode::Ready => self.data.get().expect("LazyTrieData must be initialized"),
125 LazyTrieDataMode::Deferred(compute) => self.data.get_or_init(|| compute.as_ref()()),
126 #[cfg(feature = "std")]
127 LazyTrieDataMode::Pending => self.data.wait(),
128 }
129 }
130
131 pub fn hashed_state(&self) -> Arc<HashedPostStateSorted> {
135 Arc::clone(&self.get().hashed_state)
136 }
137
138 pub fn trie_updates(&self) -> Arc<TrieUpdatesSorted> {
142 Arc::clone(&self.get().trie_updates)
143 }
144
145 pub fn sorted_trie_data(&self) -> SortedTrieData {
149 self.get().clone()
150 }
151}
152
153#[cfg(feature = "serde")]
154impl serde::Serialize for LazyTrieData {
155 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
156 where
157 S: serde::Serializer,
158 {
159 self.get().serialize(serializer)
160 }
161}
162
163#[cfg(feature = "serde")]
164impl<'de> serde::Deserialize<'de> for LazyTrieData {
165 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
166 where
167 D: serde::Deserializer<'de>,
168 {
169 let data = SortedTrieData::deserialize(deserializer)?;
170 Ok(Self::ready(data))
171 }
172}
173
174#[derive(Clone)]
175enum LazyTrieDataMode {
176 Ready,
177 Deferred(Arc<dyn Fn() -> SortedTrieData + Send + Sync>),
178 #[cfg(feature = "std")]
179 Pending,
180}
181
182impl fmt::Debug for LazyTrieDataMode {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 match self {
185 Self::Ready => write!(f, "Ready"),
186 Self::Deferred(_) => write!(f, "Deferred(..)"),
187 #[cfg(feature = "std")]
188 Self::Pending => write!(f, "Pending"),
189 }
190 }
191}
192
193#[must_use = "LazyTrieDataProducer must be consumed with compute_and_publish to wake trie data waiters"]
198pub struct LazyTrieDataProducer {
199 value: Arc<OnceLock<SortedTrieData>>,
201 inputs: PendingInputs,
203}
204
205impl fmt::Debug for LazyTrieDataProducer {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 f.debug_struct("LazyTrieDataProducer").field("inputs", &self.inputs).finish_non_exhaustive()
208 }
209}
210
211impl LazyTrieDataProducer {
212 pub fn compute_and_publish(self) -> SortedTrieData {
214 let Self { value, inputs } = self;
215 let computed = Self::sort(inputs.hashed_state, inputs.trie_updates);
216 let _ = value.set(computed.clone());
217 computed
218 }
219
220 pub fn sort(
222 hashed_state: Arc<HashedPostState>,
223 trie_updates: Arc<TrieUpdates>,
224 ) -> SortedTrieData {
225 #[cfg(feature = "rayon")]
226 let (sorted_hashed_state, sorted_trie_updates) = rayon::join(
227 || match Arc::try_unwrap(hashed_state) {
228 Ok(state) => state.into_sorted(),
229 Err(arc) => arc.clone_into_sorted(),
230 },
231 || match Arc::try_unwrap(trie_updates) {
232 Ok(updates) => updates.into_sorted(),
233 Err(arc) => arc.clone_into_sorted(),
234 },
235 );
236
237 #[cfg(not(feature = "rayon"))]
238 let (sorted_hashed_state, sorted_trie_updates) = (
239 match Arc::try_unwrap(hashed_state) {
240 Ok(state) => state.into_sorted(),
241 Err(arc) => arc.clone_into_sorted(),
242 },
243 match Arc::try_unwrap(trie_updates) {
244 Ok(updates) => updates.into_sorted(),
245 Err(arc) => arc.clone_into_sorted(),
246 },
247 );
248
249 SortedTrieData::new(Arc::new(sorted_hashed_state), Arc::new(sorted_trie_updates))
250 }
251}
252
253#[derive(Clone, Debug)]
255struct PendingInputs {
256 hashed_state: Arc<HashedPostState>,
258 trie_updates: Arc<TrieUpdates>,
260}
261
262#[cfg(test)]
263mod tests {
264 use crate::HashedStorage;
265
266 use super::*;
267 use alloy_primitives::{B256, U256};
268 use reth_primitives_traits::Account;
269 use std::{thread, time::Duration};
270
271 fn empty_pending() -> (LazyTrieData, LazyTrieDataProducer) {
272 LazyTrieData::pending(
273 Arc::new(HashedPostState::default()),
274 Arc::new(TrieUpdates::default()),
275 )
276 }
277
278 #[test]
279 fn test_lazy_clone_shares_state() {
280 let lazy1 = LazyTrieData::ready(SortedTrieData::default());
281 let lazy2 = lazy1.clone();
282
283 assert!(Arc::ptr_eq(&lazy1.hashed_state(), &lazy2.hashed_state()));
285 assert!(Arc::ptr_eq(&lazy1.trie_updates(), &lazy2.trie_updates()));
286 }
287
288 #[test]
289 fn test_lazy_deferred() {
290 let lazy = LazyTrieData::deferred(SortedTrieData::default);
291 assert!(lazy.hashed_state().is_empty());
292 assert!(lazy.trie_updates().is_empty());
293 }
294
295 #[test]
296 fn pending_wait_blocks_until_task_publishes() {
297 let (deferred, task) = empty_pending();
298
299 let handle = thread::spawn(move || deferred.get().clone());
300 thread::sleep(Duration::from_millis(20));
301 assert!(!handle.is_finished());
302
303 let published = task.compute_and_publish();
304 let result = handle.join().unwrap();
305
306 assert!(Arc::ptr_eq(&published.hashed_state, &result.hashed_state));
307 assert!(Arc::ptr_eq(&published.trie_updates, &result.trie_updates));
308 }
309
310 #[test]
311 fn concurrent_waits_share_published_result() {
312 let (deferred, task) = empty_pending();
313 let deferred2 = deferred.clone();
314
315 let handle = thread::spawn(move || deferred2.get().clone());
316 let published = task.compute_and_publish();
317 let result1 = deferred.get().clone();
318 let result2 = handle.join().unwrap();
319
320 assert!(Arc::ptr_eq(&published.hashed_state, &result1.hashed_state));
321 assert!(Arc::ptr_eq(&published.trie_updates, &result1.trie_updates));
322 assert!(Arc::ptr_eq(&result1.hashed_state, &result2.hashed_state));
323 assert!(Arc::ptr_eq(&result1.trie_updates, &result2.trie_updates));
324 }
325
326 #[test]
327 fn sorts_non_empty_inputs() {
328 let hashed_address = B256::with_last_byte(1);
329 let hashed_slot = B256::with_last_byte(2);
330 let hashed_state = HashedPostState::default()
331 .with_accounts([(hashed_address, Some(Account::default()))])
332 .with_storages([(
333 hashed_address,
334 HashedStorage::from_iter([(hashed_slot, U256::ONE)]),
335 )]);
336
337 let (deferred, task) =
338 LazyTrieData::pending(Arc::new(hashed_state), Arc::new(TrieUpdates::default()));
339 let _ = task.compute_and_publish();
340 let result = deferred.get().clone();
341
342 assert_eq!(result.hashed_state.total_len(), 2);
343 assert_eq!(result.trie_updates.total_len(), 0);
344 }
345}