1#[cfg(feature = "trie-debug")]
2use crate::debug_recorder::TrieDebugRecorder;
3use crate::{
4 lfu::BucketedLfu, traits::SparseTrie as SparseTrieTrait, ParallelSparseTrie,
5 RevealableSparseTrie,
6};
7use alloc::vec::Vec;
8use alloy_primitives::{map::B256Map, B256};
9use alloy_rlp::{Decodable, Encodable};
10use either::Either;
11use reth_execution_errors::{SparseStateTrieResult, SparseTrieErrorKind};
12use reth_primitives_traits::Account;
13use reth_trie_common::{
14 updates::{StorageTrieUpdates, TrieUpdates},
15 DecodedMultiProof, MultiProof, Nibbles, ProofTrieNodeV2, TrieAccount, EMPTY_ROOT_HASH,
16 TRIE_ACCOUNT_RLP_MAX_SIZE,
17};
18#[cfg(feature = "std")]
19use tracing::debug;
20use tracing::{instrument, trace};
21
22#[derive(Debug, Default)]
27pub struct DeferredDrops {
28 pub proof_nodes_bufs: Vec<Vec<ProofTrieNodeV2>>,
30}
31
32#[derive(Debug)]
33pub struct SparseStateTrie<
35 A = ParallelSparseTrie, S = ParallelSparseTrie, > {
38 state: RevealableSparseTrie<A>,
40 storage: StorageTries<S>,
42 retain_updates: bool,
44 account_rlp_buf: Vec<u8>,
46 deferred_drops: DeferredDrops,
48 hot_slots_lfu: BucketedLfu<HotSlotKey>,
50 hot_accounts_lfu: BucketedLfu<B256>,
52 #[cfg(feature = "metrics")]
54 metrics: crate::metrics::SparseStateTrieMetrics,
55}
56
57impl<A, S> Default for SparseStateTrie<A, S>
58where
59 A: Default,
60 S: Default,
61{
62 fn default() -> Self {
63 Self {
64 state: Default::default(),
65 storage: Default::default(),
66 retain_updates: false,
67 account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
68 deferred_drops: DeferredDrops::default(),
69 hot_slots_lfu: BucketedLfu::default(),
70 hot_accounts_lfu: BucketedLfu::default(),
71 #[cfg(feature = "metrics")]
72 metrics: Default::default(),
73 }
74 }
75}
76
77#[cfg(test)]
78impl SparseStateTrie {
79 pub fn from_state(state: RevealableSparseTrie) -> Self {
81 Self { state, ..Default::default() }
82 }
83}
84
85impl<A, S> SparseStateTrie<A, S> {
86 pub const fn set_updates(&mut self, retain_updates: bool) {
88 self.retain_updates = retain_updates;
89 }
90
91 pub const fn with_updates(mut self, retain_updates: bool) -> Self {
93 self.set_updates(retain_updates);
94 self
95 }
96
97 pub fn set_hot_cache_capacities(&mut self, max_hot_slots: usize, max_hot_accounts: usize) {
102 self.hot_slots_lfu.decay_and_evict(max_hot_slots);
103 self.hot_accounts_lfu.decay_and_evict(max_hot_accounts);
104 }
105
106 pub fn with_hot_cache_capacities(
108 mut self,
109 max_hot_slots: usize,
110 max_hot_accounts: usize,
111 ) -> Self {
112 self.set_hot_cache_capacities(max_hot_slots, max_hot_accounts);
113 self
114 }
115
116 pub fn set_accounts_trie(&mut self, trie: RevealableSparseTrie<A>) {
118 self.state = trie;
119 }
120
121 pub fn with_accounts_trie(mut self, trie: RevealableSparseTrie<A>) -> Self {
123 self.set_accounts_trie(trie);
124 self
125 }
126
127 pub fn set_default_storage_trie(&mut self, trie: RevealableSparseTrie<S>) {
130 self.storage.default_trie = trie;
131 }
132
133 pub fn with_default_storage_trie(mut self, trie: RevealableSparseTrie<S>) -> Self {
136 self.set_default_storage_trie(trie);
137 self
138 }
139
140 pub fn take_deferred_drops(&mut self) -> DeferredDrops {
145 core::mem::take(&mut self.deferred_drops)
146 }
147}
148
149impl SparseStateTrie {
150 pub fn new() -> Self {
152 Self::default()
153 }
154}
155
156impl<A: SparseTrieTrait, S: SparseTrieTrait> SparseStateTrie<A, S> {
157 #[cfg(feature = "trie-debug")]
162 pub fn take_debug_recorders(&mut self) -> alloc::vec::Vec<(Option<B256>, TrieDebugRecorder)> {
163 let mut recorders = alloc::vec::Vec::new();
164 if let Some(trie) = self.state.as_revealed_mut() {
165 recorders.push((None, trie.take_debug_recorder()));
166 }
167 for (address, trie) in &mut self.storage.tries {
168 if let Some(trie) = trie.as_revealed_mut() {
169 recorders.push((Some(*address), trie.take_debug_recorder()));
170 }
171 }
172 recorders
173 }
174}
175
176impl<A, S> SparseStateTrie<A, S>
177where
178 A: SparseTrieTrait + Default,
179 S: SparseTrieTrait + Default + Clone,
180{
181 pub const fn trie_mut(&mut self) -> &mut RevealableSparseTrie<A> {
183 &mut self.state
184 }
185
186 pub fn is_account_revealed(&self, account: B256) -> bool {
188 let path = Nibbles::unpack(account);
189 let trie = match self.state_trie_ref() {
190 Some(t) => t,
191 None => return false,
192 };
193
194 trie.find_leaf(&path, None).is_ok()
195 }
196
197 pub fn check_valid_storage_witness(&self, address: B256, slot: B256) -> bool {
199 let path = Nibbles::unpack(slot);
200 let trie = match self.storage_trie_ref(&address) {
201 Some(t) => t,
202 None => return false,
203 };
204
205 trie.find_leaf(&path, None).is_ok()
206 }
207
208 #[inline]
210 pub fn record_slot_touch(&mut self, account: B256, slot: B256) {
211 self.hot_slots_lfu.touch(HotSlotKey { address: account, slot });
212 }
213
214 #[inline]
216 pub fn record_account_touch(&mut self, account: B256) {
217 self.hot_accounts_lfu.touch(account);
218 }
219
220 pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
222 self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
223 }
224
225 pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
227 self.storage.tries.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
228 }
229
230 pub const fn state_trie_ref(&self) -> Option<&A> {
232 self.state.as_revealed_ref()
233 }
234
235 pub fn storage_trie_ref(&self, address: &B256) -> Option<&S> {
237 self.storage.tries.get(address).and_then(|e| e.as_revealed_ref())
238 }
239
240 pub fn storage_trie_mut(&mut self, address: &B256) -> Option<&mut S> {
242 self.storage.tries.get_mut(address).and_then(|e| e.as_revealed_mut())
243 }
244
245 pub const fn storage_tries_mut(&mut self) -> &mut B256Map<RevealableSparseTrie<S>> {
247 &mut self.storage.tries
248 }
249
250 pub fn take_storage_trie(&mut self, address: &B256) -> Option<RevealableSparseTrie<S>> {
252 self.storage.tries.remove(address)
253 }
254
255 pub fn take_or_create_storage_trie(&mut self, address: &B256) -> RevealableSparseTrie<S> {
257 self.storage.tries.remove(address).unwrap_or_else(|| {
258 self.storage.cleared_tries.pop().unwrap_or_else(|| self.storage.default_trie.clone())
259 })
260 }
261
262 pub fn insert_storage_trie(&mut self, address: B256, storage_trie: RevealableSparseTrie<S>) {
264 self.storage.tries.insert(address, storage_trie);
265 }
266
267 pub fn get_or_create_storage_trie_mut(
269 &mut self,
270 address: B256,
271 ) -> &mut RevealableSparseTrie<S> {
272 self.storage.get_or_create_trie_mut(address)
273 }
274
275 pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
278 let decoded_multiproof = multiproof.try_into()?;
280
281 self.reveal_decoded_multiproof(decoded_multiproof)
283 }
284
285 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
288 pub fn reveal_decoded_multiproof(
289 &mut self,
290 multiproof: DecodedMultiProof,
291 ) -> SparseStateTrieResult<()> {
292 self.reveal_decoded_multiproof_v2(multiproof.into())
293 }
294
295 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
300 pub fn reveal_decoded_multiproof_v2(
301 &mut self,
302 multiproof: reth_trie_common::DecodedMultiProofV2,
303 ) -> SparseStateTrieResult<()> {
304 let reth_trie_common::DecodedMultiProofV2 { account_proofs, mut storage_proofs, .. } =
305 multiproof;
306
307 let mut targets = Vec::with_capacity(storage_proofs.len() + 1);
310
311 if !account_proofs.is_empty() {
312 #[cfg(feature = "metrics")]
313 self.metrics.increment_total_account_nodes(account_proofs.len() as u64);
314 targets.push((Either::Left(&mut self.state), account_proofs));
315 }
316
317 for &account in storage_proofs.keys() {
319 let _ = self.storage.get_or_create_trie_mut(account);
320 }
321
322 for (account, trie) in &mut self.storage.tries {
323 if let Some(nodes) = storage_proofs.remove(account) {
324 #[cfg(feature = "metrics")]
325 self.metrics.increment_total_storage_nodes(nodes.len() as u64);
326 targets.push((Either::Right(trie), nodes));
327 }
328 }
329
330 let retain_updates = self.retain_updates;
331
332 #[cfg(not(feature = "std"))]
333 let results: Vec<_> = targets
334 .into_iter()
335 .map(|(target, mut nodes)| {
336 let result = match target {
337 Either::Left(trie) => trie.reveal_v2_proof_nodes(&mut nodes, retain_updates),
338 Either::Right(trie) => trie.reveal_v2_proof_nodes(&mut nodes, retain_updates),
339 };
340 (result, nodes)
341 })
342 .collect();
343
344 #[cfg(feature = "std")]
345 let results: Vec<_> = {
346 use rayon::iter::ParallelIterator;
347 use reth_primitives_traits::ParallelBridgeBuffered;
348
349 targets
350 .into_iter()
351 .par_bridge_buffered()
352 .map(|(target, mut nodes)| {
353 let result = match target {
354 Either::Left(trie) => {
355 trie.reveal_v2_proof_nodes(&mut nodes, retain_updates)
356 }
357 Either::Right(trie) => {
358 trie.reveal_v2_proof_nodes(&mut nodes, retain_updates)
359 }
360 };
361 (result, nodes)
362 })
363 .collect()
364 };
365
366 let mut any_err = Ok(());
368 for (result, nodes) in results {
369 if result.is_err() && any_err.is_ok() {
370 any_err = result.map_err(Into::into);
371 }
372 self.deferred_drops.proof_nodes_bufs.push(nodes);
373 }
374
375 any_err
376 }
377
378 pub fn wipe_storage(&mut self, address: B256) -> SparseStateTrieResult<()> {
380 if let Some(trie) = self.storage.tries.get_mut(&address) {
381 trie.wipe()?;
382 }
383 Ok(())
384 }
385
386 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
390 pub fn calculate_subtries(&mut self) {
391 if let RevealableSparseTrie::Revealed(trie) = &mut self.state {
392 trie.update_subtrie_hashes();
393 }
394 }
395
396 pub fn storage_root(&mut self, account: &B256) -> Option<B256> {
398 self.storage.tries.get_mut(account).and_then(|trie| trie.root())
399 }
400
401 fn revealed_trie_mut(&mut self) -> SparseStateTrieResult<&mut A> {
403 self.state.as_revealed_mut().ok_or_else(|| SparseTrieErrorKind::Blind.into())
404 }
405
406 pub fn root(&mut self) -> SparseStateTrieResult<B256> {
408 #[cfg(feature = "metrics")]
410 self.metrics.record();
411
412 Ok(self.revealed_trie_mut()?.root())
413 }
414
415 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
419 pub fn root_with_updates(&mut self) -> SparseStateTrieResult<(B256, TrieUpdates)> {
420 #[cfg(feature = "metrics")]
422 self.metrics.record();
423
424 let storage_tries = self.storage_trie_updates();
425 let revealed = self.revealed_trie_mut()?;
426
427 let (root, updates) = (revealed.root(), revealed.take_updates());
428 let updates = TrieUpdates {
429 account_nodes: updates.updated_nodes,
430 removed_nodes: updates.removed_nodes,
431 storage_tries,
432 };
433 Ok((root, updates))
434 }
435
436 pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdates> {
440 self.storage
441 .tries
442 .iter_mut()
443 .map(|(address, trie)| {
444 let trie = trie.as_revealed_mut().unwrap();
445 let updates = trie.take_updates();
446 let updates = StorageTrieUpdates {
447 is_deleted: updates.wiped,
448 storage_nodes: updates.updated_nodes,
449 removed_nodes: updates.removed_nodes,
450 };
451 (*address, updates)
452 })
453 .filter(|(_, updates)| !updates.is_empty())
454 .collect()
455 }
456
457 pub fn take_trie_updates(&mut self) -> Option<TrieUpdates> {
461 let storage_tries = self.storage_trie_updates();
462 self.state.as_revealed_mut().map(|state| {
463 let updates = state.take_updates();
464 TrieUpdates {
465 account_nodes: updates.updated_nodes,
466 removed_nodes: updates.removed_nodes,
467 storage_tries,
468 }
469 })
470 }
471}
472
473impl SparseStateTrie {
474 pub fn update_account_leaf(
476 &mut self,
477 path: Nibbles,
478 value: Vec<u8>,
479 ) -> SparseStateTrieResult<()> {
480 self.state.update_leaf(path, value)?;
481 Ok(())
482 }
483
484 pub fn update_storage_leaf(
486 &mut self,
487 address: B256,
488 slot: Nibbles,
489 value: Vec<u8>,
490 ) -> SparseStateTrieResult<()> {
491 self.storage
492 .tries
493 .get_mut(&address)
494 .ok_or(SparseTrieErrorKind::Blind)?
495 .update_leaf(slot, value)?;
496 Ok(())
497 }
498
499 #[instrument(level = "trace", target = "trie::sparse", skip_all)]
505 pub fn update_account(
506 &mut self,
507 address: B256,
508 account: Account,
509 ) -> SparseStateTrieResult<bool> {
510 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
511 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
512 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
513 } else if self.is_account_revealed(address) {
514 trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
515 if let Some(value) = self.get_account_value(&address) {
517 TrieAccount::decode(&mut &value[..])?.storage_root
519 } else {
520 EMPTY_ROOT_HASH
522 }
523 } else {
524 return Err(SparseTrieErrorKind::Blind.into())
525 };
526
527 if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
528 return Ok(false);
529 }
530
531 trace!(target: "trie::sparse", ?address, "Updating account");
532 let nibbles = Nibbles::unpack(address);
533 self.account_rlp_buf.clear();
534 account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
535 self.update_account_leaf(nibbles, self.account_rlp_buf.clone())?;
536
537 Ok(true)
538 }
539
540 #[instrument(level = "trace", target = "trie::sparse", skip_all)]
548 pub fn update_account_stateless(
549 &mut self,
550 address: B256,
551 account: Option<Account>,
552 ) -> SparseStateTrieResult<()> {
553 let nibbles = Nibbles::unpack(address);
554
555 let Some(account) = account else {
556 trace!(target: "trie::sparse", ?address, "Removing account");
557 return self.remove_account_leaf(&nibbles);
558 };
559
560 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
561 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
562 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
563 } else if let Some(value) = self.get_account_value(&address) {
564 trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
565 TrieAccount::decode(&mut &value[..])?.storage_root
566 } else {
567 EMPTY_ROOT_HASH
568 };
569
570 trace!(target: "trie::sparse", ?address, "Updating account");
571 self.account_rlp_buf.clear();
572 account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
573 self.update_account_leaf(nibbles, self.account_rlp_buf.clone())?;
574
575 Ok(())
576 }
577
578 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
585 pub fn update_account_storage_root(&mut self, address: B256) -> SparseStateTrieResult<bool> {
586 if !self.is_account_revealed(address) {
587 return Err(SparseTrieErrorKind::Blind.into())
588 }
589
590 let Some(mut trie_account) = self
592 .get_account_value(&address)
593 .map(|v| TrieAccount::decode(&mut &v[..]))
594 .transpose()?
595 else {
596 trace!(target: "trie::sparse", ?address, "Account not found in trie, skipping storage root update");
597 return Ok(true)
598 };
599
600 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
602 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
603 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
604 } else {
605 EMPTY_ROOT_HASH
606 };
607
608 trie_account.storage_root = storage_root;
610
611 if trie_account == TrieAccount::default() {
613 return Ok(false)
614 }
615
616 trace!(target: "trie::sparse", ?address, "Updating account with the new storage root");
618 let nibbles = Nibbles::unpack(address);
619 self.account_rlp_buf.clear();
620 trie_account.encode(&mut self.account_rlp_buf);
621 self.update_account_leaf(nibbles, self.account_rlp_buf.clone())?;
622
623 Ok(true)
624 }
625
626 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
628 pub fn remove_account_leaf(&mut self, path: &Nibbles) -> SparseStateTrieResult<()> {
629 self.state.remove_leaf(path)?;
630 Ok(())
631 }
632
633 pub fn remove_storage_leaf(
635 &mut self,
636 address: B256,
637 slot: &Nibbles,
638 ) -> SparseStateTrieResult<()> {
639 let storage_trie =
640 self.storage.tries.get_mut(&address).ok_or(SparseTrieErrorKind::Blind)?;
641
642 storage_trie.remove_leaf(slot)?;
643 Ok(())
644 }
645}
646
647impl<A, S> SparseStateTrie<A, S>
648where
649 A: SparseTrieTrait + Default,
650 S: SparseTrieTrait + Default + Clone,
651{
652 pub fn clear(&mut self) {
657 self.state.clear();
658 self.storage.clear();
659 self.account_rlp_buf.clear();
660 }
661
662 pub fn memory_size(&self) -> usize {
667 let mut size = core::mem::size_of::<Self>();
668
669 size += match &self.state {
670 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
671 t.memory_size()
672 }
673 RevealableSparseTrie::Blind(None) => 0,
674 };
675
676 for trie in self.storage.tries.values() {
677 size += match trie {
678 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
679 t.memory_size()
680 }
681 RevealableSparseTrie::Blind(None) => 0,
682 };
683 }
684 for trie in &self.storage.cleared_tries {
685 size += match trie {
686 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
687 t.memory_size()
688 }
689 RevealableSparseTrie::Blind(None) => 0,
690 };
691 }
692
693 size
694 }
695
696 pub fn retained_storage_tries_count(&self) -> usize {
698 self.storage.tries.len() + self.storage.cleared_tries.len()
699 }
700
701 pub fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
706 let storage_tries_count = self.storage.tries.len() + self.storage.cleared_tries.len();
708
709 let total_tries = 1 + storage_tries_count;
711
712 let nodes_per_trie = max_nodes / total_tries;
714 let values_per_trie = max_values / total_tries;
715
716 self.state.shrink_nodes_to(nodes_per_trie);
718 self.state.shrink_values_to(values_per_trie);
719
720 let storage_nodes = max_nodes.saturating_sub(nodes_per_trie);
722 let storage_values = max_values.saturating_sub(values_per_trie);
723
724 self.storage.shrink_to(storage_nodes, storage_values);
726 }
727
728 #[cfg(feature = "std")]
743 #[instrument(
744 level = "debug",
745 name = "SparseStateTrie::prune",
746 target = "trie::sparse",
747 skip_all,
748 fields(%max_hot_slots, %max_hot_accounts)
749 )]
750 pub fn prune(&mut self, max_hot_slots: usize, max_hot_accounts: usize) {
751 self.hot_slots_lfu.decay_and_evict(max_hot_slots);
752 self.hot_accounts_lfu.decay_and_evict(max_hot_accounts);
753 let retained = self.hot_slots_lfu.retained_slots_by_address();
754
755 let retained_account_paths: Vec<Nibbles> =
756 self.hot_accounts_lfu.keys().map(|k| Nibbles::unpack(*k)).collect();
757
758 let retained_accounts = retained_account_paths.len();
759 let retained_storage_tries = retained.len();
760 let total_storage_tries_before = self.storage.tries.len();
761
762 let (account_nodes_pruned, storage_tries_evicted) = rayon::join(
764 || {
765 self.state
766 .as_revealed_mut()
767 .map(|trie| trie.prune(&retained_account_paths))
768 .unwrap_or(0)
769 },
770 || self.storage.prune_by_retained_slots(retained),
771 );
772
773 debug!(
774 target: "trie::sparse",
775 retained_accounts,
776 retained_storage_tries,
777 account_nodes_pruned,
778 storage_tries_evicted,
779 storage_tries_after = total_storage_tries_before - storage_tries_evicted,
780 "SparseStateTrie::prune completed"
781 );
782 }
783
784 pub fn commit_updates(&mut self, updates: &TrieUpdates) {
786 if let Some(trie) = self.state.as_revealed_mut() {
787 trie.commit_updates(&updates.account_nodes, &updates.removed_nodes);
788 }
789 for (address, updates) in &updates.storage_tries {
790 if let Some(trie) =
791 self.storage.tries.get_mut(address).and_then(|t| t.as_revealed_mut())
792 {
793 trie.commit_updates(&updates.storage_nodes, &updates.removed_nodes);
794 }
795 }
796 }
797}
798
799#[derive(Debug, Default)]
802struct StorageTries<S = ParallelSparseTrie> {
803 tries: B256Map<RevealableSparseTrie<S>>,
805 cleared_tries: Vec<RevealableSparseTrie<S>>,
807 default_trie: RevealableSparseTrie<S>,
809}
810
811#[cfg(feature = "std")]
812impl<S: SparseTrieTrait> StorageTries<S> {
813 fn prune_by_retained_slots(&mut self, retained_slots: B256Map<Vec<Nibbles>>) -> usize {
818 {
820 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
821 self.tries.par_iter_mut().for_each(|(address, trie)| {
822 if let Some(slots) = retained_slots.get(address) {
823 if let Some(t) = trie.as_revealed_mut() {
824 t.prune(slots);
825 }
826 } else {
827 trie.clear();
828 }
829 });
830 }
831
832 let addresses_to_evict: Vec<B256> = self
834 .tries
835 .keys()
836 .filter(|address| !retained_slots.contains_key(*address))
837 .copied()
838 .collect();
839
840 let evicted = addresses_to_evict.len();
841 self.cleared_tries.reserve(evicted);
842 for address in &addresses_to_evict {
843 if let Some(trie) = self.tries.remove(address) {
844 self.cleared_tries.push(trie);
845 }
846 }
847
848 evicted
849 }
850}
851
852impl<S: SparseTrieTrait> StorageTries<S> {
853 fn clear(&mut self) {
856 self.cleared_tries.extend(self.tries.drain().map(|(_, mut trie)| {
857 trie.clear();
858 trie
859 }));
860 }
861
862 fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
866 let total_tries = self.tries.len() + self.cleared_tries.len();
867 if total_tries == 0 {
868 return;
869 }
870
871 let nodes_per_trie = max_nodes / total_tries;
873 let values_per_trie = max_values / total_tries;
874
875 for trie in self.tries.values_mut() {
877 trie.shrink_nodes_to(nodes_per_trie);
878 trie.shrink_values_to(values_per_trie);
879 }
880
881 for trie in &mut self.cleared_tries {
883 trie.shrink_nodes_to(nodes_per_trie);
884 trie.shrink_values_to(values_per_trie);
885 }
886 }
887}
888
889impl<S: SparseTrieTrait + Clone> StorageTries<S> {
890 fn get_or_create_trie_mut(&mut self, address: B256) -> &mut RevealableSparseTrie<S> {
892 self.tries.entry(address).or_insert_with(|| {
893 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
894 })
895 }
896}
897
898#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
900struct HotSlotKey {
901 address: B256,
902 slot: B256,
903}
904
905#[cfg(feature = "std")]
907impl BucketedLfu<HotSlotKey> {
908 fn retained_slots_by_address(&self) -> B256Map<Vec<Nibbles>> {
910 let mut grouped =
911 B256Map::<Vec<Nibbles>>::with_capacity_and_hasher(self.len(), Default::default());
912 for key in self.keys() {
913 grouped.entry(key.address).or_default().push(Nibbles::unpack(key.slot));
914 }
915
916 for slots in grouped.values_mut() {
917 slots.sort_unstable();
918 slots.dedup();
919 }
920
921 grouped
922 }
923}
924
925#[cfg(test)]
926mod tests {
927 use super::*;
928 use crate::{LeafLookup, ParallelSparseTrie};
929 use alloy_primitives::{
930 b256,
931 map::{HashMap, HashSet},
932 U256,
933 };
934 use arbitrary::Arbitrary;
935 use rand::{rngs::StdRng, Rng, SeedableRng};
936 use reth_execution_errors::{SparseStateTrieErrorKind, SparseTrieErrorKind};
937 use reth_primitives_traits::Account;
938 use reth_trie::{updates::StorageTrieUpdates, HashBuilder, MultiProof, EMPTY_ROOT_HASH};
939 use reth_trie_common::{
940 proof::{ProofNodes, ProofRetainer},
941 BranchNodeMasks, BranchNodeMasksMap, BranchNodeV2, LeafNode, RlpNode, StorageMultiProof,
942 TrieMask, TrieNodeV2,
943 };
944
945 fn leaf_key(suffix: impl AsRef<[u8]>, total_len: usize) -> Nibbles {
947 let suffix = suffix.as_ref();
948 let mut nibbles = Nibbles::from_nibbles(suffix);
949 nibbles.extend(&Nibbles::from_nibbles_unchecked(vec![0; total_len - suffix.len()]));
950 nibbles
951 }
952
953 #[test]
954 fn reveal_account_path_twice() {
955 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
956
957 let full_path_0 = leaf_key([0x0], 64);
959 let _full_path_1 = leaf_key([0x1], 64);
960
961 let leaf_value = alloy_rlp::encode(TrieAccount::default());
962 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
964 leaf_key([], 63),
965 leaf_value.clone(),
966 )));
967 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
968 leaf_key([], 63),
969 leaf_value.clone(),
970 )));
971
972 let multiproof = MultiProof {
973 account_subtree: ProofNodes::from_iter([
974 (
975 Nibbles::default(),
976 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
977 key: Nibbles::default(),
978 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
979 state_mask: TrieMask::new(0b11),
980 branch_rlp_node: None,
981 }))
982 .into(),
983 ),
984 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
985 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
986 ]),
987 ..Default::default()
988 };
989
990 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
992 assert!(matches!(
993 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
994 Ok(LeafLookup::Exists)
995 ));
996 assert_eq!(
997 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
998 Some(&leaf_value)
999 );
1000
1001 sparse.remove_account_leaf(&full_path_0).unwrap();
1004 assert!(matches!(
1005 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1006 Ok(LeafLookup::NonExistent)
1007 ));
1008 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
1009 }
1010
1011 #[test]
1012 fn reveal_storage_path_twice() {
1013 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1014
1015 let full_path_0 = leaf_key([0x0], 64);
1017
1018 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1019 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1020 leaf_key([], 63),
1021 leaf_value.clone(),
1022 )));
1023 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1024 leaf_key([], 63),
1025 leaf_value.clone(),
1026 )));
1027
1028 let multiproof = MultiProof {
1029 storages: HashMap::from_iter([(
1030 B256::ZERO,
1031 StorageMultiProof {
1032 root: B256::ZERO,
1033 subtree: ProofNodes::from_iter([
1034 (
1035 Nibbles::default(),
1036 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
1037 key: Nibbles::default(),
1038 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1039 state_mask: TrieMask::new(0b11),
1040 branch_rlp_node: None,
1041 }))
1042 .into(),
1043 ),
1044 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1045 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1046 ]),
1047 branch_node_masks: Default::default(),
1048 },
1049 )]),
1050 ..Default::default()
1051 };
1052
1053 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1055 assert!(matches!(
1056 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1057 Ok(LeafLookup::Exists)
1058 ));
1059 assert_eq!(
1060 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
1061 Some(&leaf_value)
1062 );
1063
1064 sparse.remove_storage_leaf(B256::ZERO, &full_path_0).unwrap();
1067 assert!(matches!(
1068 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1069 Ok(LeafLookup::NonExistent)
1070 ));
1071 assert!(sparse
1072 .storage_trie_ref(&B256::ZERO)
1073 .unwrap()
1074 .get_leaf_value(&full_path_0)
1075 .is_none());
1076 }
1077
1078 #[test]
1079 fn seeded_hot_cache_capacities_preserve_first_cycle_touches() {
1080 let account = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1081 let slot = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1082 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1083
1084 sparse.set_hot_cache_capacities(1, 1);
1085 sparse.record_account_touch(account);
1086 sparse.record_slot_touch(account, slot);
1087 sparse.prune(1, 1);
1088
1089 assert_eq!(sparse.hot_accounts_lfu.keys().copied().collect::<Vec<_>>(), vec![account]);
1090 assert_eq!(
1091 sparse.hot_slots_lfu.keys().copied().collect::<Vec<_>>(),
1092 vec![HotSlotKey { address: account, slot }]
1093 );
1094 }
1095
1096 #[test]
1097 fn reveal_v2_proof_nodes() {
1098 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1099
1100 let full_path_0 = leaf_key([0x0], 64);
1102
1103 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1104 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
1105 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
1106
1107 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
1108 key: Nibbles::default(),
1109 stack: vec![
1110 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1111 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1112 ],
1113 state_mask: TrieMask::new(0b11),
1114 branch_rlp_node: None,
1115 });
1116
1117 let v2_proof_nodes = vec![
1119 ProofTrieNodeV2 {
1120 path: Nibbles::default(),
1121 node: branch_node,
1122 masks: Some(BranchNodeMasks {
1123 hash_mask: TrieMask::default(),
1124 tree_mask: TrieMask::default(),
1125 }),
1126 },
1127 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1128 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1129 ];
1130
1131 sparse
1133 .reveal_decoded_multiproof_v2(reth_trie_common::DecodedMultiProofV2 {
1134 account_proofs: v2_proof_nodes,
1135 ..Default::default()
1136 })
1137 .unwrap();
1138
1139 assert!(matches!(
1141 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1142 Ok(LeafLookup::Exists)
1143 ));
1144 assert_eq!(
1145 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
1146 Some(&leaf_value)
1147 );
1148
1149 sparse.remove_account_leaf(&full_path_0).unwrap();
1151 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
1152 }
1153
1154 #[test]
1155 fn reveal_storage_v2_proof_nodes() {
1156 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1157
1158 let full_path_0 = leaf_key([0x0], 64);
1160
1161 let storage_value: Vec<u8> = alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec();
1162 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
1163 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
1164
1165 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
1166 key: Nibbles::default(),
1167 stack: vec![
1168 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1169 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1170 ],
1171 state_mask: TrieMask::new(0b11),
1172 branch_rlp_node: None,
1173 });
1174
1175 let v2_proof_nodes = vec![
1176 ProofTrieNodeV2 { path: Nibbles::default(), node: branch_node, masks: None },
1177 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1178 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1179 ];
1180
1181 sparse
1183 .reveal_decoded_multiproof_v2(reth_trie_common::DecodedMultiProofV2 {
1184 storage_proofs: B256Map::from_iter([(B256::ZERO, v2_proof_nodes)]),
1185 ..Default::default()
1186 })
1187 .unwrap();
1188
1189 assert!(matches!(
1191 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1192 Ok(LeafLookup::Exists)
1193 ));
1194 assert_eq!(
1195 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
1196 Some(&storage_value)
1197 );
1198
1199 sparse.remove_storage_leaf(B256::ZERO, &full_path_0).unwrap();
1201 assert!(sparse
1202 .storage_trie_ref(&B256::ZERO)
1203 .unwrap()
1204 .get_leaf_value(&full_path_0)
1205 .is_none());
1206 }
1207
1208 #[test]
1209 fn root_on_blind_trie_returns_blind_error() {
1210 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1211
1212 let err = sparse.root().unwrap_err();
1213
1214 assert!(matches!(err.kind(), SparseStateTrieErrorKind::Sparse(SparseTrieErrorKind::Blind)));
1215 }
1216
1217 #[test]
1218 fn take_trie_updates() {
1219 reth_tracing::init_test_tracing();
1220
1221 let mut rng = StdRng::seed_from_u64(1);
1223
1224 let mut bytes = [0u8; 1024];
1225 rng.fill(bytes.as_mut_slice());
1226
1227 let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1228 let slot_path_1 = Nibbles::unpack(slot_1);
1229 let value_1 = U256::from(rng.random::<u64>());
1230 let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1231 let slot_path_2 = Nibbles::unpack(slot_2);
1232 let value_2 = U256::from(rng.random::<u64>());
1233 let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1234 let slot_path_3 = Nibbles::unpack(slot_3);
1235 let value_3 = U256::from(rng.random::<u64>());
1236
1237 let mut storage_hash_builder = HashBuilder::default()
1238 .with_proof_retainer(ProofRetainer::from_iter([slot_path_1, slot_path_2]));
1239 storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
1240 storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
1241
1242 let storage_root = storage_hash_builder.root();
1243 let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
1244 let storage_branch_node_masks = BranchNodeMasksMap::from_iter([
1245 (
1246 Nibbles::default(),
1247 BranchNodeMasks { hash_mask: TrieMask::new(0b010), tree_mask: TrieMask::default() },
1248 ),
1249 (
1250 Nibbles::from_nibbles([0x1]),
1251 BranchNodeMasks { hash_mask: TrieMask::new(0b11), tree_mask: TrieMask::default() },
1252 ),
1253 ]);
1254
1255 let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1256 let address_path_1 = Nibbles::unpack(address_1);
1257 let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1258 let mut trie_account_1 = account_1.into_trie_account(storage_root);
1259 let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1260 let address_path_2 = Nibbles::unpack(address_2);
1261 let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1262 let mut trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
1263
1264 let mut hash_builder = HashBuilder::default()
1265 .with_proof_retainer(ProofRetainer::from_iter([address_path_1, address_path_2]));
1266 hash_builder.add_leaf(address_path_1, &alloy_rlp::encode(trie_account_1));
1267 hash_builder.add_leaf(address_path_2, &alloy_rlp::encode(trie_account_2));
1268
1269 let root = hash_builder.root();
1270 let proof_nodes = hash_builder.take_proof_nodes();
1271 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default().with_updates(true);
1272 sparse
1273 .reveal_decoded_multiproof(
1274 MultiProof {
1275 account_subtree: proof_nodes,
1276 branch_node_masks: BranchNodeMasksMap::from_iter([(
1277 Nibbles::from_nibbles([0x1]),
1278 BranchNodeMasks {
1279 hash_mask: TrieMask::new(0b00),
1280 tree_mask: TrieMask::default(),
1281 },
1282 )]),
1283 storages: HashMap::from_iter([
1284 (
1285 address_1,
1286 StorageMultiProof {
1287 root,
1288 subtree: storage_proof_nodes.clone(),
1289 branch_node_masks: storage_branch_node_masks.clone(),
1290 },
1291 ),
1292 (
1293 address_2,
1294 StorageMultiProof {
1295 root,
1296 subtree: storage_proof_nodes,
1297 branch_node_masks: storage_branch_node_masks,
1298 },
1299 ),
1300 ]),
1301 }
1302 .try_into()
1303 .unwrap(),
1304 )
1305 .unwrap();
1306
1307 assert_eq!(sparse.root().unwrap(), root);
1308
1309 let address_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1310 let address_path_3 = Nibbles::unpack(address_3);
1311 let account_3 = Account { nonce: account_1.nonce + 1, ..account_1 };
1312 let trie_account_3 = account_3.into_trie_account(EMPTY_ROOT_HASH);
1313
1314 sparse.update_account_leaf(address_path_3, alloy_rlp::encode(trie_account_3)).unwrap();
1315
1316 sparse.update_storage_leaf(address_1, slot_path_3, alloy_rlp::encode(value_3)).unwrap();
1317 trie_account_1.storage_root = sparse.storage_root(&address_1).unwrap();
1318 sparse.update_account_leaf(address_path_1, alloy_rlp::encode(trie_account_1)).unwrap();
1319
1320 sparse.wipe_storage(address_2).unwrap();
1321 trie_account_2.storage_root = sparse.storage_root(&address_2).unwrap();
1322 sparse.update_account_leaf(address_path_2, alloy_rlp::encode(trie_account_2)).unwrap();
1323
1324 sparse.root().unwrap();
1325
1326 let sparse_updates = sparse.take_trie_updates().unwrap();
1327 pretty_assertions::assert_eq!(
1329 sparse_updates,
1330 TrieUpdates {
1331 account_nodes: HashMap::default(),
1332 storage_tries: HashMap::from_iter([(
1333 b256!("0x1100000000000000000000000000000000000000000000000000000000000000"),
1334 StorageTrieUpdates {
1335 is_deleted: true,
1336 storage_nodes: HashMap::default(),
1337 removed_nodes: HashSet::default()
1338 }
1339 )]),
1340 removed_nodes: HashSet::default()
1341 }
1342 );
1343 }
1344}