1#[cfg(feature = "trie-debug")]
2use crate::debug_recorder::TrieDebugRecorder;
3use crate::{
4 lfu::BucketedLfu, traits::SparseTrie as SparseTrieTrait, ArenaParallelSparseTrie,
5 RevealableSparseTrie,
6};
7use alloc::vec::Vec;
8use alloy_primitives::{map::B256Map, B256};
9use either::Either;
10use reth_execution_errors::{SparseStateTrieResult, SparseTrieErrorKind};
11use reth_trie_common::{
12 updates::{StorageTrieUpdates, TrieUpdates},
13 DecodedMultiProof, MultiProof, Nibbles, ProofTrieNodeV2,
14};
15#[cfg(feature = "std")]
16use tracing::debug;
17use tracing::instrument;
18
19#[derive(Debug, Default)]
24pub struct DeferredDrops {
25 pub proof_nodes_bufs: Vec<Vec<ProofTrieNodeV2>>,
27}
28
29#[derive(Debug)]
30pub struct SparseStateTrie<
32 A = ArenaParallelSparseTrie, S = ArenaParallelSparseTrie, > {
35 state: RevealableSparseTrie<A>,
37 storage: StorageTries<S>,
39 retain_updates: bool,
41 deferred_drops: DeferredDrops,
43 hot_slots_lfu: BucketedLfu<HotSlotKey>,
45 hot_accounts_lfu: BucketedLfu<B256>,
47 #[cfg(feature = "metrics")]
49 metrics: crate::metrics::SparseStateTrieMetrics,
50}
51
52impl<A, S> Default for SparseStateTrie<A, S>
53where
54 A: Default,
55 S: Default,
56{
57 fn default() -> Self {
58 Self {
59 state: Default::default(),
60 storage: Default::default(),
61 retain_updates: false,
62 deferred_drops: DeferredDrops::default(),
63 hot_slots_lfu: BucketedLfu::default(),
64 hot_accounts_lfu: BucketedLfu::default(),
65 #[cfg(feature = "metrics")]
66 metrics: Default::default(),
67 }
68 }
69}
70
71#[cfg(test)]
72impl SparseStateTrie {
73 pub fn from_state(state: RevealableSparseTrie) -> Self {
75 Self { state, ..Default::default() }
76 }
77}
78
79impl<A, S> SparseStateTrie<A, S> {
80 pub const fn set_updates(&mut self, retain_updates: bool) {
82 self.retain_updates = retain_updates;
83 }
84
85 pub const fn with_updates(mut self, retain_updates: bool) -> Self {
87 self.set_updates(retain_updates);
88 self
89 }
90
91 pub fn set_hot_cache_capacities(&mut self, max_hot_slots: usize, max_hot_accounts: usize) {
96 self.hot_slots_lfu.decay_and_evict(max_hot_slots);
97 self.hot_accounts_lfu.decay_and_evict(max_hot_accounts);
98 }
99
100 pub fn with_hot_cache_capacities(
102 mut self,
103 max_hot_slots: usize,
104 max_hot_accounts: usize,
105 ) -> Self {
106 self.set_hot_cache_capacities(max_hot_slots, max_hot_accounts);
107 self
108 }
109
110 pub fn set_accounts_trie(&mut self, trie: RevealableSparseTrie<A>) {
112 self.state = trie;
113 }
114
115 pub fn with_accounts_trie(mut self, trie: RevealableSparseTrie<A>) -> Self {
117 self.set_accounts_trie(trie);
118 self
119 }
120
121 pub fn set_default_storage_trie(&mut self, trie: RevealableSparseTrie<S>) {
124 self.storage.default_trie = trie;
125 }
126
127 pub fn with_default_storage_trie(mut self, trie: RevealableSparseTrie<S>) -> Self {
130 self.set_default_storage_trie(trie);
131 self
132 }
133
134 pub fn take_deferred_drops(&mut self) -> DeferredDrops {
139 core::mem::take(&mut self.deferred_drops)
140 }
141}
142
143impl SparseStateTrie {
144 pub fn new() -> Self {
146 Self::default()
147 }
148}
149
150impl<A: SparseTrieTrait, S: SparseTrieTrait> SparseStateTrie<A, S> {
151 #[cfg(feature = "trie-debug")]
156 pub fn take_debug_recorders(&mut self) -> alloc::vec::Vec<(Option<B256>, TrieDebugRecorder)> {
157 let mut recorders = alloc::vec::Vec::new();
158 if let Some(trie) = self.state.as_revealed_mut() {
159 recorders.push((None, trie.take_debug_recorder()));
160 }
161 for (address, trie) in &mut self.storage.tries {
162 if let Some(trie) = trie.as_revealed_mut() {
163 recorders.push((Some(*address), trie.take_debug_recorder()));
164 }
165 }
166 recorders
167 }
168}
169
170impl<A, S> SparseStateTrie<A, S>
171where
172 A: SparseTrieTrait + Default,
173 S: SparseTrieTrait + Default + Clone,
174{
175 pub const fn trie_mut(&mut self) -> &mut RevealableSparseTrie<A> {
177 &mut self.state
178 }
179
180 pub fn is_account_revealed(&self, account: B256) -> bool {
182 let path = Nibbles::unpack(account);
183 let trie = match self.state_trie_ref() {
184 Some(t) => t,
185 None => return false,
186 };
187
188 trie.find_leaf(&path, None).is_ok()
189 }
190
191 pub fn check_valid_storage_witness(&self, address: B256, slot: B256) -> bool {
193 let path = Nibbles::unpack(slot);
194 let trie = match self.storage_trie_ref(&address) {
195 Some(t) => t,
196 None => return false,
197 };
198
199 trie.find_leaf(&path, None).is_ok()
200 }
201
202 #[inline]
204 pub fn record_slot_touch(&mut self, account: B256, slot: B256) {
205 self.hot_slots_lfu.touch(HotSlotKey { address: account, slot });
206 }
207
208 #[inline]
210 pub fn record_account_touch(&mut self, account: B256) {
211 self.hot_accounts_lfu.touch(account);
212 }
213
214 pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
216 self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
217 }
218
219 pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
221 self.storage.tries.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
222 }
223
224 pub const fn state_trie_ref(&self) -> Option<&A> {
226 self.state.as_revealed_ref()
227 }
228
229 pub fn storage_trie_ref(&self, address: &B256) -> Option<&S> {
231 self.storage.tries.get(address).and_then(|e| e.as_revealed_ref())
232 }
233
234 pub fn storage_trie_mut(&mut self, address: &B256) -> Option<&mut S> {
236 self.storage.tries.get_mut(address).and_then(|e| e.as_revealed_mut())
237 }
238
239 pub const fn storage_tries_mut(&mut self) -> &mut B256Map<RevealableSparseTrie<S>> {
241 &mut self.storage.tries
242 }
243
244 pub fn take_storage_trie(&mut self, address: &B256) -> Option<RevealableSparseTrie<S>> {
246 self.storage.tries.remove(address)
247 }
248
249 pub fn take_or_create_storage_trie(&mut self, address: &B256) -> RevealableSparseTrie<S> {
251 self.storage.tries.remove(address).unwrap_or_else(|| {
252 self.storage.cleared_tries.pop().unwrap_or_else(|| self.storage.default_trie.clone())
253 })
254 }
255
256 pub fn insert_storage_trie(&mut self, address: B256, storage_trie: RevealableSparseTrie<S>) {
258 self.storage.tries.insert(address, storage_trie);
259 }
260
261 pub fn get_or_create_storage_trie_mut(
263 &mut self,
264 address: B256,
265 ) -> &mut RevealableSparseTrie<S> {
266 self.storage.get_or_create_trie_mut(address)
267 }
268
269 pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
272 let decoded_multiproof = multiproof.try_into()?;
274
275 self.reveal_decoded_multiproof(decoded_multiproof)
277 }
278
279 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
282 pub fn reveal_decoded_multiproof(
283 &mut self,
284 multiproof: DecodedMultiProof,
285 ) -> SparseStateTrieResult<()> {
286 self.reveal_decoded_multiproof_v2(multiproof.into())
287 }
288
289 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
294 pub fn reveal_decoded_multiproof_v2(
295 &mut self,
296 multiproof: reth_trie_common::DecodedMultiProofV2,
297 ) -> SparseStateTrieResult<()> {
298 let reth_trie_common::DecodedMultiProofV2 { account_proofs, mut storage_proofs, .. } =
299 multiproof;
300
301 let mut targets = Vec::with_capacity(storage_proofs.len() + 1);
304
305 if !account_proofs.is_empty() {
306 #[cfg(feature = "metrics")]
307 self.metrics.increment_total_account_nodes(account_proofs.len() as u64);
308 targets.push((Either::Left(&mut self.state), account_proofs));
309 }
310
311 for &account in storage_proofs.keys() {
313 let _ = self.storage.get_or_create_trie_mut(account);
314 }
315
316 for (account, trie) in &mut self.storage.tries {
317 if let Some(nodes) = storage_proofs.remove(account) {
318 #[cfg(feature = "metrics")]
319 self.metrics.increment_total_storage_nodes(nodes.len() as u64);
320 targets.push((Either::Right(trie), nodes));
321 }
322 }
323
324 let retain_updates = self.retain_updates;
325
326 #[cfg(not(feature = "std"))]
327 let results: Vec<_> = targets
328 .into_iter()
329 .map(|(target, mut nodes)| {
330 let result = match target {
331 Either::Left(trie) => trie.reveal_v2_proof_nodes(&mut nodes, retain_updates),
332 Either::Right(trie) => trie.reveal_v2_proof_nodes(&mut nodes, retain_updates),
333 };
334 (result, nodes)
335 })
336 .collect();
337
338 #[cfg(feature = "std")]
339 let results: Vec<_> = {
340 use rayon::iter::ParallelIterator;
341 use reth_primitives_traits::ParallelBridgeBuffered;
342
343 targets
344 .into_iter()
345 .par_bridge_buffered()
346 .map(|(target, mut nodes)| {
347 let result = match target {
348 Either::Left(trie) => {
349 trie.reveal_v2_proof_nodes(&mut nodes, retain_updates)
350 }
351 Either::Right(trie) => {
352 trie.reveal_v2_proof_nodes(&mut nodes, retain_updates)
353 }
354 };
355 (result, nodes)
356 })
357 .collect()
358 };
359
360 let mut any_err = Ok(());
362 for (result, nodes) in results {
363 if result.is_err() && any_err.is_ok() {
364 any_err = result.map_err(Into::into);
365 }
366 self.deferred_drops.proof_nodes_bufs.push(nodes);
367 }
368
369 any_err
370 }
371
372 pub fn wipe_storage(&mut self, address: B256) -> SparseStateTrieResult<()> {
374 if let Some(trie) = self.storage.tries.get_mut(&address) {
375 trie.wipe()?;
376 }
377 Ok(())
378 }
379
380 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
384 pub fn calculate_subtries(&mut self) {
385 if let RevealableSparseTrie::Revealed(trie) = &mut self.state {
386 trie.update_subtrie_hashes();
387 }
388 }
389
390 pub fn storage_root(&mut self, account: &B256) -> Option<B256> {
392 self.storage.tries.get_mut(account).and_then(|trie| trie.root())
393 }
394
395 fn revealed_trie_mut(&mut self) -> SparseStateTrieResult<&mut A> {
397 self.state.as_revealed_mut().ok_or_else(|| SparseTrieErrorKind::Blind.into())
398 }
399
400 pub fn root(&mut self) -> SparseStateTrieResult<B256> {
402 #[cfg(feature = "metrics")]
404 self.metrics.record();
405
406 Ok(self.revealed_trie_mut()?.root())
407 }
408
409 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
413 pub fn root_with_updates(&mut self) -> SparseStateTrieResult<(B256, TrieUpdates)> {
414 #[cfg(feature = "metrics")]
416 self.metrics.record();
417
418 let storage_tries = self.storage_trie_updates();
419 let revealed = self.revealed_trie_mut()?;
420
421 let (root, updates) = (revealed.root(), revealed.take_updates());
422 let updates = TrieUpdates {
423 account_nodes: updates.updated_nodes,
424 removed_nodes: updates.removed_nodes,
425 storage_tries,
426 };
427 Ok((root, updates))
428 }
429
430 pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdates> {
434 self.storage
435 .tries
436 .iter_mut()
437 .map(|(address, trie)| {
438 let trie = trie.as_revealed_mut().unwrap();
439 let updates = trie.take_updates();
440 let updates = StorageTrieUpdates {
441 is_deleted: updates.wiped,
442 storage_nodes: updates.updated_nodes,
443 removed_nodes: updates.removed_nodes,
444 };
445 (*address, updates)
446 })
447 .filter(|(_, updates)| !updates.is_empty())
448 .collect()
449 }
450
451 pub fn take_trie_updates(&mut self) -> Option<TrieUpdates> {
455 let storage_tries = self.storage_trie_updates();
456 self.state.as_revealed_mut().map(|state| {
457 let updates = state.take_updates();
458 TrieUpdates {
459 account_nodes: updates.updated_nodes,
460 removed_nodes: updates.removed_nodes,
461 storage_tries,
462 }
463 })
464 }
465}
466
467impl<A, S> SparseStateTrie<A, S>
468where
469 A: SparseTrieTrait + Default,
470 S: SparseTrieTrait + Default + Clone,
471{
472 pub fn clear(&mut self) {
477 self.state.clear();
478 self.storage.clear();
479 }
480
481 pub fn memory_size(&self) -> usize {
486 let mut size = core::mem::size_of::<Self>();
487
488 size += match &self.state {
489 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
490 t.memory_size()
491 }
492 RevealableSparseTrie::Blind(None) => 0,
493 };
494
495 for trie in self.storage.tries.values() {
496 size += match trie {
497 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
498 t.memory_size()
499 }
500 RevealableSparseTrie::Blind(None) => 0,
501 };
502 }
503 for trie in &self.storage.cleared_tries {
504 size += match trie {
505 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
506 t.memory_size()
507 }
508 RevealableSparseTrie::Blind(None) => 0,
509 };
510 }
511
512 size
513 }
514
515 pub fn retained_storage_tries_count(&self) -> usize {
517 self.storage.tries.len() + self.storage.cleared_tries.len()
518 }
519
520 #[cfg(feature = "std")]
535 #[instrument(
536 level = "debug",
537 name = "SparseStateTrie::prune",
538 target = "trie::sparse",
539 skip_all,
540 fields(%max_hot_slots, %max_hot_accounts)
541 )]
542 pub fn prune(&mut self, max_hot_slots: usize, max_hot_accounts: usize) {
543 self.hot_slots_lfu.decay_and_evict(max_hot_slots);
544 self.hot_accounts_lfu.decay_and_evict(max_hot_accounts);
545 let retained = self.hot_slots_lfu.retained_slots_by_address();
546
547 let retained_account_paths: Vec<Nibbles> =
548 self.hot_accounts_lfu.keys().map(|k| Nibbles::unpack(*k)).collect();
549
550 let retained_accounts = retained_account_paths.len();
551 let retained_storage_tries = retained.len();
552 let total_storage_tries_before = self.storage.tries.len();
553
554 let (account_nodes_pruned, storage_tries_evicted) = rayon::join(
556 || {
557 self.state
558 .as_revealed_mut()
559 .map(|trie| trie.prune(&retained_account_paths))
560 .unwrap_or(0)
561 },
562 || self.storage.prune_by_retained_slots(retained),
563 );
564
565 debug!(
566 target: "trie::sparse",
567 retained_accounts,
568 retained_storage_tries,
569 account_nodes_pruned,
570 storage_tries_evicted,
571 storage_tries_after = total_storage_tries_before - storage_tries_evicted,
572 "SparseStateTrie::prune completed"
573 );
574 }
575}
576
577#[derive(Debug, Default)]
580struct StorageTries<S = ArenaParallelSparseTrie> {
581 tries: B256Map<RevealableSparseTrie<S>>,
583 cleared_tries: Vec<RevealableSparseTrie<S>>,
585 default_trie: RevealableSparseTrie<S>,
587}
588
589#[cfg(feature = "std")]
590impl<S: SparseTrieTrait> StorageTries<S> {
591 fn prune_by_retained_slots(&mut self, retained_slots: B256Map<Vec<Nibbles>>) -> usize {
596 {
598 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
599 self.tries.par_iter_mut().for_each(|(address, trie)| {
600 if let Some(slots) = retained_slots.get(address) {
601 if let Some(t) = trie.as_revealed_mut() {
602 t.prune(slots);
603 }
604 } else {
605 trie.clear();
606 }
607 });
608 }
609
610 let addresses_to_evict: Vec<B256> = self
612 .tries
613 .keys()
614 .filter(|address| !retained_slots.contains_key(*address))
615 .copied()
616 .collect();
617
618 let evicted = addresses_to_evict.len();
619 self.cleared_tries.reserve(evicted);
620 for address in &addresses_to_evict {
621 if let Some(trie) = self.tries.remove(address) {
622 self.cleared_tries.push(trie);
623 }
624 }
625
626 evicted
627 }
628}
629
630impl<S: SparseTrieTrait> StorageTries<S> {
631 fn clear(&mut self) {
634 self.cleared_tries.extend(self.tries.drain().map(|(_, mut trie)| {
635 trie.clear();
636 trie
637 }));
638 }
639}
640
641impl<S: SparseTrieTrait + Clone> StorageTries<S> {
642 fn get_or_create_trie_mut(&mut self, address: B256) -> &mut RevealableSparseTrie<S> {
644 self.tries.entry(address).or_insert_with(|| {
645 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
646 })
647 }
648}
649
650#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
652struct HotSlotKey {
653 address: B256,
654 slot: B256,
655}
656
657#[cfg(feature = "std")]
659impl BucketedLfu<HotSlotKey> {
660 fn retained_slots_by_address(&self) -> B256Map<Vec<Nibbles>> {
662 let mut grouped =
663 B256Map::<Vec<Nibbles>>::with_capacity_and_hasher(self.len(), Default::default());
664 for key in self.keys() {
665 grouped.entry(key.address).or_default().push(Nibbles::unpack(key.slot));
666 }
667
668 for slots in grouped.values_mut() {
669 slots.sort_unstable();
670 slots.dedup();
671 }
672
673 grouped
674 }
675}
676
677#[cfg(test)]
678mod tests {
679 use super::*;
680 use crate::{ArenaParallelSparseTrie, LeafLookup, LeafUpdate};
681 use alloy_primitives::{
682 b256,
683 map::{HashMap, HashSet},
684 U256,
685 };
686 use arbitrary::Arbitrary;
687 use rand::{rngs::StdRng, Rng, SeedableRng};
688 use reth_execution_errors::{SparseStateTrieErrorKind, SparseTrieErrorKind};
689 use reth_primitives_traits::Account;
690 use reth_trie::{updates::StorageTrieUpdates, HashBuilder, MultiProof, EMPTY_ROOT_HASH};
691 use reth_trie_common::{
692 proof::{ProofNodes, ProofRetainer},
693 BranchNodeMasks, BranchNodeMasksMap, BranchNodeV2, LeafNode, RlpNode, StorageMultiProof,
694 TrieAccount, TrieMask, TrieNodeV2,
695 };
696
697 fn leaf_key(suffix: impl AsRef<[u8]>, total_len: usize) -> Nibbles {
699 let suffix = suffix.as_ref();
700 let mut nibbles = Nibbles::from_nibbles(suffix);
701 nibbles.extend(&Nibbles::from_nibbles_unchecked(vec![0; total_len - suffix.len()]));
702 nibbles
703 }
704
705 fn apply_account_update(sparse: &mut SparseStateTrie, address: B256, update: LeafUpdate) {
706 let mut updates = B256Map::from_iter([(address, update)]);
707 sparse.trie_mut().update_leaves(&mut updates, |_, _| {}).unwrap();
708 assert!(updates.is_empty());
709 }
710
711 #[test]
712 fn reveal_account_path_twice() {
713 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
714
715 let full_path_0 = leaf_key([0x0], 64);
717 let _full_path_1 = leaf_key([0x1], 64);
718
719 let leaf_value = alloy_rlp::encode(TrieAccount::default());
720 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
722 leaf_key([], 63),
723 leaf_value.clone(),
724 )));
725 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
726 leaf_key([], 63),
727 leaf_value.clone(),
728 )));
729
730 let multiproof = MultiProof {
731 account_subtree: ProofNodes::from_iter([
732 (
733 Nibbles::default(),
734 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
735 key: Nibbles::default(),
736 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
737 state_mask: TrieMask::new(0b11),
738 branch_rlp_node: None,
739 }))
740 .into(),
741 ),
742 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
743 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
744 ]),
745 ..Default::default()
746 };
747
748 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
750 assert!(matches!(
751 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
752 Ok(LeafLookup::Exists)
753 ));
754 assert_eq!(
755 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
756 Some(&leaf_value)
757 );
758
759 apply_account_update(&mut sparse, B256::ZERO, LeafUpdate::Changed(Vec::new()));
762 assert!(matches!(
763 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
764 Ok(LeafLookup::NonExistent)
765 ));
766 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
767 }
768
769 #[test]
770 fn reveal_storage_path_twice() {
771 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
772
773 let full_path_0 = leaf_key([0x0], 64);
775
776 let leaf_value = alloy_rlp::encode(TrieAccount::default());
777 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
778 leaf_key([], 63),
779 leaf_value.clone(),
780 )));
781 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
782 leaf_key([], 63),
783 leaf_value.clone(),
784 )));
785
786 let multiproof = MultiProof {
787 storages: HashMap::from_iter([(
788 B256::ZERO,
789 StorageMultiProof {
790 root: B256::ZERO,
791 subtree: ProofNodes::from_iter([
792 (
793 Nibbles::default(),
794 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
795 key: Nibbles::default(),
796 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
797 state_mask: TrieMask::new(0b11),
798 branch_rlp_node: None,
799 }))
800 .into(),
801 ),
802 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
803 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
804 ]),
805 branch_node_masks: Default::default(),
806 },
807 )]),
808 ..Default::default()
809 };
810
811 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
813 assert!(matches!(
814 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
815 Ok(LeafLookup::Exists)
816 ));
817 assert_eq!(
818 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
819 Some(&leaf_value)
820 );
821
822 let mut updates = B256Map::from_iter([(B256::ZERO, LeafUpdate::Changed(Vec::new()))]);
825 sparse
826 .storage_trie_mut(&B256::ZERO)
827 .unwrap()
828 .update_leaves(&mut updates, |_, _| {})
829 .unwrap();
830 assert!(updates.is_empty());
831 assert!(matches!(
832 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
833 Ok(LeafLookup::NonExistent)
834 ));
835 assert!(sparse
836 .storage_trie_ref(&B256::ZERO)
837 .unwrap()
838 .get_leaf_value(&full_path_0)
839 .is_none());
840 }
841
842 #[test]
843 fn seeded_hot_cache_capacities_preserve_first_cycle_touches() {
844 let account = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
845 let slot = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
846 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
847
848 sparse.set_hot_cache_capacities(1, 1);
849 sparse.record_account_touch(account);
850 sparse.record_slot_touch(account, slot);
851 sparse.prune(1, 1);
852
853 assert_eq!(sparse.hot_accounts_lfu.keys().copied().collect::<Vec<_>>(), vec![account]);
854 assert_eq!(
855 sparse.hot_slots_lfu.keys().copied().collect::<Vec<_>>(),
856 vec![HotSlotKey { address: account, slot }]
857 );
858 }
859
860 #[test]
861 fn reveal_v2_proof_nodes() {
862 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
863
864 let full_path_0 = leaf_key([0x0], 64);
866
867 let leaf_value = alloy_rlp::encode(TrieAccount::default());
868 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
869 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
870
871 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
872 key: Nibbles::default(),
873 stack: vec![
874 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
875 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
876 ],
877 state_mask: TrieMask::new(0b11),
878 branch_rlp_node: None,
879 });
880
881 let v2_proof_nodes = vec![
883 ProofTrieNodeV2 {
884 path: Nibbles::default(),
885 node: branch_node,
886 masks: Some(BranchNodeMasks {
887 hash_mask: TrieMask::default(),
888 tree_mask: TrieMask::default(),
889 }),
890 },
891 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
892 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
893 ];
894
895 sparse
897 .reveal_decoded_multiproof_v2(reth_trie_common::DecodedMultiProofV2 {
898 account_proofs: v2_proof_nodes,
899 ..Default::default()
900 })
901 .unwrap();
902
903 assert!(matches!(
905 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
906 Ok(LeafLookup::Exists)
907 ));
908 assert_eq!(
909 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
910 Some(&leaf_value)
911 );
912
913 apply_account_update(&mut sparse, B256::ZERO, LeafUpdate::Changed(Vec::new()));
915 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
916 }
917
918 #[test]
919 fn reveal_storage_v2_proof_nodes() {
920 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
921
922 let full_path_0 = leaf_key([0x0], 64);
924
925 let storage_value: Vec<u8> = alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec();
926 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
927 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
928
929 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
930 key: Nibbles::default(),
931 stack: vec![
932 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
933 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
934 ],
935 state_mask: TrieMask::new(0b11),
936 branch_rlp_node: None,
937 });
938
939 let v2_proof_nodes = vec![
940 ProofTrieNodeV2 { path: Nibbles::default(), node: branch_node, masks: None },
941 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
942 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
943 ];
944
945 sparse
947 .reveal_decoded_multiproof_v2(reth_trie_common::DecodedMultiProofV2 {
948 storage_proofs: B256Map::from_iter([(B256::ZERO, v2_proof_nodes)]),
949 ..Default::default()
950 })
951 .unwrap();
952
953 assert!(matches!(
955 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
956 Ok(LeafLookup::Exists)
957 ));
958 assert_eq!(
959 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
960 Some(&storage_value)
961 );
962
963 let mut updates = B256Map::from_iter([(B256::ZERO, LeafUpdate::Changed(Vec::new()))]);
965 sparse
966 .storage_trie_mut(&B256::ZERO)
967 .unwrap()
968 .update_leaves(&mut updates, |_, _| {})
969 .unwrap();
970 assert!(updates.is_empty());
971 assert!(sparse
972 .storage_trie_ref(&B256::ZERO)
973 .unwrap()
974 .get_leaf_value(&full_path_0)
975 .is_none());
976 }
977
978 #[test]
979 fn root_on_blind_trie_returns_blind_error() {
980 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
981
982 let err = sparse.root().unwrap_err();
983
984 assert!(matches!(err.kind(), SparseStateTrieErrorKind::Sparse(SparseTrieErrorKind::Blind)));
985 }
986
987 #[test]
988 fn take_trie_updates() {
989 reth_tracing::init_test_tracing();
990
991 let mut rng = StdRng::seed_from_u64(1);
993
994 let mut bytes = [0u8; 1024];
995 rng.fill(bytes.as_mut_slice());
996
997 let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
998 let slot_path_1 = Nibbles::unpack(slot_1);
999 let value_1 = U256::from(rng.random::<u64>());
1000 let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1001 let slot_path_2 = Nibbles::unpack(slot_2);
1002 let value_2 = U256::from(rng.random::<u64>());
1003 let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1004 let value_3 = U256::from(rng.random::<u64>());
1005
1006 let mut storage_hash_builder = HashBuilder::default()
1007 .with_proof_retainer(ProofRetainer::from_iter([slot_path_1, slot_path_2]));
1008 storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
1009 storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
1010
1011 let storage_root = storage_hash_builder.root();
1012 let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
1013 let storage_branch_node_masks = BranchNodeMasksMap::from_iter([
1014 (
1015 Nibbles::default(),
1016 BranchNodeMasks { hash_mask: TrieMask::new(0b010), tree_mask: TrieMask::default() },
1017 ),
1018 (
1019 Nibbles::from_nibbles([0x1]),
1020 BranchNodeMasks { hash_mask: TrieMask::new(0b11), tree_mask: TrieMask::default() },
1021 ),
1022 ]);
1023
1024 let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1025 let address_path_1 = Nibbles::unpack(address_1);
1026 let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1027 let mut trie_account_1 = account_1.into_trie_account(storage_root);
1028 let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1029 let address_path_2 = Nibbles::unpack(address_2);
1030 let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1031 let mut trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
1032
1033 let mut hash_builder = HashBuilder::default()
1034 .with_proof_retainer(ProofRetainer::from_iter([address_path_1, address_path_2]));
1035 hash_builder.add_leaf(address_path_1, &alloy_rlp::encode(trie_account_1));
1036 hash_builder.add_leaf(address_path_2, &alloy_rlp::encode(trie_account_2));
1037
1038 let root = hash_builder.root();
1039 let proof_nodes = hash_builder.take_proof_nodes();
1040 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default().with_updates(true);
1041 sparse
1042 .reveal_decoded_multiproof(
1043 MultiProof {
1044 account_subtree: proof_nodes,
1045 branch_node_masks: BranchNodeMasksMap::from_iter([(
1046 Nibbles::from_nibbles([0x1]),
1047 BranchNodeMasks {
1048 hash_mask: TrieMask::new(0b00),
1049 tree_mask: TrieMask::default(),
1050 },
1051 )]),
1052 storages: HashMap::from_iter([
1053 (
1054 address_1,
1055 StorageMultiProof {
1056 root,
1057 subtree: storage_proof_nodes.clone(),
1058 branch_node_masks: storage_branch_node_masks.clone(),
1059 },
1060 ),
1061 (
1062 address_2,
1063 StorageMultiProof {
1064 root,
1065 subtree: storage_proof_nodes,
1066 branch_node_masks: storage_branch_node_masks,
1067 },
1068 ),
1069 ]),
1070 }
1071 .try_into()
1072 .unwrap(),
1073 )
1074 .unwrap();
1075
1076 assert_eq!(sparse.root().unwrap(), root);
1077
1078 let address_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1079 let account_3 = Account { nonce: account_1.nonce + 1, ..account_1 };
1080 let trie_account_3 = account_3.into_trie_account(EMPTY_ROOT_HASH);
1081
1082 apply_account_update(
1083 &mut sparse,
1084 address_3,
1085 LeafUpdate::Changed(alloy_rlp::encode(trie_account_3)),
1086 );
1087
1088 let mut updates =
1089 B256Map::from_iter([(slot_3, LeafUpdate::Changed(alloy_rlp::encode(value_3)))]);
1090 sparse
1091 .storage_trie_mut(&address_1)
1092 .unwrap()
1093 .update_leaves(&mut updates, |_, _| {})
1094 .unwrap();
1095 assert!(updates.is_empty());
1096 trie_account_1.storage_root = sparse.storage_root(&address_1).unwrap();
1097 apply_account_update(
1098 &mut sparse,
1099 address_1,
1100 LeafUpdate::Changed(alloy_rlp::encode(trie_account_1)),
1101 );
1102
1103 sparse.wipe_storage(address_2).unwrap();
1104 trie_account_2.storage_root = sparse.storage_root(&address_2).unwrap();
1105 apply_account_update(
1106 &mut sparse,
1107 address_2,
1108 LeafUpdate::Changed(alloy_rlp::encode(trie_account_2)),
1109 );
1110
1111 sparse.root().unwrap();
1112
1113 let sparse_updates = sparse.take_trie_updates().unwrap();
1114 pretty_assertions::assert_eq!(
1116 sparse_updates,
1117 TrieUpdates {
1118 account_nodes: HashMap::default(),
1119 storage_tries: HashMap::from_iter([
1120 (
1121 b256!("0x1000000000000000000000000000000000000000000000000000000000000000"),
1122 StorageTrieUpdates {
1123 is_deleted: false,
1124 storage_nodes: HashMap::default(),
1125 removed_nodes: HashSet::from_iter([Nibbles::from_nibbles([0x1])])
1126 }
1127 ),
1128 (
1129 b256!("0x1100000000000000000000000000000000000000000000000000000000000000"),
1130 StorageTrieUpdates {
1131 is_deleted: true,
1132 storage_nodes: HashMap::default(),
1133 removed_nodes: HashSet::default()
1134 }
1135 )
1136 ]),
1137 removed_nodes: HashSet::default()
1138 }
1139 );
1140 }
1141}