1use crate::{
2 traits::SparseTrie as SparseTrieTrait, ArenaParallelSparseTrie, RevealableSparseTrie,
3 TrieNodeEpoch,
4};
5use alloc::vec::Vec;
6use alloy_primitives::{map::B256Map, B256};
7use either::Either;
8use reth_execution_errors::{SparseStateTrieResult, SparseTrieErrorKind};
9use reth_trie_common::{
10 updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted},
11 DecodedMultiProof, MultiProof, Nibbles, ProofTrieNodeV2,
12};
13use tracing::instrument;
14
15#[cfg(feature = "metrics")]
16use reth_primitives_traits::FastInstant as Instant;
17
18#[cfg(feature = "std")]
19use tracing::debug;
20
21#[derive(Debug, Default)]
26pub struct DeferredDrops {
27 pub proof_nodes_bufs: Vec<Vec<ProofTrieNodeV2>>,
29}
30
31#[derive(Debug)]
32pub struct SparseStateTrie<
34 A = ArenaParallelSparseTrie, S = ArenaParallelSparseTrie, > {
37 state: RevealableSparseTrie<A>,
39 storage: StorageTries<S>,
41 retain_updates: bool,
43 deferred_drops: DeferredDrops,
45 #[cfg(feature = "metrics")]
47 metrics: crate::metrics::SparseStateTrieMetrics,
48}
49
50impl<A, S> Default for SparseStateTrie<A, S>
51where
52 A: Default,
53 S: Default,
54{
55 fn default() -> Self {
56 Self {
57 state: Default::default(),
58 storage: Default::default(),
59 retain_updates: false,
60 deferred_drops: DeferredDrops::default(),
61 #[cfg(feature = "metrics")]
62 metrics: Default::default(),
63 }
64 }
65}
66
67#[cfg(test)]
68impl SparseStateTrie {
69 pub fn from_state(state: RevealableSparseTrie) -> Self {
71 Self { state, ..Default::default() }
72 }
73}
74
75impl<A, S> SparseStateTrie<A, S> {
76 pub const fn set_updates(&mut self, retain_updates: bool) {
78 self.retain_updates = retain_updates;
79 }
80
81 pub const fn with_updates(mut self, retain_updates: bool) -> Self {
83 self.set_updates(retain_updates);
84 self
85 }
86
87 pub const fn retains_updates(&self) -> bool {
89 self.retain_updates
90 }
91
92 pub fn set_accounts_trie(&mut self, trie: RevealableSparseTrie<A>) {
94 self.state = trie;
95 }
96
97 pub fn with_accounts_trie(mut self, trie: RevealableSparseTrie<A>) -> Self {
99 self.set_accounts_trie(trie);
100 self
101 }
102
103 pub fn set_default_storage_trie(&mut self, trie: RevealableSparseTrie<S>) {
106 self.storage.default_trie = trie;
107 }
108
109 pub fn with_default_storage_trie(mut self, trie: RevealableSparseTrie<S>) -> Self {
112 self.set_default_storage_trie(trie);
113 self
114 }
115
116 pub fn take_deferred_drops(&mut self) -> DeferredDrops {
121 core::mem::take(&mut self.deferred_drops)
122 }
123}
124
125impl SparseStateTrie {
126 pub fn new() -> Self {
128 Self::default()
129 }
130}
131
132impl<A, S> SparseStateTrie<A, S>
133where
134 A: SparseTrieTrait + Default,
135 S: SparseTrieTrait + Default + Clone,
136{
137 pub const fn trie_mut(&mut self) -> &mut RevealableSparseTrie<A> {
139 &mut self.state
140 }
141
142 pub fn is_account_revealed(&self, account: B256) -> bool {
144 let path = Nibbles::unpack(account);
145 let trie = match self.state_trie_ref() {
146 Some(t) => t,
147 None => return false,
148 };
149
150 trie.find_leaf(&path, None).is_ok()
151 }
152
153 pub fn check_valid_storage_witness(&self, address: B256, slot: B256) -> bool {
155 let path = Nibbles::unpack(slot);
156 let trie = match self.storage_trie_ref(&address) {
157 Some(t) => t,
158 None => return false,
159 };
160
161 trie.find_leaf(&path, None).is_ok()
162 }
163
164 pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
166 self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
167 }
168
169 pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
171 self.storage.tries.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
172 }
173
174 pub const fn state_trie_ref(&self) -> Option<&A> {
176 self.state.as_revealed_ref()
177 }
178
179 pub fn storage_trie_ref(&self, address: &B256) -> Option<&S> {
181 self.storage.tries.get(address).and_then(|e| e.as_revealed_ref())
182 }
183
184 pub fn storage_trie_mut(&mut self, address: &B256) -> Option<&mut S> {
186 self.storage.tries.get_mut(address).and_then(|e| e.as_revealed_mut())
187 }
188
189 pub const fn storage_tries_mut(&mut self) -> &mut B256Map<RevealableSparseTrie<S>> {
191 &mut self.storage.tries
192 }
193
194 pub fn take_storage_trie(&mut self, address: &B256) -> Option<RevealableSparseTrie<S>> {
196 self.storage.tries.remove(address)
197 }
198
199 pub fn take_or_create_storage_trie(&mut self, address: &B256) -> RevealableSparseTrie<S> {
201 self.storage.tries.remove(address).unwrap_or_else(|| {
202 self.storage.cleared_tries.pop().unwrap_or_else(|| self.storage.default_trie.clone())
203 })
204 }
205
206 pub fn insert_storage_trie(&mut self, address: B256, storage_trie: RevealableSparseTrie<S>) {
208 self.storage.tries.insert(address, storage_trie);
209 }
210
211 pub fn get_or_create_storage_trie_mut(
213 &mut self,
214 address: B256,
215 ) -> &mut RevealableSparseTrie<S> {
216 self.storage.get_or_create_trie_mut(address)
217 }
218
219 pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
222 let decoded_multiproof = multiproof.try_into()?;
224
225 self.reveal_decoded_multiproof(decoded_multiproof)
227 }
228
229 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
232 pub fn reveal_decoded_multiproof(
233 &mut self,
234 multiproof: DecodedMultiProof,
235 ) -> SparseStateTrieResult<()> {
236 self.reveal_decoded_multiproof_v2(multiproof.into())
237 }
238
239 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
244 pub fn reveal_decoded_multiproof_v2(
245 &mut self,
246 multiproof: reth_trie_common::DecodedMultiProofV2,
247 ) -> SparseStateTrieResult<()> {
248 let reth_trie_common::DecodedMultiProofV2 { account_proofs, mut storage_proofs, .. } =
249 multiproof;
250
251 let mut targets = Vec::with_capacity(storage_proofs.len() + 1);
254
255 if !account_proofs.is_empty() {
256 #[cfg(feature = "metrics")]
257 self.metrics.increment_total_account_nodes(account_proofs.len() as u64);
258 targets.push((None, Either::Left(&mut self.state), account_proofs));
259 }
260
261 for &account in storage_proofs.keys() {
263 let _ = self.storage.get_or_create_trie_mut(account);
264 }
265
266 for (account, trie) in &mut self.storage.tries {
267 if let Some(nodes) = storage_proofs.remove(account) {
268 #[cfg(feature = "metrics")]
269 self.metrics.increment_total_storage_nodes(nodes.len() as u64);
270 targets.push((Some(*account), Either::Right(trie), nodes));
271 }
272 }
273
274 let retain_updates = self.retain_updates;
275
276 #[cfg(not(feature = "std"))]
277 let results: Vec<_> = targets
278 .into_iter()
279 .map(|(_, target, mut nodes)| {
280 let result = match target {
281 Either::Left(trie) => trie.reveal_v2_proof_nodes(&mut nodes, retain_updates),
282 Either::Right(trie) => trie.reveal_v2_proof_nodes(&mut nodes, retain_updates),
283 };
284 (result, nodes)
285 })
286 .collect();
287
288 #[cfg(feature = "std")]
289 let results: Vec<_> = {
290 use rayon::iter::ParallelIterator;
291 use reth_primitives_traits::ParallelBridgeBuffered;
292
293 let parent_span = tracing::Span::current();
294 targets
295 .into_iter()
296 .par_bridge_buffered()
297 .map(|(hashed_address, target, mut nodes)| {
298 let _span = tracing::trace_span!(
299 target: "trie::sparse",
300 parent: &parent_span,
301 "reveal_v2_proof_nodes",
302 ?hashed_address,
303 )
304 .entered();
305
306 let result = match target {
307 Either::Left(trie) => {
308 trie.reveal_v2_proof_nodes(&mut nodes, retain_updates)
309 }
310 Either::Right(trie) => {
311 trie.reveal_v2_proof_nodes(&mut nodes, retain_updates)
312 }
313 };
314 (result, nodes)
315 })
316 .collect()
317 };
318
319 let mut any_err = Ok(());
321 for (result, nodes) in results {
322 if result.is_err() && any_err.is_ok() {
323 any_err = result.map_err(Into::into);
324 }
325 self.deferred_drops.proof_nodes_bufs.push(nodes);
326 }
327
328 any_err
329 }
330
331 pub fn reveal_account_proof_nodes(
336 &mut self,
337 mut nodes: Vec<ProofTrieNodeV2>,
338 ) -> SparseStateTrieResult<()> {
339 if nodes.is_empty() {
340 return Ok(())
341 }
342
343 #[cfg(feature = "metrics")]
344 self.metrics.increment_total_account_nodes(nodes.len() as u64);
345
346 let result = self.state.reveal_v2_proof_nodes(&mut nodes, self.retain_updates);
347 self.deferred_drops.proof_nodes_bufs.push(nodes);
348
349 Ok(result?)
350 }
351
352 pub const fn record_revealed_storage_nodes(&mut self, nodes: usize) {
355 #[cfg(feature = "metrics")]
356 self.metrics.increment_total_storage_nodes(nodes as u64);
357 #[cfg(not(feature = "metrics"))]
358 let _ = nodes;
359 }
360
361 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
365 pub fn calculate_subtries(&mut self, new_epoch: TrieNodeEpoch) {
366 if let RevealableSparseTrie::Revealed(trie) = &mut self.state {
367 trie.update_subtrie_hashes(new_epoch);
368 }
369 }
370
371 pub fn storage_root(&mut self, account: &B256, new_epoch: TrieNodeEpoch) -> Option<B256> {
373 self.storage.tries.get_mut(account).and_then(|trie| trie.root(new_epoch))
374 }
375
376 fn revealed_trie_mut(&mut self) -> SparseStateTrieResult<&mut A> {
378 self.state.as_revealed_mut().ok_or_else(|| SparseTrieErrorKind::Blind.into())
379 }
380
381 pub fn root(&mut self, new_epoch: TrieNodeEpoch) -> SparseStateTrieResult<B256> {
383 #[cfg(feature = "metrics")]
385 self.metrics.record();
386
387 Ok(self.revealed_trie_mut()?.root(new_epoch))
388 }
389
390 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
394 pub fn root_with_updates(
395 &mut self,
396 new_epoch: TrieNodeEpoch,
397 ) -> SparseStateTrieResult<(B256, TrieUpdatesSorted)> {
398 #[cfg(feature = "metrics")]
400 self.metrics.record();
401
402 let root = self.revealed_trie_mut()?.root(new_epoch);
403
404 #[cfg(feature = "metrics")]
405 let start = Instant::now();
406
407 let storage_tries = self.storage_trie_updates();
408 let updates = self.revealed_trie_mut()?.take_updates();
409 let updates = TrieUpdatesSorted::new(updates, storage_tries);
410
411 #[cfg(feature = "metrics")]
412 self.metrics.histograms.take_updates_duration_seconds.record(start.elapsed().as_secs_f64());
413
414 Ok((root, updates))
415 }
416
417 pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdatesSorted> {
421 self.storage
422 .tries
423 .iter_mut()
424 .map(|(address, trie)| {
425 let trie = trie.as_revealed_mut().unwrap();
426 let updates = trie.take_updates();
427 let updates = StorageTrieUpdatesSorted { storage_nodes: updates };
428 (*address, updates)
429 })
430 .filter(|(_, updates)| !updates.is_empty())
431 .collect()
432 }
433
434 pub fn take_trie_updates(&mut self) -> Option<TrieUpdatesSorted> {
438 let storage_tries = self.storage_trie_updates();
439 self.state.as_revealed_mut().map(|state| {
440 let updates = state.take_updates();
441 TrieUpdatesSorted::new(updates, storage_tries)
442 })
443 }
444}
445
446impl<A, S> SparseStateTrie<A, S>
447where
448 A: SparseTrieTrait + Default,
449 S: SparseTrieTrait + Default + Clone,
450{
451 pub fn clear(&mut self) {
456 self.state.clear();
457 self.storage.clear();
458 }
459
460 pub fn retained_storage_tries_count(&self) -> usize {
462 self.storage.tries.len() + self.storage.cleared_tries.len()
463 }
464
465 #[cfg(feature = "std")]
475 #[instrument(
476 level = "debug",
477 name = "SparseStateTrie::prune",
478 target = "trie::sparse",
479 skip_all
480 )]
481 pub fn prune(&mut self, prune_before: TrieNodeEpoch) {
482 let total_storage_tries_before = self.storage.tries.len();
483
484 let parent_span = tracing::Span::current();
485 let account_parent_span = parent_span.clone();
486
487 let (account_nodes_pruned, storage_tries_evicted) = rayon::join(
489 || {
490 let hashed_address = Option::<B256>::None;
491 let _span = tracing::trace_span!(
492 target: "trie::sparse",
493 parent: &account_parent_span,
494 "prune_trie",
495 ?hashed_address,
496 )
497 .entered();
498
499 self.state.as_revealed_mut().map(|trie| trie.prune(prune_before)).unwrap_or(0)
500 },
501 || self.storage.prune(prune_before, &parent_span),
502 );
503
504 debug!(
505 target: "trie::sparse",
506 prune_before = prune_before.get(),
507 account_nodes_pruned,
508 storage_tries_evicted,
509 storage_tries_after = total_storage_tries_before - storage_tries_evicted,
510 "SparseStateTrie::prune completed"
511 );
512 }
513}
514
515#[derive(Debug, Default)]
518struct StorageTries<S = ArenaParallelSparseTrie> {
519 tries: B256Map<RevealableSparseTrie<S>>,
521 cleared_tries: Vec<RevealableSparseTrie<S>>,
523 default_trie: RevealableSparseTrie<S>,
525}
526
527#[cfg(feature = "std")]
528impl<S: SparseTrieTrait> StorageTries<S> {
529 fn prune(&mut self, prune_before: TrieNodeEpoch, parent_span: &tracing::Span) -> usize {
531 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
532
533 let addresses_to_evict: Vec<B256> = self
534 .tries
535 .par_iter_mut()
536 .filter_map(|(address, trie)| {
537 let hashed_address = Some(*address);
538 let _span = tracing::trace_span!(
539 target: "trie::sparse",
540 parent: parent_span,
541 "prune_trie",
542 ?hashed_address,
543 )
544 .entered();
545
546 let evict = trie.as_revealed_mut().is_none_or(|revealed| {
547 let root_epoch =
549 revealed.root_epoch().expect("storage trie root must not be dirty");
550 if !root_epoch.should_prune(prune_before) {
551 revealed.prune(prune_before);
552 }
553 root_epoch.should_prune(prune_before)
554 });
555
556 evict.then(|| {
557 trie.clear();
558 *address
559 })
560 })
561 .collect();
562
563 let evicted = addresses_to_evict.len();
564 self.cleared_tries.reserve(evicted);
565 for address in addresses_to_evict {
566 if let Some(trie) = self.tries.remove(&address) {
567 self.cleared_tries.push(trie);
568 }
569 }
570
571 evicted
572 }
573}
574
575impl<S: SparseTrieTrait> StorageTries<S> {
576 fn clear(&mut self) {
579 self.cleared_tries.extend(self.tries.drain().map(|(_, mut trie)| {
580 trie.clear();
581 trie
582 }));
583 }
584}
585
586impl<S: SparseTrieTrait + Clone> StorageTries<S> {
587 fn get_or_create_trie_mut(&mut self, address: B256) -> &mut RevealableSparseTrie<S> {
589 self.tries.entry(address).or_insert_with(|| {
590 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
591 })
592 }
593}
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598 use crate::{ArenaParallelSparseTrie, LeafLookup, LeafUpdate};
599 use alloy_primitives::{b256, map::HashMap, U256};
600 use arbitrary::Arbitrary;
601 use rand::{rngs::StdRng, Rng, SeedableRng};
602 use reth_execution_errors::{SparseStateTrieErrorKind, SparseTrieErrorKind};
603 use reth_primitives_traits::Account;
604 use reth_trie::{HashBuilder, MultiProof, EMPTY_ROOT_HASH};
605 use reth_trie_common::{
606 proof::{ProofNodes, ProofRetainer},
607 BranchNodeMasks, BranchNodeMasksMap, BranchNodeV2, LeafNode, RlpNode, StorageMultiProof,
608 TrieAccount, TrieMask, TrieNodeV2,
609 };
610
611 const fn epoch(value: u64) -> TrieNodeEpoch {
612 TrieNodeEpoch::new(value)
613 }
614
615 fn leaf_key(suffix: impl AsRef<[u8]>, total_len: usize) -> Nibbles {
617 let suffix = suffix.as_ref();
618 let mut nibbles = Nibbles::from_nibbles(suffix);
619 nibbles.extend(&Nibbles::from_nibbles_unchecked(vec![0; total_len - suffix.len()]));
620 nibbles
621 }
622
623 fn apply_account_update(sparse: &mut SparseStateTrie, address: B256, update: LeafUpdate) {
624 let mut updates = B256Map::from_iter([(address, update)]);
625 sparse.trie_mut().update_leaves(&mut updates, |_, _| {}).unwrap();
626 assert!(updates.is_empty());
627 }
628
629 #[test]
630 fn reveal_account_path_twice() {
631 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
632
633 let full_path_0 = leaf_key([0x0], 64);
635 let _full_path_1 = leaf_key([0x1], 64);
636
637 let leaf_value = alloy_rlp::encode(TrieAccount::default());
638 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
640 leaf_key([], 63),
641 leaf_value.clone(),
642 )));
643 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
644 leaf_key([], 63),
645 leaf_value.clone(),
646 )));
647
648 let multiproof = MultiProof {
649 account_subtree: ProofNodes::from_iter([
650 (
651 Nibbles::default(),
652 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
653 key: Nibbles::default(),
654 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
655 state_mask: TrieMask::new(0b11),
656 branch_rlp_node: None,
657 }))
658 .into(),
659 ),
660 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
661 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
662 ]),
663 ..Default::default()
664 };
665
666 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
668 assert!(matches!(
669 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
670 Ok(LeafLookup::Exists)
671 ));
672 assert_eq!(
673 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
674 Some(&leaf_value)
675 );
676
677 apply_account_update(&mut sparse, B256::ZERO, LeafUpdate::Changed(Vec::new()));
680 assert!(matches!(
681 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
682 Ok(LeafLookup::NonExistent)
683 ));
684 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
685 }
686
687 #[test]
688 fn reveal_storage_path_twice() {
689 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
690
691 let full_path_0 = leaf_key([0x0], 64);
693
694 let leaf_value = alloy_rlp::encode(TrieAccount::default());
695 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
696 leaf_key([], 63),
697 leaf_value.clone(),
698 )));
699 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
700 leaf_key([], 63),
701 leaf_value.clone(),
702 )));
703
704 let multiproof = MultiProof {
705 storages: HashMap::from_iter([(
706 B256::ZERO,
707 StorageMultiProof {
708 root: B256::ZERO,
709 subtree: ProofNodes::from_iter([
710 (
711 Nibbles::default(),
712 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
713 key: Nibbles::default(),
714 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
715 state_mask: TrieMask::new(0b11),
716 branch_rlp_node: None,
717 }))
718 .into(),
719 ),
720 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
721 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
722 ]),
723 branch_node_masks: Default::default(),
724 },
725 )]),
726 ..Default::default()
727 };
728
729 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
731 assert!(matches!(
732 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
733 Ok(LeafLookup::Exists)
734 ));
735 assert_eq!(
736 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
737 Some(&leaf_value)
738 );
739
740 let mut updates = B256Map::from_iter([(B256::ZERO, LeafUpdate::Changed(Vec::new()))]);
743 sparse
744 .storage_trie_mut(&B256::ZERO)
745 .unwrap()
746 .update_leaves(&mut updates, |_, _| {})
747 .unwrap();
748 assert!(updates.is_empty());
749 assert!(matches!(
750 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
751 Ok(LeafLookup::NonExistent)
752 ));
753 assert!(sparse
754 .storage_trie_ref(&B256::ZERO)
755 .unwrap()
756 .get_leaf_value(&full_path_0)
757 .is_none());
758 }
759
760 #[test]
761 fn prune_uses_epochs_for_account_and_storage_tries() {
762 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
763
764 let account = B256::ZERO;
765 let old_account =
766 b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
767 let prewarmed_account =
768 b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
769 let slot = B256::ZERO;
770 let account_path = leaf_key([0x0], 64);
771 let old_account_path = leaf_key([0x1], 64);
772 let storage_path = leaf_key([0x0], 64);
773
774 let leaf_value = alloy_rlp::encode(TrieAccount::default());
775 let leaf_0 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
776 leaf_key([], 63),
777 leaf_value.clone(),
778 )));
779 let leaf_1 =
780 alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value)));
781
782 let subtree = || {
783 ProofNodes::from_iter([
784 (
785 Nibbles::default(),
786 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
787 key: Nibbles::default(),
788 stack: vec![RlpNode::from_rlp(&leaf_0), RlpNode::from_rlp(&leaf_1)],
789 state_mask: TrieMask::new(0b11),
790 branch_rlp_node: None,
791 }))
792 .into(),
793 ),
794 (Nibbles::from_nibbles([0x0]), leaf_0.clone().into()),
795 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
796 ])
797 };
798
799 let multiproof = MultiProof {
800 account_subtree: subtree(),
801 storages: HashMap::from_iter([
802 (
803 account,
804 StorageMultiProof {
805 root: B256::ZERO,
806 subtree: subtree(),
807 branch_node_masks: Default::default(),
808 },
809 ),
810 (
811 old_account,
812 StorageMultiProof {
813 root: B256::ZERO,
814 subtree: subtree(),
815 branch_node_masks: Default::default(),
816 },
817 ),
818 (
819 prewarmed_account,
820 StorageMultiProof {
821 root: B256::ZERO,
822 subtree: subtree(),
823 branch_node_masks: Default::default(),
824 },
825 ),
826 ]),
827 ..Default::default()
828 };
829
830 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
831
832 sparse.storage_root(&account, epoch(0)).unwrap();
833 sparse.storage_root(&old_account, epoch(0)).unwrap();
834 sparse.root(epoch(0)).unwrap();
835 assert!(!sparse.storage_trie_ref(&prewarmed_account).unwrap().is_root_cached());
836
837 let mut storage_updates = B256Map::from_iter([(
838 slot,
839 LeafUpdate::Changed(alloy_rlp::encode_fixed_size(&U256::from(2)).to_vec()),
840 )]);
841 sparse
842 .storage_trie_mut(&account)
843 .unwrap()
844 .update_leaves(&mut storage_updates, |_, _| {
845 panic!("fully revealed storage trie must not request proofs")
846 })
847 .unwrap();
848 assert!(storage_updates.is_empty());
849
850 let trie_account = TrieAccount {
851 storage_root: sparse.storage_root(&account, epoch(10)).unwrap(),
852 ..Default::default()
853 };
854 apply_account_update(
855 &mut sparse,
856 account,
857 LeafUpdate::Changed(alloy_rlp::encode(trie_account)),
858 );
859 let root_before = sparse.root(epoch(10)).unwrap();
860 sparse.prune(epoch(10));
861
862 assert!(matches!(
863 sparse.state_trie_ref().unwrap().find_leaf(&account_path, None),
864 Ok(LeafLookup::Exists)
865 ));
866 assert!(matches!(
867 sparse.storage_trie_ref(&account).unwrap().find_leaf(&storage_path, None),
868 Ok(LeafLookup::Exists)
869 ));
870 assert!(matches!(
871 sparse.state_trie_ref().unwrap().find_leaf(&old_account_path, None),
872 Err(crate::LeafLookupError::BlindedNode { .. })
873 ));
874 assert!(sparse.storage_trie_ref(&old_account).is_none());
875 assert!(sparse.storage_trie_ref(&prewarmed_account).is_none());
876 assert_eq!(sparse.root(epoch(10)).unwrap(), root_before);
877
878 sparse.prune(epoch(11));
881 assert!(sparse.storage_trie_ref(&account).is_none());
882 assert_eq!(sparse.root(epoch(11)).unwrap(), root_before);
883 }
884
885 #[test]
886 fn reveal_v2_proof_nodes() {
887 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
888
889 let full_path_0 = leaf_key([0x0], 64);
891
892 let leaf_value = alloy_rlp::encode(TrieAccount::default());
893 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
894 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
895
896 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
897 key: Nibbles::default(),
898 stack: vec![
899 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
900 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
901 ],
902 state_mask: TrieMask::new(0b11),
903 branch_rlp_node: None,
904 });
905
906 let v2_proof_nodes = vec![
908 ProofTrieNodeV2 {
909 path: Nibbles::default(),
910 node: branch_node,
911 masks: Some(BranchNodeMasks {
912 hash_mask: TrieMask::default(),
913 tree_mask: TrieMask::default(),
914 }),
915 },
916 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
917 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
918 ];
919
920 sparse
922 .reveal_decoded_multiproof_v2(reth_trie_common::DecodedMultiProofV2 {
923 account_proofs: v2_proof_nodes,
924 ..Default::default()
925 })
926 .unwrap();
927
928 assert!(matches!(
930 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
931 Ok(LeafLookup::Exists)
932 ));
933 assert_eq!(
934 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
935 Some(&leaf_value)
936 );
937
938 apply_account_update(&mut sparse, B256::ZERO, LeafUpdate::Changed(Vec::new()));
940 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
941 }
942
943 #[test]
944 fn reveal_storage_v2_proof_nodes() {
945 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
946
947 let full_path_0 = leaf_key([0x0], 64);
949
950 let storage_value: Vec<u8> = alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec();
951 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
952 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
953
954 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
955 key: Nibbles::default(),
956 stack: vec![
957 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
958 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
959 ],
960 state_mask: TrieMask::new(0b11),
961 branch_rlp_node: None,
962 });
963
964 let v2_proof_nodes = vec![
965 ProofTrieNodeV2 { path: Nibbles::default(), node: branch_node, masks: None },
966 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
967 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
968 ];
969
970 sparse
972 .reveal_decoded_multiproof_v2(reth_trie_common::DecodedMultiProofV2 {
973 storage_proofs: B256Map::from_iter([(B256::ZERO, v2_proof_nodes)]),
974 ..Default::default()
975 })
976 .unwrap();
977
978 assert!(matches!(
980 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
981 Ok(LeafLookup::Exists)
982 ));
983 assert_eq!(
984 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
985 Some(&storage_value)
986 );
987
988 let mut updates = B256Map::from_iter([(B256::ZERO, LeafUpdate::Changed(Vec::new()))]);
990 sparse
991 .storage_trie_mut(&B256::ZERO)
992 .unwrap()
993 .update_leaves(&mut updates, |_, _| {})
994 .unwrap();
995 assert!(updates.is_empty());
996 assert!(sparse
997 .storage_trie_ref(&B256::ZERO)
998 .unwrap()
999 .get_leaf_value(&full_path_0)
1000 .is_none());
1001 }
1002
1003 #[test]
1004 fn root_on_blind_trie_returns_blind_error() {
1005 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default();
1006
1007 let err = sparse.root(epoch(0)).unwrap_err();
1008
1009 assert!(matches!(err.kind(), SparseStateTrieErrorKind::Sparse(SparseTrieErrorKind::Blind)));
1010 }
1011
1012 #[test]
1013 #[allow(clippy::clone_on_copy)]
1014 fn take_trie_updates() {
1015 reth_tracing::init_test_tracing();
1016
1017 let mut rng = StdRng::seed_from_u64(1);
1019
1020 let mut bytes = [0u8; 1024];
1021 rng.fill(bytes.as_mut_slice());
1022
1023 let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1024 let slot_path_1 = Nibbles::unpack(slot_1);
1025 let value_1 = U256::from(rng.random::<u64>());
1026 let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1027 let slot_path_2 = Nibbles::unpack(slot_2);
1028 let value_2 = U256::from(rng.random::<u64>());
1029 let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1030 let value_3 = U256::from(rng.random::<u64>());
1031
1032 let mut storage_hash_builder = HashBuilder::default()
1033 .with_proof_retainer(ProofRetainer::from_iter([slot_path_1, slot_path_2]));
1034 storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
1035 storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
1036
1037 let storage_root = storage_hash_builder.root();
1038 let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
1039 let storage_branch_node_masks = BranchNodeMasksMap::from_iter([
1040 (
1041 Nibbles::default(),
1042 BranchNodeMasks { hash_mask: TrieMask::new(0b010), tree_mask: TrieMask::default() },
1043 ),
1044 (
1045 Nibbles::from_nibbles([0x1]),
1046 BranchNodeMasks { hash_mask: TrieMask::new(0b11), tree_mask: TrieMask::default() },
1047 ),
1048 ]);
1049
1050 let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1051 let address_path_1 = Nibbles::unpack(address_1);
1052 let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1053 let mut trie_account_1 = account_1.clone().into_trie_account(storage_root);
1054 let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1055 let address_path_2 = Nibbles::unpack(address_2);
1056 let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1057 let trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
1058
1059 let mut hash_builder = HashBuilder::default()
1060 .with_proof_retainer(ProofRetainer::from_iter([address_path_1, address_path_2]));
1061 hash_builder.add_leaf(address_path_1, &alloy_rlp::encode(trie_account_1.clone()));
1062 hash_builder.add_leaf(address_path_2, &alloy_rlp::encode(trie_account_2));
1063
1064 let root = hash_builder.root();
1065 let proof_nodes = hash_builder.take_proof_nodes();
1066 let mut sparse = SparseStateTrie::<ArenaParallelSparseTrie>::default().with_updates(true);
1067 sparse
1068 .reveal_decoded_multiproof(
1069 MultiProof {
1070 account_subtree: proof_nodes,
1071 branch_node_masks: BranchNodeMasksMap::from_iter([(
1072 Nibbles::from_nibbles([0x1]),
1073 BranchNodeMasks {
1074 hash_mask: TrieMask::new(0b00),
1075 tree_mask: TrieMask::default(),
1076 },
1077 )]),
1078 storages: HashMap::from_iter([
1079 (
1080 address_1,
1081 StorageMultiProof {
1082 root,
1083 subtree: storage_proof_nodes.clone(),
1084 branch_node_masks: storage_branch_node_masks.clone(),
1085 },
1086 ),
1087 (
1088 address_2,
1089 StorageMultiProof {
1090 root,
1091 subtree: storage_proof_nodes,
1092 branch_node_masks: storage_branch_node_masks,
1093 },
1094 ),
1095 ]),
1096 }
1097 .try_into()
1098 .unwrap(),
1099 )
1100 .unwrap();
1101
1102 assert_eq!(sparse.root(epoch(0)).unwrap(), root);
1103
1104 let address_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1105 let account_3 = Account { nonce: account_1.nonce + 1, ..account_1 };
1106 let trie_account_3 = account_3.into_trie_account(EMPTY_ROOT_HASH);
1107
1108 apply_account_update(
1109 &mut sparse,
1110 address_3,
1111 LeafUpdate::Changed(alloy_rlp::encode(trie_account_3)),
1112 );
1113
1114 let mut updates =
1115 B256Map::from_iter([(slot_3, LeafUpdate::Changed(alloy_rlp::encode(value_3)))]);
1116 sparse
1117 .storage_trie_mut(&address_1)
1118 .unwrap()
1119 .update_leaves(&mut updates, |_, _| {})
1120 .unwrap();
1121 assert!(updates.is_empty());
1122 trie_account_1.storage_root = sparse.storage_root(&address_1, epoch(0)).unwrap();
1123 apply_account_update(
1124 &mut sparse,
1125 address_1,
1126 LeafUpdate::Changed(alloy_rlp::encode(trie_account_1)),
1127 );
1128
1129 sparse.root(epoch(0)).unwrap();
1130
1131 let sparse_updates = sparse.take_trie_updates().unwrap();
1132 pretty_assertions::assert_eq!(
1134 sparse_updates,
1135 TrieUpdatesSorted::new(
1136 Vec::new(),
1137 B256Map::from_iter([(
1138 b256!("0x1000000000000000000000000000000000000000000000000000000000000000"),
1139 StorageTrieUpdatesSorted {
1140 storage_nodes: vec![(Nibbles::from_nibbles([0x1]), None)],
1141 }
1142 )]),
1143 )
1144 );
1145 }
1146}