1#[cfg(feature = "trie-debug")]
2use crate::debug_recorder::TrieDebugRecorder;
3use crate::{
4 provider::{TrieNodeProvider, TrieNodeProviderFactory},
5 traits::SparseTrie as SparseTrieTrait,
6 ParallelSparseTrie, RevealableSparseTrie,
7};
8use alloc::vec::Vec;
9use alloy_primitives::{
10 map::{B256Map, B256Set},
11 B256,
12};
13use alloy_rlp::{Decodable, Encodable};
14use reth_execution_errors::{SparseStateTrieResult, SparseTrieErrorKind};
15use reth_primitives_traits::Account;
16#[cfg(feature = "std")]
17use reth_primitives_traits::FastInstant as Instant;
18use reth_trie_common::{
19 updates::{StorageTrieUpdates, TrieUpdates},
20 BranchNodeMasks, DecodedMultiProof, MultiProof, Nibbles, ProofTrieNodeV2, TrieAccount,
21 TrieNodeV2, EMPTY_ROOT_HASH, TRIE_ACCOUNT_RLP_MAX_SIZE,
22};
23#[cfg(feature = "std")]
24use tracing::debug;
25use tracing::{instrument, trace};
26
27#[derive(Debug, Default)]
32pub struct DeferredDrops {
33 pub proof_nodes_bufs: Vec<Vec<ProofTrieNodeV2>>,
35}
36
37#[derive(Debug)]
38pub struct SparseStateTrie<
40 A = ParallelSparseTrie, S = ParallelSparseTrie, > {
43 state: RevealableSparseTrie<A>,
45 storage: StorageTries<S>,
47 retain_updates: bool,
49 account_rlp_buf: Vec<u8>,
51 deferred_drops: DeferredDrops,
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 #[cfg(feature = "metrics")]
71 metrics: Default::default(),
72 }
73 }
74}
75
76#[cfg(test)]
77impl SparseStateTrie {
78 pub fn from_state(state: RevealableSparseTrie) -> Self {
80 Self { state, ..Default::default() }
81 }
82}
83
84impl<A, S> SparseStateTrie<A, S> {
85 pub const fn set_updates(&mut self, retain_updates: bool) {
87 self.retain_updates = retain_updates;
88 }
89
90 pub const fn with_updates(mut self, retain_updates: bool) -> Self {
92 self.set_updates(retain_updates);
93 self
94 }
95
96 pub fn set_accounts_trie(&mut self, trie: RevealableSparseTrie<A>) {
98 self.state = trie;
99 }
100
101 pub fn with_accounts_trie(mut self, trie: RevealableSparseTrie<A>) -> Self {
103 self.set_accounts_trie(trie);
104 self
105 }
106
107 pub fn set_default_storage_trie(&mut self, trie: RevealableSparseTrie<S>) {
110 self.storage.default_trie = trie;
111 }
112
113 pub fn with_default_storage_trie(mut self, trie: RevealableSparseTrie<S>) -> Self {
116 self.set_default_storage_trie(trie);
117 self
118 }
119
120 pub fn take_deferred_drops(&mut self) -> DeferredDrops {
125 core::mem::take(&mut self.deferred_drops)
126 }
127}
128
129impl SparseStateTrie {
130 pub fn new() -> Self {
132 Self::default()
133 }
134}
135
136impl<A: SparseTrieTrait, S: SparseTrieTrait> SparseStateTrie<A, S> {
137 #[cfg(feature = "trie-debug")]
142 pub fn take_debug_recorders(&mut self) -> alloc::vec::Vec<(Option<B256>, TrieDebugRecorder)> {
143 let mut recorders = alloc::vec::Vec::new();
144 if let Some(trie) = self.state.as_revealed_mut() {
145 recorders.push((None, trie.take_debug_recorder()));
146 }
147 for (address, trie) in &mut self.storage.tries {
148 if let Some(trie) = trie.as_revealed_mut() {
149 recorders.push((Some(*address), trie.take_debug_recorder()));
150 }
151 }
152 recorders
153 }
154}
155
156impl<A, S> SparseStateTrie<A, S>
157where
158 A: SparseTrieTrait + Default,
159 S: SparseTrieTrait + Default + Clone,
160{
161 pub const fn trie_mut(&mut self) -> &mut RevealableSparseTrie<A> {
163 &mut self.state
164 }
165
166 pub fn is_account_revealed(&self, account: B256) -> bool {
168 let path = Nibbles::unpack(account);
169 let trie = match self.state_trie_ref() {
170 Some(t) => t,
171 None => return false,
172 };
173
174 trie.find_leaf(&path, None).is_ok()
175 }
176
177 pub fn check_valid_storage_witness(&self, address: B256, slot: B256) -> bool {
179 let path = Nibbles::unpack(slot);
180 let trie = match self.storage_trie_ref(&address) {
181 Some(t) => t,
182 None => return false,
183 };
184
185 trie.find_leaf(&path, None).is_ok()
186 }
187
188 pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
190 self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
191 }
192
193 pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
195 self.storage.tries.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
196 }
197
198 pub const fn state_trie_ref(&self) -> Option<&A> {
200 self.state.as_revealed_ref()
201 }
202
203 pub fn storage_trie_ref(&self, address: &B256) -> Option<&S> {
205 self.storage.tries.get(address).and_then(|e| e.as_revealed_ref())
206 }
207
208 pub fn storage_trie_mut(&mut self, address: &B256) -> Option<&mut S> {
210 self.storage.tries.get_mut(address).and_then(|e| e.as_revealed_mut())
211 }
212
213 pub const fn storage_tries_mut(&mut self) -> &mut B256Map<RevealableSparseTrie<S>> {
215 &mut self.storage.tries
216 }
217
218 pub fn take_storage_trie(&mut self, address: &B256) -> Option<RevealableSparseTrie<S>> {
220 self.storage.tries.remove(address)
221 }
222
223 pub fn take_or_create_storage_trie(&mut self, address: &B256) -> RevealableSparseTrie<S> {
225 self.storage.tries.remove(address).unwrap_or_else(|| {
226 self.storage.cleared_tries.pop().unwrap_or_else(|| self.storage.default_trie.clone())
227 })
228 }
229
230 pub fn insert_storage_trie(&mut self, address: B256, storage_trie: RevealableSparseTrie<S>) {
232 self.storage.tries.insert(address, storage_trie);
233 }
234
235 pub fn get_or_create_storage_trie_mut(
237 &mut self,
238 address: B256,
239 ) -> &mut RevealableSparseTrie<S> {
240 self.storage.get_or_create_trie_mut(address)
241 }
242
243 pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
246 let decoded_multiproof = multiproof.try_into()?;
248
249 self.reveal_decoded_multiproof(decoded_multiproof)
251 }
252
253 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
256 pub fn reveal_decoded_multiproof(
257 &mut self,
258 multiproof: DecodedMultiProof,
259 ) -> SparseStateTrieResult<()> {
260 self.reveal_decoded_multiproof_v2(multiproof.into())
261 }
262
263 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
268 pub fn reveal_decoded_multiproof_v2(
269 &mut self,
270 multiproof: reth_trie_common::DecodedMultiProofV2,
271 ) -> SparseStateTrieResult<()> {
272 if !multiproof.account_proofs.is_empty() {
280 self.reveal_account_v2_proof_nodes(multiproof.account_proofs)?;
281 }
282
283 #[cfg(not(feature = "std"))]
284 {
286 for (account, storage_proofs) in multiproof.storage_proofs {
287 self.reveal_storage_v2_proof_nodes(account, storage_proofs)?;
288 self.storage.modifications.mark_accessed(account);
290 }
291
292 Ok(())
293 }
294
295 #[cfg(feature = "std")]
296 {
298 use rayon::iter::ParallelIterator;
299 use reth_primitives_traits::ParallelBridgeBuffered;
300
301 let retain_updates = self.retain_updates;
302
303 let results: Vec<_> = multiproof
307 .storage_proofs
308 .into_iter()
309 .map(|(account, storage_proofs)| {
310 let trie = self.storage.take_or_create_trie(&account);
311 (account, storage_proofs, trie)
312 })
313 .par_bridge_buffered()
314 .map(|(account, storage_proofs, mut trie)| {
315 let mut bufs = Vec::new();
316 let result = Self::reveal_storage_v2_proof_nodes_inner(
317 account,
318 storage_proofs,
319 &mut trie,
320 &mut bufs,
321 retain_updates,
322 );
323 (account, result, trie, bufs)
324 })
325 .collect();
326
327 let mut any_err = Ok(());
328 for (account, result, trie, bufs) in results {
329 self.storage.tries.insert(account, trie);
330 self.storage.modifications.mark_accessed(account);
332 if let Ok(_total_nodes) = result {
333 #[cfg(feature = "metrics")]
334 {
335 self.metrics.increment_total_storage_nodes(_total_nodes as u64);
336 }
337 } else {
338 any_err = result.map(|_| ());
339 }
340
341 self.deferred_drops.proof_nodes_bufs.extend(bufs);
343 }
344
345 any_err
346 }
347 }
348
349 pub fn reveal_account_v2_proof_nodes(
354 &mut self,
355 mut nodes: Vec<ProofTrieNodeV2>,
356 ) -> SparseStateTrieResult<()> {
357 let capacity = estimate_v2_proof_capacity(&nodes);
358
359 #[cfg(feature = "metrics")]
360 self.metrics.increment_total_account_nodes(nodes.len() as u64);
361
362 let root_node = nodes.iter().find(|n| n.path.is_empty());
363 let trie = if let Some(root_node) = root_node {
364 trace!(target: "trie::sparse", ?root_node, "Revealing root account node from V2 proof");
365 self.state.reveal_root(root_node.node.clone(), root_node.masks, self.retain_updates)?
366 } else {
367 self.state.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
368 };
369 trie.reserve_nodes(capacity);
370 trace!(target: "trie::sparse", total_nodes = ?nodes.len(), "Revealing account nodes from V2 proof");
371 trie.reveal_nodes(&mut nodes)?;
372
373 self.deferred_drops.proof_nodes_bufs.push(nodes);
374 Ok(())
375 }
376
377 pub fn reveal_storage_v2_proof_nodes(
382 &mut self,
383 account: B256,
384 nodes: Vec<ProofTrieNodeV2>,
385 ) -> SparseStateTrieResult<()> {
386 let trie = self.storage.get_or_create_trie_mut(account);
387 let _total_nodes = Self::reveal_storage_v2_proof_nodes_inner(
388 account,
389 nodes,
390 trie,
391 &mut self.deferred_drops.proof_nodes_bufs,
392 self.retain_updates,
393 )?;
394
395 #[cfg(feature = "metrics")]
396 {
397 self.metrics.increment_total_storage_nodes(_total_nodes as u64);
398 }
399
400 Ok(())
401 }
402
403 fn reveal_storage_v2_proof_nodes_inner(
406 account: B256,
407 mut nodes: Vec<ProofTrieNodeV2>,
408 trie: &mut RevealableSparseTrie<S>,
409 bufs: &mut Vec<Vec<ProofTrieNodeV2>>,
410 retain_updates: bool,
411 ) -> SparseStateTrieResult<usize> {
412 let capacity = estimate_v2_proof_capacity(&nodes);
413 let total_nodes = nodes.len();
414
415 let root_node = nodes.iter().find(|n| n.path.is_empty());
416 let trie = if let Some(root_node) = root_node {
417 trace!(target: "trie::sparse", ?account, ?root_node, "Revealing root storage node from V2 proof");
418 trie.reveal_root(root_node.node.clone(), root_node.masks, retain_updates)?
419 } else {
420 trie.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
421 };
422 trie.reserve_nodes(capacity);
423 trace!(target: "trie::sparse", ?account, total_nodes, "Revealing storage nodes from V2 proof");
424 trie.reveal_nodes(&mut nodes)?;
425
426 bufs.push(nodes);
427 Ok(total_nodes)
428 }
429
430 pub fn wipe_storage(&mut self, address: B256) -> SparseStateTrieResult<()> {
432 if let Some(trie) = self.storage.tries.get_mut(&address) {
433 trie.wipe()?;
434 }
435 Ok(())
436 }
437
438 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
442 pub fn calculate_subtries(&mut self) {
443 if let RevealableSparseTrie::Revealed(trie) = &mut self.state {
444 trie.update_subtrie_hashes();
445 }
446 }
447
448 pub fn storage_root(&mut self, account: &B256) -> Option<B256> {
450 self.storage.tries.get_mut(account).and_then(|trie| trie.root())
451 }
452
453 fn revealed_trie_mut(
457 &mut self,
458 provider_factory: impl TrieNodeProviderFactory,
459 ) -> SparseStateTrieResult<&mut A> {
460 match self.state {
461 RevealableSparseTrie::Blind(_) => {
462 let (root_node, hash_mask, tree_mask) = provider_factory
463 .account_node_provider()
464 .trie_node(&Nibbles::default())?
465 .map(|node| {
466 TrieNodeV2::decode(&mut &node.node[..])
467 .map(|decoded| (decoded, node.hash_mask, node.tree_mask))
468 })
469 .transpose()?
470 .unwrap_or((TrieNodeV2::EmptyRoot, None, None));
471 let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
472 self.state.reveal_root(root_node, masks, self.retain_updates).map_err(Into::into)
473 }
474 RevealableSparseTrie::Revealed(ref mut trie) => Ok(trie),
475 }
476 }
477
478 pub fn root(
482 &mut self,
483 provider_factory: impl TrieNodeProviderFactory,
484 ) -> SparseStateTrieResult<B256> {
485 #[cfg(feature = "metrics")]
487 self.metrics.record();
488
489 Ok(self.revealed_trie_mut(provider_factory)?.root())
490 }
491
492 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
494 pub fn root_with_updates(
495 &mut self,
496 provider_factory: impl TrieNodeProviderFactory,
497 ) -> SparseStateTrieResult<(B256, TrieUpdates)> {
498 #[cfg(feature = "metrics")]
500 self.metrics.record();
501
502 let storage_tries = self.storage_trie_updates();
503 let revealed = self.revealed_trie_mut(provider_factory)?;
504
505 let (root, updates) = (revealed.root(), revealed.take_updates());
506 let updates = TrieUpdates {
507 account_nodes: updates.updated_nodes,
508 removed_nodes: updates.removed_nodes,
509 storage_tries,
510 };
511 Ok((root, updates))
512 }
513
514 pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdates> {
518 self.storage
519 .tries
520 .iter_mut()
521 .map(|(address, trie)| {
522 let trie = trie.as_revealed_mut().unwrap();
523 let updates = trie.take_updates();
524 let updates = StorageTrieUpdates {
525 is_deleted: updates.wiped,
526 storage_nodes: updates.updated_nodes,
527 removed_nodes: updates.removed_nodes,
528 };
529 (*address, updates)
530 })
531 .filter(|(_, updates)| !updates.is_empty())
532 .collect()
533 }
534
535 pub fn take_trie_updates(&mut self) -> Option<TrieUpdates> {
539 let storage_tries = self.storage_trie_updates();
540 self.state.as_revealed_mut().map(|state| {
541 let updates = state.take_updates();
542 TrieUpdates {
543 account_nodes: updates.updated_nodes,
544 removed_nodes: updates.removed_nodes,
545 storage_tries,
546 }
547 })
548 }
549
550 pub fn update_account_leaf(
552 &mut self,
553 path: Nibbles,
554 value: Vec<u8>,
555 provider_factory: impl TrieNodeProviderFactory,
556 ) -> SparseStateTrieResult<()> {
557 let provider = provider_factory.account_node_provider();
558 self.state.update_leaf(path, value, provider)?;
559 Ok(())
560 }
561
562 pub fn update_storage_leaf(
564 &mut self,
565 address: B256,
566 slot: Nibbles,
567 value: Vec<u8>,
568 provider_factory: impl TrieNodeProviderFactory,
569 ) -> SparseStateTrieResult<()> {
570 let provider = provider_factory.storage_node_provider(address);
571 self.storage
572 .tries
573 .get_mut(&address)
574 .ok_or(SparseTrieErrorKind::Blind)?
575 .update_leaf(slot, value, provider)?;
576 Ok(())
577 }
578
579 #[instrument(level = "trace", target = "trie::sparse", skip_all)]
585 pub fn update_account(
586 &mut self,
587 address: B256,
588 account: Account,
589 provider_factory: impl TrieNodeProviderFactory,
590 ) -> SparseStateTrieResult<bool> {
591 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
592 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
593 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
594 } else if self.is_account_revealed(address) {
595 trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
596 if let Some(value) = self.get_account_value(&address) {
598 TrieAccount::decode(&mut &value[..])?.storage_root
600 } else {
601 EMPTY_ROOT_HASH
603 }
604 } else {
605 return Err(SparseTrieErrorKind::Blind.into())
606 };
607
608 if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
609 return Ok(false);
610 }
611
612 trace!(target: "trie::sparse", ?address, "Updating account");
613 let nibbles = Nibbles::unpack(address);
614 self.account_rlp_buf.clear();
615 account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
616 self.update_account_leaf(nibbles, self.account_rlp_buf.clone(), provider_factory)?;
617
618 Ok(true)
619 }
620
621 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
628 pub fn update_account_storage_root(
629 &mut self,
630 address: B256,
631 provider_factory: impl TrieNodeProviderFactory,
632 ) -> SparseStateTrieResult<bool> {
633 if !self.is_account_revealed(address) {
634 return Err(SparseTrieErrorKind::Blind.into())
635 }
636
637 let Some(mut trie_account) = self
639 .get_account_value(&address)
640 .map(|v| TrieAccount::decode(&mut &v[..]))
641 .transpose()?
642 else {
643 trace!(target: "trie::sparse", ?address, "Account not found in trie, skipping storage root update");
644 return Ok(true)
645 };
646
647 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
650 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
651 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
652 } else {
653 EMPTY_ROOT_HASH
654 };
655
656 trie_account.storage_root = storage_root;
658
659 if trie_account == TrieAccount::default() {
661 return Ok(false)
662 }
663
664 trace!(target: "trie::sparse", ?address, "Updating account with the new storage root");
666 let nibbles = Nibbles::unpack(address);
667 self.account_rlp_buf.clear();
668 trie_account.encode(&mut self.account_rlp_buf);
669 self.update_account_leaf(nibbles, self.account_rlp_buf.clone(), provider_factory)?;
670
671 Ok(true)
672 }
673
674 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
676 pub fn remove_account_leaf(
677 &mut self,
678 path: &Nibbles,
679 provider_factory: impl TrieNodeProviderFactory,
680 ) -> SparseStateTrieResult<()> {
681 let provider = provider_factory.account_node_provider();
682 self.state.remove_leaf(path, provider)?;
683 Ok(())
684 }
685
686 pub fn remove_storage_leaf(
688 &mut self,
689 address: B256,
690 slot: &Nibbles,
691 provider_factory: impl TrieNodeProviderFactory,
692 ) -> SparseStateTrieResult<()> {
693 let storage_trie =
694 self.storage.tries.get_mut(&address).ok_or(SparseTrieErrorKind::Blind)?;
695
696 let provider = provider_factory.storage_node_provider(address);
697 storage_trie.remove_leaf(slot, provider)?;
698 Ok(())
699 }
700}
701
702impl<A, S> SparseStateTrie<A, S>
703where
704 A: SparseTrieTrait + Default,
705 S: SparseTrieTrait + Default + Clone,
706{
707 pub fn clear(&mut self) {
712 self.state.clear();
713 self.storage.clear();
714 self.account_rlp_buf.clear();
715 }
716
717 pub fn memory_size(&self) -> usize {
722 let mut size = core::mem::size_of::<Self>();
723
724 size += match &self.state {
725 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
726 t.memory_size()
727 }
728 RevealableSparseTrie::Blind(None) => 0,
729 };
730
731 for trie in self.storage.tries.values() {
732 size += match trie {
733 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
734 t.memory_size()
735 }
736 RevealableSparseTrie::Blind(None) => 0,
737 };
738 }
739 for trie in &self.storage.cleared_tries {
740 size += match trie {
741 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
742 t.memory_size()
743 }
744 RevealableSparseTrie::Blind(None) => 0,
745 };
746 }
747
748 size
749 }
750
751 pub fn retained_storage_tries_count(&self) -> usize {
753 self.storage.tries.len() + self.storage.cleared_tries.len()
754 }
755
756 pub fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
761 let storage_tries_count = self.storage.tries.len() + self.storage.cleared_tries.len();
763
764 let total_tries = 1 + storage_tries_count;
766
767 let nodes_per_trie = max_nodes / total_tries;
769 let values_per_trie = max_values / total_tries;
770
771 self.state.shrink_nodes_to(nodes_per_trie);
773 self.state.shrink_values_to(values_per_trie);
774
775 let storage_nodes = max_nodes.saturating_sub(nodes_per_trie);
777 let storage_values = max_values.saturating_sub(values_per_trie);
778
779 self.storage.shrink_to(storage_nodes, storage_values);
781 }
782
783 #[cfg(feature = "std")]
793 #[instrument(
794 level = "debug",
795 name = "SparseStateTrie::prune",
796 target = "trie::sparse",
797 skip_all,
798 fields(%max_depth, %max_storage_tries)
799 )]
800 pub fn prune(&mut self, max_depth: usize, max_storage_tries: usize) {
801 rayon::join(
803 || {
804 if let Some(trie) = self.state.as_revealed_mut() {
805 trie.prune(max_depth);
806 }
807 },
808 || {
809 self.storage.prune(max_depth, max_storage_tries);
810 },
811 );
812 }
813
814 pub fn commit_updates(&mut self, updates: &TrieUpdates) {
816 if let Some(trie) = self.state.as_revealed_mut() {
817 trie.commit_updates(&updates.account_nodes, &updates.removed_nodes);
818 }
819 for (address, updates) in &updates.storage_tries {
820 if let Some(trie) =
821 self.storage.tries.get_mut(address).and_then(|t| t.as_revealed_mut())
822 {
823 trie.commit_updates(&updates.storage_nodes, &updates.removed_nodes);
824 }
825 }
826 }
827}
828
829#[derive(Debug, Default)]
832struct StorageTries<S = ParallelSparseTrie> {
833 tries: B256Map<RevealableSparseTrie<S>>,
835 cleared_tries: Vec<RevealableSparseTrie<S>>,
837 default_trie: RevealableSparseTrie<S>,
839 modifications: StorageTrieModifications,
841}
842
843#[cfg(feature = "std")]
844impl<S: SparseTrieTrait> StorageTries<S> {
845 fn prune(&mut self, max_depth: usize, max_storage_tries: usize) {
850 let fn_start = Instant::now();
851 let mut stats =
852 StorageTriesPruneStats { total_tries_before: self.tries.len(), ..Default::default() };
853
854 self.modifications.update_and_reset();
856
857 let mut trie_info: Vec<(B256, usize, usize)> = self
861 .tries
862 .iter()
863 .map(|(address, trie)| {
864 let size = match trie {
865 RevealableSparseTrie::Blind(_) => return (*address, 0, 0),
866 RevealableSparseTrie::Revealed(t) => t.size_hint(),
867 };
868 let heat = self.modifications.heat(address);
869 let heat_multiplier = 1 + (heat.min(4) / 2) as usize;
871 (*address, size, size * heat_multiplier)
872 })
873 .collect();
874
875 if trie_info.len() > max_storage_tries {
877 trie_info
878 .select_nth_unstable_by(max_storage_tries.saturating_sub(1), |a, b| b.2.cmp(&a.2));
879 trie_info.truncate(max_storage_tries);
880 }
881 let tries_to_keep: B256Map<usize> =
882 trie_info.iter().map(|(address, size, _)| (*address, *size)).collect();
883 stats.tries_to_keep = tries_to_keep.len();
884
885 let tries_to_clear: Vec<B256> =
887 self.tries.keys().filter(|addr| !tries_to_keep.contains_key(*addr)).copied().collect();
888 stats.tries_to_evict = tries_to_clear.len();
889
890 for address in &tries_to_clear {
892 if let Some(mut trie) = self.tries.remove(address) {
893 trie.clear();
894 self.cleared_tries.push(trie);
895 }
896 self.modifications.remove(address);
897 }
898
899 const MIN_SIZE_TO_PRUNE: usize = 1000;
903 let prune_start = Instant::now();
904 for (address, size) in &tries_to_keep {
905 if *size < MIN_SIZE_TO_PRUNE {
906 stats.skipped_small += 1;
907 continue; }
909 let Some(heat_state) = self.modifications.get_mut(address) else {
910 continue; };
912 if heat_state.prune_backlog < 2 {
914 stats.skipped_recently_pruned += 1;
915 continue; }
917 if let Some(trie) = self.tries.get_mut(address).and_then(|t| t.as_revealed_mut()) {
918 trie.prune(max_depth);
919 heat_state.prune_backlog = 0; stats.pruned_count += 1;
921 }
922 }
923 stats.prune_elapsed = prune_start.elapsed();
924
925 stats.total_tries_after = self.tries.len();
926 stats.total_elapsed = fn_start.elapsed();
927
928 debug!(
929 target: "trie::sparse",
930 before = stats.total_tries_before,
931 after = stats.total_tries_after,
932 kept = stats.tries_to_keep,
933 evicted = stats.tries_to_evict,
934 pruned = stats.pruned_count,
935 skipped_small = stats.skipped_small,
936 skipped_recent = stats.skipped_recently_pruned,
937 ?stats.prune_elapsed,
938 ?stats.total_elapsed,
939 "StorageTries::prune completed"
940 );
941 }
942}
943
944impl<S: SparseTrieTrait> StorageTries<S> {
945 fn clear(&mut self) {
948 self.cleared_tries.extend(self.tries.drain().map(|(_, mut trie)| {
949 trie.clear();
950 trie
951 }));
952 self.modifications.clear();
953 }
954
955 fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
959 let total_tries = self.tries.len() + self.cleared_tries.len();
960 if total_tries == 0 {
961 return;
962 }
963
964 let nodes_per_trie = max_nodes / total_tries;
966 let values_per_trie = max_values / total_tries;
967
968 for trie in self.tries.values_mut() {
970 trie.shrink_nodes_to(nodes_per_trie);
971 trie.shrink_values_to(values_per_trie);
972 }
973
974 for trie in &mut self.cleared_tries {
976 trie.shrink_nodes_to(nodes_per_trie);
977 trie.shrink_values_to(values_per_trie);
978 }
979 }
980}
981
982impl<S: SparseTrieTrait + Clone> StorageTries<S> {
983 fn get_or_create_trie_mut(&mut self, address: B256) -> &mut RevealableSparseTrie<S> {
985 self.tries.entry(address).or_insert_with(|| {
986 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
987 })
988 }
989
990 #[cfg(feature = "std")]
993 fn take_or_create_trie(&mut self, account: &B256) -> RevealableSparseTrie<S> {
994 self.tries.remove(account).unwrap_or_else(|| {
995 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
996 })
997 }
998}
999
1000#[derive(Debug, Default)]
1002#[allow(dead_code)]
1003struct StorageTriesPruneStats {
1004 total_tries_before: usize,
1005 total_tries_after: usize,
1006 tries_to_keep: usize,
1007 tries_to_evict: usize,
1008 pruned_count: usize,
1009 skipped_small: usize,
1010 skipped_recently_pruned: usize,
1011 prune_elapsed: core::time::Duration,
1012 total_elapsed: core::time::Duration,
1013}
1014
1015#[derive(Debug, Clone, Copy, Default)]
1020#[allow(dead_code)]
1021struct TrieModificationState {
1022 heat: u8,
1025 prune_backlog: u8,
1028}
1029
1030#[derive(Debug, Default)]
1039struct StorageTrieModifications {
1040 state: B256Map<TrieModificationState>,
1042 accessed_this_cycle: B256Set,
1044}
1045
1046#[allow(dead_code)]
1047impl StorageTrieModifications {
1048 #[inline]
1051 fn mark_accessed(&mut self, address: B256) {
1052 self.accessed_this_cycle.insert(address);
1053 }
1054
1055 #[inline]
1057 fn get_mut(&mut self, address: &B256) -> Option<&mut TrieModificationState> {
1058 self.state.get_mut(address)
1059 }
1060
1061 #[inline]
1063 fn heat(&self, address: &B256) -> u8 {
1064 self.state.get(address).map_or(0, |s| s.heat)
1065 }
1066
1067 fn update_and_reset(&mut self) {
1070 for address in self.accessed_this_cycle.drain() {
1071 let entry = self.state.entry(address).or_default();
1072 entry.heat = entry.heat.saturating_add(1);
1073 entry.prune_backlog = entry.prune_backlog.saturating_add(1);
1074 }
1075 }
1076
1077 fn remove(&mut self, address: &B256) {
1079 self.state.remove(address);
1080 self.accessed_this_cycle.remove(address);
1081 }
1082
1083 fn clear(&mut self) {
1085 self.state.clear();
1086 self.accessed_this_cycle.clear();
1087 }
1088}
1089
1090fn estimate_v2_proof_capacity(nodes: &[ProofTrieNodeV2]) -> usize {
1095 let mut capacity = nodes.len();
1096
1097 for node in nodes {
1098 if let TrieNodeV2::Branch(branch) = &node.node {
1099 capacity += branch.state_mask.count_ones() as usize;
1100 }
1101 }
1102
1103 capacity
1104}
1105
1106#[cfg(test)]
1107mod tests {
1108 use super::*;
1109 use crate::{provider::DefaultTrieNodeProviderFactory, LeafLookup, ParallelSparseTrie};
1110 use alloy_primitives::{
1111 b256,
1112 map::{HashMap, HashSet},
1113 U256,
1114 };
1115 use arbitrary::Arbitrary;
1116 use rand::{rngs::StdRng, Rng, SeedableRng};
1117 use reth_primitives_traits::Account;
1118 use reth_trie::{updates::StorageTrieUpdates, HashBuilder, MultiProof, EMPTY_ROOT_HASH};
1119 use reth_trie_common::{
1120 proof::{ProofNodes, ProofRetainer},
1121 BranchNodeMasks, BranchNodeMasksMap, BranchNodeV2, LeafNode, RlpNode, StorageMultiProof,
1122 TrieMask,
1123 };
1124
1125 fn leaf_key(suffix: impl AsRef<[u8]>, total_len: usize) -> Nibbles {
1127 let suffix = suffix.as_ref();
1128 let mut nibbles = Nibbles::from_nibbles(suffix);
1129 nibbles.extend(&Nibbles::from_nibbles_unchecked(vec![0; total_len - suffix.len()]));
1130 nibbles
1131 }
1132
1133 #[test]
1134 fn reveal_account_path_twice() {
1135 let provider_factory = DefaultTrieNodeProviderFactory;
1136 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1137
1138 let full_path_0 = leaf_key([0x0], 64);
1140 let _full_path_1 = leaf_key([0x1], 64);
1141
1142 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1143 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1145 leaf_key([], 63),
1146 leaf_value.clone(),
1147 )));
1148 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1149 leaf_key([], 63),
1150 leaf_value.clone(),
1151 )));
1152
1153 let multiproof = MultiProof {
1154 account_subtree: ProofNodes::from_iter([
1155 (
1156 Nibbles::default(),
1157 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
1158 key: Nibbles::default(),
1159 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1160 state_mask: TrieMask::new(0b11),
1161 branch_rlp_node: None,
1162 }))
1163 .into(),
1164 ),
1165 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1166 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1167 ]),
1168 ..Default::default()
1169 };
1170
1171 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1173 assert!(matches!(
1174 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1175 Ok(LeafLookup::Exists)
1176 ));
1177 assert_eq!(
1178 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
1179 Some(&leaf_value)
1180 );
1181
1182 sparse.remove_account_leaf(&full_path_0, &provider_factory).unwrap();
1185 assert!(matches!(
1186 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1187 Ok(LeafLookup::NonExistent)
1188 ));
1189 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
1190 }
1191
1192 #[test]
1193 fn reveal_storage_path_twice() {
1194 let provider_factory = DefaultTrieNodeProviderFactory;
1195 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1196
1197 let full_path_0 = leaf_key([0x0], 64);
1199
1200 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1201 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1202 leaf_key([], 63),
1203 leaf_value.clone(),
1204 )));
1205 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1206 leaf_key([], 63),
1207 leaf_value.clone(),
1208 )));
1209
1210 let multiproof = MultiProof {
1211 storages: HashMap::from_iter([(
1212 B256::ZERO,
1213 StorageMultiProof {
1214 root: B256::ZERO,
1215 subtree: ProofNodes::from_iter([
1216 (
1217 Nibbles::default(),
1218 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
1219 key: Nibbles::default(),
1220 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1221 state_mask: TrieMask::new(0b11),
1222 branch_rlp_node: None,
1223 }))
1224 .into(),
1225 ),
1226 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1227 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1228 ]),
1229 branch_node_masks: Default::default(),
1230 },
1231 )]),
1232 ..Default::default()
1233 };
1234
1235 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1237 assert!(matches!(
1238 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1239 Ok(LeafLookup::Exists)
1240 ));
1241 assert_eq!(
1242 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
1243 Some(&leaf_value)
1244 );
1245
1246 sparse.remove_storage_leaf(B256::ZERO, &full_path_0, &provider_factory).unwrap();
1249 assert!(matches!(
1250 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1251 Ok(LeafLookup::NonExistent)
1252 ));
1253 assert!(sparse
1254 .storage_trie_ref(&B256::ZERO)
1255 .unwrap()
1256 .get_leaf_value(&full_path_0)
1257 .is_none());
1258 }
1259
1260 #[test]
1261 fn reveal_v2_proof_nodes() {
1262 let provider_factory = DefaultTrieNodeProviderFactory;
1263 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1264
1265 let full_path_0 = leaf_key([0x0], 64);
1267
1268 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1269 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
1270 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
1271
1272 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
1273 key: Nibbles::default(),
1274 stack: vec![
1275 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1276 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1277 ],
1278 state_mask: TrieMask::new(0b11),
1279 branch_rlp_node: None,
1280 });
1281
1282 let v2_proof_nodes = vec![
1284 ProofTrieNodeV2 {
1285 path: Nibbles::default(),
1286 node: branch_node,
1287 masks: Some(BranchNodeMasks {
1288 hash_mask: TrieMask::default(),
1289 tree_mask: TrieMask::default(),
1290 }),
1291 },
1292 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1293 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1294 ];
1295
1296 sparse.reveal_account_v2_proof_nodes(v2_proof_nodes).unwrap();
1298
1299 assert!(matches!(
1301 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1302 Ok(LeafLookup::Exists)
1303 ));
1304 assert_eq!(
1305 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
1306 Some(&leaf_value)
1307 );
1308
1309 sparse.remove_account_leaf(&full_path_0, &provider_factory).unwrap();
1311 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
1312 }
1313
1314 #[test]
1315 fn reveal_storage_v2_proof_nodes() {
1316 let provider_factory = DefaultTrieNodeProviderFactory;
1317 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1318
1319 let full_path_0 = leaf_key([0x0], 64);
1321
1322 let storage_value: Vec<u8> = alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec();
1323 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
1324 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
1325
1326 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
1327 key: Nibbles::default(),
1328 stack: vec![
1329 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1330 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1331 ],
1332 state_mask: TrieMask::new(0b11),
1333 branch_rlp_node: None,
1334 });
1335
1336 let v2_proof_nodes = vec![
1337 ProofTrieNodeV2 { path: Nibbles::default(), node: branch_node, masks: None },
1338 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1339 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1340 ];
1341
1342 sparse.reveal_storage_v2_proof_nodes(B256::ZERO, v2_proof_nodes).unwrap();
1344
1345 assert!(matches!(
1347 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1348 Ok(LeafLookup::Exists)
1349 ));
1350 assert_eq!(
1351 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
1352 Some(&storage_value)
1353 );
1354
1355 sparse.remove_storage_leaf(B256::ZERO, &full_path_0, &provider_factory).unwrap();
1357 assert!(sparse
1358 .storage_trie_ref(&B256::ZERO)
1359 .unwrap()
1360 .get_leaf_value(&full_path_0)
1361 .is_none());
1362 }
1363
1364 #[test]
1365 fn take_trie_updates() {
1366 reth_tracing::init_test_tracing();
1367
1368 let mut rng = StdRng::seed_from_u64(1);
1370
1371 let mut bytes = [0u8; 1024];
1372 rng.fill(bytes.as_mut_slice());
1373
1374 let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1375 let slot_path_1 = Nibbles::unpack(slot_1);
1376 let value_1 = U256::from(rng.random::<u64>());
1377 let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1378 let slot_path_2 = Nibbles::unpack(slot_2);
1379 let value_2 = U256::from(rng.random::<u64>());
1380 let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1381 let slot_path_3 = Nibbles::unpack(slot_3);
1382 let value_3 = U256::from(rng.random::<u64>());
1383
1384 let mut storage_hash_builder = HashBuilder::default()
1385 .with_proof_retainer(ProofRetainer::from_iter([slot_path_1, slot_path_2]));
1386 storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
1387 storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
1388
1389 let storage_root = storage_hash_builder.root();
1390 let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
1391 let storage_branch_node_masks = BranchNodeMasksMap::from_iter([
1392 (
1393 Nibbles::default(),
1394 BranchNodeMasks { hash_mask: TrieMask::new(0b010), tree_mask: TrieMask::default() },
1395 ),
1396 (
1397 Nibbles::from_nibbles([0x1]),
1398 BranchNodeMasks { hash_mask: TrieMask::new(0b11), tree_mask: TrieMask::default() },
1399 ),
1400 ]);
1401
1402 let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1403 let address_path_1 = Nibbles::unpack(address_1);
1404 let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1405 let mut trie_account_1 = account_1.into_trie_account(storage_root);
1406 let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1407 let address_path_2 = Nibbles::unpack(address_2);
1408 let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1409 let mut trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
1410
1411 let mut hash_builder = HashBuilder::default()
1412 .with_proof_retainer(ProofRetainer::from_iter([address_path_1, address_path_2]));
1413 hash_builder.add_leaf(address_path_1, &alloy_rlp::encode(trie_account_1));
1414 hash_builder.add_leaf(address_path_2, &alloy_rlp::encode(trie_account_2));
1415
1416 let root = hash_builder.root();
1417 let proof_nodes = hash_builder.take_proof_nodes();
1418
1419 let provider_factory = DefaultTrieNodeProviderFactory;
1420 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default().with_updates(true);
1421 sparse
1422 .reveal_decoded_multiproof(
1423 MultiProof {
1424 account_subtree: proof_nodes,
1425 branch_node_masks: BranchNodeMasksMap::from_iter([(
1426 Nibbles::from_nibbles([0x1]),
1427 BranchNodeMasks {
1428 hash_mask: TrieMask::new(0b00),
1429 tree_mask: TrieMask::default(),
1430 },
1431 )]),
1432 storages: HashMap::from_iter([
1433 (
1434 address_1,
1435 StorageMultiProof {
1436 root,
1437 subtree: storage_proof_nodes.clone(),
1438 branch_node_masks: storage_branch_node_masks.clone(),
1439 },
1440 ),
1441 (
1442 address_2,
1443 StorageMultiProof {
1444 root,
1445 subtree: storage_proof_nodes,
1446 branch_node_masks: storage_branch_node_masks,
1447 },
1448 ),
1449 ]),
1450 }
1451 .try_into()
1452 .unwrap(),
1453 )
1454 .unwrap();
1455
1456 assert_eq!(sparse.root(&provider_factory).unwrap(), root);
1457
1458 let address_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1459 let address_path_3 = Nibbles::unpack(address_3);
1460 let account_3 = Account { nonce: account_1.nonce + 1, ..account_1 };
1461 let trie_account_3 = account_3.into_trie_account(EMPTY_ROOT_HASH);
1462
1463 sparse
1464 .update_account_leaf(
1465 address_path_3,
1466 alloy_rlp::encode(trie_account_3),
1467 &provider_factory,
1468 )
1469 .unwrap();
1470
1471 sparse
1472 .update_storage_leaf(
1473 address_1,
1474 slot_path_3,
1475 alloy_rlp::encode(value_3),
1476 &provider_factory,
1477 )
1478 .unwrap();
1479 trie_account_1.storage_root = sparse.storage_root(&address_1).unwrap();
1480 sparse
1481 .update_account_leaf(
1482 address_path_1,
1483 alloy_rlp::encode(trie_account_1),
1484 &provider_factory,
1485 )
1486 .unwrap();
1487
1488 sparse.wipe_storage(address_2).unwrap();
1489 trie_account_2.storage_root = sparse.storage_root(&address_2).unwrap();
1490 sparse
1491 .update_account_leaf(
1492 address_path_2,
1493 alloy_rlp::encode(trie_account_2),
1494 &provider_factory,
1495 )
1496 .unwrap();
1497
1498 sparse.root(&provider_factory).unwrap();
1499
1500 let sparse_updates = sparse.take_trie_updates().unwrap();
1501 pretty_assertions::assert_eq!(
1503 sparse_updates,
1504 TrieUpdates {
1505 account_nodes: HashMap::default(),
1506 storage_tries: HashMap::from_iter([(
1507 b256!("0x1100000000000000000000000000000000000000000000000000000000000000"),
1508 StorageTrieUpdates {
1509 is_deleted: true,
1510 storage_nodes: HashMap::default(),
1511 removed_nodes: HashSet::default()
1512 }
1513 )]),
1514 removed_nodes: HashSet::default()
1515 }
1516 );
1517 }
1518}