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