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