reth_trie_sparse/
state.rs

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