reth_trie_sparse/
state.rs

1use crate::{
2    provider::{TrieNodeProvider, TrieNodeProviderFactory},
3    traits::SparseTrieInterface,
4    SerialSparseTrie, SparseTrie,
5};
6use alloc::{collections::VecDeque, vec::Vec};
7use alloy_primitives::{
8    map::{B256Map, 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    BranchNodeMasksMap, DecodedMultiProof, DecodedStorageMultiProof, MultiProof, Nibbles,
19    ProofTrieNode, RlpNode, StorageMultiProof, TrieAccount, TrieMasks, TrieNode, EMPTY_ROOT_HASH,
20    TRIE_ACCOUNT_RLP_MAX_SIZE,
21};
22use tracing::{instrument, trace};
23
24/// Provides type-safe re-use of cleared [`SparseStateTrie`]s, which helps to save allocations
25/// across payload runs.
26#[derive(Debug)]
27pub struct ClearedSparseStateTrie<
28    A = SerialSparseTrie, // Account trie implementation
29    S = SerialSparseTrie, // Storage trie implementation
30>(SparseStateTrie<A, S>);
31
32impl<A, S> ClearedSparseStateTrie<A, S>
33where
34    A: SparseTrieInterface,
35    S: SparseTrieInterface,
36{
37    /// Creates a [`ClearedSparseStateTrie`] by clearing all the existing internal state of a
38    /// [`SparseStateTrie`] and then storing that instance for later re-use.
39    pub fn from_state_trie(mut trie: SparseStateTrie<A, S>) -> Self {
40        trie.state = trie.state.clear();
41        trie.revealed_account_paths.clear();
42        trie.storage.clear();
43        trie.account_rlp_buf.clear();
44        Self(trie)
45    }
46
47    /// Shrink the cleared sparse trie's capacity to the given node and value size.
48    /// This helps reduce memory usage when the trie has excess capacity.
49    /// The capacity is distributed equally across the account trie and all storage tries.
50    pub fn shrink_to(&mut self, node_size: usize, value_size: usize) {
51        // Count total number of storage tries (active + cleared + default)
52        let storage_tries_count = self.0.storage.tries.len() + self.0.storage.cleared_tries.len();
53
54        // Total tries = 1 account trie + all storage tries
55        let total_tries = 1 + storage_tries_count;
56
57        // Distribute capacity equally among all tries
58        let node_size_per_trie = node_size / total_tries;
59        let value_size_per_trie = value_size / total_tries;
60
61        // Shrink the account trie
62        self.0.state.shrink_nodes_to(node_size_per_trie);
63        self.0.state.shrink_values_to(value_size_per_trie);
64
65        // Give storage tries the remaining capacity after account trie allocation
66        let storage_node_size = node_size.saturating_sub(node_size_per_trie);
67        let storage_value_size = value_size.saturating_sub(value_size_per_trie);
68
69        // Shrink all storage tries (they will redistribute internally)
70        self.0.storage.shrink_to(storage_node_size, storage_value_size);
71    }
72
73    /// Returns the cleared [`SparseStateTrie`], consuming this instance.
74    pub fn into_inner(self) -> SparseStateTrie<A, S> {
75        self.0
76    }
77}
78
79#[derive(Debug)]
80/// Sparse state trie representing lazy-loaded Ethereum state trie.
81pub struct SparseStateTrie<
82    A = SerialSparseTrie, // Account trie implementation
83    S = SerialSparseTrie, // Storage trie implementation
84> {
85    /// Sparse account trie.
86    state: SparseTrie<A>,
87    /// Collection of revealed account trie paths.
88    revealed_account_paths: HashSet<Nibbles>,
89    /// State related to storage tries.
90    storage: StorageTries<S>,
91    /// Flag indicating whether trie updates should be retained.
92    retain_updates: bool,
93    /// Reusable buffer for RLP encoding of trie accounts.
94    account_rlp_buf: Vec<u8>,
95    /// Metrics for the sparse state trie.
96    #[cfg(feature = "metrics")]
97    metrics: crate::metrics::SparseStateTrieMetrics,
98}
99
100impl<A, S> Default for SparseStateTrie<A, S>
101where
102    A: Default,
103    S: Default,
104{
105    fn default() -> Self {
106        Self {
107            state: Default::default(),
108            revealed_account_paths: Default::default(),
109            storage: Default::default(),
110            retain_updates: false,
111            account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
112            #[cfg(feature = "metrics")]
113            metrics: Default::default(),
114        }
115    }
116}
117
118#[cfg(test)]
119impl SparseStateTrie {
120    /// Create state trie from state trie.
121    pub fn from_state(state: SparseTrie) -> Self {
122        Self { state, ..Default::default() }
123    }
124}
125
126impl<A, S> SparseStateTrie<A, S> {
127    /// Set the retention of branch node updates and deletions.
128    pub const fn with_updates(mut self, retain_updates: bool) -> Self {
129        self.retain_updates = retain_updates;
130        self
131    }
132
133    /// Set the accounts trie to the given `SparseTrie`.
134    pub fn with_accounts_trie(mut self, trie: SparseTrie<A>) -> Self {
135        self.state = trie;
136        self
137    }
138
139    /// Set the default trie which will be cloned when creating new storage [`SparseTrie`]s.
140    pub fn with_default_storage_trie(mut self, trie: SparseTrie<S>) -> Self {
141        self.storage.default_trie = trie;
142        self
143    }
144}
145
146impl<A, S> SparseStateTrie<A, S>
147where
148    A: SparseTrieInterface + Default,
149    S: SparseTrieInterface + Default + Clone,
150{
151    /// Create new [`SparseStateTrie`]
152    pub fn new() -> Self {
153        Self::default()
154    }
155
156    /// Returns `true` if account was already revealed.
157    pub fn is_account_revealed(&self, account: B256) -> bool {
158        self.revealed_account_paths.contains(&Nibbles::unpack(account))
159    }
160
161    /// Was the account witness for `address` complete?
162    pub fn check_valid_account_witness(&self, address: B256) -> bool {
163        let path = Nibbles::unpack(address);
164        let trie = match self.state_trie_ref() {
165            Some(t) => t,
166            None => return false,
167        };
168
169        trie.find_leaf(&path, None).is_ok()
170    }
171
172    /// Was the storage-slot witness for (`address`,`slot`) complete?
173    pub fn check_valid_storage_witness(&self, address: B256, slot: B256) -> bool {
174        let path = Nibbles::unpack(slot);
175        let trie = match self.storage_trie_ref(&address) {
176            Some(t) => t,
177            None => return false,
178        };
179
180        trie.find_leaf(&path, None).is_ok()
181    }
182
183    /// Returns `true` if storage slot for account was already revealed.
184    pub fn is_storage_slot_revealed(&self, account: B256, slot: B256) -> bool {
185        self.storage
186            .revealed_paths
187            .get(&account)
188            .is_some_and(|slots| slots.contains(&Nibbles::unpack(slot)))
189    }
190
191    /// Returns reference to bytes representing leaf value for the target account.
192    pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
193        self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
194    }
195
196    /// Returns reference to bytes representing leaf value for the target account and storage slot.
197    pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
198        self.storage.tries.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
199    }
200
201    /// Returns reference to state trie if it was revealed.
202    pub const fn state_trie_ref(&self) -> Option<&A> {
203        self.state.as_revealed_ref()
204    }
205
206    /// Returns reference to storage trie if it was revealed.
207    pub fn storage_trie_ref(&self, address: &B256) -> Option<&S> {
208        self.storage.tries.get(address).and_then(|e| e.as_revealed_ref())
209    }
210
211    /// Returns mutable reference to storage sparse trie if it was revealed.
212    pub fn storage_trie_mut(&mut self, address: &B256) -> Option<&mut S> {
213        self.storage.tries.get_mut(address).and_then(|e| e.as_revealed_mut())
214    }
215
216    /// Takes the storage trie for the provided address.
217    pub fn take_storage_trie(&mut self, address: &B256) -> Option<SparseTrie<S>> {
218        self.storage.tries.remove(address)
219    }
220
221    /// Inserts storage trie for the provided address.
222    pub fn insert_storage_trie(&mut self, address: B256, storage_trie: SparseTrie<S>) {
223        self.storage.tries.insert(address, storage_trie);
224    }
225
226    /// Reveal unknown trie paths from multiproof.
227    /// NOTE: This method does not extensively validate the proof.
228    pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
229        // first decode the multiproof
230        let decoded_multiproof = multiproof.try_into()?;
231
232        // then reveal the decoded multiproof
233        self.reveal_decoded_multiproof(decoded_multiproof)
234    }
235
236    /// Reveal unknown trie paths from decoded multiproof.
237    /// NOTE: This method does not extensively validate the proof.
238    #[instrument(
239        target = "trie::sparse",
240        skip_all,
241        fields(
242            account_nodes = multiproof.account_subtree.len(),
243            storages = multiproof.storages.len()
244        )
245    )]
246    pub fn reveal_decoded_multiproof(
247        &mut self,
248        multiproof: DecodedMultiProof,
249    ) -> SparseStateTrieResult<()> {
250        let DecodedMultiProof { account_subtree, storages, branch_node_masks } = multiproof;
251
252        // first reveal the account proof nodes
253        self.reveal_decoded_account_multiproof(account_subtree, branch_node_masks)?;
254
255        #[cfg(not(feature = "std"))]
256        // If nostd then serially reveal storage proof nodes for each storage trie
257        {
258            for (account, storage_subtree) in storages {
259                self.reveal_decoded_storage_multiproof(account, storage_subtree)?;
260            }
261
262            Ok(())
263        }
264
265        #[cfg(feature = "std")]
266        // If std then reveal storage proofs in parallel
267        {
268            use rayon::iter::{ParallelBridge, ParallelIterator};
269
270            let retain_updates = self.retain_updates;
271
272            // Process all storage trie revealings in parallel, having first removed the
273            // `reveal_nodes` tracking and `SparseTrie`s for each account from their HashMaps.
274            // These will be returned after processing.
275            let results: Vec<_> = storages
276                .into_iter()
277                .map(|(account, storage_subtree)| {
278                    let revealed_nodes = self.storage.take_or_create_revealed_paths(&account);
279                    let trie = self.storage.take_or_create_trie(&account);
280                    (account, storage_subtree, revealed_nodes, trie)
281                })
282                .par_bridge()
283                .map(|(account, storage_subtree, mut revealed_nodes, mut trie)| {
284                    let result = Self::reveal_decoded_storage_multiproof_inner(
285                        account,
286                        storage_subtree,
287                        &mut revealed_nodes,
288                        &mut trie,
289                        retain_updates,
290                    );
291
292                    (account, revealed_nodes, trie, result)
293                })
294                .collect();
295
296            // Return `revealed_nodes` and `SparseTrie` for each account, incrementing metrics and
297            // returning the last error seen if any.
298            let mut any_err = Ok(());
299            for (account, revealed_nodes, trie, result) in results {
300                self.storage.revealed_paths.insert(account, revealed_nodes);
301                self.storage.tries.insert(account, trie);
302                if let Ok(_metric_values) = result {
303                    #[cfg(feature = "metrics")]
304                    {
305                        self.metrics
306                            .increment_total_storage_nodes(_metric_values.total_nodes as u64);
307                        self.metrics
308                            .increment_skipped_storage_nodes(_metric_values.skipped_nodes as u64);
309                    }
310                } else {
311                    any_err = result.map(|_| ());
312                }
313            }
314
315            any_err
316        }
317    }
318
319    /// Reveals an account multiproof.
320    pub fn reveal_account_multiproof(
321        &mut self,
322        account_subtree: ProofNodes,
323        branch_node_masks: BranchNodeMasksMap,
324    ) -> SparseStateTrieResult<()> {
325        // decode the multiproof first
326        let decoded_multiproof = account_subtree.try_into()?;
327        self.reveal_decoded_account_multiproof(decoded_multiproof, branch_node_masks)
328    }
329
330    /// Reveals a decoded account multiproof.
331    pub fn reveal_decoded_account_multiproof(
332        &mut self,
333        account_subtree: DecodedProofNodes,
334        branch_node_masks: BranchNodeMasksMap,
335    ) -> SparseStateTrieResult<()> {
336        let FilterMappedProofNodes { root_node, nodes, new_nodes, metric_values: _metric_values } =
337            filter_map_revealed_nodes(
338                account_subtree,
339                &mut self.revealed_account_paths,
340                &branch_node_masks,
341            )?;
342        #[cfg(feature = "metrics")]
343        {
344            self.metrics.increment_total_account_nodes(_metric_values.total_nodes as u64);
345            self.metrics.increment_skipped_account_nodes(_metric_values.skipped_nodes as u64);
346        }
347
348        if let Some(root_node) = root_node {
349            // Reveal root node if it wasn't already.
350            trace!(target: "trie::sparse", ?root_node, "Revealing root account node");
351            let trie =
352                self.state.reveal_root(root_node.node, root_node.masks, self.retain_updates)?;
353
354            // Reserve the capacity for new nodes ahead of time, if the trie implementation
355            // supports doing so.
356            trie.reserve_nodes(new_nodes);
357
358            trace!(target: "trie::sparse", total_nodes = ?nodes.len(), "Revealing account nodes");
359            trie.reveal_nodes(nodes)?;
360        }
361
362        Ok(())
363    }
364
365    /// Reveals a storage multiproof for the given address.
366    pub fn reveal_storage_multiproof(
367        &mut self,
368        account: B256,
369        storage_subtree: StorageMultiProof,
370    ) -> SparseStateTrieResult<()> {
371        // decode the multiproof first
372        let decoded_multiproof = storage_subtree.try_into()?;
373        self.reveal_decoded_storage_multiproof(account, decoded_multiproof)
374    }
375
376    /// Reveals a decoded storage multiproof for the given address.
377    pub fn reveal_decoded_storage_multiproof(
378        &mut self,
379        account: B256,
380        storage_subtree: DecodedStorageMultiProof,
381    ) -> SparseStateTrieResult<()> {
382        let (trie, revealed_paths) = self.storage.get_trie_and_revealed_paths_mut(account);
383        let _metric_values = Self::reveal_decoded_storage_multiproof_inner(
384            account,
385            storage_subtree,
386            revealed_paths,
387            trie,
388            self.retain_updates,
389        )?;
390
391        #[cfg(feature = "metrics")]
392        {
393            self.metrics.increment_total_storage_nodes(_metric_values.total_nodes as u64);
394            self.metrics.increment_skipped_storage_nodes(_metric_values.skipped_nodes as u64);
395        }
396
397        Ok(())
398    }
399
400    /// Reveals a decoded storage multiproof for the given address. This is internal static function
401    /// is designed to handle a variety of associated public functions.
402    fn reveal_decoded_storage_multiproof_inner(
403        account: B256,
404        storage_subtree: DecodedStorageMultiProof,
405        revealed_nodes: &mut HashSet<Nibbles>,
406        trie: &mut SparseTrie<S>,
407        retain_updates: bool,
408    ) -> SparseStateTrieResult<ProofNodesMetricValues> {
409        let FilterMappedProofNodes { root_node, nodes, new_nodes, metric_values } =
410            filter_map_revealed_nodes(
411                storage_subtree.subtree,
412                revealed_nodes,
413                &storage_subtree.branch_node_masks,
414            )?;
415
416        if let Some(root_node) = root_node {
417            // Reveal root node if it wasn't already.
418            trace!(target: "trie::sparse", ?account, ?root_node, "Revealing root storage node");
419            let trie = trie.reveal_root(root_node.node, root_node.masks, retain_updates)?;
420
421            // Reserve the capacity for new nodes ahead of time, if the trie implementation
422            // supports doing so.
423            trie.reserve_nodes(new_nodes);
424
425            trace!(target: "trie::sparse", ?account, total_nodes = ?nodes.len(), "Revealing storage nodes");
426            trie.reveal_nodes(nodes)?;
427        }
428
429        Ok(metric_values)
430    }
431
432    /// Reveal state witness with the given state root.
433    /// The state witness is expected to be a map of `keccak(rlp(node)): rlp(node).`
434    /// NOTE: This method does not extensively validate the witness.
435    pub fn reveal_witness(
436        &mut self,
437        state_root: B256,
438        witness: &B256Map<Bytes>,
439    ) -> SparseStateTrieResult<()> {
440        // Create a `(hash, path, maybe_account)` queue for traversing witness trie nodes
441        // starting from the root node.
442        let mut queue = VecDeque::from([(state_root, Nibbles::default(), None)]);
443
444        while let Some((hash, path, maybe_account)) = queue.pop_front() {
445            // Retrieve the trie node and decode it.
446            let Some(trie_node_bytes) = witness.get(&hash) else { continue };
447            let trie_node = TrieNode::decode(&mut &trie_node_bytes[..])?;
448
449            // Push children nodes into the queue.
450            match &trie_node {
451                TrieNode::Branch(branch) => {
452                    for (idx, maybe_child) in branch.as_ref().children() {
453                        if let Some(child_hash) = maybe_child.and_then(RlpNode::as_hash) {
454                            let mut child_path = path;
455                            child_path.push_unchecked(idx);
456                            queue.push_back((child_hash, child_path, maybe_account));
457                        }
458                    }
459                }
460                TrieNode::Extension(ext) => {
461                    if let Some(child_hash) = ext.child.as_hash() {
462                        let mut child_path = path;
463                        child_path.extend(&ext.key);
464                        queue.push_back((child_hash, child_path, maybe_account));
465                    }
466                }
467                TrieNode::Leaf(leaf) => {
468                    let mut full_path = path;
469                    full_path.extend(&leaf.key);
470                    if maybe_account.is_none() {
471                        let hashed_address = B256::from_slice(&full_path.pack());
472                        let account = TrieAccount::decode(&mut &leaf.value[..])?;
473                        if account.storage_root != EMPTY_ROOT_HASH {
474                            queue.push_back((
475                                account.storage_root,
476                                Nibbles::default(),
477                                Some(hashed_address),
478                            ));
479                        }
480                    }
481                }
482                TrieNode::EmptyRoot => {} // nothing to do here
483            };
484
485            // Reveal the node itself.
486            if let Some(account) = maybe_account {
487                // Check that the path was not already revealed.
488                if self
489                    .storage
490                    .revealed_paths
491                    .get(&account)
492                    .is_none_or(|paths| !paths.contains(&path))
493                {
494                    let retain_updates = self.retain_updates;
495                    let (storage_trie_entry, revealed_storage_paths) =
496                        self.storage.get_trie_and_revealed_paths_mut(account);
497
498                    if path.is_empty() {
499                        // Handle special storage state root node case.
500                        storage_trie_entry.reveal_root(
501                            trie_node,
502                            TrieMasks::none(),
503                            retain_updates,
504                        )?;
505                    } else {
506                        // Reveal non-root storage trie node.
507                        storage_trie_entry
508                            .as_revealed_mut()
509                            .ok_or(SparseTrieErrorKind::Blind)?
510                            .reveal_node(path, trie_node, TrieMasks::none())?;
511                    }
512
513                    // Track the revealed path.
514                    revealed_storage_paths.insert(path);
515                }
516            }
517            // Check that the path was not already revealed.
518            else if !self.revealed_account_paths.contains(&path) {
519                if path.is_empty() {
520                    // Handle special state root node case.
521                    self.state.reveal_root(trie_node, TrieMasks::none(), self.retain_updates)?;
522                } else {
523                    // Reveal non-root state trie node.
524                    self.state.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?.reveal_node(
525                        path,
526                        trie_node,
527                        TrieMasks::none(),
528                    )?;
529                }
530
531                // Track the revealed path.
532                self.revealed_account_paths.insert(path);
533            }
534        }
535
536        Ok(())
537    }
538
539    /// Wipe the storage trie at the provided address.
540    pub fn wipe_storage(&mut self, address: B256) -> SparseStateTrieResult<()> {
541        if let Some(trie) = self.storage.tries.get_mut(&address) {
542            trie.wipe()?;
543        }
544        Ok(())
545    }
546
547    /// Calculates the hashes of subtries.
548    ///
549    /// If the trie has not been revealed, this function does nothing.
550    #[instrument(target = "trie::sparse", skip_all)]
551    pub fn calculate_subtries(&mut self) {
552        if let SparseTrie::Revealed(trie) = &mut self.state {
553            trie.update_subtrie_hashes();
554        }
555    }
556
557    /// Returns storage sparse trie root if the trie has been revealed.
558    pub fn storage_root(&mut self, account: B256) -> Option<B256> {
559        self.storage.tries.get_mut(&account).and_then(|trie| trie.root())
560    }
561
562    /// Returns mutable reference to the revealed account sparse trie.
563    ///
564    /// If the trie is not revealed yet, its root will be revealed using the trie node provider.
565    fn revealed_trie_mut(
566        &mut self,
567        provider_factory: impl TrieNodeProviderFactory,
568    ) -> SparseStateTrieResult<&mut A> {
569        match self.state {
570            SparseTrie::Blind(_) => {
571                let (root_node, hash_mask, tree_mask) = provider_factory
572                    .account_node_provider()
573                    .trie_node(&Nibbles::default())?
574                    .map(|node| {
575                        TrieNode::decode(&mut &node.node[..])
576                            .map(|decoded| (decoded, node.hash_mask, node.tree_mask))
577                    })
578                    .transpose()?
579                    .unwrap_or((TrieNode::EmptyRoot, None, None));
580                self.state
581                    .reveal_root(root_node, TrieMasks { hash_mask, tree_mask }, self.retain_updates)
582                    .map_err(Into::into)
583            }
584            SparseTrie::Revealed(ref mut trie) => Ok(trie),
585        }
586    }
587
588    /// Returns sparse trie root.
589    ///
590    /// If the trie has not been revealed, this function reveals the root node and returns its hash.
591    pub fn root(
592        &mut self,
593        provider_factory: impl TrieNodeProviderFactory,
594    ) -> SparseStateTrieResult<B256> {
595        // record revealed node metrics
596        #[cfg(feature = "metrics")]
597        self.metrics.record();
598
599        Ok(self.revealed_trie_mut(provider_factory)?.root())
600    }
601
602    /// Returns sparse trie root and trie updates if the trie has been revealed.
603    #[instrument(target = "trie::sparse", skip_all)]
604    pub fn root_with_updates(
605        &mut self,
606        provider_factory: impl TrieNodeProviderFactory,
607    ) -> SparseStateTrieResult<(B256, TrieUpdates)> {
608        // record revealed node metrics
609        #[cfg(feature = "metrics")]
610        self.metrics.record();
611
612        let storage_tries = self.storage_trie_updates();
613        let revealed = self.revealed_trie_mut(provider_factory)?;
614
615        let (root, updates) = (revealed.root(), revealed.take_updates());
616        let updates = TrieUpdates {
617            account_nodes: updates.updated_nodes,
618            removed_nodes: updates.removed_nodes,
619            storage_tries,
620        };
621        Ok((root, updates))
622    }
623
624    /// Returns storage trie updates for tries that have been revealed.
625    ///
626    /// Panics if any of the storage tries are not revealed.
627    pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdates> {
628        self.storage
629            .tries
630            .iter_mut()
631            .map(|(address, trie)| {
632                let trie = trie.as_revealed_mut().unwrap();
633                let updates = trie.take_updates();
634                let updates = StorageTrieUpdates {
635                    is_deleted: updates.wiped,
636                    storage_nodes: updates.updated_nodes,
637                    removed_nodes: updates.removed_nodes,
638                };
639                (*address, updates)
640            })
641            .filter(|(_, updates)| !updates.is_empty())
642            .collect()
643    }
644
645    /// Returns [`TrieUpdates`] by taking the updates from the revealed sparse tries.
646    ///
647    /// Returns `None` if the accounts trie is not revealed.
648    pub fn take_trie_updates(&mut self) -> Option<TrieUpdates> {
649        let storage_tries = self.storage_trie_updates();
650        self.state.as_revealed_mut().map(|state| {
651            let updates = state.take_updates();
652            TrieUpdates {
653                account_nodes: updates.updated_nodes,
654                removed_nodes: updates.removed_nodes,
655                storage_tries,
656            }
657        })
658    }
659
660    /// Update the account leaf node.
661    pub fn update_account_leaf(
662        &mut self,
663        path: Nibbles,
664        value: Vec<u8>,
665        provider_factory: impl TrieNodeProviderFactory,
666    ) -> SparseStateTrieResult<()> {
667        if !self.revealed_account_paths.contains(&path) {
668            self.revealed_account_paths.insert(path);
669        }
670
671        let provider = provider_factory.account_node_provider();
672        self.state.update_leaf(path, value, provider)?;
673        Ok(())
674    }
675
676    /// Update the leaf node of a revealed storage trie at the provided address.
677    pub fn update_storage_leaf(
678        &mut self,
679        address: B256,
680        slot: Nibbles,
681        value: Vec<u8>,
682        provider_factory: impl TrieNodeProviderFactory,
683    ) -> SparseStateTrieResult<()> {
684        let provider = provider_factory.storage_node_provider(address);
685        self.storage
686            .tries
687            .get_mut(&address)
688            .ok_or(SparseTrieErrorKind::Blind)?
689            .update_leaf(slot, value, provider)?;
690        self.storage.get_revealed_paths_mut(address).insert(slot);
691        Ok(())
692    }
693
694    /// Update or remove trie account based on new account info. This method will either recompute
695    /// the storage root based on update storage trie or look it up from existing leaf value.
696    ///
697    /// Returns false if the new account info and storage trie are empty, indicating the account
698    /// leaf should be removed.
699    #[instrument(level = "trace", target = "trie::sparse", skip_all)]
700    pub fn update_account(
701        &mut self,
702        address: B256,
703        account: Account,
704        provider_factory: impl TrieNodeProviderFactory,
705    ) -> SparseStateTrieResult<bool> {
706        let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
707            trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
708            storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
709        } else if self.is_account_revealed(address) {
710            trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
711            // The account was revealed, either...
712            if let Some(value) = self.get_account_value(&address) {
713                // ..it exists and we should take its current storage root or...
714                TrieAccount::decode(&mut &value[..])?.storage_root
715            } else {
716                // ...the account is newly created and the storage trie is empty.
717                EMPTY_ROOT_HASH
718            }
719        } else {
720            return Err(SparseTrieErrorKind::Blind.into())
721        };
722
723        if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
724            return Ok(false);
725        }
726
727        trace!(target: "trie::sparse", ?address, "Updating account");
728        let nibbles = Nibbles::unpack(address);
729        self.account_rlp_buf.clear();
730        account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
731        self.update_account_leaf(nibbles, self.account_rlp_buf.clone(), provider_factory)?;
732
733        Ok(true)
734    }
735
736    /// Update the storage root of a revealed account.
737    ///
738    /// If the account doesn't exist in the trie, the function is a no-op.
739    ///
740    /// Returns false if the new storage root is empty, and the account info was already empty,
741    /// indicating the account leaf should be removed.
742    #[instrument(target = "trie::sparse", skip_all)]
743    pub fn update_account_storage_root(
744        &mut self,
745        address: B256,
746        provider_factory: impl TrieNodeProviderFactory,
747    ) -> SparseStateTrieResult<bool> {
748        if !self.is_account_revealed(address) {
749            return Err(SparseTrieErrorKind::Blind.into())
750        }
751
752        // Nothing to update if the account doesn't exist in the trie.
753        let Some(mut trie_account) = self
754            .get_account_value(&address)
755            .map(|v| TrieAccount::decode(&mut &v[..]))
756            .transpose()?
757        else {
758            trace!(target: "trie::sparse", ?address, "Account not found in trie, skipping storage root update");
759            return Ok(true)
760        };
761
762        // Calculate the new storage root. If the storage trie doesn't exist, the storage root will
763        // be empty.
764        let storage_root = if let Some(storage_trie) = self.storage.tries.get_mut(&address) {
765            trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
766            storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
767        } else {
768            EMPTY_ROOT_HASH
769        };
770
771        // Update the account with the new storage root.
772        trie_account.storage_root = storage_root;
773
774        // If the account is empty, indicate that it should be removed.
775        if trie_account == TrieAccount::default() {
776            return Ok(false)
777        }
778
779        // Otherwise, update the account leaf.
780        trace!(target: "trie::sparse", ?address, "Updating account with the new storage root");
781        let nibbles = Nibbles::unpack(address);
782        self.account_rlp_buf.clear();
783        trie_account.encode(&mut self.account_rlp_buf);
784        self.update_account_leaf(nibbles, self.account_rlp_buf.clone(), provider_factory)?;
785
786        Ok(true)
787    }
788
789    /// Remove the account leaf node.
790    #[instrument(target = "trie::sparse", skip_all)]
791    pub fn remove_account_leaf(
792        &mut self,
793        path: &Nibbles,
794        provider_factory: impl TrieNodeProviderFactory,
795    ) -> SparseStateTrieResult<()> {
796        let provider = provider_factory.account_node_provider();
797        self.state.remove_leaf(path, provider)?;
798        Ok(())
799    }
800
801    /// Update the leaf node of a storage trie at the provided address.
802    pub fn remove_storage_leaf(
803        &mut self,
804        address: B256,
805        slot: &Nibbles,
806        provider_factory: impl TrieNodeProviderFactory,
807    ) -> SparseStateTrieResult<()> {
808        let storage_trie =
809            self.storage.tries.get_mut(&address).ok_or(SparseTrieErrorKind::Blind)?;
810
811        let provider = provider_factory.storage_node_provider(address);
812        storage_trie.remove_leaf(slot, provider)?;
813        Ok(())
814    }
815}
816
817/// The fields of [`SparseStateTrie`] related to storage tries. This is kept separate from the rest
818/// of [`SparseStateTrie`] both to help enforce allocation re-use and to allow us to implement
819/// methods like `get_trie_and_revealed_paths` which return multiple mutable borrows.
820#[derive(Debug, Default)]
821struct StorageTries<S = SerialSparseTrie> {
822    /// Sparse storage tries.
823    tries: B256Map<SparseTrie<S>>,
824    /// Cleared storage tries, kept for re-use.
825    cleared_tries: Vec<SparseTrie<S>>,
826    /// Collection of revealed storage trie paths, per account.
827    revealed_paths: B256Map<HashSet<Nibbles>>,
828    /// Cleared revealed storage trie path collections, kept for re-use.
829    cleared_revealed_paths: Vec<HashSet<Nibbles>>,
830    /// A default cleared trie instance, which will be cloned when creating new tries.
831    default_trie: SparseTrie<S>,
832}
833
834impl<S: SparseTrieInterface> StorageTries<S> {
835    /// Returns all fields to a cleared state, equivalent to the default state, keeping cleared
836    /// collections for re-use later when possible.
837    fn clear(&mut self) {
838        self.cleared_tries.extend(self.tries.drain().map(|(_, trie)| trie.clear()));
839        self.cleared_revealed_paths.extend(self.revealed_paths.drain().map(|(_, mut set)| {
840            set.clear();
841            set
842        }));
843    }
844
845    /// Shrinks the capacity of all storage tries (active, cleared, and default) to the given sizes.
846    /// The capacity is distributed equally among all tries that have allocations.
847    fn shrink_to(&mut self, node_size: usize, value_size: usize) {
848        // Count total number of tries with capacity (active + cleared + default)
849        let active_count = self.tries.len();
850        let cleared_count = self.cleared_tries.len();
851        let total_tries = 1 + active_count + cleared_count;
852
853        // Distribute capacity equally among all tries
854        let node_size_per_trie = node_size / total_tries;
855        let value_size_per_trie = value_size / total_tries;
856
857        // Shrink active storage tries
858        for trie in self.tries.values_mut() {
859            trie.shrink_nodes_to(node_size_per_trie);
860            trie.shrink_values_to(value_size_per_trie);
861        }
862
863        // Shrink cleared storage tries
864        for trie in &mut self.cleared_tries {
865            trie.shrink_nodes_to(node_size_per_trie);
866            trie.shrink_values_to(value_size_per_trie);
867        }
868    }
869}
870
871impl<S: SparseTrieInterface + Clone> StorageTries<S> {
872    /// Returns the set of already revealed trie node paths for an account's storage, creating the
873    /// set if it didn't previously exist.
874    fn get_revealed_paths_mut(&mut self, account: B256) -> &mut HashSet<Nibbles> {
875        self.revealed_paths
876            .entry(account)
877            .or_insert_with(|| self.cleared_revealed_paths.pop().unwrap_or_default())
878    }
879
880    /// Returns the `SparseTrie` and the set of already revealed trie node paths for an account's
881    /// storage, creating them if they didn't previously exist.
882    fn get_trie_and_revealed_paths_mut(
883        &mut self,
884        account: B256,
885    ) -> (&mut SparseTrie<S>, &mut HashSet<Nibbles>) {
886        let trie = self.tries.entry(account).or_insert_with(|| {
887            self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
888        });
889
890        let revealed_paths = self
891            .revealed_paths
892            .entry(account)
893            .or_insert_with(|| self.cleared_revealed_paths.pop().unwrap_or_default());
894
895        (trie, revealed_paths)
896    }
897
898    /// Takes the storage trie for the account from the internal `HashMap`, creating it if it
899    /// doesn't already exist.
900    #[cfg(feature = "std")]
901    fn take_or_create_trie(&mut self, account: &B256) -> SparseTrie<S> {
902        self.tries.remove(account).unwrap_or_else(|| {
903            self.cleared_tries.pop().unwrap_or_else(|| self.default_trie.clone())
904        })
905    }
906
907    /// Takes the revealed paths set from the account from the internal `HashMap`, creating one if
908    /// it doesn't exist.
909    #[cfg(feature = "std")]
910    fn take_or_create_revealed_paths(&mut self, account: &B256) -> HashSet<Nibbles> {
911        self.revealed_paths
912            .remove(account)
913            .unwrap_or_else(|| self.cleared_revealed_paths.pop().unwrap_or_default())
914    }
915}
916
917#[derive(Debug, PartialEq, Eq, Default)]
918struct ProofNodesMetricValues {
919    /// Number of nodes in the proof.
920    total_nodes: usize,
921    /// Number of nodes that were skipped because they were already revealed.
922    skipped_nodes: usize,
923}
924
925/// Result of [`filter_map_revealed_nodes`].
926#[derive(Debug, PartialEq, Eq)]
927struct FilterMappedProofNodes {
928    /// Root node which was pulled out of the original node set to be handled specially.
929    root_node: Option<ProofTrieNode>,
930    /// Filtered, decoded and unsorted proof nodes. Root node is removed.
931    nodes: Vec<ProofTrieNode>,
932    /// Number of new nodes that will be revealed. This includes all children of branch nodes, even
933    /// if they are not in the proof.
934    new_nodes: usize,
935    /// Values which are being returned so they can be incremented into metrics.
936    metric_values: ProofNodesMetricValues,
937}
938
939/// Filters the decoded nodes that are already revealed, maps them to `SparseTrieNode`s,
940/// separates the root node if present, and returns additional information about the number of
941/// total, skipped, and new nodes.
942fn filter_map_revealed_nodes(
943    proof_nodes: DecodedProofNodes,
944    revealed_nodes: &mut HashSet<Nibbles>,
945    branch_node_masks: &BranchNodeMasksMap,
946) -> SparseStateTrieResult<FilterMappedProofNodes> {
947    let mut result = FilterMappedProofNodes {
948        root_node: None,
949        nodes: Vec::with_capacity(proof_nodes.len()),
950        new_nodes: 0,
951        metric_values: Default::default(),
952    };
953
954    let proof_nodes_len = proof_nodes.len();
955    for (path, proof_node) in proof_nodes.into_inner() {
956        result.metric_values.total_nodes += 1;
957
958        let is_root = path.is_empty();
959
960        // If the node is already revealed, skip it. We don't ever skip the root node, nor do we add
961        // it to `revealed_nodes`.
962        if !is_root && !revealed_nodes.insert(path) {
963            result.metric_values.skipped_nodes += 1;
964            continue
965        }
966
967        result.new_nodes += 1;
968
969        // Extract hash/tree masks based on the node type (only branch nodes have masks). At the
970        // same time increase the new_nodes counter if the node is a type which has children.
971        let masks = match &proof_node {
972            TrieNode::Branch(branch) => {
973                // If it's a branch node, increase the number of new nodes by the number of children
974                // according to the state mask.
975                result.new_nodes += branch.state_mask.count_ones() as usize;
976                if let Some(branch_masks) = branch_node_masks.get(&path) {
977                    TrieMasks {
978                        hash_mask: Some(branch_masks.hash_mask),
979                        tree_mask: Some(branch_masks.tree_mask),
980                    }
981                } else {
982                    TrieMasks::none()
983                }
984            }
985            TrieNode::Extension(_) => {
986                // There is always exactly one child of an extension node.
987                result.new_nodes += 1;
988                TrieMasks::none()
989            }
990            _ => TrieMasks::none(),
991        };
992
993        let node = ProofTrieNode { path, node: proof_node, masks };
994
995        if is_root {
996            // Perform sanity check.
997            if matches!(node.node, TrieNode::EmptyRoot) && proof_nodes_len > 1 {
998                return Err(SparseStateTrieErrorKind::InvalidRootNode {
999                    path,
1000                    node: alloy_rlp::encode(&node.node).into(),
1001                }
1002                .into())
1003            }
1004
1005            result.root_node = Some(node);
1006
1007            continue
1008        }
1009
1010        result.nodes.push(node);
1011    }
1012
1013    Ok(result)
1014}
1015
1016#[cfg(test)]
1017mod tests {
1018    use super::*;
1019    use crate::provider::DefaultTrieNodeProviderFactory;
1020    use alloy_primitives::{
1021        b256,
1022        map::{HashMap, HashSet},
1023        U256,
1024    };
1025    use arbitrary::Arbitrary;
1026    use rand::{rngs::StdRng, Rng, SeedableRng};
1027    use reth_primitives_traits::Account;
1028    use reth_trie::{updates::StorageTrieUpdates, HashBuilder, MultiProof, EMPTY_ROOT_HASH};
1029    use reth_trie_common::{
1030        proof::{ProofNodes, ProofRetainer},
1031        BranchNode, BranchNodeMasks, LeafNode, StorageMultiProof, TrieMask,
1032    };
1033
1034    #[test]
1035    fn reveal_account_path_twice() {
1036        let provider_factory = DefaultTrieNodeProviderFactory;
1037        let mut sparse = SparseStateTrie::<SerialSparseTrie>::default();
1038
1039        let leaf_value = alloy_rlp::encode(TrieAccount::default());
1040        let leaf_1 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
1041            Nibbles::default(),
1042            leaf_value.clone(),
1043        )));
1044        let leaf_2 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
1045            Nibbles::default(),
1046            leaf_value.clone(),
1047        )));
1048
1049        let multiproof = MultiProof {
1050            account_subtree: ProofNodes::from_iter([
1051                (
1052                    Nibbles::default(),
1053                    alloy_rlp::encode(TrieNode::Branch(BranchNode {
1054                        stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1055                        state_mask: TrieMask::new(0b11),
1056                    }))
1057                    .into(),
1058                ),
1059                (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1060                (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1061            ]),
1062            ..Default::default()
1063        };
1064
1065        // Reveal multiproof and check that the state trie contains the leaf node and value
1066        sparse.reveal_decoded_multiproof(multiproof.clone().try_into().unwrap()).unwrap();
1067        assert!(sparse
1068            .state_trie_ref()
1069            .unwrap()
1070            .nodes_ref()
1071            .contains_key(&Nibbles::from_nibbles([0x0])),);
1072        assert_eq!(
1073            sparse.state_trie_ref().unwrap().get_leaf_value(&Nibbles::from_nibbles([0x0])),
1074            Some(&leaf_value)
1075        );
1076
1077        // Remove the leaf node and check that the state trie does not contain the leaf node and
1078        // value
1079        sparse.remove_account_leaf(&Nibbles::from_nibbles([0x0]), &provider_factory).unwrap();
1080        assert!(!sparse
1081            .state_trie_ref()
1082            .unwrap()
1083            .nodes_ref()
1084            .contains_key(&Nibbles::from_nibbles([0x0])),);
1085        assert!(sparse
1086            .state_trie_ref()
1087            .unwrap()
1088            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1089            .is_none());
1090
1091        // Reveal multiproof again and check that the state trie still does not contain the leaf
1092        // node and value, because they were already revealed before
1093        sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1094        assert!(!sparse
1095            .state_trie_ref()
1096            .unwrap()
1097            .nodes_ref()
1098            .contains_key(&Nibbles::from_nibbles([0x0])));
1099        assert!(sparse
1100            .state_trie_ref()
1101            .unwrap()
1102            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1103            .is_none());
1104    }
1105
1106    #[test]
1107    fn reveal_storage_path_twice() {
1108        let provider_factory = DefaultTrieNodeProviderFactory;
1109        let mut sparse = SparseStateTrie::<SerialSparseTrie>::default();
1110
1111        let leaf_value = alloy_rlp::encode(TrieAccount::default());
1112        let leaf_1 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
1113            Nibbles::default(),
1114            leaf_value.clone(),
1115        )));
1116        let leaf_2 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
1117            Nibbles::default(),
1118            leaf_value.clone(),
1119        )));
1120
1121        let multiproof = MultiProof {
1122            storages: HashMap::from_iter([(
1123                B256::ZERO,
1124                StorageMultiProof {
1125                    root: B256::ZERO,
1126                    subtree: ProofNodes::from_iter([
1127                        (
1128                            Nibbles::default(),
1129                            alloy_rlp::encode(TrieNode::Branch(BranchNode {
1130                                stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
1131                                state_mask: TrieMask::new(0b11),
1132                            }))
1133                            .into(),
1134                        ),
1135                        (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
1136                        (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
1137                    ]),
1138                    branch_node_masks: Default::default(),
1139                },
1140            )]),
1141            ..Default::default()
1142        };
1143
1144        // Reveal multiproof and check that the storage trie contains the leaf node and value
1145        sparse.reveal_decoded_multiproof(multiproof.clone().try_into().unwrap()).unwrap();
1146        assert!(sparse
1147            .storage_trie_ref(&B256::ZERO)
1148            .unwrap()
1149            .nodes_ref()
1150            .contains_key(&Nibbles::from_nibbles([0x0])),);
1151        assert_eq!(
1152            sparse
1153                .storage_trie_ref(&B256::ZERO)
1154                .unwrap()
1155                .get_leaf_value(&Nibbles::from_nibbles([0x0])),
1156            Some(&leaf_value)
1157        );
1158
1159        // Remove the leaf node and check that the storage trie does not contain the leaf node and
1160        // value
1161        sparse
1162            .remove_storage_leaf(B256::ZERO, &Nibbles::from_nibbles([0x0]), &provider_factory)
1163            .unwrap();
1164        assert!(!sparse
1165            .storage_trie_ref(&B256::ZERO)
1166            .unwrap()
1167            .nodes_ref()
1168            .contains_key(&Nibbles::from_nibbles([0x0])),);
1169        assert!(sparse
1170            .storage_trie_ref(&B256::ZERO)
1171            .unwrap()
1172            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1173            .is_none());
1174
1175        // Reveal multiproof again and check that the storage trie still does not contain the leaf
1176        // node and value, because they were already revealed before
1177        sparse.reveal_decoded_multiproof(multiproof.try_into().unwrap()).unwrap();
1178        assert!(!sparse
1179            .storage_trie_ref(&B256::ZERO)
1180            .unwrap()
1181            .nodes_ref()
1182            .contains_key(&Nibbles::from_nibbles([0x0])));
1183        assert!(sparse
1184            .storage_trie_ref(&B256::ZERO)
1185            .unwrap()
1186            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
1187            .is_none());
1188    }
1189
1190    #[test]
1191    fn take_trie_updates() {
1192        reth_tracing::init_test_tracing();
1193
1194        // let mut rng = generators::rng();
1195        let mut rng = StdRng::seed_from_u64(1);
1196
1197        let mut bytes = [0u8; 1024];
1198        rng.fill(bytes.as_mut_slice());
1199
1200        let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1201        let slot_path_1 = Nibbles::unpack(slot_1);
1202        let value_1 = U256::from(rng.random::<u64>());
1203        let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1204        let slot_path_2 = Nibbles::unpack(slot_2);
1205        let value_2 = U256::from(rng.random::<u64>());
1206        let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1207        let slot_path_3 = Nibbles::unpack(slot_3);
1208        let value_3 = U256::from(rng.random::<u64>());
1209
1210        let mut storage_hash_builder = HashBuilder::default()
1211            .with_proof_retainer(ProofRetainer::from_iter([slot_path_1, slot_path_2]));
1212        storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
1213        storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
1214
1215        let storage_root = storage_hash_builder.root();
1216        let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
1217        let storage_branch_node_masks = BranchNodeMasksMap::from_iter([
1218            (
1219                Nibbles::default(),
1220                BranchNodeMasks { hash_mask: TrieMask::new(0b010), tree_mask: TrieMask::default() },
1221            ),
1222            (
1223                Nibbles::from_nibbles([0x1]),
1224                BranchNodeMasks { hash_mask: TrieMask::new(0b11), tree_mask: TrieMask::default() },
1225            ),
1226        ]);
1227
1228        let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
1229        let address_path_1 = Nibbles::unpack(address_1);
1230        let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1231        let mut trie_account_1 = account_1.into_trie_account(storage_root);
1232        let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
1233        let address_path_2 = Nibbles::unpack(address_2);
1234        let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
1235        let mut trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
1236
1237        let mut hash_builder = HashBuilder::default()
1238            .with_proof_retainer(ProofRetainer::from_iter([address_path_1, address_path_2]));
1239        hash_builder.add_leaf(address_path_1, &alloy_rlp::encode(trie_account_1));
1240        hash_builder.add_leaf(address_path_2, &alloy_rlp::encode(trie_account_2));
1241
1242        let root = hash_builder.root();
1243        let proof_nodes = hash_builder.take_proof_nodes();
1244
1245        let provider_factory = DefaultTrieNodeProviderFactory;
1246        let mut sparse = SparseStateTrie::<SerialSparseTrie>::default().with_updates(true);
1247        sparse
1248            .reveal_decoded_multiproof(
1249                MultiProof {
1250                    account_subtree: proof_nodes,
1251                    branch_node_masks: BranchNodeMasksMap::from_iter([(
1252                        Nibbles::from_nibbles([0x1]),
1253                        BranchNodeMasks {
1254                            hash_mask: TrieMask::new(0b00),
1255                            tree_mask: TrieMask::default(),
1256                        },
1257                    )]),
1258                    storages: HashMap::from_iter([
1259                        (
1260                            address_1,
1261                            StorageMultiProof {
1262                                root,
1263                                subtree: storage_proof_nodes.clone(),
1264                                branch_node_masks: storage_branch_node_masks.clone(),
1265                            },
1266                        ),
1267                        (
1268                            address_2,
1269                            StorageMultiProof {
1270                                root,
1271                                subtree: storage_proof_nodes,
1272                                branch_node_masks: storage_branch_node_masks,
1273                            },
1274                        ),
1275                    ]),
1276                }
1277                .try_into()
1278                .unwrap(),
1279            )
1280            .unwrap();
1281
1282        assert_eq!(sparse.root(&provider_factory).unwrap(), root);
1283
1284        let address_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1285        let address_path_3 = Nibbles::unpack(address_3);
1286        let account_3 = Account { nonce: account_1.nonce + 1, ..account_1 };
1287        let trie_account_3 = account_3.into_trie_account(EMPTY_ROOT_HASH);
1288
1289        sparse
1290            .update_account_leaf(
1291                address_path_3,
1292                alloy_rlp::encode(trie_account_3),
1293                &provider_factory,
1294            )
1295            .unwrap();
1296
1297        sparse
1298            .update_storage_leaf(
1299                address_1,
1300                slot_path_3,
1301                alloy_rlp::encode(value_3),
1302                &provider_factory,
1303            )
1304            .unwrap();
1305        trie_account_1.storage_root = sparse.storage_root(address_1).unwrap();
1306        sparse
1307            .update_account_leaf(
1308                address_path_1,
1309                alloy_rlp::encode(trie_account_1),
1310                &provider_factory,
1311            )
1312            .unwrap();
1313
1314        sparse.wipe_storage(address_2).unwrap();
1315        trie_account_2.storage_root = sparse.storage_root(address_2).unwrap();
1316        sparse
1317            .update_account_leaf(
1318                address_path_2,
1319                alloy_rlp::encode(trie_account_2),
1320                &provider_factory,
1321            )
1322            .unwrap();
1323
1324        sparse.root(&provider_factory).unwrap();
1325
1326        let sparse_updates = sparse.take_trie_updates().unwrap();
1327        // TODO(alexey): assert against real state root calculation updates
1328        pretty_assertions::assert_eq!(
1329            sparse_updates,
1330            TrieUpdates {
1331                account_nodes: HashMap::default(),
1332                storage_tries: HashMap::from_iter([(
1333                    b256!("0x1100000000000000000000000000000000000000000000000000000000000000"),
1334                    StorageTrieUpdates {
1335                        is_deleted: true,
1336                        storage_nodes: HashMap::default(),
1337                        removed_nodes: HashSet::default()
1338                    }
1339                )]),
1340                removed_nodes: HashSet::default()
1341            }
1342        );
1343    }
1344
1345    #[test]
1346    fn test_filter_map_revealed_nodes() {
1347        let mut revealed_nodes = HashSet::from_iter([Nibbles::from_nibbles([0x0])]);
1348        let leaf = TrieNode::Leaf(LeafNode::new(Nibbles::default(), alloy_rlp::encode([])));
1349        let leaf_encoded = alloy_rlp::encode(&leaf);
1350        let branch = TrieNode::Branch(BranchNode::new(
1351            vec![RlpNode::from_rlp(&leaf_encoded), RlpNode::from_rlp(&leaf_encoded)],
1352            TrieMask::new(0b11),
1353        ));
1354        let proof_nodes = alloy_trie::proof::DecodedProofNodes::from_iter([
1355            (Nibbles::default(), branch.clone()),
1356            (Nibbles::from_nibbles([0x0]), leaf.clone()),
1357            (Nibbles::from_nibbles([0x1]), leaf.clone()),
1358        ]);
1359
1360        let branch_node_masks = BranchNodeMasksMap::default();
1361
1362        let decoded =
1363            filter_map_revealed_nodes(proof_nodes, &mut revealed_nodes, &branch_node_masks)
1364                .unwrap();
1365
1366        assert_eq!(
1367            decoded,
1368            FilterMappedProofNodes {
1369                root_node: Some(ProofTrieNode {
1370                    path: Nibbles::default(),
1371                    node: branch,
1372                    masks: TrieMasks::none(),
1373                }),
1374                nodes: vec![ProofTrieNode {
1375                    path: Nibbles::from_nibbles([0x1]),
1376                    node: leaf,
1377                    masks: TrieMasks::none(),
1378                }],
1379                // Branch, two of its children, one leaf
1380                new_nodes: 4,
1381                // Metric values
1382                metric_values: ProofNodesMetricValues {
1383                    // Branch, leaf, leaf
1384                    total_nodes: 3,
1385                    // Revealed leaf node with path 0x1
1386                    skipped_nodes: 1,
1387                },
1388            }
1389        );
1390    }
1391}