1#[cfg(feature = "trie-debug")]
2use crate::debug_recorder::TrieDebugRecorder;
3use crate::{
4 lfu::BucketedLfu,
5 provider::{TrieNodeProvider, TrieNodeProviderFactory},
6 traits::SparseTrie as SparseTrieTrait,
7 ParallelSparseTrie, RevealableSparseTrie,
8};
9use alloc::vec::Vec;
10use alloy_primitives::{map::B256Map, B256};
11use alloy_rlp::{Decodable, Encodable};
12use reth_execution_errors::{SparseStateTrieResult, SparseTrieErrorKind};
13use reth_primitives_traits::Account;
14use reth_trie_common::{
15 updates::{StorageTrieUpdates, TrieUpdates},
16 BranchNodeMasks, DecodedMultiProof, MultiProof, Nibbles, ProofTrieNodeV2, TrieAccount,
17 TrieNodeV2, EMPTY_ROOT_HASH, TRIE_ACCOUNT_RLP_MAX_SIZE,
18};
19#[cfg(feature = "std")]
20use tracing::debug;
21use tracing::{instrument, trace};
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 = ParallelSparseTrie, S = ParallelSparseTrie, > {
39 state: RevealableSparseTrie<A>,
41 storage: StorageTries<S>,
43 retain_updates: bool,
45 account_rlp_buf: Vec<u8>,
47 deferred_drops: DeferredDrops,
49 hot_slots_lfu: BucketedLfu<HotSlotKey>,
51 hot_accounts_lfu: BucketedLfu<B256>,
53 #[cfg(feature = "metrics")]
55 metrics: crate::metrics::SparseStateTrieMetrics,
56}
57
58impl<A, S> Default for SparseStateTrie<A, S>
59where
60 A: Default,
61 S: Default,
62{
63 fn default() -> Self {
64 Self {
65 state: Default::default(),
66 storage: Default::default(),
67 retain_updates: false,
68 account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
69 deferred_drops: DeferredDrops::default(),
70 hot_slots_lfu: BucketedLfu::default(),
71 hot_accounts_lfu: BucketedLfu::default(),
72 #[cfg(feature = "metrics")]
73 metrics: Default::default(),
74 }
75 }
76}
77
78#[cfg(test)]
79impl SparseStateTrie {
80 pub fn from_state(state: RevealableSparseTrie) -> Self {
82 Self { state, ..Default::default() }
83 }
84}
85
86impl<A, S> SparseStateTrie<A, S> {
87 pub const fn set_updates(&mut self, retain_updates: bool) {
89 self.retain_updates = retain_updates;
90 }
91
92 pub const fn with_updates(mut self, retain_updates: bool) -> Self {
94 self.set_updates(retain_updates);
95 self
96 }
97
98 pub fn set_hot_cache_capacities(&mut self, max_hot_slots: usize, max_hot_accounts: usize) {
103 self.hot_slots_lfu.decay_and_evict(max_hot_slots);
104 self.hot_accounts_lfu.decay_and_evict(max_hot_accounts);
105 }
106
107 pub fn with_hot_cache_capacities(
109 mut self,
110 max_hot_slots: usize,
111 max_hot_accounts: usize,
112 ) -> Self {
113 self.set_hot_cache_capacities(max_hot_slots, max_hot_accounts);
114 self
115 }
116
117 pub fn set_accounts_trie(&mut self, trie: RevealableSparseTrie<A>) {
119 self.state = trie;
120 }
121
122 pub fn with_accounts_trie(mut self, trie: RevealableSparseTrie<A>) -> Self {
124 self.set_accounts_trie(trie);
125 self
126 }
127
128 pub fn set_default_storage_trie(&mut self, trie: RevealableSparseTrie<S>) {
131 self.storage.default_trie = trie;
132 }
133
134 pub fn with_default_storage_trie(mut self, trie: RevealableSparseTrie<S>) -> Self {
137 self.set_default_storage_trie(trie);
138 self
139 }
140
141 pub fn take_deferred_drops(&mut self) -> DeferredDrops {
146 core::mem::take(&mut self.deferred_drops)
147 }
148}
149
150impl SparseStateTrie {
151 pub fn new() -> Self {
153 Self::default()
154 }
155}
156
157impl<A: SparseTrieTrait, S: SparseTrieTrait> SparseStateTrie<A, S> {
158 #[cfg(feature = "trie-debug")]
163 pub fn take_debug_recorders(&mut self) -> alloc::vec::Vec<(Option<B256>, TrieDebugRecorder)> {
164 let mut recorders = alloc::vec::Vec::new();
165 if let Some(trie) = self.state.as_revealed_mut() {
166 recorders.push((None, trie.take_debug_recorder()));
167 }
168 for (address, trie) in &mut self.storage.tries {
169 if let Some(trie) = trie.as_revealed_mut() {
170 recorders.push((Some(*address), trie.take_debug_recorder()));
171 }
172 }
173 recorders
174 }
175}
176
177impl<A, S> SparseStateTrie<A, S>
178where
179 A: SparseTrieTrait + Default,
180 S: SparseTrieTrait + Default + Clone,
181{
182 pub const fn trie_mut(&mut self) -> &mut RevealableSparseTrie<A> {
184 &mut self.state
185 }
186
187 pub fn is_account_revealed(&self, account: B256) -> bool {
189 let path = Nibbles::unpack(account);
190 let trie = match self.state_trie_ref() {
191 Some(t) => t,
192 None => return false,
193 };
194
195 trie.find_leaf(&path, None).is_ok()
196 }
197
198 pub fn check_valid_storage_witness(&self, address: B256, slot: B256) -> bool {
200 let path = Nibbles::unpack(slot);
201 let trie = match self.storage_trie_ref(&address) {
202 Some(t) => t,
203 None => return false,
204 };
205
206 trie.find_leaf(&path, None).is_ok()
207 }
208
209 #[inline]
211 pub fn record_slot_touch(&mut self, account: B256, slot: B256) {
212 self.hot_slots_lfu.touch(HotSlotKey { address: account, slot });
213 }
214
215 #[inline]
217 pub fn record_account_touch(&mut self, account: B256) {
218 self.hot_accounts_lfu.touch(account);
219 }
220
221 pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
223 self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
224 }
225
226 pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
228 self.storage.tries.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
229 }
230
231 pub const fn state_trie_ref(&self) -> Option<&A> {
233 self.state.as_revealed_ref()
234 }
235
236 pub fn storage_trie_ref(&self, address: &B256) -> Option<&S> {
238 self.storage.tries.get(address).and_then(|e| e.as_revealed_ref())
239 }
240
241 pub fn storage_trie_mut(&mut self, address: &B256) -> Option<&mut S> {
243 self.storage.tries.get_mut(address).and_then(|e| e.as_revealed_mut())
244 }
245
246 pub const fn storage_tries_mut(&mut self) -> &mut B256Map<RevealableSparseTrie<S>> {
248 &mut self.storage.tries
249 }
250
251 pub fn take_storage_trie(&mut self, address: &B256) -> Option<RevealableSparseTrie<S>> {
253 self.storage.tries.remove(address)
254 }
255
256 pub fn take_or_create_storage_trie(&mut self, address: &B256) -> RevealableSparseTrie<S> {
258 self.storage.tries.remove(address).unwrap_or_else(|| {
259 self.storage.cleared_tries.pop().unwrap_or_else(|| self.storage.default_trie.clone())
260 })
261 }
262
263 pub fn insert_storage_trie(&mut self, address: B256, storage_trie: RevealableSparseTrie<S>) {
265 self.storage.tries.insert(address, storage_trie);
266 }
267
268 pub fn get_or_create_storage_trie_mut(
270 &mut self,
271 address: B256,
272 ) -> &mut RevealableSparseTrie<S> {
273 self.storage.get_or_create_trie_mut(address)
274 }
275
276 pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
279 let decoded_multiproof = multiproof.try_into()?;
281
282 self.reveal_decoded_multiproof(decoded_multiproof)
284 }
285
286 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
289 pub fn reveal_decoded_multiproof(
290 &mut self,
291 multiproof: DecodedMultiProof,
292 ) -> SparseStateTrieResult<()> {
293 self.reveal_decoded_multiproof_v2(multiproof.into())
294 }
295
296 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
301 pub fn reveal_decoded_multiproof_v2(
302 &mut self,
303 multiproof: reth_trie_common::DecodedMultiProofV2,
304 ) -> SparseStateTrieResult<()> {
305 if !multiproof.account_proofs.is_empty() {
313 self.reveal_account_v2_proof_nodes(multiproof.account_proofs)?;
314 }
315
316 #[cfg(not(feature = "std"))]
317 {
319 for (account, storage_proofs) in multiproof.storage_proofs {
320 self.reveal_storage_v2_proof_nodes(account, storage_proofs)?;
321 }
322
323 Ok(())
324 }
325
326 #[cfg(feature = "std")]
327 {
329 use rayon::iter::ParallelIterator;
330 use reth_primitives_traits::ParallelBridgeBuffered;
331
332 let retain_updates = self.retain_updates;
333
334 let results: Vec<_> = multiproof
338 .storage_proofs
339 .into_iter()
340 .map(|(account, storage_proofs)| {
341 let trie = self.storage.take_or_create_trie(&account);
342 (account, storage_proofs, trie)
343 })
344 .par_bridge_buffered()
345 .map(|(account, storage_proofs, mut trie)| {
346 let mut bufs = Vec::new();
347 let result = Self::reveal_storage_v2_proof_nodes_inner(
348 account,
349 storage_proofs,
350 &mut trie,
351 &mut bufs,
352 retain_updates,
353 );
354 (account, result, trie, bufs)
355 })
356 .collect();
357
358 let mut any_err = Ok(());
359 for (account, result, trie, bufs) in results {
360 self.storage.tries.insert(account, trie);
361 if let Ok(_total_nodes) = result {
362 #[cfg(feature = "metrics")]
363 {
364 self.metrics.increment_total_storage_nodes(_total_nodes as u64);
365 }
366 } else {
367 any_err = result.map(|_| ());
368 }
369
370 self.deferred_drops.proof_nodes_bufs.extend(bufs);
372 }
373
374 any_err
375 }
376 }
377
378 pub fn reveal_account_v2_proof_nodes(
383 &mut self,
384 mut nodes: Vec<ProofTrieNodeV2>,
385 ) -> SparseStateTrieResult<()> {
386 let capacity = estimate_v2_proof_capacity(&nodes);
387
388 #[cfg(feature = "metrics")]
389 self.metrics.increment_total_account_nodes(nodes.len() as u64);
390
391 let root_node = nodes.iter().find(|n| n.path.is_empty());
392 let trie = if let Some(root_node) = root_node {
393 trace!(target: "trie::sparse", ?root_node, "Revealing root account node from V2 proof");
394 self.state.reveal_root(root_node.node.clone(), root_node.masks, self.retain_updates)?
395 } else {
396 self.state.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
397 };
398 trie.reserve_nodes(capacity);
399 trace!(target: "trie::sparse", total_nodes = ?nodes.len(), "Revealing account nodes from V2 proof");
400 trie.reveal_nodes(&mut nodes)?;
401
402 self.deferred_drops.proof_nodes_bufs.push(nodes);
403 Ok(())
404 }
405
406 pub fn reveal_storage_v2_proof_nodes(
411 &mut self,
412 account: B256,
413 nodes: Vec<ProofTrieNodeV2>,
414 ) -> SparseStateTrieResult<()> {
415 let trie = self.storage.get_or_create_trie_mut(account);
416 let _total_nodes = Self::reveal_storage_v2_proof_nodes_inner(
417 account,
418 nodes,
419 trie,
420 &mut self.deferred_drops.proof_nodes_bufs,
421 self.retain_updates,
422 )?;
423
424 #[cfg(feature = "metrics")]
425 {
426 self.metrics.increment_total_storage_nodes(_total_nodes as u64);
427 }
428
429 Ok(())
430 }
431
432 fn reveal_storage_v2_proof_nodes_inner(
435 account: B256,
436 mut nodes: Vec<ProofTrieNodeV2>,
437 trie: &mut RevealableSparseTrie<S>,
438 bufs: &mut Vec<Vec<ProofTrieNodeV2>>,
439 retain_updates: bool,
440 ) -> SparseStateTrieResult<usize> {
441 let capacity = estimate_v2_proof_capacity(&nodes);
442 let total_nodes = nodes.len();
443
444 let root_node = nodes.iter().find(|n| n.path.is_empty());
445 let trie = if let Some(root_node) = root_node {
446 trace!(target: "trie::sparse", ?account, ?root_node, "Revealing root storage node from V2 proof");
447 trie.reveal_root(root_node.node.clone(), root_node.masks, retain_updates)?
448 } else {
449 trie.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
450 };
451 trie.reserve_nodes(capacity);
452 trace!(target: "trie::sparse", ?account, total_nodes, "Revealing storage nodes from V2 proof");
453 trie.reveal_nodes(&mut nodes)?;
454
455 bufs.push(nodes);
456 Ok(total_nodes)
457 }
458
459 pub fn wipe_storage(&mut self, address: B256) -> SparseStateTrieResult<()> {
461 if let Some(trie) = self.storage.tries.get_mut(&address) {
462 trie.wipe()?;
463 }
464 Ok(())
465 }
466
467 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
471 pub fn calculate_subtries(&mut self) {
472 if let RevealableSparseTrie::Revealed(trie) = &mut self.state {
473 trie.update_subtrie_hashes();
474 }
475 }
476
477 pub fn storage_root(&mut self, account: &B256) -> Option<B256> {
479 self.storage.tries.get_mut(account).and_then(|trie| trie.root())
480 }
481
482 fn revealed_trie_mut(
486 &mut self,
487 provider_factory: impl TrieNodeProviderFactory,
488 ) -> SparseStateTrieResult<&mut A> {
489 match self.state {
490 RevealableSparseTrie::Blind(_) => {
491 let (root_node, hash_mask, tree_mask) = provider_factory
492 .account_node_provider()
493 .trie_node(&Nibbles::default())?
494 .map(|node| {
495 TrieNodeV2::decode(&mut &node.node[..])
496 .map(|decoded| (decoded, node.hash_mask, node.tree_mask))
497 })
498 .transpose()?
499 .unwrap_or((TrieNodeV2::EmptyRoot, None, None));
500 let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
501 self.state.reveal_root(root_node, masks, self.retain_updates).map_err(Into::into)
502 }
503 RevealableSparseTrie::Revealed(ref mut trie) => Ok(trie),
504 }
505 }
506
507 pub fn root(
511 &mut self,
512 provider_factory: impl TrieNodeProviderFactory,
513 ) -> SparseStateTrieResult<B256> {
514 #[cfg(feature = "metrics")]
516 self.metrics.record();
517
518 Ok(self.revealed_trie_mut(provider_factory)?.root())
519 }
520
521 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
523 pub fn root_with_updates(
524 &mut self,
525 provider_factory: impl TrieNodeProviderFactory,
526 ) -> SparseStateTrieResult<(B256, TrieUpdates)> {
527 #[cfg(feature = "metrics")]
529 self.metrics.record();
530
531 let storage_tries = self.storage_trie_updates();
532 let revealed = self.revealed_trie_mut(provider_factory)?;
533
534 let (root, updates) = (revealed.root(), revealed.take_updates());
535 let updates = TrieUpdates {
536 account_nodes: updates.updated_nodes,
537 removed_nodes: updates.removed_nodes,
538 storage_tries,
539 };
540 Ok((root, updates))
541 }
542
543 pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdates> {
547 self.storage
548 .tries
549 .iter_mut()
550 .map(|(address, trie)| {
551 let trie = trie.as_revealed_mut().unwrap();
552 let updates = trie.take_updates();
553 let updates = StorageTrieUpdates {
554 is_deleted: updates.wiped,
555 storage_nodes: updates.updated_nodes,
556 removed_nodes: updates.removed_nodes,
557 };
558 (*address, updates)
559 })
560 .filter(|(_, updates)| !updates.is_empty())
561 .collect()
562 }
563
564 pub fn take_trie_updates(&mut self) -> Option<TrieUpdates> {
568 let storage_tries = self.storage_trie_updates();
569 self.state.as_revealed_mut().map(|state| {
570 let updates = state.take_updates();
571 TrieUpdates {
572 account_nodes: updates.updated_nodes,
573 removed_nodes: updates.removed_nodes,
574 storage_tries,
575 }
576 })
577 }
578
579 pub fn update_account_leaf(
581 &mut self,
582 path: Nibbles,
583 value: Vec<u8>,
584 provider_factory: impl TrieNodeProviderFactory,
585 ) -> SparseStateTrieResult<()> {
586 let provider = provider_factory.account_node_provider();
587 self.state.update_leaf(path, value, provider)?;
588 Ok(())
589 }
590
591 pub fn update_storage_leaf(
593 &mut self,
594 address: B256,
595 slot: Nibbles,
596 value: Vec<u8>,
597 provider_factory: impl TrieNodeProviderFactory,
598 ) -> SparseStateTrieResult<()> {
599 let provider = provider_factory.storage_node_provider(address);
600 self.storage
601 .tries
602 .get_mut(&address)
603 .ok_or(SparseTrieErrorKind::Blind)?
604 .update_leaf(slot, value, provider)?;
605 Ok(())
606 }
607
608 #[instrument(level = "trace", target = "trie::sparse", skip_all)]
614 pub fn update_account(
615 &mut self,
616 address: B256,
617 account: Account,
618 provider_factory: impl TrieNodeProviderFactory,
619 ) -> SparseStateTrieResult<bool> {
620 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
621 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
622 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
623 } else if self.is_account_revealed(address) {
624 trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
625 if let Some(value) = self.get_account_value(&address) {
627 TrieAccount::decode(&mut &value[..])?.storage_root
629 } else {
630 EMPTY_ROOT_HASH
632 }
633 } else {
634 return Err(SparseTrieErrorKind::Blind.into())
635 };
636
637 if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
638 return Ok(false);
639 }
640
641 trace!(target: "trie::sparse", ?address, "Updating account");
642 let nibbles = Nibbles::unpack(address);
643 self.account_rlp_buf.clear();
644 account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
645 self.update_account_leaf(nibbles, self.account_rlp_buf.clone(), provider_factory)?;
646
647 Ok(true)
648 }
649
650 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
657 pub fn update_account_storage_root(
658 &mut self,
659 address: B256,
660 provider_factory: impl TrieNodeProviderFactory,
661 ) -> SparseStateTrieResult<bool> {
662 if !self.is_account_revealed(address) {
663 return Err(SparseTrieErrorKind::Blind.into())
664 }
665
666 let Some(mut trie_account) = self
668 .get_account_value(&address)
669 .map(|v| TrieAccount::decode(&mut &v[..]))
670 .transpose()?
671 else {
672 trace!(target: "trie::sparse", ?address, "Account not found in trie, skipping storage root update");
673 return Ok(true)
674 };
675
676 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
679 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
680 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
681 } else {
682 EMPTY_ROOT_HASH
683 };
684
685 trie_account.storage_root = storage_root;
687
688 if trie_account == TrieAccount::default() {
690 return Ok(false)
691 }
692
693 trace!(target: "trie::sparse", ?address, "Updating account with the new storage root");
695 let nibbles = Nibbles::unpack(address);
696 self.account_rlp_buf.clear();
697 trie_account.encode(&mut self.account_rlp_buf);
698 self.update_account_leaf(nibbles, self.account_rlp_buf.clone(), provider_factory)?;
699
700 Ok(true)
701 }
702
703 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
705 pub fn remove_account_leaf(
706 &mut self,
707 path: &Nibbles,
708 provider_factory: impl TrieNodeProviderFactory,
709 ) -> SparseStateTrieResult<()> {
710 let provider = provider_factory.account_node_provider();
711 self.state.remove_leaf(path, provider)?;
712 Ok(())
713 }
714
715 pub fn remove_storage_leaf(
717 &mut self,
718 address: B256,
719 slot: &Nibbles,
720 provider_factory: impl TrieNodeProviderFactory,
721 ) -> SparseStateTrieResult<()> {
722 let storage_trie =
723 self.storage.tries.get_mut(&address).ok_or(SparseTrieErrorKind::Blind)?;
724
725 let provider = provider_factory.storage_node_provider(address);
726 storage_trie.remove_leaf(slot, provider)?;
727 Ok(())
728 }
729}
730
731impl<A, S> SparseStateTrie<A, S>
732where
733 A: SparseTrieTrait + Default,
734 S: SparseTrieTrait + Default + Clone,
735{
736 pub fn clear(&mut self) {
741 self.state.clear();
742 self.storage.clear();
743 self.account_rlp_buf.clear();
744 }
745
746 pub fn memory_size(&self) -> usize {
751 let mut size = core::mem::size_of::<Self>();
752
753 size += match &self.state {
754 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
755 t.memory_size()
756 }
757 RevealableSparseTrie::Blind(None) => 0,
758 };
759
760 for trie in self.storage.tries.values() {
761 size += match trie {
762 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
763 t.memory_size()
764 }
765 RevealableSparseTrie::Blind(None) => 0,
766 };
767 }
768 for trie in &self.storage.cleared_tries {
769 size += match trie {
770 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
771 t.memory_size()
772 }
773 RevealableSparseTrie::Blind(None) => 0,
774 };
775 }
776
777 size
778 }
779
780 pub fn retained_storage_tries_count(&self) -> usize {
782 self.storage.tries.len() + self.storage.cleared_tries.len()
783 }
784
785 pub fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
790 let storage_tries_count = self.storage.tries.len() + self.storage.cleared_tries.len();
792
793 let total_tries = 1 + storage_tries_count;
795
796 let nodes_per_trie = max_nodes / total_tries;
798 let values_per_trie = max_values / total_tries;
799
800 self.state.shrink_nodes_to(nodes_per_trie);
802 self.state.shrink_values_to(values_per_trie);
803
804 let storage_nodes = max_nodes.saturating_sub(nodes_per_trie);
806 let storage_values = max_values.saturating_sub(values_per_trie);
807
808 self.storage.shrink_to(storage_nodes, storage_values);
810 }
811
812 #[cfg(feature = "std")]
826 #[instrument(
827 level = "debug",
828 name = "SparseStateTrie::prune",
829 target = "trie::sparse",
830 skip_all,
831 fields(%max_hot_slots, %max_hot_accounts)
832 )]
833 pub fn prune(&mut self, max_hot_slots: usize, max_hot_accounts: usize) {
834 self.hot_slots_lfu.decay_and_evict(max_hot_slots);
835 self.hot_accounts_lfu.decay_and_evict(max_hot_accounts);
836 let retained = self.hot_slots_lfu.retained_slots_by_address();
837
838 let retained_account_paths: Vec<Nibbles> =
839 self.hot_accounts_lfu.keys().map(|k| Nibbles::unpack(*k)).collect();
840
841 let retained_accounts = retained_account_paths.len();
842 let retained_storage_tries = retained.len();
843 let total_storage_tries_before = self.storage.tries.len();
844
845 let (account_nodes_pruned, storage_tries_evicted) = rayon::join(
847 || {
848 self.state
849 .as_revealed_mut()
850 .map(|trie| trie.prune(&retained_account_paths))
851 .unwrap_or(0)
852 },
853 || self.storage.prune_by_retained_slots(retained),
854 );
855
856 debug!(
857 target: "trie::sparse",
858 retained_accounts,
859 retained_storage_tries,
860 account_nodes_pruned,
861 storage_tries_evicted,
862 storage_tries_after = total_storage_tries_before - storage_tries_evicted,
863 "SparseStateTrie::prune completed"
864 );
865 }
866
867 pub fn commit_updates(&mut self, updates: &TrieUpdates) {
869 if let Some(trie) = self.state.as_revealed_mut() {
870 trie.commit_updates(&updates.account_nodes, &updates.removed_nodes);
871 }
872 for (address, updates) in &updates.storage_tries {
873 if let Some(trie) =
874 self.storage.tries.get_mut(address).and_then(|t| t.as_revealed_mut())
875 {
876 trie.commit_updates(&updates.storage_nodes, &updates.removed_nodes);
877 }
878 }
879 }
880}
881
882#[derive(Debug, Default)]
885struct StorageTries<S = ParallelSparseTrie> {
886 tries: B256Map<RevealableSparseTrie<S>>,
888 cleared_tries: Vec<RevealableSparseTrie<S>>,
890 default_trie: RevealableSparseTrie<S>,
892}
893
894#[cfg(feature = "std")]
895impl<S: SparseTrieTrait> StorageTries<S> {
896 fn prune_by_retained_slots(&mut self, retained_slots: B256Map<Vec<Nibbles>>) -> usize {
901 {
903 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
904 self.tries.par_iter_mut().for_each(|(address, trie)| {
905 if let Some(slots) = retained_slots.get(address) {
906 if let Some(t) = trie.as_revealed_mut() {
907 t.prune(slots);
908 }
909 } else {
910 trie.clear();
911 }
912 });
913 }
914
915 let addresses_to_evict: Vec<B256> = self
917 .tries
918 .keys()
919 .filter(|address| !retained_slots.contains_key(*address))
920 .copied()
921 .collect();
922
923 let evicted = addresses_to_evict.len();
924 self.cleared_tries.reserve(evicted);
925 for address in &addresses_to_evict {
926 if let Some(trie) = self.tries.remove(address) {
927 self.cleared_tries.push(trie);
928 }
929 }
930
931 evicted
932 }
933}
934
935impl<S: SparseTrieTrait> StorageTries<S> {
936 fn clear(&mut self) {
939 self.cleared_tries.extend(self.tries.drain().map(|(_, mut trie)| {
940 trie.clear();
941 trie
942 }));
943 }
944
945 fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
949 let total_tries = self.tries.len() + self.cleared_tries.len();
950 if total_tries == 0 {
951 return;
952 }
953
954 let nodes_per_trie = max_nodes / total_tries;
956 let values_per_trie = max_values / total_tries;
957
958 for trie in self.tries.values_mut() {
960 trie.shrink_nodes_to(nodes_per_trie);
961 trie.shrink_values_to(values_per_trie);
962 }
963
964 for trie in &mut self.cleared_tries {
966 trie.shrink_nodes_to(nodes_per_trie);
967 trie.shrink_values_to(values_per_trie);
968 }
969 }
970}
971
972impl<S: SparseTrieTrait + Clone> StorageTries<S> {
973 fn get_or_create_trie_mut(&mut self, address: B256) -> &mut RevealableSparseTrie<S> {
975 self.tries.entry(address).or_insert_with(|| {
976 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
977 })
978 }
979
980 #[cfg(feature = "std")]
983 fn take_or_create_trie(&mut self, account: &B256) -> RevealableSparseTrie<S> {
984 self.tries.remove(account).unwrap_or_else(|| {
985 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
986 })
987 }
988}
989
990#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
992struct HotSlotKey {
993 address: B256,
994 slot: B256,
995}
996
997#[cfg(feature = "std")]
999impl BucketedLfu<HotSlotKey> {
1000 fn retained_slots_by_address(&self) -> B256Map<Vec<Nibbles>> {
1002 let mut grouped =
1003 B256Map::<Vec<Nibbles>>::with_capacity_and_hasher(self.len(), Default::default());
1004 for key in self.keys() {
1005 grouped.entry(key.address).or_default().push(Nibbles::unpack(key.slot));
1006 }
1007
1008 for slots in grouped.values_mut() {
1009 slots.sort_unstable();
1010 slots.dedup();
1011 }
1012
1013 grouped
1014 }
1015}
1016
1017fn estimate_v2_proof_capacity(nodes: &[ProofTrieNodeV2]) -> usize {
1022 let mut capacity = nodes.len();
1023
1024 for node in nodes {
1025 if let TrieNodeV2::Branch(branch) = &node.node {
1026 capacity += branch.state_mask.count_ones() as usize;
1027 }
1028 }
1029
1030 capacity
1031}
1032
1033#[cfg(test)]
1034mod tests {
1035 use super::*;
1036 use crate::{provider::DefaultTrieNodeProviderFactory, LeafLookup, ParallelSparseTrie};
1037 use alloy_primitives::{
1038 b256,
1039 map::{HashMap, HashSet},
1040 U256,
1041 };
1042 use arbitrary::Arbitrary;
1043 use rand::{rngs::StdRng, Rng, SeedableRng};
1044 use reth_primitives_traits::Account;
1045 use reth_trie::{updates::StorageTrieUpdates, HashBuilder, MultiProof, EMPTY_ROOT_HASH};
1046 use reth_trie_common::{
1047 proof::{ProofNodes, ProofRetainer},
1048 BranchNodeMasks, BranchNodeMasksMap, BranchNodeV2, LeafNode, RlpNode, StorageMultiProof,
1049 TrieMask,
1050 };
1051
1052 fn leaf_key(suffix: impl AsRef<[u8]>, total_len: usize) -> Nibbles {
1054 let suffix = suffix.as_ref();
1055 let mut nibbles = Nibbles::from_nibbles(suffix);
1056 nibbles.extend(&Nibbles::from_nibbles_unchecked(vec![0; total_len - suffix.len()]));
1057 nibbles
1058 }
1059
1060 #[test]
1061 fn reveal_account_path_twice() {
1062 let provider_factory = DefaultTrieNodeProviderFactory;
1063 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1064
1065 let full_path_0 = leaf_key([0x0], 64);
1067 let _full_path_1 = leaf_key([0x1], 64);
1068
1069 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1070 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1072 leaf_key([], 63),
1073 leaf_value.clone(),
1074 )));
1075 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1076 leaf_key([], 63),
1077 leaf_value.clone(),
1078 )));
1079
1080 let multiproof = MultiProof {
1081 account_subtree: ProofNodes::from_iter([
1082 (
1083 Nibbles::default(),
1084 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
1085 key: Nibbles::default(),
1086 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1087 state_mask: TrieMask::new(0b11),
1088 branch_rlp_node: None,
1089 }))
1090 .into(),
1091 ),
1092 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1093 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1094 ]),
1095 ..Default::default()
1096 };
1097
1098 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1100 assert!(matches!(
1101 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1102 Ok(LeafLookup::Exists)
1103 ));
1104 assert_eq!(
1105 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
1106 Some(&leaf_value)
1107 );
1108
1109 sparse.remove_account_leaf(&full_path_0, &provider_factory).unwrap();
1112 assert!(matches!(
1113 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1114 Ok(LeafLookup::NonExistent)
1115 ));
1116 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
1117 }
1118
1119 #[test]
1120 fn reveal_storage_path_twice() {
1121 let provider_factory = DefaultTrieNodeProviderFactory;
1122 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1123
1124 let full_path_0 = leaf_key([0x0], 64);
1126
1127 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1128 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1129 leaf_key([], 63),
1130 leaf_value.clone(),
1131 )));
1132 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1133 leaf_key([], 63),
1134 leaf_value.clone(),
1135 )));
1136
1137 let multiproof = MultiProof {
1138 storages: HashMap::from_iter([(
1139 B256::ZERO,
1140 StorageMultiProof {
1141 root: B256::ZERO,
1142 subtree: ProofNodes::from_iter([
1143 (
1144 Nibbles::default(),
1145 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
1146 key: Nibbles::default(),
1147 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1148 state_mask: TrieMask::new(0b11),
1149 branch_rlp_node: None,
1150 }))
1151 .into(),
1152 ),
1153 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1154 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1155 ]),
1156 branch_node_masks: Default::default(),
1157 },
1158 )]),
1159 ..Default::default()
1160 };
1161
1162 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1164 assert!(matches!(
1165 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1166 Ok(LeafLookup::Exists)
1167 ));
1168 assert_eq!(
1169 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
1170 Some(&leaf_value)
1171 );
1172
1173 sparse.remove_storage_leaf(B256::ZERO, &full_path_0, &provider_factory).unwrap();
1176 assert!(matches!(
1177 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1178 Ok(LeafLookup::NonExistent)
1179 ));
1180 assert!(sparse
1181 .storage_trie_ref(&B256::ZERO)
1182 .unwrap()
1183 .get_leaf_value(&full_path_0)
1184 .is_none());
1185 }
1186
1187 #[test]
1188 fn seeded_hot_cache_capacities_preserve_first_cycle_touches() {
1189 let account = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1190 let slot = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1191 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1192
1193 sparse.set_hot_cache_capacities(1, 1);
1194 sparse.record_account_touch(account);
1195 sparse.record_slot_touch(account, slot);
1196 sparse.prune(1, 1);
1197
1198 assert_eq!(sparse.hot_accounts_lfu.keys().copied().collect::<Vec<_>>(), vec![account]);
1199 assert_eq!(
1200 sparse.hot_slots_lfu.keys().copied().collect::<Vec<_>>(),
1201 vec![HotSlotKey { address: account, slot }]
1202 );
1203 }
1204
1205 #[test]
1206 fn reveal_v2_proof_nodes() {
1207 let provider_factory = DefaultTrieNodeProviderFactory;
1208 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1209
1210 let full_path_0 = leaf_key([0x0], 64);
1212
1213 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1214 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
1215 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
1216
1217 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
1218 key: Nibbles::default(),
1219 stack: vec![
1220 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1221 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1222 ],
1223 state_mask: TrieMask::new(0b11),
1224 branch_rlp_node: None,
1225 });
1226
1227 let v2_proof_nodes = vec![
1229 ProofTrieNodeV2 {
1230 path: Nibbles::default(),
1231 node: branch_node,
1232 masks: Some(BranchNodeMasks {
1233 hash_mask: TrieMask::default(),
1234 tree_mask: TrieMask::default(),
1235 }),
1236 },
1237 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1238 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1239 ];
1240
1241 sparse.reveal_account_v2_proof_nodes(v2_proof_nodes).unwrap();
1243
1244 assert!(matches!(
1246 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1247 Ok(LeafLookup::Exists)
1248 ));
1249 assert_eq!(
1250 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
1251 Some(&leaf_value)
1252 );
1253
1254 sparse.remove_account_leaf(&full_path_0, &provider_factory).unwrap();
1256 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
1257 }
1258
1259 #[test]
1260 fn reveal_storage_v2_proof_nodes() {
1261 let provider_factory = DefaultTrieNodeProviderFactory;
1262 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1263
1264 let full_path_0 = leaf_key([0x0], 64);
1266
1267 let storage_value: Vec<u8> = alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec();
1268 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
1269 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
1270
1271 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
1272 key: Nibbles::default(),
1273 stack: vec![
1274 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1275 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1276 ],
1277 state_mask: TrieMask::new(0b11),
1278 branch_rlp_node: None,
1279 });
1280
1281 let v2_proof_nodes = vec![
1282 ProofTrieNodeV2 { path: Nibbles::default(), node: branch_node, masks: None },
1283 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1284 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1285 ];
1286
1287 sparse.reveal_storage_v2_proof_nodes(B256::ZERO, v2_proof_nodes).unwrap();
1289
1290 assert!(matches!(
1292 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1293 Ok(LeafLookup::Exists)
1294 ));
1295 assert_eq!(
1296 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
1297 Some(&storage_value)
1298 );
1299
1300 sparse.remove_storage_leaf(B256::ZERO, &full_path_0, &provider_factory).unwrap();
1302 assert!(sparse
1303 .storage_trie_ref(&B256::ZERO)
1304 .unwrap()
1305 .get_leaf_value(&full_path_0)
1306 .is_none());
1307 }
1308
1309 #[test]
1310 fn take_trie_updates() {
1311 reth_tracing::init_test_tracing();
1312
1313 let mut rng = StdRng::seed_from_u64(1);
1315
1316 let mut bytes = [0u8; 1024];
1317 rng.fill(bytes.as_mut_slice());
1318
1319 let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1320 let slot_path_1 = Nibbles::unpack(slot_1);
1321 let value_1 = U256::from(rng.random::<u64>());
1322 let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1323 let slot_path_2 = Nibbles::unpack(slot_2);
1324 let value_2 = U256::from(rng.random::<u64>());
1325 let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1326 let slot_path_3 = Nibbles::unpack(slot_3);
1327 let value_3 = U256::from(rng.random::<u64>());
1328
1329 let mut storage_hash_builder = HashBuilder::default()
1330 .with_proof_retainer(ProofRetainer::from_iter([slot_path_1, slot_path_2]));
1331 storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
1332 storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
1333
1334 let storage_root = storage_hash_builder.root();
1335 let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
1336 let storage_branch_node_masks = BranchNodeMasksMap::from_iter([
1337 (
1338 Nibbles::default(),
1339 BranchNodeMasks { hash_mask: TrieMask::new(0b010), tree_mask: TrieMask::default() },
1340 ),
1341 (
1342 Nibbles::from_nibbles([0x1]),
1343 BranchNodeMasks { hash_mask: TrieMask::new(0b11), tree_mask: TrieMask::default() },
1344 ),
1345 ]);
1346
1347 let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1348 let address_path_1 = Nibbles::unpack(address_1);
1349 let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1350 let mut trie_account_1 = account_1.into_trie_account(storage_root);
1351 let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1352 let address_path_2 = Nibbles::unpack(address_2);
1353 let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1354 let mut trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
1355
1356 let mut hash_builder = HashBuilder::default()
1357 .with_proof_retainer(ProofRetainer::from_iter([address_path_1, address_path_2]));
1358 hash_builder.add_leaf(address_path_1, &alloy_rlp::encode(trie_account_1));
1359 hash_builder.add_leaf(address_path_2, &alloy_rlp::encode(trie_account_2));
1360
1361 let root = hash_builder.root();
1362 let proof_nodes = hash_builder.take_proof_nodes();
1363
1364 let provider_factory = DefaultTrieNodeProviderFactory;
1365 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default().with_updates(true);
1366 sparse
1367 .reveal_decoded_multiproof(
1368 MultiProof {
1369 account_subtree: proof_nodes,
1370 branch_node_masks: BranchNodeMasksMap::from_iter([(
1371 Nibbles::from_nibbles([0x1]),
1372 BranchNodeMasks {
1373 hash_mask: TrieMask::new(0b00),
1374 tree_mask: TrieMask::default(),
1375 },
1376 )]),
1377 storages: HashMap::from_iter([
1378 (
1379 address_1,
1380 StorageMultiProof {
1381 root,
1382 subtree: storage_proof_nodes.clone(),
1383 branch_node_masks: storage_branch_node_masks.clone(),
1384 },
1385 ),
1386 (
1387 address_2,
1388 StorageMultiProof {
1389 root,
1390 subtree: storage_proof_nodes,
1391 branch_node_masks: storage_branch_node_masks,
1392 },
1393 ),
1394 ]),
1395 }
1396 .try_into()
1397 .unwrap(),
1398 )
1399 .unwrap();
1400
1401 assert_eq!(sparse.root(&provider_factory).unwrap(), root);
1402
1403 let address_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1404 let address_path_3 = Nibbles::unpack(address_3);
1405 let account_3 = Account { nonce: account_1.nonce + 1, ..account_1 };
1406 let trie_account_3 = account_3.into_trie_account(EMPTY_ROOT_HASH);
1407
1408 sparse
1409 .update_account_leaf(
1410 address_path_3,
1411 alloy_rlp::encode(trie_account_3),
1412 &provider_factory,
1413 )
1414 .unwrap();
1415
1416 sparse
1417 .update_storage_leaf(
1418 address_1,
1419 slot_path_3,
1420 alloy_rlp::encode(value_3),
1421 &provider_factory,
1422 )
1423 .unwrap();
1424 trie_account_1.storage_root = sparse.storage_root(&address_1).unwrap();
1425 sparse
1426 .update_account_leaf(
1427 address_path_1,
1428 alloy_rlp::encode(trie_account_1),
1429 &provider_factory,
1430 )
1431 .unwrap();
1432
1433 sparse.wipe_storage(address_2).unwrap();
1434 trie_account_2.storage_root = sparse.storage_root(&address_2).unwrap();
1435 sparse
1436 .update_account_leaf(
1437 address_path_2,
1438 alloy_rlp::encode(trie_account_2),
1439 &provider_factory,
1440 )
1441 .unwrap();
1442
1443 sparse.root(&provider_factory).unwrap();
1444
1445 let sparse_updates = sparse.take_trie_updates().unwrap();
1446 pretty_assertions::assert_eq!(
1448 sparse_updates,
1449 TrieUpdates {
1450 account_nodes: HashMap::default(),
1451 storage_tries: HashMap::from_iter([(
1452 b256!("0x1100000000000000000000000000000000000000000000000000000000000000"),
1453 StorageTrieUpdates {
1454 is_deleted: true,
1455 storage_nodes: HashMap::default(),
1456 removed_nodes: HashSet::default()
1457 }
1458 )]),
1459 removed_nodes: HashSet::default()
1460 }
1461 );
1462 }
1463}