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