1use crate::{
7 prefix_set::TriePrefixSetsMut,
8 updates::{TrieUpdates, TrieUpdatesSorted},
9 HashedPostState, HashedPostStateSorted,
10};
11use alloc::sync::Arc;
12use core::fmt;
13use reth_primitives_traits::sync::OnceLock;
14
15#[derive(Clone, Debug, Default, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct SortedTrieData {
22 pub hashed_state: Arc<HashedPostStateSorted>,
24 pub trie_updates: Arc<TrieUpdatesSorted>,
26}
27
28impl SortedTrieData {
29 pub const fn new(
31 hashed_state: Arc<HashedPostStateSorted>,
32 trie_updates: Arc<TrieUpdatesSorted>,
33 ) -> Self {
34 Self { hashed_state, trie_updates }
35 }
36}
37
38#[derive(Clone, Debug, Default, PartialEq, Eq)]
40pub struct ComputedTrieData {
41 pub sorted: SortedTrieData,
43 pub changed_paths: Option<Arc<TriePrefixSetsMut>>,
45}
46
47impl ComputedTrieData {
48 pub const fn new(
50 hashed_state: Arc<HashedPostStateSorted>,
51 trie_updates: Arc<TrieUpdatesSorted>,
52 ) -> Self {
53 Self::new_with_changed_paths(hashed_state, trie_updates, None)
54 }
55
56 pub const fn new_with_changed_paths(
58 hashed_state: Arc<HashedPostStateSorted>,
59 trie_updates: Arc<TrieUpdatesSorted>,
60 changed_paths: Option<Arc<TriePrefixSetsMut>>,
61 ) -> Self {
62 Self { sorted: SortedTrieData::new(hashed_state, trie_updates), changed_paths }
63 }
64}
65
66pub struct LazyTrieData {
79 data: Arc<OnceLock<ComputedTrieData>>,
81 mode: LazyTrieDataMode,
85}
86
87impl Clone for LazyTrieData {
88 fn clone(&self) -> Self {
89 Self { data: Arc::clone(&self.data), mode: self.mode.clone() }
90 }
91}
92
93impl fmt::Debug for LazyTrieData {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 f.debug_struct("LazyTrieData")
96 .field("data", &if self.data.get().is_some() { "initialized" } else { "pending" })
97 .finish()
98 }
99}
100
101impl PartialEq for LazyTrieData {
102 fn eq(&self, other: &Self) -> bool {
103 self.get() == other.get()
104 }
105}
106
107impl Eq for LazyTrieData {}
108
109impl LazyTrieData {
110 pub fn ready(sorted: ComputedTrieData) -> Self {
112 Self { data: Arc::new(OnceLock::from(sorted)), mode: LazyTrieDataMode::Ready }
113 }
114
115 pub fn deferred(compute: impl Fn() -> ComputedTrieData + Send + Sync + 'static) -> Self {
120 Self {
121 data: Arc::new(OnceLock::new()),
122 mode: LazyTrieDataMode::Deferred(Arc::new(compute)),
123 }
124 }
125
126 #[cfg(feature = "std")]
128 pub fn pending(
129 hashed_state: Arc<HashedPostState>,
130 trie_updates: Arc<TrieUpdates>,
131 changed_paths: Option<Arc<TriePrefixSetsMut>>,
132 ) -> (Self, LazyTrieDataProducer) {
133 let value = Arc::new(OnceLock::new());
134 (
135 Self { data: Arc::clone(&value), mode: LazyTrieDataMode::Pending },
136 LazyTrieDataProducer {
137 value,
138 inputs: PendingInputs { hashed_state, trie_updates, changed_paths },
139 },
140 )
141 }
142
143 pub fn get(&self) -> &ComputedTrieData {
149 match &self.mode {
150 LazyTrieDataMode::Ready => self.data.get().expect("LazyTrieData must be initialized"),
151 LazyTrieDataMode::Deferred(compute) => self.data.get_or_init(|| compute.as_ref()()),
152 #[cfg(feature = "std")]
153 LazyTrieDataMode::Pending => self.data.wait(),
154 }
155 }
156
157 pub fn hashed_state(&self) -> Arc<HashedPostStateSorted> {
161 Arc::clone(&self.get().sorted.hashed_state)
162 }
163
164 pub fn trie_updates(&self) -> Arc<TrieUpdatesSorted> {
168 Arc::clone(&self.get().sorted.trie_updates)
169 }
170
171 pub fn sorted_trie_data(&self) -> SortedTrieData {
175 self.get().sorted.clone()
176 }
177}
178
179#[cfg(feature = "serde")]
180impl serde::Serialize for LazyTrieData {
181 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
182 where
183 S: serde::Serializer,
184 {
185 self.get().sorted.serialize(serializer)
186 }
187}
188
189#[cfg(feature = "serde")]
190impl<'de> serde::Deserialize<'de> for LazyTrieData {
191 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
192 where
193 D: serde::Deserializer<'de>,
194 {
195 let data = SortedTrieData::deserialize(deserializer)?;
196 Ok(Self::ready(ComputedTrieData::new(data.hashed_state, data.trie_updates)))
197 }
198}
199
200#[derive(Clone)]
201enum LazyTrieDataMode {
202 Ready,
203 Deferred(Arc<dyn Fn() -> ComputedTrieData + Send + Sync>),
204 #[cfg(feature = "std")]
205 Pending,
206}
207
208impl fmt::Debug for LazyTrieDataMode {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 match self {
211 Self::Ready => write!(f, "Ready"),
212 Self::Deferred(_) => write!(f, "Deferred(..)"),
213 #[cfg(feature = "std")]
214 Self::Pending => write!(f, "Pending"),
215 }
216 }
217}
218
219#[must_use = "LazyTrieDataProducer must be consumed with compute_and_publish to wake trie data waiters"]
221pub struct LazyTrieDataProducer {
222 value: Arc<OnceLock<ComputedTrieData>>,
224 inputs: PendingInputs,
226}
227
228impl fmt::Debug for LazyTrieDataProducer {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 f.debug_struct("LazyTrieDataProducer").field("inputs", &self.inputs).finish_non_exhaustive()
231 }
232}
233
234impl LazyTrieDataProducer {
235 pub fn compute_and_publish(self) -> ComputedTrieData {
237 let Self { value, inputs } = self;
238 let computed = Self::sort(inputs.hashed_state, inputs.trie_updates, inputs.changed_paths);
239 let _ = value.set(computed.clone());
240 computed
241 }
242
243 pub fn sort(
245 hashed_state: Arc<HashedPostState>,
246 trie_updates: Arc<TrieUpdates>,
247 changed_paths: Option<Arc<TriePrefixSetsMut>>,
248 ) -> ComputedTrieData {
249 #[cfg(feature = "rayon")]
250 let (sorted_hashed_state, sorted_trie_updates) = rayon::join(
251 || match Arc::try_unwrap(hashed_state) {
252 Ok(state) => state.into_sorted(),
253 Err(arc) => arc.clone_into_sorted(),
254 },
255 || match Arc::try_unwrap(trie_updates) {
256 Ok(updates) => updates.into_sorted(),
257 Err(arc) => arc.clone_into_sorted(),
258 },
259 );
260
261 #[cfg(not(feature = "rayon"))]
262 let (sorted_hashed_state, sorted_trie_updates) = (
263 match Arc::try_unwrap(hashed_state) {
264 Ok(state) => state.into_sorted(),
265 Err(arc) => arc.clone_into_sorted(),
266 },
267 match Arc::try_unwrap(trie_updates) {
268 Ok(updates) => updates.into_sorted(),
269 Err(arc) => arc.clone_into_sorted(),
270 },
271 );
272
273 ComputedTrieData::new_with_changed_paths(
274 Arc::new(sorted_hashed_state),
275 Arc::new(sorted_trie_updates),
276 changed_paths,
277 )
278 }
279}
280
281#[derive(Clone, Debug)]
283struct PendingInputs {
284 hashed_state: Arc<HashedPostState>,
286 trie_updates: Arc<TrieUpdates>,
288 changed_paths: Option<Arc<TriePrefixSetsMut>>,
290}
291
292#[cfg(test)]
293mod tests {
294 use crate::HashedStorage;
295
296 use super::*;
297 use alloy_primitives::{B256, U256};
298 use reth_primitives_traits::Account;
299 use revm::primitives::B256Map;
300 use std::{
301 thread,
302 time::{Duration, Instant},
303 };
304
305 fn empty_pending() -> (LazyTrieData, LazyTrieDataProducer) {
306 LazyTrieData::pending(
307 Arc::new(HashedPostState::default()),
308 Arc::new(TrieUpdates::default()),
309 None,
310 )
311 }
312
313 fn assert_changed_paths_ptr_eq(
314 left: &Option<Arc<TriePrefixSetsMut>>,
315 right: &Option<Arc<TriePrefixSetsMut>>,
316 ) {
317 match (left, right) {
318 (Some(left), Some(right)) => assert!(Arc::ptr_eq(left, right)),
319 (None, None) => {}
320 _ => panic!("changed paths presence mismatch"),
321 }
322 }
323
324 #[test]
325 fn test_lazy_ready_is_initialized() {
326 let lazy = LazyTrieData::ready(ComputedTrieData::default());
327 let _ = lazy.hashed_state();
328 let _ = lazy.trie_updates();
329 }
330
331 #[test]
332 fn test_lazy_clone_shares_state() {
333 let lazy1 = LazyTrieData::ready(ComputedTrieData::default());
334 let lazy2 = lazy1.clone();
335
336 assert!(Arc::ptr_eq(&lazy1.hashed_state(), &lazy2.hashed_state()));
338 assert!(Arc::ptr_eq(&lazy1.trie_updates(), &lazy2.trie_updates()));
339 }
340
341 #[test]
342 fn test_lazy_deferred() {
343 let lazy = LazyTrieData::deferred(ComputedTrieData::default);
344 assert!(lazy.hashed_state().is_empty());
345 assert!(lazy.trie_updates().is_empty());
346 }
347
348 #[test]
349 fn ready_returns_immediately() {
350 let bundle = ComputedTrieData::default();
351 let deferred = LazyTrieData::ready(bundle.clone());
352
353 let result = deferred.get();
354
355 assert_eq!(result.sorted.hashed_state.total_len(), bundle.sorted.hashed_state.total_len());
356 assert_eq!(result.sorted.trie_updates.total_len(), bundle.sorted.trie_updates.total_len());
357 }
358
359 #[test]
360 fn pending_waits_for_task_and_caches_result() {
361 let (deferred, task) = empty_pending();
362
363 let published = task.compute_and_publish();
364 let first = deferred.get();
365 let second = deferred.get();
366
367 assert!(Arc::ptr_eq(&published.sorted.hashed_state, &first.sorted.hashed_state));
368 assert!(Arc::ptr_eq(&published.sorted.trie_updates, &first.sorted.trie_updates));
369 assert_changed_paths_ptr_eq(&published.changed_paths, &first.changed_paths);
370 assert!(Arc::ptr_eq(&first.sorted.hashed_state, &second.sorted.hashed_state));
371 assert!(Arc::ptr_eq(&first.sorted.trie_updates, &second.sorted.trie_updates));
372 assert_changed_paths_ptr_eq(&first.changed_paths, &second.changed_paths);
373 }
374
375 #[test]
376 fn pending_wait_blocks_until_task_publishes() {
377 let (deferred, task) = empty_pending();
378
379 let handle = thread::spawn(move || deferred.get().clone());
380 thread::sleep(Duration::from_millis(20));
381 assert!(!handle.is_finished());
382
383 let published = task.compute_and_publish();
384 let result = handle.join().unwrap();
385
386 assert!(Arc::ptr_eq(&published.sorted.hashed_state, &result.sorted.hashed_state));
387 assert!(Arc::ptr_eq(&published.sorted.trie_updates, &result.sorted.trie_updates));
388 assert_changed_paths_ptr_eq(&published.changed_paths, &result.changed_paths);
389 }
390
391 #[test]
392 fn concurrent_waits_share_published_result() {
393 let (deferred, task) = empty_pending();
394 let deferred2 = deferred.clone();
395
396 let handle = thread::spawn(move || deferred2.get().clone());
397 let published = task.compute_and_publish();
398 let result1 = deferred.get().clone();
399 let result2 = handle.join().unwrap();
400
401 assert!(Arc::ptr_eq(&published.sorted.hashed_state, &result1.sorted.hashed_state));
402 assert!(Arc::ptr_eq(&published.sorted.trie_updates, &result1.sorted.trie_updates));
403 assert_changed_paths_ptr_eq(&published.changed_paths, &result1.changed_paths);
404 assert!(Arc::ptr_eq(&result1.sorted.hashed_state, &result2.sorted.hashed_state));
405 assert!(Arc::ptr_eq(&result1.sorted.trie_updates, &result2.sorted.trie_updates));
406 assert_changed_paths_ptr_eq(&result1.changed_paths, &result2.changed_paths);
407 }
408
409 #[test]
410 fn sorts_non_empty_inputs() {
411 let hashed_address = B256::with_last_byte(1);
412 let hashed_slot = B256::with_last_byte(2);
413 let hashed_state = HashedPostState::default()
414 .with_accounts([(hashed_address, Some(Account::default()))])
415 .with_storages([(
416 hashed_address,
417 HashedStorage::from_iter(false, [(hashed_slot, U256::from(1))]),
418 )]);
419
420 let (deferred, task) =
421 LazyTrieData::pending(Arc::new(hashed_state), Arc::new(TrieUpdates::default()), None);
422 let _ = task.compute_and_publish();
423 let result = deferred.get().clone();
424
425 assert_eq!(result.sorted.hashed_state.total_len(), 2);
426 assert_eq!(result.sorted.trie_updates.total_len(), 0);
427 }
428
429 #[test]
430 fn wait_does_not_block_after_first_compute() {
431 let mut accounts = B256Map::default();
432 for i in 0..100 {
433 accounts.insert(B256::with_last_byte(i), Some(Account::default()));
434 }
435 let (deferred, task) = LazyTrieData::pending(
436 Arc::new(HashedPostState { accounts, storages: Default::default() }),
437 Arc::new(TrieUpdates::default()),
438 None,
439 );
440
441 let _ = task.compute_and_publish();
442 let _ = deferred.get().clone();
443 let start = Instant::now();
444 let _ = deferred.get().clone();
445
446 assert!(start.elapsed() < Duration::from_millis(10));
447 }
448}