reth_trie_sparse/
state.rs

1use crate::{
2    blinded::{BlindedProvider, BlindedProviderFactory, DefaultBlindedProviderFactory},
3    metrics::SparseStateTrieMetrics,
4    RevealedSparseTrie, SparseTrie, TrieMasks,
5};
6use alloy_primitives::{
7    hex,
8    map::{B256Map, HashSet},
9    Bytes, B256,
10};
11use alloy_rlp::{Decodable, Encodable};
12use reth_execution_errors::{SparseStateTrieErrorKind, SparseStateTrieResult, SparseTrieErrorKind};
13use reth_primitives_traits::Account;
14use reth_tracing::tracing::trace;
15use reth_trie_common::{
16    updates::{StorageTrieUpdates, TrieUpdates},
17    MultiProof, Nibbles, RlpNode, TrieAccount, TrieNode, EMPTY_ROOT_HASH,
18    TRIE_ACCOUNT_RLP_MAX_SIZE,
19};
20use std::{collections::VecDeque, fmt, iter::Peekable};
21
22/// Sparse state trie representing lazy-loaded Ethereum state trie.
23pub struct SparseStateTrie<F: BlindedProviderFactory = DefaultBlindedProviderFactory> {
24    /// Blinded node provider factory.
25    provider_factory: F,
26    /// Sparse account trie.
27    state: SparseTrie<F::AccountNodeProvider>,
28    /// Sparse storage tries.
29    storages: B256Map<SparseTrie<F::StorageNodeProvider>>,
30    /// Collection of revealed account trie paths.
31    revealed_account_paths: HashSet<Nibbles>,
32    /// Collection of revealed storage trie paths, per account.
33    revealed_storage_paths: B256Map<HashSet<Nibbles>>,
34    /// Flag indicating whether trie updates should be retained.
35    retain_updates: bool,
36    /// Reusable buffer for RLP encoding of trie accounts.
37    account_rlp_buf: Vec<u8>,
38    /// Metrics for the sparse state trie.
39    metrics: SparseStateTrieMetrics,
40}
41
42impl Default for SparseStateTrie {
43    fn default() -> Self {
44        Self {
45            provider_factory: Default::default(),
46            state: Default::default(),
47            storages: Default::default(),
48            revealed_account_paths: Default::default(),
49            revealed_storage_paths: Default::default(),
50            retain_updates: false,
51            account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
52            metrics: Default::default(),
53        }
54    }
55}
56
57impl<P: BlindedProviderFactory> fmt::Debug for SparseStateTrie<P> {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        f.debug_struct("SparseStateTrie")
60            .field("state", &self.state)
61            .field("storages", &self.storages)
62            .field("revealed_account_paths", &self.revealed_account_paths)
63            .field("revealed_storage_paths", &self.revealed_storage_paths)
64            .field("retain_updates", &self.retain_updates)
65            .field("account_rlp_buf", &hex::encode(&self.account_rlp_buf))
66            .finish_non_exhaustive()
67    }
68}
69
70impl SparseStateTrie {
71    /// Create state trie from state trie.
72    pub fn from_state(state: SparseTrie) -> Self {
73        Self { state, ..Default::default() }
74    }
75}
76
77impl<F: BlindedProviderFactory> SparseStateTrie<F> {
78    /// Create new [`SparseStateTrie`] with blinded node provider factory.
79    pub fn new(provider_factory: F) -> Self {
80        Self {
81            provider_factory,
82            state: Default::default(),
83            storages: Default::default(),
84            revealed_account_paths: Default::default(),
85            revealed_storage_paths: Default::default(),
86            retain_updates: false,
87            account_rlp_buf: Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE),
88            metrics: Default::default(),
89        }
90    }
91
92    /// Set the retention of branch node updates and deletions.
93    pub const fn with_updates(mut self, retain_updates: bool) -> Self {
94        self.retain_updates = retain_updates;
95        self
96    }
97
98    /// Returns `true` if account was already revealed.
99    pub fn is_account_revealed(&self, account: B256) -> bool {
100        self.revealed_account_paths.contains(&Nibbles::unpack(account))
101    }
102
103    /// Returns `true` if storage slot for account was already revealed.
104    pub fn is_storage_slot_revealed(&self, account: B256, slot: B256) -> bool {
105        self.revealed_storage_paths
106            .get(&account)
107            .is_some_and(|slots| slots.contains(&Nibbles::unpack(slot)))
108    }
109
110    /// Returns reference to bytes representing leaf value for the target account.
111    pub fn get_account_value(&self, account: &B256) -> Option<&Vec<u8>> {
112        self.state.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(account))
113    }
114
115    /// Returns reference to bytes representing leaf value for the target account and storage slot.
116    pub fn get_storage_slot_value(&self, account: &B256, slot: &B256) -> Option<&Vec<u8>> {
117        self.storages.get(account)?.as_revealed_ref()?.get_leaf_value(&Nibbles::unpack(slot))
118    }
119
120    /// Returns reference to state trie if it was revealed.
121    pub const fn state_trie_ref(&self) -> Option<&RevealedSparseTrie<F::AccountNodeProvider>> {
122        self.state.as_revealed_ref()
123    }
124
125    /// Returns reference to storage trie if it was revealed.
126    pub fn storage_trie_ref(
127        &self,
128        address: &B256,
129    ) -> Option<&RevealedSparseTrie<F::StorageNodeProvider>> {
130        self.storages.get(address).and_then(|e| e.as_revealed_ref())
131    }
132
133    /// Returns mutable reference to storage sparse trie if it was revealed.
134    pub fn storage_trie_mut(
135        &mut self,
136        address: &B256,
137    ) -> Option<&mut RevealedSparseTrie<F::StorageNodeProvider>> {
138        self.storages.get_mut(address).and_then(|e| e.as_revealed_mut())
139    }
140
141    /// Takes the storage trie for the provided address.
142    pub fn take_storage_trie(
143        &mut self,
144        address: &B256,
145    ) -> Option<SparseTrie<F::StorageNodeProvider>> {
146        self.storages.remove(address)
147    }
148
149    /// Inserts storage trie for the provided address.
150    pub fn insert_storage_trie(
151        &mut self,
152        address: B256,
153        storage_trie: SparseTrie<F::StorageNodeProvider>,
154    ) {
155        self.storages.insert(address, storage_trie);
156    }
157
158    /// Reveal unknown trie paths from provided leaf path and its proof for the account.
159    ///
160    /// Panics if trie updates retention is enabled.
161    ///
162    /// NOTE: This method does not extensively validate the proof.
163    pub fn reveal_account(
164        &mut self,
165        account: B256,
166        proof: impl IntoIterator<Item = (Nibbles, Bytes)>,
167    ) -> SparseStateTrieResult<()> {
168        assert!(!self.retain_updates);
169
170        if self.is_account_revealed(account) {
171            return Ok(());
172        }
173
174        let mut proof = proof.into_iter().peekable();
175
176        let Some(root_node) = self.validate_root_node(&mut proof)? else { return Ok(()) };
177
178        // Reveal root node if it wasn't already.
179        let trie = self.state.reveal_root_with_provider(
180            self.provider_factory.account_node_provider(),
181            root_node,
182            TrieMasks::none(),
183            self.retain_updates,
184        )?;
185
186        // Reveal the remaining proof nodes.
187        for (path, bytes) in proof {
188            if self.revealed_account_paths.contains(&path) {
189                continue
190            }
191            let node = TrieNode::decode(&mut &bytes[..])?;
192            trie.reveal_node(path.clone(), node, TrieMasks::none())?;
193
194            // Track the revealed path.
195            self.revealed_account_paths.insert(path);
196        }
197
198        Ok(())
199    }
200
201    /// Reveal unknown trie paths from provided leaf path and its proof for the storage slot.
202    ///
203    /// Panics if trie updates retention is enabled.
204    ///
205    /// NOTE: This method does not extensively validate the proof.
206    pub fn reveal_storage_slot(
207        &mut self,
208        account: B256,
209        slot: B256,
210        proof: impl IntoIterator<Item = (Nibbles, Bytes)>,
211    ) -> SparseStateTrieResult<()> {
212        assert!(!self.retain_updates);
213
214        if self.is_storage_slot_revealed(account, slot) {
215            return Ok(());
216        }
217
218        let mut proof = proof.into_iter().peekable();
219
220        let Some(root_node) = self.validate_root_node(&mut proof)? else { return Ok(()) };
221
222        // Reveal root node if it wasn't already.
223        let trie = self.storages.entry(account).or_default().reveal_root_with_provider(
224            self.provider_factory.storage_node_provider(account),
225            root_node,
226            TrieMasks::none(),
227            self.retain_updates,
228        )?;
229
230        let revealed_nodes = self.revealed_storage_paths.entry(account).or_default();
231
232        // Reveal the remaining proof nodes.
233        for (path, bytes) in proof {
234            // If the node is already revealed, skip it.
235            if revealed_nodes.contains(&path) {
236                continue
237            }
238            let node = TrieNode::decode(&mut &bytes[..])?;
239            trie.reveal_node(path.clone(), node, TrieMasks::none())?;
240
241            // Track the revealed path.
242            revealed_nodes.insert(path);
243        }
244
245        Ok(())
246    }
247
248    /// Reveal unknown trie paths from multiproof.
249    /// NOTE: This method does not extensively validate the proof.
250    pub fn reveal_multiproof(&mut self, multiproof: MultiProof) -> SparseStateTrieResult<()> {
251        let account_subtree = multiproof.account_subtree.into_nodes_sorted();
252        let mut account_nodes = account_subtree.into_iter().peekable();
253
254        if let Some(root_node) = self.validate_root_node(&mut account_nodes)? {
255            // Reveal root node if it wasn't already.
256            let trie = self.state.reveal_root_with_provider(
257                self.provider_factory.account_node_provider(),
258                root_node,
259                TrieMasks {
260                    hash_mask: multiproof.branch_node_hash_masks.get(&Nibbles::default()).copied(),
261                    tree_mask: multiproof.branch_node_tree_masks.get(&Nibbles::default()).copied(),
262                },
263                self.retain_updates,
264            )?;
265
266            // Reveal the remaining proof nodes.
267            for (path, bytes) in account_nodes {
268                self.metrics.increment_total_account_nodes();
269                // If the node is already revealed, skip it.
270                if self.revealed_account_paths.contains(&path) {
271                    self.metrics.increment_skipped_account_nodes();
272                    continue
273                }
274                let node = TrieNode::decode(&mut &bytes[..])?;
275                let (hash_mask, tree_mask) = if let TrieNode::Branch(_) = node {
276                    (
277                        multiproof.branch_node_hash_masks.get(&path).copied(),
278                        multiproof.branch_node_tree_masks.get(&path).copied(),
279                    )
280                } else {
281                    (None, None)
282                };
283
284                trace!(target: "trie::sparse", ?path, ?node, ?hash_mask, ?tree_mask, "Revealing account node");
285                trie.reveal_node(path.clone(), node, TrieMasks { hash_mask, tree_mask })?;
286
287                // Track the revealed path.
288                self.revealed_account_paths.insert(path);
289            }
290        }
291
292        for (account, storage_subtree) in multiproof.storages {
293            let subtree = storage_subtree.subtree.into_nodes_sorted();
294            let mut nodes = subtree.into_iter().peekable();
295
296            if let Some(root_node) = self.validate_root_node(&mut nodes)? {
297                // Reveal root node if it wasn't already.
298                let trie = self.storages.entry(account).or_default().reveal_root_with_provider(
299                    self.provider_factory.storage_node_provider(account),
300                    root_node,
301                    TrieMasks {
302                        hash_mask: storage_subtree
303                            .branch_node_hash_masks
304                            .get(&Nibbles::default())
305                            .copied(),
306                        tree_mask: storage_subtree
307                            .branch_node_tree_masks
308                            .get(&Nibbles::default())
309                            .copied(),
310                    },
311                    self.retain_updates,
312                )?;
313                let revealed_nodes = self.revealed_storage_paths.entry(account).or_default();
314
315                // Reveal the remaining proof nodes.
316                for (path, bytes) in nodes {
317                    self.metrics.increment_total_storage_nodes();
318                    // If the node is already revealed, skip it.
319                    if revealed_nodes.contains(&path) {
320                        self.metrics.increment_skipped_storage_nodes();
321                        continue
322                    }
323                    let node = TrieNode::decode(&mut &bytes[..])?;
324                    let (hash_mask, tree_mask) = if let TrieNode::Branch(_) = node {
325                        (
326                            storage_subtree.branch_node_hash_masks.get(&path).copied(),
327                            storage_subtree.branch_node_tree_masks.get(&path).copied(),
328                        )
329                    } else {
330                        (None, None)
331                    };
332
333                    trace!(target: "trie::sparse", ?account, ?path, ?node, ?hash_mask, ?tree_mask, "Revealing storage node");
334                    trie.reveal_node(path.clone(), node, TrieMasks { hash_mask, tree_mask })?;
335
336                    // Track the revealed path.
337                    revealed_nodes.insert(path);
338                }
339            }
340        }
341
342        Ok(())
343    }
344
345    /// Reveal state witness with the given state root.
346    /// The state witness is expected to be a map of `keccak(rlp(node)): rlp(node).`
347    /// NOTE: This method does not extensively validate the witness.
348    pub fn reveal_witness(
349        &mut self,
350        state_root: B256,
351        witness: &B256Map<Bytes>,
352    ) -> SparseStateTrieResult<()> {
353        // Create a `(hash, path, maybe_account)` queue for traversing witness trie nodes
354        // starting from the root node.
355        let mut queue = VecDeque::from([(state_root, Nibbles::default(), None)]);
356
357        while let Some((hash, path, maybe_account)) = queue.pop_front() {
358            // Retrieve the trie node and decode it.
359            let Some(trie_node_bytes) = witness.get(&hash) else { continue };
360            let trie_node = TrieNode::decode(&mut &trie_node_bytes[..])?;
361
362            // Push children nodes into the queue.
363            match &trie_node {
364                TrieNode::Branch(branch) => {
365                    for (idx, maybe_child) in branch.as_ref().children() {
366                        if let Some(child_hash) = maybe_child.and_then(RlpNode::as_hash) {
367                            let mut child_path = path.clone();
368                            child_path.push_unchecked(idx);
369                            queue.push_back((child_hash, child_path, maybe_account));
370                        }
371                    }
372                }
373                TrieNode::Extension(ext) => {
374                    if let Some(child_hash) = ext.child.as_hash() {
375                        let mut child_path = path.clone();
376                        child_path.extend_from_slice_unchecked(&ext.key);
377                        queue.push_back((child_hash, child_path, maybe_account));
378                    }
379                }
380                TrieNode::Leaf(leaf) => {
381                    let mut full_path = path.clone();
382                    full_path.extend_from_slice_unchecked(&leaf.key);
383                    if maybe_account.is_none() {
384                        let hashed_address = B256::from_slice(&full_path.pack());
385                        let account = TrieAccount::decode(&mut &leaf.value[..])?;
386                        if account.storage_root != EMPTY_ROOT_HASH {
387                            queue.push_back((
388                                account.storage_root,
389                                Nibbles::default(),
390                                Some(hashed_address),
391                            ));
392                        }
393                    }
394                }
395                TrieNode::EmptyRoot => {} // nothing to do here
396            };
397
398            // Reveal the node itself.
399            if let Some(account) = maybe_account {
400                // Check that the path was not already revealed.
401                if self
402                    .revealed_storage_paths
403                    .get(&account)
404                    .is_none_or(|paths| !paths.contains(&path))
405                {
406                    let storage_trie_entry = self.storages.entry(account).or_default();
407                    if path.is_empty() {
408                        // Handle special storage state root node case.
409                        storage_trie_entry.reveal_root_with_provider(
410                            self.provider_factory.storage_node_provider(account),
411                            trie_node,
412                            TrieMasks::none(),
413                            self.retain_updates,
414                        )?;
415                    } else {
416                        // Reveal non-root storage trie node.
417                        storage_trie_entry
418                            .as_revealed_mut()
419                            .ok_or(SparseTrieErrorKind::Blind)?
420                            .reveal_node(path.clone(), trie_node, TrieMasks::none())?;
421                    }
422
423                    // Track the revealed path.
424                    self.revealed_storage_paths.entry(account).or_default().insert(path);
425                }
426            }
427            // Check that the path was not already revealed.
428            else if !self.revealed_account_paths.contains(&path) {
429                if path.is_empty() {
430                    // Handle special state root node case.
431                    self.state.reveal_root_with_provider(
432                        self.provider_factory.account_node_provider(),
433                        trie_node,
434                        TrieMasks::none(),
435                        self.retain_updates,
436                    )?;
437                } else {
438                    // Reveal non-root state trie node.
439                    self.state.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?.reveal_node(
440                        path.clone(),
441                        trie_node,
442                        TrieMasks::none(),
443                    )?;
444                }
445
446                // Track the revealed path.
447                self.revealed_account_paths.insert(path);
448            }
449        }
450
451        Ok(())
452    }
453
454    /// Validates the root node of the proof and returns it if it exists and is valid.
455    fn validate_root_node<I: Iterator<Item = (Nibbles, Bytes)>>(
456        &self,
457        proof: &mut Peekable<I>,
458    ) -> SparseStateTrieResult<Option<TrieNode>> {
459        let mut proof = proof.into_iter().peekable();
460
461        // Validate root node.
462        let Some((path, node)) = proof.next() else { return Ok(None) };
463        if !path.is_empty() {
464            return Err(SparseStateTrieErrorKind::InvalidRootNode { path, node }.into())
465        }
466
467        // Decode root node and perform sanity check.
468        let root_node = TrieNode::decode(&mut &node[..])?;
469        if matches!(root_node, TrieNode::EmptyRoot) && proof.peek().is_some() {
470            return Err(SparseStateTrieErrorKind::InvalidRootNode { path, node }.into())
471        }
472
473        Ok(Some(root_node))
474    }
475
476    /// Wipe the storage trie at the provided address.
477    pub fn wipe_storage(&mut self, address: B256) -> SparseStateTrieResult<()> {
478        if let Some(trie) = self.storages.get_mut(&address) {
479            trie.wipe()?;
480        }
481        Ok(())
482    }
483
484    /// Calculates the hashes of the nodes below the provided level.
485    ///
486    /// If the trie has not been revealed, this function does nothing.
487    pub fn calculate_below_level(&mut self, level: usize) {
488        if let SparseTrie::Revealed(trie) = &mut self.state {
489            trie.update_rlp_node_level(level);
490        }
491    }
492
493    /// Returns storage sparse trie root if the trie has been revealed.
494    pub fn storage_root(&mut self, account: B256) -> Option<B256> {
495        self.storages.get_mut(&account).and_then(|trie| trie.root())
496    }
497
498    /// Returns mutable reference to the revealed sparse trie.
499    ///
500    /// If the trie is not revealed yet, its root will be revealed using the blinded node provider.
501    fn revealed_trie_mut(
502        &mut self,
503    ) -> SparseStateTrieResult<&mut RevealedSparseTrie<F::AccountNodeProvider>> {
504        match self.state {
505            SparseTrie::Blind => {
506                let (root_node, hash_mask, tree_mask) = self
507                    .provider_factory
508                    .account_node_provider()
509                    .blinded_node(&Nibbles::default())?
510                    .map(|node| {
511                        TrieNode::decode(&mut &node.node[..])
512                            .map(|decoded| (decoded, node.hash_mask, node.tree_mask))
513                    })
514                    .transpose()?
515                    .unwrap_or((TrieNode::EmptyRoot, None, None));
516                self.state
517                    .reveal_root_with_provider(
518                        self.provider_factory.account_node_provider(),
519                        root_node,
520                        TrieMasks { hash_mask, tree_mask },
521                        self.retain_updates,
522                    )
523                    .map_err(Into::into)
524            }
525            SparseTrie::Revealed(ref mut trie) => Ok(trie),
526        }
527    }
528
529    /// Returns sparse trie root.
530    ///
531    /// If the trie has not been revealed, this function reveals the root node and returns its hash.
532    pub fn root(&mut self) -> SparseStateTrieResult<B256> {
533        // record revealed node metrics
534        self.metrics.record();
535
536        Ok(self.revealed_trie_mut()?.root())
537    }
538
539    /// Returns sparse trie root and trie updates if the trie has been revealed.
540    pub fn root_with_updates(&mut self) -> SparseStateTrieResult<(B256, TrieUpdates)> {
541        // record revealed node metrics
542        self.metrics.record();
543
544        let storage_tries = self.storage_trie_updates();
545        let revealed = self.revealed_trie_mut()?;
546
547        let (root, updates) = (revealed.root(), revealed.take_updates());
548        let updates = TrieUpdates {
549            account_nodes: updates.updated_nodes,
550            removed_nodes: updates.removed_nodes,
551            storage_tries,
552        };
553        Ok((root, updates))
554    }
555
556    /// Returns storage trie updates for tries that have been revealed.
557    ///
558    /// Panics if any of the storage tries are not revealed.
559    pub fn storage_trie_updates(&mut self) -> B256Map<StorageTrieUpdates> {
560        self.storages
561            .iter_mut()
562            .map(|(address, trie)| {
563                let trie = trie.as_revealed_mut().unwrap();
564                let updates = trie.take_updates();
565                let updates = StorageTrieUpdates {
566                    is_deleted: updates.wiped,
567                    storage_nodes: updates.updated_nodes,
568                    removed_nodes: updates.removed_nodes,
569                };
570                (*address, updates)
571            })
572            .filter(|(_, updates)| !updates.is_empty())
573            .collect()
574    }
575
576    /// Returns [`TrieUpdates`] by taking the updates from the revealed sparse tries.
577    ///
578    /// Returns `None` if the accounts trie is not revealed.
579    pub fn take_trie_updates(&mut self) -> Option<TrieUpdates> {
580        let storage_tries = self.storage_trie_updates();
581        self.state.as_revealed_mut().map(|state| {
582            let updates = state.take_updates();
583            TrieUpdates {
584                account_nodes: updates.updated_nodes,
585                removed_nodes: updates.removed_nodes,
586                storage_tries,
587            }
588        })
589    }
590}
591impl<F: BlindedProviderFactory> SparseStateTrie<F> {
592    /// Update the account leaf node.
593    pub fn update_account_leaf(
594        &mut self,
595        path: Nibbles,
596        value: Vec<u8>,
597    ) -> SparseStateTrieResult<()> {
598        if !self.revealed_account_paths.contains(&path) {
599            self.revealed_account_paths.insert(path.clone());
600        }
601
602        self.state.update_leaf(path, value)?;
603        Ok(())
604    }
605
606    /// Update the leaf node of a storage trie at the provided address.
607    pub fn update_storage_leaf(
608        &mut self,
609        address: B256,
610        slot: Nibbles,
611        value: Vec<u8>,
612    ) -> SparseStateTrieResult<()> {
613        if !self.revealed_storage_paths.get(&address).is_some_and(|slots| slots.contains(&slot)) {
614            self.revealed_storage_paths.entry(address).or_default().insert(slot.clone());
615        }
616
617        let storage_trie = self.storages.get_mut(&address).ok_or(SparseTrieErrorKind::Blind)?;
618        storage_trie.update_leaf(slot, value)?;
619        Ok(())
620    }
621
622    /// Update or remove trie account based on new account info. This method will either recompute
623    /// the storage root based on update storage trie or look it up from existing leaf value.
624    ///
625    /// If the new account info and storage trie are empty, the account leaf will be removed.
626    pub fn update_account(&mut self, address: B256, account: Account) -> SparseStateTrieResult<()> {
627        let nibbles = Nibbles::unpack(address);
628
629        let storage_root = if let Some(storage_trie) = self.storages.get_mut(&address) {
630            trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
631            storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
632        } else if self.is_account_revealed(address) {
633            trace!(target: "trie::sparse", ?address, "Retrieving storage root from account leaf to update account");
634            // The account was revealed, either...
635            if let Some(value) = self.get_account_value(&address) {
636                // ..it exists and we should take it's current storage root or...
637                TrieAccount::decode(&mut &value[..])?.storage_root
638            } else {
639                // ...the account is newly created and the storage trie is empty.
640                EMPTY_ROOT_HASH
641            }
642        } else {
643            return Err(SparseTrieErrorKind::Blind.into())
644        };
645
646        if account.is_empty() && storage_root == EMPTY_ROOT_HASH {
647            trace!(target: "trie::sparse", ?address, "Removing account");
648            self.remove_account_leaf(&nibbles)
649        } else {
650            trace!(target: "trie::sparse", ?address, "Updating account");
651            self.account_rlp_buf.clear();
652            account.into_trie_account(storage_root).encode(&mut self.account_rlp_buf);
653            self.update_account_leaf(nibbles, self.account_rlp_buf.clone())
654        }
655    }
656
657    /// Update the storage root of a revealed account.
658    ///
659    /// If the account doesn't exist in the trie, the function is a no-op.
660    ///
661    /// If the new storage root is empty, and the account info was already empty, the account leaf
662    /// will be removed.
663    pub fn update_account_storage_root(&mut self, address: B256) -> SparseStateTrieResult<()> {
664        if !self.is_account_revealed(address) {
665            return Err(SparseTrieErrorKind::Blind.into())
666        }
667
668        // Nothing to update if the account doesn't exist in the trie.
669        let Some(mut trie_account) = self
670            .get_account_value(&address)
671            .map(|v| TrieAccount::decode(&mut &v[..]))
672            .transpose()?
673        else {
674            trace!(target: "trie::sparse", ?address, "Account not found in trie, skipping storage root update");
675            return Ok(())
676        };
677
678        // Calculate the new storage root. If the storage trie doesn't exist, the storage root will
679        // be empty.
680        let storage_root = if let Some(storage_trie) = self.storages.get_mut(&address) {
681            trace!(target: "trie::sparse", ?address, "Calculating storage root to update account");
682            storage_trie.root().ok_or(SparseTrieErrorKind::Blind)?
683        } else {
684            EMPTY_ROOT_HASH
685        };
686
687        // Update the account with the new storage root.
688        trie_account.storage_root = storage_root;
689
690        let nibbles = Nibbles::unpack(address);
691        if trie_account == TrieAccount::default() {
692            // If the account is empty, remove it.
693            trace!(target: "trie::sparse", ?address, "Removing account because the storage root is empty");
694            self.remove_account_leaf(&nibbles)?;
695        } else {
696            // Otherwise, update the account leaf.
697            trace!(target: "trie::sparse", ?address, "Updating account with the new storage root");
698            self.account_rlp_buf.clear();
699            trie_account.encode(&mut self.account_rlp_buf);
700            self.update_account_leaf(nibbles, self.account_rlp_buf.clone())?;
701        }
702
703        Ok(())
704    }
705
706    /// Remove the account leaf node.
707    pub fn remove_account_leaf(&mut self, path: &Nibbles) -> SparseStateTrieResult<()> {
708        self.state.remove_leaf(path)?;
709        Ok(())
710    }
711
712    /// Update the leaf node of a storage trie at the provided address.
713    pub fn remove_storage_leaf(
714        &mut self,
715        address: B256,
716        slot: &Nibbles,
717    ) -> SparseStateTrieResult<()> {
718        let storage_trie = self.storages.get_mut(&address).ok_or(SparseTrieErrorKind::Blind)?;
719        storage_trie.remove_leaf(slot)?;
720        Ok(())
721    }
722}
723
724#[cfg(test)]
725mod tests {
726    use super::*;
727    use alloy_primitives::{
728        b256,
729        map::{HashMap, HashSet},
730        Bytes, U256,
731    };
732    use alloy_rlp::EMPTY_STRING_CODE;
733    use arbitrary::Arbitrary;
734    use assert_matches::assert_matches;
735    use rand::{rngs::StdRng, Rng, SeedableRng};
736    use reth_primitives_traits::Account;
737    use reth_trie::{updates::StorageTrieUpdates, HashBuilder, EMPTY_ROOT_HASH};
738    use reth_trie_common::{
739        proof::{ProofNodes, ProofRetainer},
740        BranchNode, LeafNode, StorageMultiProof, TrieMask,
741    };
742
743    #[test]
744    fn validate_root_node_first_node_not_root() {
745        let sparse = SparseStateTrie::default();
746        let proof = [(Nibbles::from_nibbles([0x1]), Bytes::from([EMPTY_STRING_CODE]))];
747        assert_matches!(
748            sparse.validate_root_node(&mut proof.into_iter().peekable()).map_err(|e| e.into_kind()),
749            Err(SparseStateTrieErrorKind::InvalidRootNode { .. })
750        );
751    }
752
753    #[test]
754    fn validate_root_node_invalid_proof_with_empty_root() {
755        let sparse = SparseStateTrie::default();
756        let proof = [
757            (Nibbles::default(), Bytes::from([EMPTY_STRING_CODE])),
758            (Nibbles::from_nibbles([0x1]), Bytes::new()),
759        ];
760        assert_matches!(
761            sparse.validate_root_node(&mut proof.into_iter().peekable()).map_err(|e| e.into_kind()),
762            Err(SparseStateTrieErrorKind::InvalidRootNode { .. })
763        );
764    }
765
766    #[test]
767    fn reveal_account_empty() {
768        let retainer = ProofRetainer::from_iter([Nibbles::default()]);
769        let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
770        hash_builder.root();
771        let proofs = hash_builder.take_proof_nodes();
772        assert_eq!(proofs.len(), 1);
773
774        let mut sparse = SparseStateTrie::default();
775        assert_eq!(sparse.state, SparseTrie::Blind);
776
777        sparse.reveal_account(Default::default(), proofs.into_inner()).unwrap();
778        assert_eq!(sparse.state, SparseTrie::revealed_empty());
779    }
780
781    #[test]
782    fn reveal_storage_slot_empty() {
783        let retainer = ProofRetainer::from_iter([Nibbles::default()]);
784        let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer);
785        hash_builder.root();
786        let proofs = hash_builder.take_proof_nodes();
787        assert_eq!(proofs.len(), 1);
788
789        let mut sparse = SparseStateTrie::default();
790        assert!(sparse.storages.is_empty());
791
792        sparse
793            .reveal_storage_slot(Default::default(), Default::default(), proofs.into_inner())
794            .unwrap();
795        assert_eq!(
796            sparse.storages,
797            HashMap::from_iter([(Default::default(), SparseTrie::revealed_empty())])
798        );
799    }
800
801    #[test]
802    fn reveal_account_path_twice() {
803        let mut sparse = SparseStateTrie::default();
804
805        let leaf_value = alloy_rlp::encode(TrieAccount::default());
806        let leaf_1 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
807            Nibbles::default(),
808            leaf_value.clone(),
809        )));
810        let leaf_2 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
811            Nibbles::default(),
812            leaf_value.clone(),
813        )));
814
815        let multiproof = MultiProof {
816            account_subtree: ProofNodes::from_iter([
817                (
818                    Nibbles::default(),
819                    alloy_rlp::encode(TrieNode::Branch(BranchNode {
820                        stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
821                        state_mask: TrieMask::new(0b11),
822                    }))
823                    .into(),
824                ),
825                (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
826                (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
827            ]),
828            ..Default::default()
829        };
830
831        // Reveal multiproof and check that the state trie contains the leaf node and value
832        sparse.reveal_multiproof(multiproof.clone()).unwrap();
833        assert!(sparse
834            .state_trie_ref()
835            .unwrap()
836            .nodes_ref()
837            .contains_key(&Nibbles::from_nibbles([0x0])),);
838        assert_eq!(
839            sparse.state_trie_ref().unwrap().get_leaf_value(&Nibbles::from_nibbles([0x0])),
840            Some(&leaf_value)
841        );
842
843        // Remove the leaf node and check that the state trie does not contain the leaf node and
844        // value
845        sparse.remove_account_leaf(&Nibbles::from_nibbles([0x0])).unwrap();
846        assert!(!sparse
847            .state_trie_ref()
848            .unwrap()
849            .nodes_ref()
850            .contains_key(&Nibbles::from_nibbles([0x0])),);
851        assert!(sparse
852            .state_trie_ref()
853            .unwrap()
854            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
855            .is_none());
856
857        // Reveal multiproof again and check that the state trie still does not contain the leaf
858        // node and value, because they were already revealed before
859        sparse.reveal_multiproof(multiproof).unwrap();
860        assert!(!sparse
861            .state_trie_ref()
862            .unwrap()
863            .nodes_ref()
864            .contains_key(&Nibbles::from_nibbles([0x0])));
865        assert!(sparse
866            .state_trie_ref()
867            .unwrap()
868            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
869            .is_none());
870    }
871
872    #[test]
873    fn reveal_storage_path_twice() {
874        let mut sparse = SparseStateTrie::default();
875
876        let leaf_value = alloy_rlp::encode(TrieAccount::default());
877        let leaf_1 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
878            Nibbles::default(),
879            leaf_value.clone(),
880        )));
881        let leaf_2 = alloy_rlp::encode(TrieNode::Leaf(LeafNode::new(
882            Nibbles::default(),
883            leaf_value.clone(),
884        )));
885
886        let multiproof = MultiProof {
887            storages: HashMap::from_iter([(
888                B256::ZERO,
889                StorageMultiProof {
890                    root: B256::ZERO,
891                    subtree: ProofNodes::from_iter([
892                        (
893                            Nibbles::default(),
894                            alloy_rlp::encode(TrieNode::Branch(BranchNode {
895                                stack: vec![RlpNode::from_rlp(&leaf_1), RlpNode::from_rlp(&leaf_2)],
896                                state_mask: TrieMask::new(0b11),
897                            }))
898                            .into(),
899                        ),
900                        (Nibbles::from_nibbles([0x0]), leaf_1.clone().into()),
901                        (Nibbles::from_nibbles([0x1]), leaf_1.clone().into()),
902                    ]),
903                    branch_node_hash_masks: Default::default(),
904                    branch_node_tree_masks: Default::default(),
905                },
906            )]),
907            ..Default::default()
908        };
909
910        // Reveal multiproof and check that the storage trie contains the leaf node and value
911        sparse.reveal_multiproof(multiproof.clone()).unwrap();
912        assert!(sparse
913            .storage_trie_ref(&B256::ZERO)
914            .unwrap()
915            .nodes_ref()
916            .contains_key(&Nibbles::from_nibbles([0x0])),);
917        assert_eq!(
918            sparse
919                .storage_trie_ref(&B256::ZERO)
920                .unwrap()
921                .get_leaf_value(&Nibbles::from_nibbles([0x0])),
922            Some(&leaf_value)
923        );
924
925        // Remove the leaf node and check that the storage trie does not contain the leaf node and
926        // value
927        sparse.remove_storage_leaf(B256::ZERO, &Nibbles::from_nibbles([0x0])).unwrap();
928        assert!(!sparse
929            .storage_trie_ref(&B256::ZERO)
930            .unwrap()
931            .nodes_ref()
932            .contains_key(&Nibbles::from_nibbles([0x0])),);
933        assert!(sparse
934            .storage_trie_ref(&B256::ZERO)
935            .unwrap()
936            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
937            .is_none());
938
939        // Reveal multiproof again and check that the storage trie still does not contain the leaf
940        // node and value, because they were already revealed before
941        sparse.reveal_multiproof(multiproof).unwrap();
942        assert!(!sparse
943            .storage_trie_ref(&B256::ZERO)
944            .unwrap()
945            .nodes_ref()
946            .contains_key(&Nibbles::from_nibbles([0x0])));
947        assert!(sparse
948            .storage_trie_ref(&B256::ZERO)
949            .unwrap()
950            .get_leaf_value(&Nibbles::from_nibbles([0x0]))
951            .is_none());
952    }
953
954    #[test]
955    fn take_trie_updates() {
956        reth_tracing::init_test_tracing();
957
958        // let mut rng = generators::rng();
959        let mut rng = StdRng::seed_from_u64(1);
960
961        let mut bytes = [0u8; 1024];
962        rng.fill(bytes.as_mut_slice());
963
964        let slot_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
965        let slot_path_1 = Nibbles::unpack(slot_1);
966        let value_1 = U256::from(rng.gen::<u64>());
967        let slot_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
968        let slot_path_2 = Nibbles::unpack(slot_2);
969        let value_2 = U256::from(rng.gen::<u64>());
970        let slot_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
971        let slot_path_3 = Nibbles::unpack(slot_3);
972        let value_3 = U256::from(rng.gen::<u64>());
973
974        let mut storage_hash_builder =
975            HashBuilder::default().with_proof_retainer(ProofRetainer::from_iter([
976                slot_path_1.clone(),
977                slot_path_2.clone(),
978            ]));
979        storage_hash_builder.add_leaf(slot_path_1, &alloy_rlp::encode_fixed_size(&value_1));
980        storage_hash_builder.add_leaf(slot_path_2, &alloy_rlp::encode_fixed_size(&value_2));
981
982        let storage_root = storage_hash_builder.root();
983        let storage_proof_nodes = storage_hash_builder.take_proof_nodes();
984        let storage_branch_node_hash_masks = HashMap::from_iter([
985            (Nibbles::default(), TrieMask::new(0b010)),
986            (Nibbles::from_nibbles([0x1]), TrieMask::new(0b11)),
987        ]);
988
989        let address_1 = b256!("0x1000000000000000000000000000000000000000000000000000000000000000");
990        let address_path_1 = Nibbles::unpack(address_1);
991        let account_1 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
992        let mut trie_account_1 = account_1.into_trie_account(storage_root);
993        let address_2 = b256!("0x1100000000000000000000000000000000000000000000000000000000000000");
994        let address_path_2 = Nibbles::unpack(address_2);
995        let account_2 = Account::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
996        let mut trie_account_2 = account_2.into_trie_account(EMPTY_ROOT_HASH);
997
998        let mut hash_builder =
999            HashBuilder::default().with_proof_retainer(ProofRetainer::from_iter([
1000                address_path_1.clone(),
1001                address_path_2.clone(),
1002            ]));
1003        hash_builder.add_leaf(address_path_1.clone(), &alloy_rlp::encode(trie_account_1));
1004        hash_builder.add_leaf(address_path_2.clone(), &alloy_rlp::encode(trie_account_2));
1005
1006        let root = hash_builder.root();
1007        let proof_nodes = hash_builder.take_proof_nodes();
1008
1009        let mut sparse = SparseStateTrie::default().with_updates(true);
1010        sparse
1011            .reveal_multiproof(MultiProof {
1012                account_subtree: proof_nodes,
1013                branch_node_hash_masks: HashMap::from_iter([(
1014                    Nibbles::from_nibbles([0x1]),
1015                    TrieMask::new(0b00),
1016                )]),
1017                branch_node_tree_masks: HashMap::default(),
1018                storages: HashMap::from_iter([
1019                    (
1020                        address_1,
1021                        StorageMultiProof {
1022                            root,
1023                            subtree: storage_proof_nodes.clone(),
1024                            branch_node_hash_masks: storage_branch_node_hash_masks.clone(),
1025                            branch_node_tree_masks: HashMap::default(),
1026                        },
1027                    ),
1028                    (
1029                        address_2,
1030                        StorageMultiProof {
1031                            root,
1032                            subtree: storage_proof_nodes,
1033                            branch_node_hash_masks: storage_branch_node_hash_masks,
1034                            branch_node_tree_masks: HashMap::default(),
1035                        },
1036                    ),
1037                ]),
1038            })
1039            .unwrap();
1040
1041        assert_eq!(sparse.root().unwrap(), root);
1042
1043        let address_3 = b256!("0x2000000000000000000000000000000000000000000000000000000000000000");
1044        let address_path_3 = Nibbles::unpack(address_3);
1045        let account_3 = Account { nonce: account_1.nonce + 1, ..account_1 };
1046        let trie_account_3 = account_3.into_trie_account(EMPTY_ROOT_HASH);
1047
1048        sparse.update_account_leaf(address_path_3, alloy_rlp::encode(trie_account_3)).unwrap();
1049
1050        sparse.update_storage_leaf(address_1, slot_path_3, alloy_rlp::encode(value_3)).unwrap();
1051        trie_account_1.storage_root = sparse.storage_root(address_1).unwrap();
1052        sparse.update_account_leaf(address_path_1, alloy_rlp::encode(trie_account_1)).unwrap();
1053
1054        sparse.wipe_storage(address_2).unwrap();
1055        trie_account_2.storage_root = sparse.storage_root(address_2).unwrap();
1056        sparse.update_account_leaf(address_path_2, alloy_rlp::encode(trie_account_2)).unwrap();
1057
1058        sparse.root().unwrap();
1059
1060        let sparse_updates = sparse.take_trie_updates().unwrap();
1061        // TODO(alexey): assert against real state root calculation updates
1062        pretty_assertions::assert_eq!(
1063            sparse_updates,
1064            TrieUpdates {
1065                account_nodes: HashMap::default(),
1066                storage_tries: HashMap::from_iter([(
1067                    b256!("0x1100000000000000000000000000000000000000000000000000000000000000"),
1068                    StorageTrieUpdates {
1069                        is_deleted: true,
1070                        storage_nodes: HashMap::default(),
1071                        removed_nodes: HashSet::default()
1072                    }
1073                )]),
1074                removed_nodes: HashSet::default()
1075            }
1076        );
1077    }
1078}