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