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