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 reth_execution_errors::{SparseStateTrieResult, SparseTrieErrorKind};
11use reth_primitives_traits::Account;
12use reth_trie_common::{
13 updates::{StorageTrieUpdates, TrieUpdates},
14 DecodedMultiProof, MultiProof, Nibbles, ProofTrieNodeV2, TrieAccount, TrieNodeV2,
15 EMPTY_ROOT_HASH, TRIE_ACCOUNT_RLP_MAX_SIZE,
16};
17#[cfg(feature = "std")]
18use tracing::debug;
19use tracing::{instrument, trace};
20
21#[derive(Debug, Default)]
26pub struct DeferredDrops {
27 pub proof_nodes_bufs: Vec<Vec<ProofTrieNodeV2>>,
29}
30
31#[derive(Debug)]
32pub struct SparseStateTrie<
34 A = ParallelSparseTrie, S = ParallelSparseTrie, > {
37 state: RevealableSparseTrie<A>,
39 storage: StorageTries<S>,
41 retain_updates: bool,
43 account_rlp_buf: Vec<u8>,
45 deferred_drops: DeferredDrops,
47 hot_slots_lfu: BucketedLfu<HotSlotKey>,
49 hot_accounts_lfu: BucketedLfu<B256>,
51 #[cfg(feature = "metrics")]
53 metrics: crate::metrics::SparseStateTrieMetrics,
54}
55
56impl<A, S> Default for SparseStateTrie<A, S>
57where
58 A: Default,
59 S: Default,
60{
61 fn default() -> Self {
62 Self {
63 state: Default::default(),
64 storage: Default::default(),
65 retain_updates: false,
66 account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
67 deferred_drops: DeferredDrops::default(),
68 hot_slots_lfu: BucketedLfu::default(),
69 hot_accounts_lfu: BucketedLfu::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_hot_cache_capacities(&mut self, max_hot_slots: usize, max_hot_accounts: usize) {
101 self.hot_slots_lfu.decay_and_evict(max_hot_slots);
102 self.hot_accounts_lfu.decay_and_evict(max_hot_accounts);
103 }
104
105 pub fn with_hot_cache_capacities(
107 mut self,
108 max_hot_slots: usize,
109 max_hot_accounts: usize,
110 ) -> Self {
111 self.set_hot_cache_capacities(max_hot_slots, max_hot_accounts);
112 self
113 }
114
115 pub fn set_accounts_trie(&mut self, trie: RevealableSparseTrie<A>) {
117 self.state = trie;
118 }
119
120 pub fn with_accounts_trie(mut self, trie: RevealableSparseTrie<A>) -> Self {
122 self.set_accounts_trie(trie);
123 self
124 }
125
126 pub fn set_default_storage_trie(&mut self, trie: RevealableSparseTrie<S>) {
129 self.storage.default_trie = trie;
130 }
131
132 pub fn with_default_storage_trie(mut self, trie: RevealableSparseTrie<S>) -> Self {
135 self.set_default_storage_trie(trie);
136 self
137 }
138
139 pub fn take_deferred_drops(&mut self) -> DeferredDrops {
144 core::mem::take(&mut self.deferred_drops)
145 }
146}
147
148impl SparseStateTrie {
149 pub fn new() -> Self {
151 Self::default()
152 }
153}
154
155impl<A: SparseTrieTrait, S: SparseTrieTrait> SparseStateTrie<A, S> {
156 #[cfg(feature = "trie-debug")]
161 pub fn take_debug_recorders(&mut self) -> alloc::vec::Vec<(Option<B256>, TrieDebugRecorder)> {
162 let mut recorders = alloc::vec::Vec::new();
163 if let Some(trie) = self.state.as_revealed_mut() {
164 recorders.push((None, trie.take_debug_recorder()));
165 }
166 for (address, trie) in &mut self.storage.tries {
167 if let Some(trie) = trie.as_revealed_mut() {
168 recorders.push((Some(*address), trie.take_debug_recorder()));
169 }
170 }
171 recorders
172 }
173}
174
175impl<A, S> SparseStateTrie<A, S>
176where
177 A: SparseTrieTrait + Default,
178 S: SparseTrieTrait + Default + Clone,
179{
180 pub const fn trie_mut(&mut self) -> &mut RevealableSparseTrie<A> {
182 &mut self.state
183 }
184
185 pub fn is_account_revealed(&self, account: B256) -> bool {
187 let path = Nibbles::unpack(account);
188 let trie = match self.state_trie_ref() {
189 Some(t) => t,
190 None => return false,
191 };
192
193 trie.find_leaf(&path, None).is_ok()
194 }
195
196 pub fn check_valid_storage_witness(&self, address: B256, slot: B256) -> bool {
198 let path = Nibbles::unpack(slot);
199 let trie = match self.storage_trie_ref(&address) {
200 Some(t) => t,
201 None => return false,
202 };
203
204 trie.find_leaf(&path, None).is_ok()
205 }
206
207 #[inline]
209 pub fn record_slot_touch(&mut self, account: B256, slot: B256) {
210 self.hot_slots_lfu.touch(HotSlotKey { address: account, slot });
211 }
212
213 #[inline]
215 pub fn record_account_touch(&mut self, account: B256) {
216 self.hot_accounts_lfu.touch(account);
217 }
218
219 pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
221 self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
222 }
223
224 pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
226 self.storage.tries.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
227 }
228
229 pub const fn state_trie_ref(&self) -> Option<&A> {
231 self.state.as_revealed_ref()
232 }
233
234 pub fn storage_trie_ref(&self, address: &B256) -> Option<&S> {
236 self.storage.tries.get(address).and_then(|e| e.as_revealed_ref())
237 }
238
239 pub fn storage_trie_mut(&mut self, address: &B256) -> Option<&mut S> {
241 self.storage.tries.get_mut(address).and_then(|e| e.as_revealed_mut())
242 }
243
244 pub const fn storage_tries_mut(&mut self) -> &mut B256Map<RevealableSparseTrie<S>> {
246 &mut self.storage.tries
247 }
248
249 pub fn take_storage_trie(&mut self, address: &B256) -> Option<RevealableSparseTrie<S>> {
251 self.storage.tries.remove(address)
252 }
253
254 pub fn take_or_create_storage_trie(&mut self, address: &B256) -> RevealableSparseTrie<S> {
256 self.storage.tries.remove(address).unwrap_or_else(|| {
257 self.storage.cleared_tries.pop().unwrap_or_else(|| self.storage.default_trie.clone())
258 })
259 }
260
261 pub fn insert_storage_trie(&mut self, address: B256, storage_trie: RevealableSparseTrie<S>) {
263 self.storage.tries.insert(address, storage_trie);
264 }
265
266 pub fn get_or_create_storage_trie_mut(
268 &mut self,
269 address: B256,
270 ) -> &mut RevealableSparseTrie<S> {
271 self.storage.get_or_create_trie_mut(address)
272 }
273
274 pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
277 let decoded_multiproof = multiproof.try_into()?;
279
280 self.reveal_decoded_multiproof(decoded_multiproof)
282 }
283
284 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
287 pub fn reveal_decoded_multiproof(
288 &mut self,
289 multiproof: DecodedMultiProof,
290 ) -> SparseStateTrieResult<()> {
291 self.reveal_decoded_multiproof_v2(multiproof.into())
292 }
293
294 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
299 pub fn reveal_decoded_multiproof_v2(
300 &mut self,
301 multiproof: reth_trie_common::DecodedMultiProofV2,
302 ) -> SparseStateTrieResult<()> {
303 if !multiproof.account_proofs.is_empty() {
311 self.reveal_account_v2_proof_nodes(multiproof.account_proofs)?;
312 }
313
314 #[cfg(not(feature = "std"))]
315 {
317 for (account, storage_proofs) in multiproof.storage_proofs {
318 self.reveal_storage_v2_proof_nodes(account, storage_proofs)?;
319 }
320
321 Ok(())
322 }
323
324 #[cfg(feature = "std")]
325 {
327 use rayon::iter::ParallelIterator;
328 use reth_primitives_traits::ParallelBridgeBuffered;
329
330 let retain_updates = self.retain_updates;
331
332 let results: Vec<_> = multiproof
336 .storage_proofs
337 .into_iter()
338 .map(|(account, storage_proofs)| {
339 let trie = self.storage.take_or_create_trie(&account);
340 (account, storage_proofs, trie)
341 })
342 .par_bridge_buffered()
343 .map(|(account, storage_proofs, mut trie)| {
344 let mut bufs = Vec::new();
345 let result = Self::reveal_storage_v2_proof_nodes_inner(
346 account,
347 storage_proofs,
348 &mut trie,
349 &mut bufs,
350 retain_updates,
351 );
352 (account, result, trie, bufs)
353 })
354 .collect();
355
356 let mut any_err = Ok(());
357 for (account, result, trie, bufs) in results {
358 self.storage.tries.insert(account, trie);
359 if let Ok(_total_nodes) = result {
360 #[cfg(feature = "metrics")]
361 {
362 self.metrics.increment_total_storage_nodes(_total_nodes as u64);
363 }
364 } else {
365 any_err = result.map(|_| ());
366 }
367
368 self.deferred_drops.proof_nodes_bufs.extend(bufs);
370 }
371
372 any_err
373 }
374 }
375
376 pub fn reveal_account_v2_proof_nodes(
381 &mut self,
382 mut nodes: Vec<ProofTrieNodeV2>,
383 ) -> SparseStateTrieResult<()> {
384 let capacity = estimate_v2_proof_capacity(&nodes);
385
386 #[cfg(feature = "metrics")]
387 self.metrics.increment_total_account_nodes(nodes.len() as u64);
388
389 let root_node = nodes.iter().find(|n| n.path.is_empty());
390 let trie = if let Some(root_node) = root_node {
391 trace!(target: "trie::sparse", ?root_node, "Revealing root account node from V2 proof");
392 self.state.reveal_root(root_node.node.clone(), root_node.masks, self.retain_updates)?
393 } else {
394 self.state.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
395 };
396 trie.reserve_nodes(capacity);
397 trace!(target: "trie::sparse", total_nodes = ?nodes.len(), "Revealing account nodes from V2 proof");
398 trie.reveal_nodes(&mut nodes)?;
399
400 self.deferred_drops.proof_nodes_bufs.push(nodes);
401 Ok(())
402 }
403
404 pub fn reveal_storage_v2_proof_nodes(
409 &mut self,
410 account: B256,
411 nodes: Vec<ProofTrieNodeV2>,
412 ) -> SparseStateTrieResult<()> {
413 let trie = self.storage.get_or_create_trie_mut(account);
414 let _total_nodes = Self::reveal_storage_v2_proof_nodes_inner(
415 account,
416 nodes,
417 trie,
418 &mut self.deferred_drops.proof_nodes_bufs,
419 self.retain_updates,
420 )?;
421
422 #[cfg(feature = "metrics")]
423 {
424 self.metrics.increment_total_storage_nodes(_total_nodes as u64);
425 }
426
427 Ok(())
428 }
429
430 fn reveal_storage_v2_proof_nodes_inner(
433 account: B256,
434 mut nodes: Vec<ProofTrieNodeV2>,
435 trie: &mut RevealableSparseTrie<S>,
436 bufs: &mut Vec<Vec<ProofTrieNodeV2>>,
437 retain_updates: bool,
438 ) -> SparseStateTrieResult<usize> {
439 let capacity = estimate_v2_proof_capacity(&nodes);
440 let total_nodes = nodes.len();
441
442 let root_node = nodes.iter().find(|n| n.path.is_empty());
443 let trie = if let Some(root_node) = root_node {
444 trace!(target: "trie::sparse", ?account, ?root_node, "Revealing root storage node from V2 proof");
445 trie.reveal_root(root_node.node.clone(), root_node.masks, retain_updates)?
446 } else {
447 trie.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
448 };
449 trie.reserve_nodes(capacity);
450 trace!(target: "trie::sparse", ?account, total_nodes, "Revealing storage nodes from V2 proof");
451 trie.reveal_nodes(&mut nodes)?;
452
453 bufs.push(nodes);
454 Ok(total_nodes)
455 }
456
457 pub fn wipe_storage(&mut self, address: B256) -> SparseStateTrieResult<()> {
459 if let Some(trie) = self.storage.tries.get_mut(&address) {
460 trie.wipe()?;
461 }
462 Ok(())
463 }
464
465 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
469 pub fn calculate_subtries(&mut self) {
470 if let RevealableSparseTrie::Revealed(trie) = &mut self.state {
471 trie.update_subtrie_hashes();
472 }
473 }
474
475 pub fn storage_root(&mut self, account: &B256) -> Option<B256> {
477 self.storage.tries.get_mut(account).and_then(|trie| trie.root())
478 }
479
480 fn revealed_trie_mut(&mut self) -> SparseStateTrieResult<&mut A> {
482 self.state.as_revealed_mut().ok_or_else(|| SparseTrieErrorKind::Blind.into())
483 }
484
485 pub fn root(&mut self) -> SparseStateTrieResult<B256> {
487 #[cfg(feature = "metrics")]
489 self.metrics.record();
490
491 Ok(self.revealed_trie_mut()?.root())
492 }
493
494 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
498 pub fn root_with_updates(&mut self) -> SparseStateTrieResult<(B256, TrieUpdates)> {
499 #[cfg(feature = "metrics")]
501 self.metrics.record();
502
503 let storage_tries = self.storage_trie_updates();
504 let revealed = self.revealed_trie_mut()?;
505
506 let (root, updates) = (revealed.root(), revealed.take_updates());
507 let updates = TrieUpdates {
508 account_nodes: updates.updated_nodes,
509 removed_nodes: updates.removed_nodes,
510 storage_tries,
511 };
512 Ok((root, updates))
513 }
514
515 pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdates> {
519 self.storage
520 .tries
521 .iter_mut()
522 .map(|(address, trie)| {
523 let trie = trie.as_revealed_mut().unwrap();
524 let updates = trie.take_updates();
525 let updates = StorageTrieUpdates {
526 is_deleted: updates.wiped,
527 storage_nodes: updates.updated_nodes,
528 removed_nodes: updates.removed_nodes,
529 };
530 (*address, updates)
531 })
532 .filter(|(_, updates)| !updates.is_empty())
533 .collect()
534 }
535
536 pub fn take_trie_updates(&mut self) -> Option<TrieUpdates> {
540 let storage_tries = self.storage_trie_updates();
541 self.state.as_revealed_mut().map(|state| {
542 let updates = state.take_updates();
543 TrieUpdates {
544 account_nodes: updates.updated_nodes,
545 removed_nodes: updates.removed_nodes,
546 storage_tries,
547 }
548 })
549 }
550}
551
552impl SparseStateTrie {
553 pub fn update_account_leaf(
555 &mut self,
556 path: Nibbles,
557 value: Vec<u8>,
558 ) -> SparseStateTrieResult<()> {
559 self.state.update_leaf(path, value)?;
560 Ok(())
561 }
562
563 pub fn update_storage_leaf(
565 &mut self,
566 address: B256,
567 slot: Nibbles,
568 value: Vec<u8>,
569 ) -> SparseStateTrieResult<()> {
570 self.storage
571 .tries
572 .get_mut(&address)
573 .ok_or(SparseTrieErrorKind::Blind)?
574 .update_leaf(slot, value)?;
575 Ok(())
576 }
577
578 #[instrument(level = "trace", target = "trie::sparse", skip_all)]
584 pub fn update_account(
585 &mut self,
586 address: B256,
587 account: Account,
588 ) -> SparseStateTrieResult<bool> {
589 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
590 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
591 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
592 } else if self.is_account_revealed(address) {
593 trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
594 if let Some(value) = self.get_account_value(&address) {
596 TrieAccount::decode(&mut &value[..])?.storage_root
598 } else {
599 EMPTY_ROOT_HASH
601 }
602 } else {
603 return Err(SparseTrieErrorKind::Blind.into())
604 };
605
606 if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
607 return Ok(false);
608 }
609
610 trace!(target: "trie::sparse", ?address, "Updating account");
611 let nibbles = Nibbles::unpack(address);
612 self.account_rlp_buf.clear();
613 account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
614 self.update_account_leaf(nibbles, self.account_rlp_buf.clone())?;
615
616 Ok(true)
617 }
618
619 #[instrument(level = "trace", target = "trie::sparse", skip_all)]
627 pub fn update_account_stateless(
628 &mut self,
629 address: B256,
630 account: Option<Account>,
631 ) -> SparseStateTrieResult<()> {
632 let nibbles = Nibbles::unpack(address);
633
634 let Some(account) = account else {
635 trace!(target: "trie::sparse", ?address, "Removing account");
636 return self.remove_account_leaf(&nibbles);
637 };
638
639 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
640 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
641 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
642 } else if let Some(value) = self.get_account_value(&address) {
643 trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
644 TrieAccount::decode(&mut &value[..])?.storage_root
645 } else {
646 EMPTY_ROOT_HASH
647 };
648
649 trace!(target: "trie::sparse", ?address, "Updating account");
650 self.account_rlp_buf.clear();
651 account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
652 self.update_account_leaf(nibbles, self.account_rlp_buf.clone())?;
653
654 Ok(())
655 }
656
657 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
664 pub fn update_account_storage_root(&mut self, address: B256) -> SparseStateTrieResult<bool> {
665 if !self.is_account_revealed(address) {
666 return Err(SparseTrieErrorKind::Blind.into())
667 }
668
669 let Some(mut trie_account) = self
671 .get_account_value(&address)
672 .map(|v| TrieAccount::decode(&mut &v[..]))
673 .transpose()?
674 else {
675 trace!(target: "trie::sparse", ?address, "Account not found in trie, skipping storage root update");
676 return Ok(true)
677 };
678
679 let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
681 trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
682 storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
683 } else {
684 EMPTY_ROOT_HASH
685 };
686
687 trie_account.storage_root = storage_root;
689
690 if trie_account == TrieAccount::default() {
692 return Ok(false)
693 }
694
695 trace!(target: "trie::sparse", ?address, "Updating account with the new storage root");
697 let nibbles = Nibbles::unpack(address);
698 self.account_rlp_buf.clear();
699 trie_account.encode(&mut self.account_rlp_buf);
700 self.update_account_leaf(nibbles, self.account_rlp_buf.clone())?;
701
702 Ok(true)
703 }
704
705 #[instrument(level = "debug", target = "trie::sparse", skip_all)]
707 pub fn remove_account_leaf(&mut self, path: &Nibbles) -> SparseStateTrieResult<()> {
708 self.state.remove_leaf(path)?;
709 Ok(())
710 }
711
712 pub fn remove_storage_leaf(
714 &mut self,
715 address: B256,
716 slot: &Nibbles,
717 ) -> SparseStateTrieResult<()> {
718 let storage_trie =
719 self.storage.tries.get_mut(&address).ok_or(SparseTrieErrorKind::Blind)?;
720
721 storage_trie.remove_leaf(slot)?;
722 Ok(())
723 }
724}
725
726impl<A, S> SparseStateTrie<A, S>
727where
728 A: SparseTrieTrait + Default,
729 S: SparseTrieTrait + Default + Clone,
730{
731 pub fn clear(&mut self) {
736 self.state.clear();
737 self.storage.clear();
738 self.account_rlp_buf.clear();
739 }
740
741 pub fn memory_size(&self) -> usize {
746 let mut size = core::mem::size_of::<Self>();
747
748 size += match &self.state {
749 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
750 t.memory_size()
751 }
752 RevealableSparseTrie::Blind(None) => 0,
753 };
754
755 for trie in self.storage.tries.values() {
756 size += match trie {
757 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
758 t.memory_size()
759 }
760 RevealableSparseTrie::Blind(None) => 0,
761 };
762 }
763 for trie in &self.storage.cleared_tries {
764 size += match trie {
765 RevealableSparseTrie::Revealed(t) | RevealableSparseTrie::Blind(Some(t)) => {
766 t.memory_size()
767 }
768 RevealableSparseTrie::Blind(None) => 0,
769 };
770 }
771
772 size
773 }
774
775 pub fn retained_storage_tries_count(&self) -> usize {
777 self.storage.tries.len() + self.storage.cleared_tries.len()
778 }
779
780 pub fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
785 let storage_tries_count = self.storage.tries.len() + self.storage.cleared_tries.len();
787
788 let total_tries = 1 + storage_tries_count;
790
791 let nodes_per_trie = max_nodes / total_tries;
793 let values_per_trie = max_values / total_tries;
794
795 self.state.shrink_nodes_to(nodes_per_trie);
797 self.state.shrink_values_to(values_per_trie);
798
799 let storage_nodes = max_nodes.saturating_sub(nodes_per_trie);
801 let storage_values = max_values.saturating_sub(values_per_trie);
802
803 self.storage.shrink_to(storage_nodes, storage_values);
805 }
806
807 #[cfg(feature = "std")]
822 #[instrument(
823 level = "debug",
824 name = "SparseStateTrie::prune",
825 target = "trie::sparse",
826 skip_all,
827 fields(%max_hot_slots, %max_hot_accounts)
828 )]
829 pub fn prune(&mut self, max_hot_slots: usize, max_hot_accounts: usize) {
830 self.hot_slots_lfu.decay_and_evict(max_hot_slots);
831 self.hot_accounts_lfu.decay_and_evict(max_hot_accounts);
832 let retained = self.hot_slots_lfu.retained_slots_by_address();
833
834 let retained_account_paths: Vec<Nibbles> =
835 self.hot_accounts_lfu.keys().map(|k| Nibbles::unpack(*k)).collect();
836
837 let retained_accounts = retained_account_paths.len();
838 let retained_storage_tries = retained.len();
839 let total_storage_tries_before = self.storage.tries.len();
840
841 let (account_nodes_pruned, storage_tries_evicted) = rayon::join(
843 || {
844 self.state
845 .as_revealed_mut()
846 .map(|trie| trie.prune(&retained_account_paths))
847 .unwrap_or(0)
848 },
849 || self.storage.prune_by_retained_slots(retained),
850 );
851
852 debug!(
853 target: "trie::sparse",
854 retained_accounts,
855 retained_storage_tries,
856 account_nodes_pruned,
857 storage_tries_evicted,
858 storage_tries_after = total_storage_tries_before - storage_tries_evicted,
859 "SparseStateTrie::prune completed"
860 );
861 }
862
863 pub fn commit_updates(&mut self, updates: &TrieUpdates) {
865 if let Some(trie) = self.state.as_revealed_mut() {
866 trie.commit_updates(&updates.account_nodes, &updates.removed_nodes);
867 }
868 for (address, updates) in &updates.storage_tries {
869 if let Some(trie) =
870 self.storage.tries.get_mut(address).and_then(|t| t.as_revealed_mut())
871 {
872 trie.commit_updates(&updates.storage_nodes, &updates.removed_nodes);
873 }
874 }
875 }
876}
877
878#[derive(Debug, Default)]
881struct StorageTries<S = ParallelSparseTrie> {
882 tries: B256Map<RevealableSparseTrie<S>>,
884 cleared_tries: Vec<RevealableSparseTrie<S>>,
886 default_trie: RevealableSparseTrie<S>,
888}
889
890#[cfg(feature = "std")]
891impl<S: SparseTrieTrait> StorageTries<S> {
892 fn prune_by_retained_slots(&mut self, retained_slots: B256Map<Vec<Nibbles>>) -> usize {
897 {
899 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
900 self.tries.par_iter_mut().for_each(|(address, trie)| {
901 if let Some(slots) = retained_slots.get(address) {
902 if let Some(t) = trie.as_revealed_mut() {
903 t.prune(slots);
904 }
905 } else {
906 trie.clear();
907 }
908 });
909 }
910
911 let addresses_to_evict: Vec<B256> = self
913 .tries
914 .keys()
915 .filter(|address| !retained_slots.contains_key(*address))
916 .copied()
917 .collect();
918
919 let evicted = addresses_to_evict.len();
920 self.cleared_tries.reserve(evicted);
921 for address in &addresses_to_evict {
922 if let Some(trie) = self.tries.remove(address) {
923 self.cleared_tries.push(trie);
924 }
925 }
926
927 evicted
928 }
929}
930
931impl<S: SparseTrieTrait> StorageTries<S> {
932 fn clear(&mut self) {
935 self.cleared_tries.extend(self.tries.drain().map(|(_, mut trie)| {
936 trie.clear();
937 trie
938 }));
939 }
940
941 fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
945 let total_tries = self.tries.len() + self.cleared_tries.len();
946 if total_tries == 0 {
947 return;
948 }
949
950 let nodes_per_trie = max_nodes / total_tries;
952 let values_per_trie = max_values / total_tries;
953
954 for trie in self.tries.values_mut() {
956 trie.shrink_nodes_to(nodes_per_trie);
957 trie.shrink_values_to(values_per_trie);
958 }
959
960 for trie in &mut self.cleared_tries {
962 trie.shrink_nodes_to(nodes_per_trie);
963 trie.shrink_values_to(values_per_trie);
964 }
965 }
966}
967
968impl<S: SparseTrieTrait + Clone> StorageTries<S> {
969 fn get_or_create_trie_mut(&mut self, address: B256) -> &mut RevealableSparseTrie<S> {
971 self.tries.entry(address).or_insert_with(|| {
972 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
973 })
974 }
975
976 #[cfg(feature = "std")]
979 fn take_or_create_trie(&mut self, account: &B256) -> RevealableSparseTrie<S> {
980 self.tries.remove(account).unwrap_or_else(|| {
981 self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
982 })
983 }
984}
985
986#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
988struct HotSlotKey {
989 address: B256,
990 slot: B256,
991}
992
993#[cfg(feature = "std")]
995impl BucketedLfu<HotSlotKey> {
996 fn retained_slots_by_address(&self) -> B256Map<Vec<Nibbles>> {
998 let mut grouped =
999 B256Map::<Vec<Nibbles>>::with_capacity_and_hasher(self.len(), Default::default());
1000 for key in self.keys() {
1001 grouped.entry(key.address).or_default().push(Nibbles::unpack(key.slot));
1002 }
1003
1004 for slots in grouped.values_mut() {
1005 slots.sort_unstable();
1006 slots.dedup();
1007 }
1008
1009 grouped
1010 }
1011}
1012
1013fn estimate_v2_proof_capacity(nodes: &[ProofTrieNodeV2]) -> usize {
1018 let mut capacity = nodes.len();
1019
1020 for node in nodes {
1021 if let TrieNodeV2::Branch(branch) = &node.node {
1022 capacity += branch.state_mask.count_ones() as usize;
1023 }
1024 }
1025
1026 capacity
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031 use super::*;
1032 use crate::{LeafLookup, ParallelSparseTrie};
1033 use alloy_primitives::{
1034 b256,
1035 map::{HashMap, HashSet},
1036 U256,
1037 };
1038 use arbitrary::Arbitrary;
1039 use rand::{rngs::StdRng, Rng, SeedableRng};
1040 use reth_execution_errors::{SparseStateTrieErrorKind, SparseTrieErrorKind};
1041 use reth_primitives_traits::Account;
1042 use reth_trie::{updates::StorageTrieUpdates, HashBuilder, MultiProof, EMPTY_ROOT_HASH};
1043 use reth_trie_common::{
1044 proof::{ProofNodes, ProofRetainer},
1045 BranchNodeMasks, BranchNodeMasksMap, BranchNodeV2, LeafNode, RlpNode, StorageMultiProof,
1046 TrieMask,
1047 };
1048
1049 fn leaf_key(suffix: impl AsRef<[u8]>, total_len: usize) -> Nibbles {
1051 let suffix = suffix.as_ref();
1052 let mut nibbles = Nibbles::from_nibbles(suffix);
1053 nibbles.extend(&Nibbles::from_nibbles_unchecked(vec![0; total_len - suffix.len()]));
1054 nibbles
1055 }
1056
1057 #[test]
1058 fn reveal_account_path_twice() {
1059 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1060
1061 let full_path_0 = leaf_key([0x0], 64);
1063 let _full_path_1 = leaf_key([0x1], 64);
1064
1065 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1066 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1068 leaf_key([], 63),
1069 leaf_value.clone(),
1070 )));
1071 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1072 leaf_key([], 63),
1073 leaf_value.clone(),
1074 )));
1075
1076 let multiproof = MultiProof {
1077 account_subtree: ProofNodes::from_iter([
1078 (
1079 Nibbles::default(),
1080 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
1081 key: Nibbles::default(),
1082 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1083 state_mask: TrieMask::new(0b11),
1084 branch_rlp_node: None,
1085 }))
1086 .into(),
1087 ),
1088 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1089 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1090 ]),
1091 ..Default::default()
1092 };
1093
1094 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1096 assert!(matches!(
1097 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1098 Ok(LeafLookup::Exists)
1099 ));
1100 assert_eq!(
1101 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
1102 Some(&leaf_value)
1103 );
1104
1105 sparse.remove_account_leaf(&full_path_0).unwrap();
1108 assert!(matches!(
1109 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1110 Ok(LeafLookup::NonExistent)
1111 ));
1112 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
1113 }
1114
1115 #[test]
1116 fn reveal_storage_path_twice() {
1117 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1118
1119 let full_path_0 = leaf_key([0x0], 64);
1121
1122 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1123 let leaf_1 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1124 leaf_key([], 63),
1125 leaf_value.clone(),
1126 )));
1127 let leaf_2 = alloy_rlp::encode(TrieNodeV2::Leaf(LeafNode::new(
1128 leaf_key([], 63),
1129 leaf_value.clone(),
1130 )));
1131
1132 let multiproof = MultiProof {
1133 storages: HashMap::from_iter([(
1134 B256::ZERO,
1135 StorageMultiProof {
1136 root: B256::ZERO,
1137 subtree: ProofNodes::from_iter([
1138 (
1139 Nibbles::default(),
1140 alloy_rlp::encode(TrieNodeV2::Branch(BranchNodeV2 {
1141 key: Nibbles::default(),
1142 stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1143 state_mask: TrieMask::new(0b11),
1144 branch_rlp_node: None,
1145 }))
1146 .into(),
1147 ),
1148 (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1149 (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1150 ]),
1151 branch_node_masks: Default::default(),
1152 },
1153 )]),
1154 ..Default::default()
1155 };
1156
1157 sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1159 assert!(matches!(
1160 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1161 Ok(LeafLookup::Exists)
1162 ));
1163 assert_eq!(
1164 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
1165 Some(&leaf_value)
1166 );
1167
1168 sparse.remove_storage_leaf(B256::ZERO, &full_path_0).unwrap();
1171 assert!(matches!(
1172 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1173 Ok(LeafLookup::NonExistent)
1174 ));
1175 assert!(sparse
1176 .storage_trie_ref(&B256::ZERO)
1177 .unwrap()
1178 .get_leaf_value(&full_path_0)
1179 .is_none());
1180 }
1181
1182 #[test]
1183 fn seeded_hot_cache_capacities_preserve_first_cycle_touches() {
1184 let account = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1185 let slot = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1186 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1187
1188 sparse.set_hot_cache_capacities(1, 1);
1189 sparse.record_account_touch(account);
1190 sparse.record_slot_touch(account, slot);
1191 sparse.prune(1, 1);
1192
1193 assert_eq!(sparse.hot_accounts_lfu.keys().copied().collect::<Vec<_>>(), vec![account]);
1194 assert_eq!(
1195 sparse.hot_slots_lfu.keys().copied().collect::<Vec<_>>(),
1196 vec![HotSlotKey { address: account, slot }]
1197 );
1198 }
1199
1200 #[test]
1201 fn reveal_v2_proof_nodes() {
1202 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1203
1204 let full_path_0 = leaf_key([0x0], 64);
1206
1207 let leaf_value = alloy_rlp::encode(TrieAccount::default());
1208 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
1209 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), leaf_value.clone()));
1210
1211 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
1212 key: Nibbles::default(),
1213 stack: vec![
1214 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1215 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1216 ],
1217 state_mask: TrieMask::new(0b11),
1218 branch_rlp_node: None,
1219 });
1220
1221 let v2_proof_nodes = vec![
1223 ProofTrieNodeV2 {
1224 path: Nibbles::default(),
1225 node: branch_node,
1226 masks: Some(BranchNodeMasks {
1227 hash_mask: TrieMask::default(),
1228 tree_mask: TrieMask::default(),
1229 }),
1230 },
1231 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1232 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1233 ];
1234
1235 sparse.reveal_account_v2_proof_nodes(v2_proof_nodes).unwrap();
1237
1238 assert!(matches!(
1240 sparse.state_trie_ref().unwrap().find_leaf(&full_path_0, None),
1241 Ok(LeafLookup::Exists)
1242 ));
1243 assert_eq!(
1244 sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0),
1245 Some(&leaf_value)
1246 );
1247
1248 sparse.remove_account_leaf(&full_path_0).unwrap();
1250 assert!(sparse.state_trie_ref().unwrap().get_leaf_value(&full_path_0).is_none());
1251 }
1252
1253 #[test]
1254 fn reveal_storage_v2_proof_nodes() {
1255 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1256
1257 let full_path_0 = leaf_key([0x0], 64);
1259
1260 let storage_value: Vec<u8> = alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec();
1261 let leaf_1_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
1262 let leaf_2_node = TrieNodeV2::Leaf(LeafNode::new(leaf_key([], 63), storage_value.clone()));
1263
1264 let branch_node = TrieNodeV2::Branch(BranchNodeV2 {
1265 key: Nibbles::default(),
1266 stack: vec![
1267 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1268 RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1269 ],
1270 state_mask: TrieMask::new(0b11),
1271 branch_rlp_node: None,
1272 });
1273
1274 let v2_proof_nodes = vec![
1275 ProofTrieNodeV2 { path: Nibbles::default(), node: branch_node, masks: None },
1276 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1277 ProofTrieNodeV2 { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1278 ];
1279
1280 sparse.reveal_storage_v2_proof_nodes(B256::ZERO, v2_proof_nodes).unwrap();
1282
1283 assert!(matches!(
1285 sparse.storage_trie_ref(&B256::ZERO).unwrap().find_leaf(&full_path_0, None),
1286 Ok(LeafLookup::Exists)
1287 ));
1288 assert_eq!(
1289 sparse.storage_trie_ref(&B256::ZERO).unwrap().get_leaf_value(&full_path_0),
1290 Some(&storage_value)
1291 );
1292
1293 sparse.remove_storage_leaf(B256::ZERO, &full_path_0).unwrap();
1295 assert!(sparse
1296 .storage_trie_ref(&B256::ZERO)
1297 .unwrap()
1298 .get_leaf_value(&full_path_0)
1299 .is_none());
1300 }
1301
1302 #[test]
1303 fn root_on_blind_trie_returns_blind_error() {
1304 let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1305
1306 let err = sparse.root().unwrap_err();
1307
1308 assert!(matches!(err.kind(), SparseStateTrieErrorKind::Sparse(SparseTrieErrorKind::Blind)));
1309 }
1310
1311 #[test]
1312 fn take_trie_updates() {
1313 reth_tracing::init_test_tracing();
1314
1315 let mut rng = StdRng::seed_from_u64(1);
1317
1318 let mut bytes = [0u8; 1024];
1319 rng.fill(bytes.as_mut_slice());
1320
1321 let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1322 let slot_path_1 = Nibbles::unpack(slot_1);
1323 let value_1 = U256::from(rng.random::<u64>());
1324 let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1325 let slot_path_2 = Nibbles::unpack(slot_2);
1326 let value_2 = U256::from(rng.random::<u64>());
1327 let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1328 let slot_path_3 = Nibbles::unpack(slot_3);
1329 let value_3 = U256::from(rng.random::<u64>());
1330
1331 let mut storage_hash_builder = HashBuilder::default()
1332 .with_proof_retainer(ProofRetainer::from_iter([slot_path_1, slot_path_2]));
1333 storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
1334 storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
1335
1336 let storage_root = storage_hash_builder.root();
1337 let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
1338 let storage_branch_node_masks = BranchNodeMasksMap::from_iter([
1339 (
1340 Nibbles::default(),
1341 BranchNodeMasks { hash_mask: TrieMask::new(0b010), tree_mask: TrieMask::default() },
1342 ),
1343 (
1344 Nibbles::from_nibbles([0x1]),
1345 BranchNodeMasks { hash_mask: TrieMask::new(0b11), tree_mask: TrieMask::default() },
1346 ),
1347 ]);
1348
1349 let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1350 let address_path_1 = Nibbles::unpack(address_1);
1351 let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1352 let mut trie_account_1 = account_1.into_trie_account(storage_root);
1353 let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1354 let address_path_2 = Nibbles::unpack(address_2);
1355 let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1356 let mut trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
1357
1358 let mut hash_builder = HashBuilder::default()
1359 .with_proof_retainer(ProofRetainer::from_iter([address_path_1, address_path_2]));
1360 hash_builder.add_leaf(address_path_1, &alloy_rlp::encode(trie_account_1));
1361 hash_builder.add_leaf(address_path_2, &alloy_rlp::encode(trie_account_2));
1362
1363 let root = hash_builder.root();
1364 let proof_nodes = hash_builder.take_proof_nodes();
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().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.update_account_leaf(address_path_3, alloy_rlp::encode(trie_account_3)).unwrap();
1409
1410 sparse.update_storage_leaf(address_1, slot_path_3, alloy_rlp::encode(value_3)).unwrap();
1411 trie_account_1.storage_root = sparse.storage_root(&address_1).unwrap();
1412 sparse.update_account_leaf(address_path_1, alloy_rlp::encode(trie_account_1)).unwrap();
1413
1414 sparse.wipe_storage(address_2).unwrap();
1415 trie_account_2.storage_root = sparse.storage_root(&address_2).unwrap();
1416 sparse.update_account_leaf(address_path_2, alloy_rlp::encode(trie_account_2)).unwrap();
1417
1418 sparse.root().unwrap();
1419
1420 let sparse_updates = sparse.take_trie_updates().unwrap();
1421 pretty_assertions::assert_eq!(
1423 sparse_updates,
1424 TrieUpdates {
1425 account_nodes: HashMap::default(),
1426 storage_tries: HashMap::from_iter([(
1427 b256!("0x1100000000000000000000000000000000000000000000000000000000000000"),
1428 StorageTrieUpdates {
1429 is_deleted: true,
1430 storage_nodes: HashMap::default(),
1431 removed_nodes: HashSet::default()
1432 }
1433 )]),
1434 removed_nodes: HashSet::default()
1435 }
1436 );
1437 }
1438}