reth_trie_sparse/
state.rs

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