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