Skip to main content

reth_trie_sparse/
state.rs

1use crate::{
2    provider::{TrieNodeProvider, TrieNodeProviderFactory},
3    traits::SparseTrie as SparseTrieTrait,
4    ParallelSparseTrie, RevealableSparseTrie,
5};
6use alloc::{collections::VecDeque, vec::Vec};
7use alloy_primitives::{
8    map::{B256Map, B256Set, HashSet},
9    Bytes, B256,
10};
11use alloy_rlp::{Decodable, Encodable};
12use alloy_trie::proof::DecodedProofNodes;
13use reth_execution_errors::{SparseStateTrieErrorKind, SparseStateTrieResult, SparseTrieErrorKind};
14use reth_primitives_traits::Account;
15use reth_trie_common::{
16    proof::ProofNodes,
17    updates::{StorageTrieUpdates, TrieUpdates},
18    BranchNodeMasks, BranchNodeMasksMap, DecodedMultiProof, DecodedStorageMultiProof, MultiProof,
19    Nibbles, ProofTrieNode, RlpNode, StorageMultiProof, TrieAccount, TrieNode, EMPTY_ROOT_HASH,
20    TRIE_ACCOUNT_RLP_MAX_SIZE,
21};
22#[cfg(feature = "std")]
23use tracing::debug;
24use tracing::{instrument, trace};
25
26/// Holds data that should be dropped after any locks are released.
27///
28/// This is used to defer expensive deallocations (like proof node buffers) until after final state
29/// root is calculated
30#[derive(Debug, Default)]
31pub struct DeferredDrops {
32    /// Each nodes reveal operation creates a new buffer, uses it, and pushes it here.
33    pub proof_nodes_bufs: Vec<Vec<ProofTrieNode>>,
34}
35
36#[derive(Debug)]
37/// Sparse state trie representing lazy-loaded Ethereum state trie.
38pub struct SparseStateTrie<
39    A = ParallelSparseTrie, // Account trie implementation
40    S = ParallelSparseTrie, // Storage trie implementation
41> {
42    /// Sparse account trie.
43    state: RevealableSparseTrie<A>,
44    /// Collection of revealed account trie paths.
45    revealed_account_paths: HashSet<Nibbles>,
46    /// State related to storage tries.
47    storage: StorageTries<S>,
48    /// Flag indicating whether trie updates should be retained.
49    retain_updates: bool,
50    /// When true, skip filtering of V2 proof nodes that have already been revealed.
51    /// This is useful when the sparse trie is being reused across blocks and already
52    /// tracks revealed nodes internally.
53    skip_proof_node_filtering: bool,
54    /// Reusable buffer for RLP encoding of trie accounts.
55    account_rlp_buf: Vec<u8>,
56    /// Holds data that should be dropped after final state root is calculated.
57    deferred_drops: DeferredDrops,
58    /// Metrics for the sparse state trie.
59    #[cfg(feature = "metrics")]
60    metrics: crate::metrics::SparseStateTrieMetrics,
61}
62
63impl<A, S> Default for SparseStateTrie<A, S>
64where
65    A: Default,
66    S: Default,
67{
68    fn default() -> Self {
69        Self {
70            state: Default::default(),
71            revealed_account_paths: Default::default(),
72            storage: Default::default(),
73            retain_updates: false,
74            skip_proof_node_filtering: false,
75            account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
76            deferred_drops: DeferredDrops::default(),
77            #[cfg(feature = "metrics")]
78            metrics: Default::default(),
79        }
80    }
81}
82
83#[cfg(test)]
84impl SparseStateTrie {
85    /// Create state trie from state trie.
86    pub fn from_state(state: RevealableSparseTrie) -> Self {
87        Self { state, ..Default::default() }
88    }
89}
90
91impl<A, S> SparseStateTrie<A, S> {
92    /// Set the retention of branch node updates and deletions.
93    pub const fn set_updates(&mut self, retain_updates: bool) {
94        self.retain_updates = retain_updates;
95    }
96
97    /// Set the retention of branch node updates and deletions.
98    pub const fn with_updates(mut self, retain_updates: bool) -> Self {
99        self.set_updates(retain_updates);
100        self
101    }
102
103    /// Set the accounts trie to the given `RevealableSparseTrie`.
104    pub fn set_accounts_trie(&mut self, trie: RevealableSparseTrie<A>) {
105        self.state = trie;
106    }
107
108    /// Set the accounts trie to the given `RevealableSparseTrie`.
109    pub fn with_accounts_trie(mut self, trie: RevealableSparseTrie<A>) -> Self {
110        self.set_accounts_trie(trie);
111        self
112    }
113
114    /// Set the default trie which will be cloned when creating new storage
115    /// [`RevealableSparseTrie`]s.
116    pub fn set_default_storage_trie(&mut self, trie: RevealableSparseTrie<S>) {
117        self.storage.default_trie = trie;
118    }
119
120    /// Set the default trie which will be cloned when creating new storage
121    /// [`RevealableSparseTrie`]s.
122    pub fn with_default_storage_trie(mut self, trie: RevealableSparseTrie<S>) -> Self {
123        self.set_default_storage_trie(trie);
124        self
125    }
126
127    /// Set whether to skip filtering of V2 proof nodes.
128    ///
129    /// When true, `reveal_*_v2_proof_nodes` methods will pass all nodes directly to the
130    /// sparse trie without filtering already-revealed paths. This is useful when the
131    /// sparse trie is being reused across blocks and handles node deduplication internally.
132    pub const fn with_skip_proof_node_filtering(mut self, skip: bool) -> Self {
133        self.skip_proof_node_filtering = skip;
134        self
135    }
136
137    /// Takes the data structures for deferred dropping.
138    ///
139    /// This allows the caller to drop the buffers later, avoiding expensive deallocations while
140    /// calculating the state root.
141    pub fn take_deferred_drops(&mut self) -> DeferredDrops {
142        core::mem::take(&mut self.deferred_drops)
143    }
144}
145
146impl SparseStateTrie {
147    /// Create new [`SparseStateTrie`] with the default trie implementation.
148    pub fn new() -> Self {
149        Self::default()
150    }
151}
152
153impl<A, S> SparseStateTrie<A, S>
154where
155    A: SparseTrieTrait + Default,
156    S: SparseTrieTrait + Default + Clone,
157{
158    /// Returns mutable reference to account trie.
159    pub const fn trie_mut(&mut self) -> &mut RevealableSparseTrie<A> {
160        &mut self.state
161    }
162
163    /// Returns `true` if account was already revealed.
164    pub fn is_account_revealed(&self, account: B256) -> bool {
165        self.revealed_account_paths.contains(&Nibbles::unpack(account))
166    }
167
168    /// Was the account witness for `address` complete?
169    pub fn check_valid_account_witness(&self, address: B256) -> bool {
170        let path = Nibbles::unpack(address);
171        let trie = match self.state_trie_ref() {
172            Some(t) => t,
173            None => return false,
174        };
175
176        trie.find_leaf(&path, None).is_ok()
177    }
178
179    /// Was the storage-slot witness for (`address`,`slot`) complete?
180    pub fn check_valid_storage_witness(&self, address: B256, slot: B256) -> bool {
181        let path = Nibbles::unpack(slot);
182        let trie = match self.storage_trie_ref(&address) {
183            Some(t) => t,
184            None => return false,
185        };
186
187        trie.find_leaf(&path, None).is_ok()
188    }
189
190    /// Returns `true` if storage slot for account was already revealed.
191    pub fn is_storage_slot_revealed(&self, account: B256, slot: B256) -> bool {
192        self.storage
193            .revealed_paths
194            .get(&account)
195            .is_some_and(|slots| slots.contains(&Nibbles::unpack(slot)))
196    }
197
198    /// Returns reference to bytes representing leaf value for the target account.
199    pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
200        self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
201    }
202
203    /// Returns reference to bytes representing leaf value for the target account and storage slot.
204    pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
205        self.storage.tries.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
206    }
207
208    /// Returns reference to state trie if it was revealed.
209    pub const fn state_trie_ref(&self) -> Option<&A> {
210        self.state.as_revealed_ref()
211    }
212
213    /// Returns reference to storage trie if it was revealed.
214    pub fn storage_trie_ref(&self, address: &B256) -> Option<&S> {
215        self.storage.tries.get(address).and_then(|e| e.as_revealed_ref())
216    }
217
218    /// Returns mutable reference to storage sparse trie if it was revealed.
219    pub fn storage_trie_mut(&mut self, address: &B256) -> Option<&mut S> {
220        self.storage.tries.get_mut(address).and_then(|e| e.as_revealed_mut())
221    }
222
223    /// Returns mutable reference to storage tries.
224    pub const fn storage_tries_mut(&mut self) -> &mut B256Map<RevealableSparseTrie<S>> {
225        &mut self.storage.tries
226    }
227
228    /// Takes the storage trie for the provided address.
229    pub fn take_storage_trie(&mut self, address: &B256) -> Option<RevealableSparseTrie<S>> {
230        self.storage.tries.remove(address)
231    }
232
233    /// Takes the storage trie for the provided address, creating a blind one if it doesn't exist.
234    pub fn take_or_create_storage_trie(&mut self, address: &B256) -> RevealableSparseTrie<S> {
235        self.storage.tries.remove(address).unwrap_or_else(|| {
236            self.storage.cleared_tries.pop().unwrap_or_else(|| self.storage.default_trie.clone())
237        })
238    }
239
240    /// Inserts storage trie for the provided address.
241    pub fn insert_storage_trie(&mut self, address: B256, storage_trie: RevealableSparseTrie<S>) {
242        self.storage.tries.insert(address, storage_trie);
243    }
244
245    /// Returns mutable reference to storage sparse trie, creating a blind one if it doesn't exist.
246    pub fn get_or_create_storage_trie_mut(
247        &mut self,
248        address: B256,
249    ) -> &mut RevealableSparseTrie<S> {
250        self.storage.get_or_create_trie_mut(address)
251    }
252
253    /// Reveal unknown trie paths from multiproof.
254    /// NOTE: This method does not extensively validate the proof.
255    pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
256        // first decode the multiproof
257        let decoded_multiproof = multiproof.try_into()?;
258
259        // then reveal the decoded multiproof
260        self.reveal_decoded_multiproof(decoded_multiproof)
261    }
262
263    /// Reveal unknown trie paths from decoded multiproof.
264    /// NOTE: This method does not extensively validate the proof.
265    #[instrument(
266        target = "trie::sparse",
267        skip_all,
268        fields(
269            account_nodes = multiproof.account_subtree.len(),
270            storages = multiproof.storages.len()
271        )
272    )]
273    pub fn reveal_decoded_multiproof(
274        &mut self,
275        multiproof: DecodedMultiProof,
276    ) -> SparseStateTrieResult<()> {
277        let DecodedMultiProof { account_subtree, storages, branch_node_masks } = multiproof;
278
279        // first reveal the account proof nodes
280        self.reveal_decoded_account_multiproof(account_subtree, branch_node_masks)?;
281
282        #[cfg(not(feature = "std"))]
283        // If nostd then serially reveal storage proof nodes for each storage trie
284        {
285            for (account, storage_subtree) in storages {
286                self.reveal_decoded_storage_multiproof(account, storage_subtree)?;
287                // Mark this storage trie as hot (accessed this tick)
288                self.storage.modifications.mark_accessed(account);
289            }
290
291            Ok(())
292        }
293
294        #[cfg(feature = "std")]
295        // If std then reveal storage proofs in parallel
296        {
297            use rayon::iter::ParallelIterator;
298            use reth_primitives_traits::ParallelBridgeBuffered;
299
300            let retain_updates = self.retain_updates;
301
302            // Process all storage trie revealings in parallel, having first removed the
303            // `reveal_nodes` tracking and `RevealableSparseTrie`s for each account from their
304            // HashMaps. These will be returned after processing.
305            let results: Vec<_> = storages
306                .into_iter()
307                .map(|(account, storage_subtree)| {
308                    let revealed_nodes = self.storage.take_or_create_revealed_paths(&account);
309                    let trie = self.storage.take_or_create_trie(&account);
310                    (account, storage_subtree, revealed_nodes, trie)
311                })
312                .par_bridge_buffered()
313                .map(|(account, storage_subtree, mut revealed_nodes, mut trie)| {
314                    let mut bufs = Vec::new();
315                    let result = Self::reveal_decoded_storage_multiproof_inner(
316                        account,
317                        storage_subtree,
318                        &mut revealed_nodes,
319                        &mut trie,
320                        &mut bufs,
321                        retain_updates,
322                    );
323
324                    (account, revealed_nodes, trie, result, bufs)
325                })
326                .collect();
327
328            // Return `revealed_nodes` and `RevealableSparseTrie` for each account, incrementing
329            // metrics and returning the last error seen if any.
330            let mut any_err = Ok(());
331            for (account, revealed_nodes, trie, result, bufs) in results {
332                self.storage.revealed_paths.insert(account, revealed_nodes);
333                self.storage.tries.insert(account, trie);
334                // Mark this storage trie as hot (accessed this tick)
335                self.storage.modifications.mark_accessed(account);
336                if let Ok(_metric_values) = result {
337                    #[cfg(feature = "metrics")]
338                    {
339                        self.metrics
340                            .increment_total_storage_nodes(_metric_values.total_nodes as u64);
341                        self.metrics
342                            .increment_skipped_storage_nodes(_metric_values.skipped_nodes as u64);
343                    }
344                } else {
345                    any_err = result.map(|_| ());
346                }
347
348                // Keep buffers for deferred dropping
349                self.deferred_drops.proof_nodes_bufs.extend(bufs);
350            }
351
352            any_err
353        }
354    }
355
356    /// Reveals a V2 decoded multiproof.
357    ///
358    /// V2 multiproofs use a simpler format where proof nodes are stored as vectors rather than
359    /// hashmaps, with masks already included in the `ProofTrieNode` structure.
360    #[instrument(
361        skip_all,
362        fields(
363            account_nodes = multiproof.account_proofs.len(),
364            storages = multiproof.storage_proofs.len()
365        )
366    )]
367    pub fn reveal_decoded_multiproof_v2(
368        &mut self,
369        multiproof: reth_trie_common::DecodedMultiProofV2,
370    ) -> SparseStateTrieResult<()> {
371        // Reveal the account proof nodes.
372        //
373        // Skip revealing account nodes if this result only contains storage proofs.
374        // `reveal_account_v2_proof_nodes` will return an error if empty `nodes` are passed into it
375        // before the accounts trie root was revealed. This might happen in cases when first account
376        // trie proof arrives later than first storage trie proof even though the account trie proof
377        // was requested first.
378        if !multiproof.account_proofs.is_empty() {
379            self.reveal_account_v2_proof_nodes(multiproof.account_proofs)?;
380        }
381
382        #[cfg(not(feature = "std"))]
383        // If nostd then serially reveal storage proof nodes for each storage trie
384        {
385            for (account, storage_proofs) in multiproof.storage_proofs {
386                self.reveal_storage_v2_proof_nodes(account, storage_proofs)?;
387                // Mark this storage trie as hot (accessed this tick)
388                self.storage.modifications.mark_accessed(account);
389            }
390
391            Ok(())
392        }
393
394        #[cfg(feature = "std")]
395        // If std then reveal storage proofs in parallel
396        {
397            use rayon::iter::ParallelIterator;
398            use reth_primitives_traits::ParallelBridgeBuffered;
399
400            let retain_updates = self.retain_updates;
401            let skip_filtering = self.skip_proof_node_filtering;
402
403            // Process all storage trie revealings in parallel, having first removed the
404            // `reveal_nodes` tracking and `RevealableSparseTrie`s for each account from their
405            // HashMaps. These will be returned after processing.
406            let results: Vec<_> = multiproof
407                .storage_proofs
408                .into_iter()
409                .map(|(account, storage_proofs)| {
410                    let revealed_nodes = self.storage.take_or_create_revealed_paths(&account);
411                    let trie = self.storage.take_or_create_trie(&account);
412                    (account, storage_proofs, revealed_nodes, trie)
413                })
414                .par_bridge_buffered()
415                .map(|(account, storage_proofs, mut revealed_nodes, mut trie)| {
416                    let mut bufs = Vec::new();
417                    let result = Self::reveal_storage_v2_proof_nodes_inner(
418                        account,
419                        storage_proofs,
420                        &mut revealed_nodes,
421                        &mut trie,
422                        &mut bufs,
423                        retain_updates,
424                        skip_filtering,
425                    );
426                    (account, result, revealed_nodes, trie, bufs)
427                })
428                .collect();
429
430            let mut any_err = Ok(());
431            for (account, result, revealed_nodes, trie, bufs) in results {
432                self.storage.revealed_paths.insert(account, revealed_nodes);
433                self.storage.tries.insert(account, trie);
434                // Mark this storage trie as hot (accessed this tick)
435                self.storage.modifications.mark_accessed(account);
436                if let Ok(_metric_values) = result {
437                    #[cfg(feature = "metrics")]
438                    {
439                        self.metrics
440                            .increment_total_storage_nodes(_metric_values.total_nodes as u64);
441                        self.metrics
442                            .increment_skipped_storage_nodes(_metric_values.skipped_nodes as u64);
443                    }
444                } else {
445                    any_err = result.map(|_| ());
446                }
447
448                // Keep buffers for deferred dropping
449                self.deferred_drops.proof_nodes_bufs.extend(bufs);
450            }
451
452            any_err
453        }
454    }
455
456    /// Reveals an account multiproof.
457    pub fn reveal_account_multiproof(
458        &mut self,
459        account_subtree: ProofNodes,
460        branch_node_masks: BranchNodeMasksMap,
461    ) -> SparseStateTrieResult<()> {
462        // decode the multiproof first
463        let decoded_multiproof = account_subtree.try_into()?;
464        self.reveal_decoded_account_multiproof(decoded_multiproof, branch_node_masks)
465    }
466
467    /// Reveals a decoded account multiproof.
468    pub fn reveal_decoded_account_multiproof(
469        &mut self,
470        account_subtree: DecodedProofNodes,
471        branch_node_masks: BranchNodeMasksMap,
472    ) -> SparseStateTrieResult<()> {
473        let FilterMappedProofNodes {
474            root_node,
475            mut nodes,
476            new_nodes,
477            metric_values: _metric_values,
478        } = filter_map_revealed_nodes(
479            account_subtree,
480            &mut self.revealed_account_paths,
481            &branch_node_masks,
482        )?;
483        #[cfg(feature = "metrics")]
484        {
485            self.metrics.increment_total_account_nodes(_metric_values.total_nodes as u64);
486            self.metrics.increment_skipped_account_nodes(_metric_values.skipped_nodes as u64);
487        }
488
489        if let Some(root_node) = root_node {
490            // Reveal root node if it wasn't already.
491            trace!(target: "trie::sparse", ?root_node, "Revealing root account node");
492            let trie =
493                self.state.reveal_root(root_node.node, root_node.masks, self.retain_updates)?;
494
495            // Reserve the capacity for new nodes ahead of time, if the trie implementation
496            // supports doing so.
497            trie.reserve_nodes(new_nodes);
498
499            trace!(target: "trie::sparse", total_nodes = ?nodes.len(), "Revealing account nodes");
500            trie.reveal_nodes(&mut nodes)?;
501        }
502
503        // Keep buffer for deferred dropping
504        self.deferred_drops.proof_nodes_bufs.push(nodes);
505
506        Ok(())
507    }
508
509    /// Reveals account proof nodes from a V2 proof.
510    ///
511    /// V2 proofs already include the masks in the `ProofTrieNode` structure,
512    /// so no separate masks map is needed.
513    pub fn reveal_account_v2_proof_nodes(
514        &mut self,
515        mut nodes: Vec<ProofTrieNode>,
516    ) -> SparseStateTrieResult<()> {
517        if self.skip_proof_node_filtering {
518            let capacity = estimate_v2_proof_capacity(&nodes);
519
520            #[cfg(feature = "metrics")]
521            self.metrics.increment_total_account_nodes(nodes.len() as u64);
522
523            let root_node = nodes.iter().find(|n| n.path.is_empty());
524            let trie = if let Some(root_node) = root_node {
525                trace!(target: "trie::sparse", ?root_node, "Revealing root account node from V2 proof");
526                self.state.reveal_root(
527                    root_node.node.clone(),
528                    root_node.masks,
529                    self.retain_updates,
530                )?
531            } else {
532                self.state.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
533            };
534            trie.reserve_nodes(capacity);
535            trace!(target: "trie::sparse", total_nodes = ?nodes.len(), "Revealing account nodes from V2 proof (unfiltered)");
536            trie.reveal_nodes(&mut nodes)?;
537
538            self.deferred_drops.proof_nodes_bufs.push(nodes);
539            return Ok(())
540        }
541
542        let FilteredV2ProofNodes { root_node, mut nodes, new_nodes, metric_values: _metric_values } =
543            filter_revealed_v2_proof_nodes(nodes, &mut self.revealed_account_paths)?;
544
545        #[cfg(feature = "metrics")]
546        {
547            self.metrics.increment_total_account_nodes(_metric_values.total_nodes as u64);
548            self.metrics.increment_skipped_account_nodes(_metric_values.skipped_nodes as u64);
549        }
550
551        let trie = if let Some(root_node) = root_node {
552            trace!(target: "trie::sparse", ?root_node, "Revealing root account node from V2 proof");
553            self.state.reveal_root(root_node.node, root_node.masks, self.retain_updates)?
554        } else {
555            self.state.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
556        };
557
558        trie.reserve_nodes(new_nodes);
559
560        trace!(target: "trie::sparse", total_nodes = ?nodes.len(), "Revealing account nodes from V2 proof");
561        trie.reveal_nodes(&mut nodes)?;
562
563        // Keep buffer for deferred dropping
564        self.deferred_drops.proof_nodes_bufs.push(nodes);
565
566        Ok(())
567    }
568
569    /// Reveals storage proof nodes from a V2 proof for the given address.
570    ///
571    /// V2 proofs already include the masks in the `ProofTrieNode` structure,
572    /// so no separate masks map is needed.
573    pub fn reveal_storage_v2_proof_nodes(
574        &mut self,
575        account: B256,
576        nodes: Vec<ProofTrieNode>,
577    ) -> SparseStateTrieResult<()> {
578        let (trie, revealed_paths) = self.storage.get_trie_and_revealed_paths_mut(account);
579        let _metric_values = Self::reveal_storage_v2_proof_nodes_inner(
580            account,
581            nodes,
582            revealed_paths,
583            trie,
584            &mut self.deferred_drops.proof_nodes_bufs,
585            self.retain_updates,
586            self.skip_proof_node_filtering,
587        )?;
588
589        #[cfg(feature = "metrics")]
590        {
591            self.metrics.increment_total_storage_nodes(_metric_values.total_nodes as u64);
592            self.metrics.increment_skipped_storage_nodes(_metric_values.skipped_nodes as u64);
593        }
594
595        Ok(())
596    }
597
598    /// Reveals storage V2 proof nodes for the given address. This is an internal static function
599    /// designed to handle a variety of associated public functions.
600    fn reveal_storage_v2_proof_nodes_inner(
601        account: B256,
602        mut nodes: Vec<ProofTrieNode>,
603        revealed_nodes: &mut HashSet<Nibbles>,
604        trie: &mut RevealableSparseTrie<S>,
605        bufs: &mut Vec<Vec<ProofTrieNode>>,
606        retain_updates: bool,
607        skip_filtering: bool,
608    ) -> SparseStateTrieResult<ProofNodesMetricValues> {
609        if skip_filtering {
610            let capacity = estimate_v2_proof_capacity(&nodes);
611            let metric_values =
612                ProofNodesMetricValues { total_nodes: nodes.len(), skipped_nodes: 0 };
613
614            let root_node = nodes.iter().find(|n| n.path.is_empty());
615            let trie = if let Some(root_node) = root_node {
616                trace!(target: "trie::sparse", ?account, ?root_node, "Revealing root storage node from V2 proof");
617                trie.reveal_root(root_node.node.clone(), root_node.masks, retain_updates)?
618            } else {
619                trie.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
620            };
621            trie.reserve_nodes(capacity);
622            trace!(target: "trie::sparse", ?account, total_nodes = ?nodes.len(), "Revealing storage nodes from V2 proof (unfiltered)");
623            trie.reveal_nodes(&mut nodes)?;
624
625            bufs.push(nodes);
626            return Ok(metric_values)
627        }
628
629        let FilteredV2ProofNodes { root_node, mut nodes, new_nodes, metric_values } =
630            filter_revealed_v2_proof_nodes(nodes, revealed_nodes)?;
631
632        let trie = if let Some(root_node) = root_node {
633            trace!(target: "trie::sparse", ?account, ?root_node, "Revealing root storage node from V2 proof");
634            trie.reveal_root(root_node.node, root_node.masks, retain_updates)?
635        } else {
636            trie.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?
637        };
638
639        trie.reserve_nodes(new_nodes);
640
641        trace!(target: "trie::sparse", ?account, total_nodes = ?nodes.len(), "Revealing storage nodes from V2 proof");
642        trie.reveal_nodes(&mut nodes)?;
643
644        // Keep buffer for deferred dropping
645        bufs.push(nodes);
646
647        Ok(metric_values)
648    }
649
650    /// Reveals a storage multiproof for the given address.
651    pub fn reveal_storage_multiproof(
652        &mut self,
653        account: B256,
654        storage_subtree: StorageMultiProof,
655    ) -> SparseStateTrieResult<()> {
656        // decode the multiproof first
657        let decoded_multiproof = storage_subtree.try_into()?;
658        self.reveal_decoded_storage_multiproof(account, decoded_multiproof)
659    }
660
661    /// Reveals a decoded storage multiproof for the given address.
662    pub fn reveal_decoded_storage_multiproof(
663        &mut self,
664        account: B256,
665        storage_subtree: DecodedStorageMultiProof,
666    ) -> SparseStateTrieResult<()> {
667        let (trie, revealed_paths) = self.storage.get_trie_and_revealed_paths_mut(account);
668        let _metric_values = Self::reveal_decoded_storage_multiproof_inner(
669            account,
670            storage_subtree,
671            revealed_paths,
672            trie,
673            &mut self.deferred_drops.proof_nodes_bufs,
674            self.retain_updates,
675        )?;
676
677        #[cfg(feature = "metrics")]
678        {
679            self.metrics.increment_total_storage_nodes(_metric_values.total_nodes as u64);
680            self.metrics.increment_skipped_storage_nodes(_metric_values.skipped_nodes as u64);
681        }
682
683        Ok(())
684    }
685
686    /// Reveals a decoded storage multiproof for the given address. This is internal static function
687    /// is designed to handle a variety of associated public functions.
688    fn reveal_decoded_storage_multiproof_inner(
689        account: B256,
690        storage_subtree: DecodedStorageMultiProof,
691        revealed_nodes: &mut HashSet<Nibbles>,
692        trie: &mut RevealableSparseTrie<S>,
693        bufs: &mut Vec<Vec<ProofTrieNode>>,
694        retain_updates: bool,
695    ) -> SparseStateTrieResult<ProofNodesMetricValues> {
696        let FilterMappedProofNodes { root_node, mut nodes, new_nodes, metric_values } =
697            filter_map_revealed_nodes(
698                storage_subtree.subtree,
699                revealed_nodes,
700                &storage_subtree.branch_node_masks,
701            )?;
702
703        if let Some(root_node) = root_node {
704            // Reveal root node if it wasn't already.
705            trace!(target: "trie::sparse", ?account, ?root_node, "Revealing root storage node");
706            let trie = trie.reveal_root(root_node.node, root_node.masks, retain_updates)?;
707
708            // Reserve the capacity for new nodes ahead of time, if the trie implementation
709            // supports doing so.
710            trie.reserve_nodes(new_nodes);
711
712            trace!(target: "trie::sparse", ?account, total_nodes = ?nodes.len(), "Revealing storage nodes");
713            trie.reveal_nodes(&mut nodes)?;
714        }
715
716        // Keep buffer for deferred dropping
717        bufs.push(nodes);
718
719        Ok(metric_values)
720    }
721
722    /// Reveal state witness with the given state root.
723    /// The state witness is expected to be a map of `keccak(rlp(node)): rlp(node).`
724    /// NOTE: This method does not extensively validate the witness.
725    pub fn reveal_witness(
726        &mut self,
727        state_root: B256,
728        witness: &B256Map<Bytes>,
729    ) -> SparseStateTrieResult<()> {
730        // Create a `(hash, path, maybe_account)` queue for traversing witness trie nodes
731        // starting from the root node.
732        let mut queue = VecDeque::from([(state_root, Nibbles::default(), None)]);
733
734        while let Some((hash, path, maybe_account)) = queue.pop_front() {
735            // Retrieve the trie node and decode it.
736            let Some(trie_node_bytes) = witness.get(&hash) else { continue };
737            let trie_node = TrieNode::decode(&mut &trie_node_bytes[..])?;
738
739            // Push children nodes into the queue.
740            match &trie_node {
741                TrieNode::Branch(branch) => {
742                    for (idx, maybe_child) in branch.as_ref().children() {
743                        if let Some(child_hash) = maybe_child.and_then(RlpNode::as_hash) {
744                            let mut child_path = path;
745                            child_path.push_unchecked(idx);
746                            queue.push_back((child_hash, child_path, maybe_account));
747                        }
748                    }
749                }
750                TrieNode::Extension(ext) => {
751                    if let Some(child_hash) = ext.child.as_hash() {
752                        let mut child_path = path;
753                        child_path.extend(&ext.key);
754                        queue.push_back((child_hash, child_path, maybe_account));
755                    }
756                }
757                TrieNode::Leaf(leaf) => {
758                    let mut full_path = path;
759                    full_path.extend(&leaf.key);
760                    if maybe_account.is_none() {
761                        let hashed_address = B256::from_slice(&full_path.pack());
762                        let account = TrieAccount::decode(&mut &leaf.value[..])?;
763                        if account.storage_root != EMPTY_ROOT_HASH {
764                            queue.push_back((
765                                account.storage_root,
766                                Nibbles::default(),
767                                Some(hashed_address),
768                            ));
769                        }
770                    }
771                }
772                TrieNode::EmptyRoot => {} // nothing to do here
773            };
774
775            // Reveal the node itself.
776            if let Some(account) = maybe_account {
777                // Check that the path was not already revealed.
778                if self
779                    .storage
780                    .revealed_paths
781                    .get(&account)
782                    .is_none_or(|paths| !paths.contains(&path))
783                {
784                    let retain_updates = self.retain_updates;
785                    let (storage_trie_entry, revealed_storage_paths) =
786                        self.storage.get_trie_and_revealed_paths_mut(account);
787
788                    if path.is_empty() {
789                        // Handle special storage state root node case.
790                        storage_trie_entry.reveal_root(trie_node, None, retain_updates)?;
791                    } else {
792                        // Reveal non-root storage trie node.
793                        storage_trie_entry
794                            .as_revealed_mut()
795                            .ok_or(SparseTrieErrorKind::Blind)?
796                            .reveal_node(path, trie_node, None)?;
797                    }
798
799                    // Track the revealed path.
800                    revealed_storage_paths.insert(path);
801                }
802            }
803            // Check that the path was not already revealed.
804            else if !self.revealed_account_paths.contains(&path) {
805                if path.is_empty() {
806                    // Handle special state root node case.
807                    self.state.reveal_root(trie_node, None, self.retain_updates)?;
808                } else {
809                    // Reveal non-root state trie node.
810                    self.state
811                        .as_revealed_mut()
812                        .ok_or(SparseTrieErrorKind::Blind)?
813                        .reveal_node(path, trie_node, None)?;
814                }
815
816                // Track the revealed path.
817                self.revealed_account_paths.insert(path);
818            }
819        }
820
821        Ok(())
822    }
823
824    /// Wipe the storage trie at the provided address.
825    pub fn wipe_storage(&mut self, address: B256) -> SparseStateTrieResult<()> {
826        if let Some(trie) = self.storage.tries.get_mut(&address) {
827            trie.wipe()?;
828        }
829        Ok(())
830    }
831
832    /// Calculates the hashes of subtries.
833    ///
834    /// If the trie has not been revealed, this function does nothing.
835    #[instrument(target = "trie::sparse", skip_all)]
836    pub fn calculate_subtries(&mut self) {
837        if let RevealableSparseTrie::Revealed(trie) = &mut self.state {
838            trie.update_subtrie_hashes();
839        }
840    }
841
842    /// Returns storage sparse trie root if the trie has been revealed.
843    pub fn storage_root(&mut self, account: &B256) -> Option<B256> {
844        self.storage.tries.get_mut(account).and_then(|trie| trie.root())
845    }
846
847    /// Returns mutable reference to the revealed account sparse trie.
848    ///
849    /// If the trie is not revealed yet, its root will be revealed using the trie node provider.
850    fn revealed_trie_mut(
851        &mut self,
852        provider_factory: impl TrieNodeProviderFactory,
853    ) -> SparseStateTrieResult<&mut A> {
854        match self.state {
855            RevealableSparseTrie::Blind(_) => {
856                let (root_node, hash_mask, tree_mask) = provider_factory
857                    .account_node_provider()
858                    .trie_node(&Nibbles::default())?
859                    .map(|node| {
860                        TrieNode::decode(&mut &node.node[..])
861                            .map(|decoded| (decoded, node.hash_mask, node.tree_mask))
862                    })
863                    .transpose()?
864                    .unwrap_or((TrieNode::EmptyRoot, None, None));
865                let masks = BranchNodeMasks::from_optional(hash_mask, tree_mask);
866                self.state.reveal_root(root_node, masks, self.retain_updates).map_err(Into::into)
867            }
868            RevealableSparseTrie::Revealed(ref mut trie) => Ok(trie),
869        }
870    }
871
872    /// Returns sparse trie root.
873    ///
874    /// If the trie has not been revealed, this function reveals the root node and returns its hash.
875    pub fn root(
876        &mut self,
877        provider_factory: impl TrieNodeProviderFactory,
878    ) -> SparseStateTrieResult<B256> {
879        // record revealed node metrics
880        #[cfg(feature = "metrics")]
881        self.metrics.record();
882
883        Ok(self.revealed_trie_mut(provider_factory)?.root())
884    }
885
886    /// Returns sparse trie root and trie updates if the trie has been revealed.
887    #[instrument(target = "trie::sparse", skip_all)]
888    pub fn root_with_updates(
889        &mut self,
890        provider_factory: impl TrieNodeProviderFactory,
891    ) -> SparseStateTrieResult<(B256, TrieUpdates)> {
892        // record revealed node metrics
893        #[cfg(feature = "metrics")]
894        self.metrics.record();
895
896        let storage_tries = self.storage_trie_updates();
897        let revealed = self.revealed_trie_mut(provider_factory)?;
898
899        let (root, updates) = (revealed.root(), revealed.take_updates());
900        let updates = TrieUpdates {
901            account_nodes: updates.updated_nodes,
902            removed_nodes: updates.removed_nodes,
903            storage_tries,
904        };
905        Ok((root, updates))
906    }
907
908    /// Returns storage trie updates for tries that have been revealed.
909    ///
910    /// Panics if any of the storage tries are not revealed.
911    pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdates> {
912        self.storage
913            .tries
914            .iter_mut()
915            .map(|(address, trie)| {
916                let trie = trie.as_revealed_mut().unwrap();
917                let updates = trie.take_updates();
918                let updates = StorageTrieUpdates {
919                    is_deleted: updates.wiped,
920                    storage_nodes: updates.updated_nodes,
921                    removed_nodes: updates.removed_nodes,
922                };
923                (*address, updates)
924            })
925            .filter(|(_, updates)| !updates.is_empty())
926            .collect()
927    }
928
929    /// Returns [`TrieUpdates`] by taking the updates from the revealed sparse tries.
930    ///
931    /// Returns `None` if the accounts trie is not revealed.
932    pub fn take_trie_updates(&mut self) -> Option<TrieUpdates> {
933        let storage_tries = self.storage_trie_updates();
934        self.state.as_revealed_mut().map(|state| {
935            let updates = state.take_updates();
936            TrieUpdates {
937                account_nodes: updates.updated_nodes,
938                removed_nodes: updates.removed_nodes,
939                storage_tries,
940            }
941        })
942    }
943
944    /// Update the account leaf node.
945    pub fn update_account_leaf(
946        &mut self,
947        path: Nibbles,
948        value: Vec<u8>,
949        provider_factory: impl TrieNodeProviderFactory,
950    ) -> SparseStateTrieResult<()> {
951        if !self.revealed_account_paths.contains(&path) {
952            self.revealed_account_paths.insert(path);
953        }
954
955        let provider = provider_factory.account_node_provider();
956        self.state.update_leaf(path, value, provider)?;
957        Ok(())
958    }
959
960    /// Update the leaf node of a revealed storage trie at the provided address.
961    pub fn update_storage_leaf(
962        &mut self,
963        address: B256,
964        slot: Nibbles,
965        value: Vec<u8>,
966        provider_factory: impl TrieNodeProviderFactory,
967    ) -> SparseStateTrieResult<()> {
968        let provider = provider_factory.storage_node_provider(address);
969        self.storage
970            .tries
971            .get_mut(&address)
972            .ok_or(SparseTrieErrorKind::Blind)?
973            .update_leaf(slot, value, provider)?;
974        self.storage.get_revealed_paths_mut(address).insert(slot);
975        Ok(())
976    }
977
978    /// Update or remove trie account based on new account info. This method will either recompute
979    /// the storage root based on update storage trie or look it up from existing leaf value.
980    ///
981    /// Returns false if the new account info and storage trie are empty, indicating the account
982    /// leaf should be removed.
983    #[instrument(level = "trace", target = "trie::sparse", skip_all)]
984    pub fn update_account(
985        &mut self,
986        address: B256,
987        account: Account,
988        provider_factory: impl TrieNodeProviderFactory,
989    ) -> SparseStateTrieResult<bool> {
990        let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
991            trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
992            storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
993        } else if self.is_account_revealed(address) {
994            trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
995            // The account was revealed, either...
996            if let Some(value) = self.get_account_value(&address) {
997                // ..it exists and we should take its current storage root or...
998                TrieAccount::decode(&mut &value[..])?.storage_root
999            } else {
1000                // ...the account is newly created and the storage trie is empty.
1001                EMPTY_ROOT_HASH
1002            }
1003        } else {
1004            return Err(SparseTrieErrorKind::Blind.into())
1005        };
1006
1007        if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
1008            return Ok(false);
1009        }
1010
1011        trace!(target: "trie::sparse", ?address, "Updating account");
1012        let nibbles = Nibbles::unpack(address);
1013        self.account_rlp_buf.clear();
1014        account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
1015        self.update_account_leaf(nibbles, self.account_rlp_buf.clone(), provider_factory)?;
1016
1017        Ok(true)
1018    }
1019
1020    /// Update the storage root of a revealed account.
1021    ///
1022    /// If the account doesn't exist in the trie, the function is a no-op.
1023    ///
1024    /// Returns false if the new storage root is empty, and the account info was already empty,
1025    /// indicating the account leaf should be removed.
1026    #[instrument(target = "trie::sparse", skip_all)]
1027    pub fn update_account_storage_root(
1028        &mut self,
1029        address: B256,
1030        provider_factory: impl TrieNodeProviderFactory,
1031    ) -> SparseStateTrieResult<bool> {
1032        if !self.is_account_revealed(address) {
1033            return Err(SparseTrieErrorKind::Blind.into())
1034        }
1035
1036        // Nothing to update if the account doesn't exist in the trie.
1037        let Some(mut trie_account) = self
1038            .get_account_value(&address)
1039            .map(|v| TrieAccount::decode(&mut &v[..]))
1040            .transpose()?
1041        else {
1042            trace!(target: "trie::sparse", ?address, "Account not found in trie, skipping storage root update");
1043            return Ok(true)
1044        };
1045
1046        // Calculate the new storage root. If the storage trie doesn't exist, the storage root will
1047        // be empty.
1048        let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
1049            trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
1050            storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
1051        } else {
1052            EMPTY_ROOT_HASH
1053        };
1054
1055        // Update the account with the new storage root.
1056        trie_account.storage_root = storage_root;
1057
1058        // If the account is empty, indicate that it should be removed.
1059        if trie_account == TrieAccount::default() {
1060            return Ok(false)
1061        }
1062
1063        // Otherwise, update the account leaf.
1064        trace!(target: "trie::sparse", ?address, "Updating account with the new storage root");
1065        let nibbles = Nibbles::unpack(address);
1066        self.account_rlp_buf.clear();
1067        trie_account.encode(&mut self.account_rlp_buf);
1068        self.update_account_leaf(nibbles, self.account_rlp_buf.clone(), provider_factory)?;
1069
1070        Ok(true)
1071    }
1072
1073    /// Remove the account leaf node.
1074    #[instrument(target = "trie::sparse", skip_all)]
1075    pub fn remove_account_leaf(
1076        &mut self,
1077        path: &Nibbles,
1078        provider_factory: impl TrieNodeProviderFactory,
1079    ) -> SparseStateTrieResult<()> {
1080        let provider = provider_factory.account_node_provider();
1081        self.state.remove_leaf(path, provider)?;
1082        Ok(())
1083    }
1084
1085    /// Update the leaf node of a storage trie at the provided address.
1086    pub fn remove_storage_leaf(
1087        &mut self,
1088        address: B256,
1089        slot: &Nibbles,
1090        provider_factory: impl TrieNodeProviderFactory,
1091    ) -> SparseStateTrieResult<()> {
1092        let storage_trie =
1093            self.storage.tries.get_mut(&address).ok_or(SparseTrieErrorKind::Blind)?;
1094
1095        let provider = provider_factory.storage_node_provider(address);
1096        storage_trie.remove_leaf(slot, provider)?;
1097        Ok(())
1098    }
1099}
1100
1101impl<A, S> SparseStateTrie<A, S>
1102where
1103    A: SparseTrieTrait + Default,
1104    S: SparseTrieTrait + Default + Clone,
1105{
1106    /// Clears all trie data while preserving allocations for reuse.
1107    ///
1108    /// This resets the trie to an empty state but keeps the underlying memory allocations,
1109    /// which can significantly reduce allocation overhead when the trie is reused.
1110    pub fn clear(&mut self) {
1111        self.state.clear();
1112        self.revealed_account_paths.clear();
1113        self.storage.clear();
1114        self.account_rlp_buf.clear();
1115    }
1116
1117    /// Shrinks the capacity of the sparse trie to the given node and value sizes.
1118    ///
1119    /// This helps reduce memory usage when the trie has excess capacity.
1120    /// Distributes capacity equally among all tries (account + storage).
1121    pub fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
1122        // Count total number of storage tries (active + cleared)
1123        let storage_tries_count = self.storage.tries.len() + self.storage.cleared_tries.len();
1124
1125        // Total tries = 1 account trie + all storage tries
1126        let total_tries = 1 + storage_tries_count;
1127
1128        // Distribute capacity equally among all tries
1129        let nodes_per_trie = max_nodes / total_tries;
1130        let values_per_trie = max_values / total_tries;
1131
1132        // Shrink the account trie
1133        self.state.shrink_nodes_to(nodes_per_trie);
1134        self.state.shrink_values_to(values_per_trie);
1135
1136        // Give storage tries the remaining capacity after account trie allocation
1137        let storage_nodes = max_nodes.saturating_sub(nodes_per_trie);
1138        let storage_values = max_values.saturating_sub(values_per_trie);
1139
1140        // Shrink all storage tries (they will redistribute internally)
1141        self.storage.shrink_to(storage_nodes, storage_values);
1142    }
1143
1144    /// Prunes the account trie and selected storage tries to reduce memory usage.
1145    ///
1146    /// Storage tries not in the top `max_storage_tries` by revealed node count are cleared
1147    /// entirely.
1148    ///
1149    /// # Preconditions
1150    ///
1151    /// Node hashes must be computed via `root()` before calling this method. Otherwise, nodes
1152    /// cannot be converted to hash stubs and pruning will have no effect.
1153    ///
1154    /// # Effects
1155    ///
1156    /// - Clears `revealed_account_paths` and `revealed_paths` for all storage tries
1157    #[cfg(feature = "std")]
1158    #[instrument(
1159        name = "SparseStateTrie::prune",
1160        target = "trie::sparse",
1161        skip_all,
1162        fields(max_depth, max_storage_tries)
1163    )]
1164    pub fn prune(&mut self, max_depth: usize, max_storage_tries: usize) {
1165        // Prune state and storage tries in parallel
1166        rayon::join(
1167            || {
1168                if let Some(trie) = self.state.as_revealed_mut() {
1169                    trie.prune(max_depth);
1170                }
1171                self.revealed_account_paths.clear();
1172            },
1173            || {
1174                self.storage.prune(max_depth, max_storage_tries);
1175            },
1176        );
1177    }
1178}
1179
1180/// The fields of [`SparseStateTrie`] related to storage tries. This is kept separate from the rest
1181/// of [`SparseStateTrie`] both to help enforce allocation re-use and to allow us to implement
1182/// methods like `get_trie_and_revealed_paths` which return multiple mutable borrows.
1183#[derive(Debug, Default)]
1184struct StorageTries<S = ParallelSparseTrie> {
1185    /// Sparse storage tries.
1186    tries: B256Map<RevealableSparseTrie<S>>,
1187    /// Cleared storage tries, kept for re-use.
1188    cleared_tries: Vec<RevealableSparseTrie<S>>,
1189    /// Collection of revealed storage trie paths, per account.
1190    revealed_paths: B256Map<HashSet<Nibbles>>,
1191    /// Cleared revealed storage trie path collections, kept for re-use.
1192    cleared_revealed_paths: Vec<HashSet<Nibbles>>,
1193    /// A default cleared trie instance, which will be cloned when creating new tries.
1194    default_trie: RevealableSparseTrie<S>,
1195    /// Tracks access patterns and modification state of storage tries for smart pruning decisions.
1196    modifications: StorageTrieModifications,
1197}
1198
1199#[cfg(feature = "std")]
1200impl<S: SparseTrieTrait> StorageTries<S> {
1201    /// Prunes and evicts storage tries.
1202    ///
1203    /// Keeps the top `max_storage_tries` by a score combining size and heat.
1204    /// Evicts lower-scored tries entirely, prunes kept tries to `max_depth`.
1205    fn prune(&mut self, max_depth: usize, max_storage_tries: usize) {
1206        let fn_start = std::time::Instant::now();
1207        let mut stats =
1208            StorageTriesPruneStats { total_tries_before: self.tries.len(), ..Default::default() };
1209
1210        // Update heat for accessed tries
1211        self.modifications.update_and_reset();
1212
1213        // Collect (address, size, score) for all tries
1214        // Score = size * heat_multiplier
1215        // Hot tries (high heat) get boosted weight
1216        let mut trie_info: Vec<(B256, usize, usize)> = self
1217            .tries
1218            .iter()
1219            .map(|(address, trie)| {
1220                let size = match trie {
1221                    RevealableSparseTrie::Blind(_) => return (*address, 0, 0),
1222                    RevealableSparseTrie::Revealed(t) => t.size_hint(),
1223                };
1224                let heat = self.modifications.heat(address);
1225                // Heat multiplier: 1 (cold) to 3 (very hot, heat >= 4)
1226                let heat_multiplier = 1 + (heat.min(4) / 2) as usize;
1227                (*address, size, size * heat_multiplier)
1228            })
1229            .collect();
1230
1231        // Use O(n) selection to find top max_storage_tries by score
1232        if trie_info.len() > max_storage_tries {
1233            trie_info
1234                .select_nth_unstable_by(max_storage_tries.saturating_sub(1), |a, b| b.2.cmp(&a.2));
1235            trie_info.truncate(max_storage_tries);
1236        }
1237        let tries_to_keep: B256Map<usize> =
1238            trie_info.iter().map(|(address, size, _)| (*address, *size)).collect();
1239        stats.tries_to_keep = tries_to_keep.len();
1240
1241        // Collect keys to evict
1242        let tries_to_clear: Vec<B256> =
1243            self.tries.keys().filter(|addr| !tries_to_keep.contains_key(*addr)).copied().collect();
1244        stats.tries_to_evict = tries_to_clear.len();
1245
1246        // Evict storage tries that exceeded limit, saving cleared allocations for reuse
1247        for address in &tries_to_clear {
1248            if let Some(mut trie) = self.tries.remove(address) {
1249                trie.clear();
1250                self.cleared_tries.push(trie);
1251            }
1252            if let Some(mut paths) = self.revealed_paths.remove(address) {
1253                paths.clear();
1254                self.cleared_revealed_paths.push(paths);
1255            }
1256            self.modifications.remove(address);
1257        }
1258
1259        // Prune storage tries that are kept, but only if:
1260        // - They haven't been pruned since last access
1261        // - They're large enough to be worth pruning
1262        const MIN_SIZE_TO_PRUNE: usize = 1000;
1263        let prune_start = std::time::Instant::now();
1264        for (address, size) in &tries_to_keep {
1265            if *size < MIN_SIZE_TO_PRUNE {
1266                stats.skipped_small += 1;
1267                continue; // Small tries aren't worth the DFS cost
1268            }
1269            let Some(heat_state) = self.modifications.get_mut(address) else {
1270                continue; // No heat state = not tracked
1271            };
1272            // Only prune if backlog >= 2 (skip every other cycle)
1273            if heat_state.prune_backlog < 2 {
1274                stats.skipped_recently_pruned += 1;
1275                continue; // Recently pruned, skip this cycle
1276            }
1277            if let Some(trie) = self.tries.get_mut(address).and_then(|t| t.as_revealed_mut()) {
1278                trie.prune(max_depth);
1279                heat_state.prune_backlog = 0; // Reset backlog after prune
1280                stats.pruned_count += 1;
1281            }
1282        }
1283        stats.prune_elapsed = prune_start.elapsed();
1284
1285        // Clear revealed_paths for kept tries
1286        for hash in tries_to_keep.keys() {
1287            if let Some(paths) = self.revealed_paths.get_mut(hash) {
1288                paths.clear();
1289            }
1290        }
1291
1292        stats.total_tries_after = self.tries.len();
1293        stats.total_elapsed = fn_start.elapsed();
1294
1295        debug!(
1296            target: "trie::sparse",
1297            before = stats.total_tries_before,
1298            after = stats.total_tries_after,
1299            kept = stats.tries_to_keep,
1300            evicted = stats.tries_to_evict,
1301            pruned = stats.pruned_count,
1302            skipped_small = stats.skipped_small,
1303            skipped_recent = stats.skipped_recently_pruned,
1304            ?stats.prune_elapsed,
1305            ?stats.total_elapsed,
1306            "StorageTries::prune completed"
1307        );
1308    }
1309}
1310
1311impl<S: SparseTrieTrait> StorageTries<S> {
1312    /// Returns all fields to a cleared state, equivalent to the default state, keeping cleared
1313    /// collections for re-use later when possible.
1314    fn clear(&mut self) {
1315        self.cleared_tries.extend(self.tries.drain().map(|(_, mut trie)| {
1316            trie.clear();
1317            trie
1318        }));
1319        self.cleared_revealed_paths.extend(self.revealed_paths.drain().map(|(_, mut set)| {
1320            set.clear();
1321            set
1322        }));
1323        self.modifications.clear();
1324    }
1325
1326    /// Shrinks the capacity of all storage tries to the given total sizes.
1327    ///
1328    /// Distributes capacity equally among all tries (active + cleared).
1329    fn shrink_to(&mut self, max_nodes: usize, max_values: usize) {
1330        let total_tries = self.tries.len() + self.cleared_tries.len();
1331        if total_tries == 0 {
1332            return;
1333        }
1334
1335        // Distribute capacity equally among all tries
1336        let nodes_per_trie = max_nodes / total_tries;
1337        let values_per_trie = max_values / total_tries;
1338
1339        // Shrink active storage tries
1340        for trie in self.tries.values_mut() {
1341            trie.shrink_nodes_to(nodes_per_trie);
1342            trie.shrink_values_to(values_per_trie);
1343        }
1344
1345        // Shrink cleared storage tries
1346        for trie in &mut self.cleared_tries {
1347            trie.shrink_nodes_to(nodes_per_trie);
1348            trie.shrink_values_to(values_per_trie);
1349        }
1350    }
1351}
1352
1353impl<S: SparseTrieTrait + Clone> StorageTries<S> {
1354    /// Returns the set of already revealed trie node paths for an account's storage, creating the
1355    /// set if it didn't previously exist.
1356    fn get_revealed_paths_mut(&mut self, account: B256) -> &mut HashSet<Nibbles> {
1357        self.revealed_paths
1358            .entry(account)
1359            .or_insert_with(|| self.cleared_revealed_paths.pop().unwrap_or_default())
1360    }
1361
1362    /// Returns the `RevealableSparseTrie` and the set of already revealed trie node paths for an
1363    /// account's storage, creating them if they didn't previously exist.
1364    fn get_trie_and_revealed_paths_mut(
1365        &mut self,
1366        account: B256,
1367    ) -> (&mut RevealableSparseTrie<S>, &mut HashSet<Nibbles>) {
1368        let trie = self.tries.entry(account).or_insert_with(|| {
1369            self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
1370        });
1371
1372        let revealed_paths = self
1373            .revealed_paths
1374            .entry(account)
1375            .or_insert_with(|| self.cleared_revealed_paths.pop().unwrap_or_default());
1376
1377        (trie, revealed_paths)
1378    }
1379
1380    // Returns mutable reference to storage sparse trie, creating a blind one if it doesn't exist.
1381    fn get_or_create_trie_mut(&mut self, address: B256) -> &mut RevealableSparseTrie<S> {
1382        self.tries.entry(address).or_insert_with(|| {
1383            self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
1384        })
1385    }
1386
1387    /// Takes the storage trie for the account from the internal `HashMap`, creating it if it
1388    /// doesn't already exist.
1389    #[cfg(feature = "std")]
1390    fn take_or_create_trie(&mut self, account: &B256) -> RevealableSparseTrie<S> {
1391        self.tries.remove(account).unwrap_or_else(|| {
1392            self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
1393        })
1394    }
1395
1396    /// Takes the revealed paths set from the account from the internal `HashMap`, creating one if
1397    /// it doesn't exist.
1398    #[cfg(feature = "std")]
1399    fn take_or_create_revealed_paths(&mut self, account: &B256) -> HashSet<Nibbles> {
1400        self.revealed_paths
1401            .remove(account)
1402            .unwrap_or_else(|| self.cleared_revealed_paths.pop().unwrap_or_default())
1403    }
1404}
1405
1406/// Statistics from a storage tries prune operation.
1407#[derive(Debug, Default)]
1408#[allow(dead_code)]
1409struct StorageTriesPruneStats {
1410    total_tries_before: usize,
1411    total_tries_after: usize,
1412    tries_to_keep: usize,
1413    tries_to_evict: usize,
1414    pruned_count: usize,
1415    skipped_small: usize,
1416    skipped_recently_pruned: usize,
1417    prune_elapsed: core::time::Duration,
1418    total_elapsed: core::time::Duration,
1419}
1420
1421/// Per-trie access tracking and prune state.
1422///
1423/// Tracks how frequently a storage trie is accessed and when it was last pruned,
1424/// enabling smart pruning decisions that preserve frequently-used tries.
1425#[derive(Debug, Clone, Copy, Default)]
1426#[allow(dead_code)]
1427struct TrieModificationState {
1428    /// Access frequency level (0-255). Incremented each cycle the trie is accessed.
1429    /// Used for prioritizing which tries to keep during pruning.
1430    heat: u8,
1431    /// Prune backlog - cycles since last prune. Incremented each cycle,
1432    /// reset to 0 when pruned. Used to decide when pruning is needed.
1433    prune_backlog: u8,
1434}
1435
1436/// Tracks access patterns and modification state of storage tries for smart pruning decisions.
1437///
1438/// Access-based tracking is more accurate than simple generation counting because it tracks
1439/// actual access patterns rather than administrative operations (take/insert).
1440///
1441/// - Access frequency is incremented when a storage proof is revealed (accessed)
1442/// - Access frequency decays each prune cycle for tries not accessed that cycle
1443/// - Tries with higher access frequency are prioritized for preservation during pruning
1444#[derive(Debug, Default)]
1445struct StorageTrieModifications {
1446    /// Access frequency and prune state per storage trie address.
1447    state: B256Map<TrieModificationState>,
1448    /// Tracks which tries were accessed in the current cycle (between prune calls).
1449    accessed_this_cycle: B256Set,
1450}
1451
1452#[allow(dead_code)]
1453impl StorageTrieModifications {
1454    /// Marks a storage trie as accessed this cycle.
1455    /// Heat and `prune_backlog` are updated in [`Self::update_and_reset`].
1456    #[inline]
1457    fn mark_accessed(&mut self, address: B256) {
1458        self.accessed_this_cycle.insert(address);
1459    }
1460
1461    /// Returns mutable reference to the heat state for a storage trie.
1462    #[inline]
1463    fn get_mut(&mut self, address: &B256) -> Option<&mut TrieModificationState> {
1464        self.state.get_mut(address)
1465    }
1466
1467    /// Returns the heat level for a storage trie (0 if not tracked).
1468    #[inline]
1469    fn heat(&self, address: &B256) -> u8 {
1470        self.state.get(address).map_or(0, |s| s.heat)
1471    }
1472
1473    /// Updates heat and prune backlog for accessed tries.
1474    /// Called at the start of each prune cycle.
1475    fn update_and_reset(&mut self) {
1476        for address in self.accessed_this_cycle.drain() {
1477            let entry = self.state.entry(address).or_default();
1478            entry.heat = entry.heat.saturating_add(1);
1479            entry.prune_backlog = entry.prune_backlog.saturating_add(1);
1480        }
1481    }
1482
1483    /// Removes tracking for a specific address (when trie is evicted).
1484    fn remove(&mut self, address: &B256) {
1485        self.state.remove(address);
1486        self.accessed_this_cycle.remove(address);
1487    }
1488
1489    /// Clears all heat tracking state.
1490    fn clear(&mut self) {
1491        self.state.clear();
1492        self.accessed_this_cycle.clear();
1493    }
1494}
1495
1496#[derive(Debug, PartialEq, Eq, Default)]
1497struct ProofNodesMetricValues {
1498    /// Number of nodes in the proof.
1499    total_nodes: usize,
1500    /// Number of nodes that were skipped because they were already revealed.
1501    skipped_nodes: usize,
1502}
1503
1504/// Result of [`filter_map_revealed_nodes`].
1505#[derive(Debug, PartialEq, Eq)]
1506struct FilterMappedProofNodes {
1507    /// Root node which was pulled out of the original node set to be handled specially.
1508    root_node: Option<ProofTrieNode>,
1509    /// Filtered, decoded and unsorted proof nodes. Root node is removed.
1510    nodes: Vec<ProofTrieNode>,
1511    /// Number of new nodes that will be revealed. This includes all children of branch nodes, even
1512    /// if they are not in the proof.
1513    new_nodes: usize,
1514    /// Values which are being returned so they can be incremented into metrics.
1515    metric_values: ProofNodesMetricValues,
1516}
1517
1518/// Filters the decoded nodes that are already revealed, maps them to `SparseTrieNode`s,
1519/// separates the root node if present, and returns additional information about the number of
1520/// total, skipped, and new nodes.
1521fn filter_map_revealed_nodes(
1522    proof_nodes: DecodedProofNodes,
1523    revealed_nodes: &mut HashSet<Nibbles>,
1524    branch_node_masks: &BranchNodeMasksMap,
1525) -> SparseStateTrieResult<FilterMappedProofNodes> {
1526    let mut result = FilterMappedProofNodes {
1527        root_node: None,
1528        nodes: Vec::with_capacity(proof_nodes.len()),
1529        new_nodes: 0,
1530        metric_values: Default::default(),
1531    };
1532
1533    let proof_nodes_len = proof_nodes.len();
1534    for (path, proof_node) in proof_nodes.into_inner() {
1535        result.metric_values.total_nodes += 1;
1536
1537        let is_root = path.is_empty();
1538
1539        // If the node is already revealed, skip it. We don't ever skip the root node, nor do we add
1540        // it to `revealed_nodes`.
1541        if !is_root && !revealed_nodes.insert(path) {
1542            result.metric_values.skipped_nodes += 1;
1543            continue
1544        }
1545
1546        result.new_nodes += 1;
1547
1548        // Extract hash/tree masks based on the node type (only branch nodes have masks). At the
1549        // same time increase the new_nodes counter if the node is a type which has children.
1550        let masks = match &proof_node {
1551            TrieNode::Branch(branch) => {
1552                // If it's a branch node, increase the number of new nodes by the number of children
1553                // according to the state mask.
1554                result.new_nodes += branch.state_mask.count_ones() as usize;
1555                branch_node_masks.get(&path).copied()
1556            }
1557            TrieNode::Extension(_) => {
1558                // There is always exactly one child of an extension node.
1559                result.new_nodes += 1;
1560                None
1561            }
1562            _ => None,
1563        };
1564
1565        let node = ProofTrieNode { path, node: proof_node, masks };
1566
1567        if is_root {
1568            // Perform sanity check.
1569            if matches!(node.node, TrieNode::EmptyRoot) && proof_nodes_len > 1 {
1570                return Err(SparseStateTrieErrorKind::InvalidRootNode {
1571                    path,
1572                    node: alloy_rlp::encode(&node.node).into(),
1573                }
1574                .into())
1575            }
1576
1577            result.root_node = Some(node);
1578
1579            continue
1580        }
1581
1582        result.nodes.push(node);
1583    }
1584
1585    Ok(result)
1586}
1587
1588/// Result of [`filter_revealed_v2_proof_nodes`].
1589#[derive(Debug, PartialEq, Eq)]
1590struct FilteredV2ProofNodes {
1591    /// Root node which was pulled out of the original node set to be handled specially.
1592    root_node: Option<ProofTrieNode>,
1593    /// Filtered proof nodes. Root node is removed.
1594    nodes: Vec<ProofTrieNode>,
1595    /// Number of new nodes that will be revealed. This includes all children of branch nodes, even
1596    /// if they are not in the proof.
1597    new_nodes: usize,
1598    /// Values which are being returned so they can be incremented into metrics.
1599    metric_values: ProofNodesMetricValues,
1600}
1601
1602/// Calculates capacity estimation for V2 proof nodes without filtering.
1603///
1604/// This counts nodes and their children (for branch and extension nodes) to provide
1605/// proper capacity hints for `reserve_nodes`. Used when `skip_proof_node_filtering` is
1606/// enabled and no filtering is performed.
1607fn estimate_v2_proof_capacity(nodes: &[ProofTrieNode]) -> usize {
1608    let mut capacity = nodes.len();
1609
1610    for node in nodes {
1611        match &node.node {
1612            TrieNode::Branch(branch) => {
1613                capacity += branch.state_mask.count_ones() as usize;
1614            }
1615            TrieNode::Extension(_) => {
1616                capacity += 1;
1617            }
1618            _ => {}
1619        };
1620    }
1621
1622    capacity
1623}
1624
1625/// Filters V2 proof nodes that are already revealed, separates the root node if present, and
1626/// returns additional information about the number of total, skipped, and new nodes.
1627///
1628/// Unlike [`filter_map_revealed_nodes`], V2 proof nodes already have masks included in the
1629/// `ProofTrieNode` structure, so no separate masks map is needed.
1630fn filter_revealed_v2_proof_nodes(
1631    proof_nodes: Vec<ProofTrieNode>,
1632    revealed_nodes: &mut HashSet<Nibbles>,
1633) -> SparseStateTrieResult<FilteredV2ProofNodes> {
1634    let mut result = FilteredV2ProofNodes {
1635        root_node: None,
1636        nodes: Vec::with_capacity(proof_nodes.len()),
1637        new_nodes: 0,
1638        metric_values: Default::default(),
1639    };
1640
1641    // Count non-EmptyRoot nodes for sanity check. When multiple proofs are extended together,
1642    // duplicate EmptyRoot nodes may appear (e.g., storage proofs split across chunks for an
1643    // account with empty storage). We only error if there's an EmptyRoot alongside real nodes.
1644    let non_empty_root_count =
1645        proof_nodes.iter().filter(|n| !matches!(n.node, TrieNode::EmptyRoot)).count();
1646
1647    for node in proof_nodes {
1648        result.metric_values.total_nodes += 1;
1649
1650        let is_root = node.path.is_empty();
1651
1652        // If the node is already revealed, skip it. We don't ever skip the root node, nor do we add
1653        // it to `revealed_nodes`.
1654        if !is_root && !revealed_nodes.insert(node.path) {
1655            result.metric_values.skipped_nodes += 1;
1656            continue
1657        }
1658
1659        result.new_nodes += 1;
1660
1661        // Count children for capacity estimation
1662        match &node.node {
1663            TrieNode::Branch(branch) => {
1664                result.new_nodes += branch.state_mask.count_ones() as usize;
1665            }
1666            TrieNode::Extension(_) => {
1667                result.new_nodes += 1;
1668            }
1669            _ => {}
1670        };
1671
1672        if is_root {
1673            // Perform sanity check: EmptyRoot is only valid if there are no other real nodes.
1674            if matches!(node.node, TrieNode::EmptyRoot) && non_empty_root_count > 0 {
1675                return Err(SparseStateTrieErrorKind::InvalidRootNode {
1676                    path: node.path,
1677                    node: alloy_rlp::encode(&node.node).into(),
1678                }
1679                .into())
1680            }
1681
1682            result.root_node = Some(node);
1683            continue
1684        }
1685
1686        result.nodes.push(node);
1687    }
1688
1689    Ok(result)
1690}
1691
1692#[cfg(test)]
1693mod tests {
1694    use super::*;
1695    use crate::{provider::DefaultTrieNodeProviderFactory, LeafLookup, ParallelSparseTrie};
1696    use alloy_primitives::{
1697        b256,
1698        map::{HashMap, HashSet},
1699        U256,
1700    };
1701    use arbitrary::Arbitrary;
1702    use rand::{rngs::StdRng, Rng, SeedableRng};
1703    use reth_primitives_traits::Account;
1704    use reth_trie::{updates::StorageTrieUpdates, HashBuilder, MultiProof, EMPTY_ROOT_HASH};
1705    use reth_trie_common::{
1706        proof::{ProofNodes, ProofRetainer},
1707        BranchNode, BranchNodeMasks, BranchNodeMasksMap, LeafNode, StorageMultiProof, TrieMask,
1708    };
1709
1710    #[test]
1711    fn reveal_account_path_twice() {
1712        let provider_factory = DefaultTrieNodeProviderFactory;
1713        let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1714
1715        let leaf_value = alloy_rlp::encode(TrieAccount::default());
1716        let leaf_1 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
1717            Nibbles::default(),
1718            leaf_value.clone(),
1719        )));
1720        let leaf_2 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
1721            Nibbles::default(),
1722            leaf_value.clone(),
1723        )));
1724
1725        let multiproof = MultiProof {
1726            account_subtree: ProofNodes::from_iter([
1727                (
1728                    Nibbles::default(),
1729                    alloy_rlp::encode(TrieNode::Branch(BranchNode {
1730                        stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1731                        state_mask: TrieMask::new(0b11),
1732                    }))
1733                    .into(),
1734                ),
1735                (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1736                (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1737            ]),
1738            ..Default::default()
1739        };
1740
1741        // Reveal multiproof and check that the state trie contains the leaf node and value
1742        sparse.reveal_decoded_multiproof(multiproof.clone().try_into().unwrap()).unwrap();
1743        assert!(matches!(
1744            sparse.state_trie_ref().unwrap().find_leaf(&Nibbles::from_nibbles([0x0]), None),
1745            Ok(LeafLookup::Exists)
1746        ));
1747        assert_eq!(
1748            sparse.state_trie_ref().unwrap().get_leaf_value(&Nibbles::from_nibbles([0x0])),
1749            Some(&leaf_value)
1750        );
1751
1752        // Remove the leaf node and check that the state trie does not contain the leaf node and
1753        // value
1754        sparse.remove_account_leaf(&Nibbles::from_nibbles([0x0]), &provider_factory).unwrap();
1755        assert!(matches!(
1756            sparse.state_trie_ref().unwrap().find_leaf(&Nibbles::from_nibbles([0x0]), None),
1757            Ok(LeafLookup::NonExistent)
1758        ));
1759        assert!(sparse
1760            .state_trie_ref()
1761            .unwrap()
1762            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1763            .is_none());
1764
1765        // Reveal multiproof again and check that the state trie still does not contain the leaf
1766        // node and value, because they were already revealed before
1767        sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1768        assert!(matches!(
1769            sparse.state_trie_ref().unwrap().find_leaf(&Nibbles::from_nibbles([0x0]), None),
1770            Ok(LeafLookup::NonExistent)
1771        ));
1772        assert!(sparse
1773            .state_trie_ref()
1774            .unwrap()
1775            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1776            .is_none());
1777    }
1778
1779    #[test]
1780    fn reveal_storage_path_twice() {
1781        let provider_factory = DefaultTrieNodeProviderFactory;
1782        let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1783
1784        let leaf_value = alloy_rlp::encode(TrieAccount::default());
1785        let leaf_1 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
1786            Nibbles::default(),
1787            leaf_value.clone(),
1788        )));
1789        let leaf_2 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
1790            Nibbles::default(),
1791            leaf_value.clone(),
1792        )));
1793
1794        let multiproof = MultiProof {
1795            storages: HashMap::from_iter([(
1796                B256::ZERO,
1797                StorageMultiProof {
1798                    root: B256::ZERO,
1799                    subtree: ProofNodes::from_iter([
1800                        (
1801                            Nibbles::default(),
1802                            alloy_rlp::encode(TrieNode::Branch(BranchNode {
1803                                stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1804                                state_mask: TrieMask::new(0b11),
1805                            }))
1806                            .into(),
1807                        ),
1808                        (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1809                        (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1810                    ]),
1811                    branch_node_masks: Default::default(),
1812                },
1813            )]),
1814            ..Default::default()
1815        };
1816
1817        // Reveal multiproof and check that the storage trie contains the leaf node and value
1818        sparse.reveal_decoded_multiproof(multiproof.clone().try_into().unwrap()).unwrap();
1819        assert!(matches!(
1820            sparse
1821                .storage_trie_ref(&B256::ZERO)
1822                .unwrap()
1823                .find_leaf(&Nibbles::from_nibbles([0x0]), None),
1824            Ok(LeafLookup::Exists)
1825        ));
1826        assert_eq!(
1827            sparse
1828                .storage_trie_ref(&B256::ZERO)
1829                .unwrap()
1830                .get_leaf_value(&Nibbles::from_nibbles([0x0])),
1831            Some(&leaf_value)
1832        );
1833
1834        // Remove the leaf node and check that the storage trie does not contain the leaf node and
1835        // value
1836        sparse
1837            .remove_storage_leaf(B256::ZERO, &Nibbles::from_nibbles([0x0]), &provider_factory)
1838            .unwrap();
1839        assert!(matches!(
1840            sparse
1841                .storage_trie_ref(&B256::ZERO)
1842                .unwrap()
1843                .find_leaf(&Nibbles::from_nibbles([0x0]), None),
1844            Ok(LeafLookup::NonExistent)
1845        ));
1846        assert!(sparse
1847            .storage_trie_ref(&B256::ZERO)
1848            .unwrap()
1849            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1850            .is_none());
1851
1852        // Reveal multiproof again and check that the storage trie still does not contain the leaf
1853        // node and value, because they were already revealed before
1854        sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1855        assert!(matches!(
1856            sparse
1857                .storage_trie_ref(&B256::ZERO)
1858                .unwrap()
1859                .find_leaf(&Nibbles::from_nibbles([0x0]), None),
1860            Ok(LeafLookup::NonExistent)
1861        ));
1862        assert!(sparse
1863            .storage_trie_ref(&B256::ZERO)
1864            .unwrap()
1865            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1866            .is_none());
1867    }
1868
1869    #[test]
1870    fn reveal_v2_proof_nodes() {
1871        let provider_factory = DefaultTrieNodeProviderFactory;
1872        let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1873
1874        let leaf_value = alloy_rlp::encode(TrieAccount::default());
1875        let leaf_1_node = TrieNode::Leaf(LeafNode::new(Nibbles::default(), leaf_value.clone()));
1876        let leaf_2_node = TrieNode::Leaf(LeafNode::new(Nibbles::default(), leaf_value.clone()));
1877
1878        let branch_node = TrieNode::Branch(BranchNode {
1879            stack: vec![
1880                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1881                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1882            ],
1883            state_mask: TrieMask::new(0b11),
1884        });
1885
1886        // Create V2 proof nodes with masks already included
1887        let v2_proof_nodes = vec![
1888            ProofTrieNode {
1889                path: Nibbles::default(),
1890                node: branch_node,
1891                masks: Some(BranchNodeMasks {
1892                    hash_mask: TrieMask::default(),
1893                    tree_mask: TrieMask::default(),
1894                }),
1895            },
1896            ProofTrieNode { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1897            ProofTrieNode { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1898        ];
1899
1900        // Reveal V2 proof nodes
1901        sparse.reveal_account_v2_proof_nodes(v2_proof_nodes.clone()).unwrap();
1902
1903        // Check that the state trie contains the leaf node and value
1904        assert!(matches!(
1905            sparse.state_trie_ref().unwrap().find_leaf(&Nibbles::from_nibbles([0x0]), None),
1906            Ok(LeafLookup::Exists)
1907        ));
1908        assert_eq!(
1909            sparse.state_trie_ref().unwrap().get_leaf_value(&Nibbles::from_nibbles([0x0])),
1910            Some(&leaf_value)
1911        );
1912
1913        // Remove the leaf node
1914        sparse.remove_account_leaf(&Nibbles::from_nibbles([0x0]), &provider_factory).unwrap();
1915        assert!(sparse
1916            .state_trie_ref()
1917            .unwrap()
1918            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1919            .is_none());
1920
1921        // Reveal again - should skip already revealed paths
1922        sparse.reveal_account_v2_proof_nodes(v2_proof_nodes).unwrap();
1923        assert!(sparse
1924            .state_trie_ref()
1925            .unwrap()
1926            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1927            .is_none());
1928    }
1929
1930    #[test]
1931    fn reveal_storage_v2_proof_nodes() {
1932        let provider_factory = DefaultTrieNodeProviderFactory;
1933        let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default();
1934
1935        let storage_value: Vec<u8> = alloy_rlp::encode_fixed_size(&U256::from(42)).to_vec();
1936        let leaf_1_node = TrieNode::Leaf(LeafNode::new(Nibbles::default(), storage_value.clone()));
1937        let leaf_2_node = TrieNode::Leaf(LeafNode::new(Nibbles::default(), storage_value.clone()));
1938
1939        let branch_node = TrieNode::Branch(BranchNode {
1940            stack: vec![
1941                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_1_node)),
1942                RlpNode::from_rlp(&alloy_rlp::encode(&leaf_2_node)),
1943            ],
1944            state_mask: TrieMask::new(0b11),
1945        });
1946
1947        let v2_proof_nodes = vec![
1948            ProofTrieNode { path: Nibbles::default(), node: branch_node, masks: None },
1949            ProofTrieNode { path: Nibbles::from_nibbles([0x0]), node: leaf_1_node, masks: None },
1950            ProofTrieNode { path: Nibbles::from_nibbles([0x1]), node: leaf_2_node, masks: None },
1951        ];
1952
1953        // Reveal V2 storage proof nodes for account
1954        sparse.reveal_storage_v2_proof_nodes(B256::ZERO, v2_proof_nodes.clone()).unwrap();
1955
1956        // Check that the storage trie contains the leaf node and value
1957        assert!(matches!(
1958            sparse
1959                .storage_trie_ref(&B256::ZERO)
1960                .unwrap()
1961                .find_leaf(&Nibbles::from_nibbles([0x0]), None),
1962            Ok(LeafLookup::Exists)
1963        ));
1964        assert_eq!(
1965            sparse
1966                .storage_trie_ref(&B256::ZERO)
1967                .unwrap()
1968                .get_leaf_value(&Nibbles::from_nibbles([0x0])),
1969            Some(&storage_value)
1970        );
1971
1972        // Remove the leaf node
1973        sparse
1974            .remove_storage_leaf(B256::ZERO, &Nibbles::from_nibbles([0x0]), &provider_factory)
1975            .unwrap();
1976        assert!(sparse
1977            .storage_trie_ref(&B256::ZERO)
1978            .unwrap()
1979            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1980            .is_none());
1981
1982        // Reveal again - should skip already revealed paths
1983        sparse.reveal_storage_v2_proof_nodes(B256::ZERO, v2_proof_nodes).unwrap();
1984        assert!(sparse
1985            .storage_trie_ref(&B256::ZERO)
1986            .unwrap()
1987            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1988            .is_none());
1989    }
1990
1991    #[test]
1992    fn take_trie_updates() {
1993        reth_tracing::init_test_tracing();
1994
1995        // let mut rng = generators::rng();
1996        let mut rng = StdRng::seed_from_u64(1);
1997
1998        let mut bytes = [0u8; 1024];
1999        rng.fill(bytes.as_mut_slice());
2000
2001        let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
2002        let slot_path_1 = Nibbles::unpack(slot_1);
2003        let value_1 = U256::from(rng.random::<u64>());
2004        let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
2005        let slot_path_2 = Nibbles::unpack(slot_2);
2006        let value_2 = U256::from(rng.random::<u64>());
2007        let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
2008        let slot_path_3 = Nibbles::unpack(slot_3);
2009        let value_3 = U256::from(rng.random::<u64>());
2010
2011        let mut storage_hash_builder = HashBuilder::default()
2012            .with_proof_retainer(ProofRetainer::from_iter([slot_path_1, slot_path_2]));
2013        storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
2014        storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
2015
2016        let storage_root = storage_hash_builder.root();
2017        let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
2018        let storage_branch_node_masks = BranchNodeMasksMap::from_iter([
2019            (
2020                Nibbles::default(),
2021                BranchNodeMasks { hash_mask: TrieMask::new(0b010), tree_mask: TrieMask::default() },
2022            ),
2023            (
2024                Nibbles::from_nibbles([0x1]),
2025                BranchNodeMasks { hash_mask: TrieMask::new(0b11), tree_mask: TrieMask::default() },
2026            ),
2027        ]);
2028
2029        let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
2030        let address_path_1 = Nibbles::unpack(address_1);
2031        let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
2032        let mut trie_account_1 = account_1.into_trie_account(storage_root);
2033        let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
2034        let address_path_2 = Nibbles::unpack(address_2);
2035        let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
2036        let mut trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
2037
2038        let mut hash_builder = HashBuilder::default()
2039            .with_proof_retainer(ProofRetainer::from_iter([address_path_1, address_path_2]));
2040        hash_builder.add_leaf(address_path_1, &alloy_rlp::encode(trie_account_1));
2041        hash_builder.add_leaf(address_path_2, &alloy_rlp::encode(trie_account_2));
2042
2043        let root = hash_builder.root();
2044        let proof_nodes = hash_builder.take_proof_nodes();
2045
2046        let provider_factory = DefaultTrieNodeProviderFactory;
2047        let mut sparse = SparseStateTrie::<ParallelSparseTrie>::default().with_updates(true);
2048        sparse
2049            .reveal_decoded_multiproof(
2050                MultiProof {
2051                    account_subtree: proof_nodes,
2052                    branch_node_masks: BranchNodeMasksMap::from_iter([(
2053                        Nibbles::from_nibbles([0x1]),
2054                        BranchNodeMasks {
2055                            hash_mask: TrieMask::new(0b00),
2056                            tree_mask: TrieMask::default(),
2057                        },
2058                    )]),
2059                    storages: HashMap::from_iter([
2060                        (
2061                            address_1,
2062                            StorageMultiProof {
2063                                root,
2064                                subtree: storage_proof_nodes.clone(),
2065                                branch_node_masks: storage_branch_node_masks.clone(),
2066                            },
2067                        ),
2068                        (
2069                            address_2,
2070                            StorageMultiProof {
2071                                root,
2072                                subtree: storage_proof_nodes,
2073                                branch_node_masks: storage_branch_node_masks,
2074                            },
2075                        ),
2076                    ]),
2077                }
2078                .try_into()
2079                .unwrap(),
2080            )
2081            .unwrap();
2082
2083        assert_eq!(sparse.root(&provider_factory).unwrap(), root);
2084
2085        let address_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
2086        let address_path_3 = Nibbles::unpack(address_3);
2087        let account_3 = Account { nonce: account_1.nonce + 1, ..account_1 };
2088        let trie_account_3 = account_3.into_trie_account(EMPTY_ROOT_HASH);
2089
2090        sparse
2091            .update_account_leaf(
2092                address_path_3,
2093                alloy_rlp::encode(trie_account_3),
2094                &provider_factory,
2095            )
2096            .unwrap();
2097
2098        sparse
2099            .update_storage_leaf(
2100                address_1,
2101                slot_path_3,
2102                alloy_rlp::encode(value_3),
2103                &provider_factory,
2104            )
2105            .unwrap();
2106        trie_account_1.storage_root = sparse.storage_root(&address_1).unwrap();
2107        sparse
2108            .update_account_leaf(
2109                address_path_1,
2110                alloy_rlp::encode(trie_account_1),
2111                &provider_factory,
2112            )
2113            .unwrap();
2114
2115        sparse.wipe_storage(address_2).unwrap();
2116        trie_account_2.storage_root = sparse.storage_root(&address_2).unwrap();
2117        sparse
2118            .update_account_leaf(
2119                address_path_2,
2120                alloy_rlp::encode(trie_account_2),
2121                &provider_factory,
2122            )
2123            .unwrap();
2124
2125        sparse.root(&provider_factory).unwrap();
2126
2127        let sparse_updates = sparse.take_trie_updates().unwrap();
2128        // TODO(alexey): assert against real state root calculation updates
2129        pretty_assertions::assert_eq!(
2130            sparse_updates,
2131            TrieUpdates {
2132                account_nodes: HashMap::default(),
2133                storage_tries: HashMap::from_iter([(
2134                    b256!("0x1100000000000000000000000000000000000000000000000000000000000000"),
2135                    StorageTrieUpdates {
2136                        is_deleted: true,
2137                        storage_nodes: HashMap::default(),
2138                        removed_nodes: HashSet::default()
2139                    }
2140                )]),
2141                removed_nodes: HashSet::default()
2142            }
2143        );
2144    }
2145
2146    #[test]
2147    fn test_filter_map_revealed_nodes() {
2148        let mut revealed_nodes = HashSet::from_iter([Nibbles::from_nibbles([0x0])]);
2149        let leaf = TrieNode::Leaf(LeafNode::new(Nibbles::default(), alloy_rlp::encode([])));
2150        let leaf_encoded = alloy_rlp::encode(&leaf);
2151        let branch = TrieNode::Branch(BranchNode::new(
2152            vec![RlpNode::from_rlp(&leaf_encoded), RlpNode::from_rlp(&leaf_encoded)],
2153            TrieMask::new(0b11),
2154        ));
2155        let proof_nodes = alloy_trie::proof::DecodedProofNodes::from_iter([
2156            (Nibbles::default(), branch.clone()),
2157            (Nibbles::from_nibbles([0x0]), leaf.clone()),
2158            (Nibbles::from_nibbles([0x1]), leaf.clone()),
2159        ]);
2160
2161        let branch_node_masks = BranchNodeMasksMap::default();
2162
2163        let decoded =
2164            filter_map_revealed_nodes(proof_nodes, &mut revealed_nodes, &branch_node_masks)
2165                .unwrap();
2166
2167        assert_eq!(
2168            decoded,
2169            FilterMappedProofNodes {
2170                root_node: Some(ProofTrieNode {
2171                    path: Nibbles::default(),
2172                    node: branch,
2173                    masks: None,
2174                }),
2175                nodes: vec![ProofTrieNode {
2176                    path: Nibbles::from_nibbles([0x1]),
2177                    node: leaf,
2178                    masks: None,
2179                }],
2180                // Branch, two of its children, one leaf
2181                new_nodes: 4,
2182                // Metric values
2183                metric_values: ProofNodesMetricValues {
2184                    // Branch, leaf, leaf
2185                    total_nodes: 3,
2186                    // Revealed leaf node with path 0x1
2187                    skipped_nodes: 1,
2188                },
2189            }
2190        );
2191    }
2192}